Merge autoland to mozilla-central. a=merge

This commit is contained in:
Marian-Vasile Laza 2022-04-27 12:35:44 +03:00
commit 2e328e8040
224 changed files with 3537 additions and 2513 deletions

View file

@ -191,7 +191,6 @@ module.exports = {
"modules/libjar/zipwriter/test/unit/test_bug419769_2.js",
"modules/libjar/zipwriter/test/unit/test_storedata.js",
"modules/libjar/zipwriter/test/unit/test_zippermissions.js",
"modules/libpref/test/unit/test_changeType.js",
"modules/libpref/test/unit/test_dirtyPrefs.js",
"toolkit/crashreporter/test/unit/test_crash_AsyncShutdown.js",
"toolkit/mozapps/update/tests/unit_aus_update/testConstants.js",

View file

@ -11,6 +11,10 @@
#include "mozilla/Components.h"
#include "nsIStringBundle.h"
#ifdef A11Y_LOG
# include "nsAccessibilityService.h"
#endif
using namespace mozilla;
using namespace mozilla::a11y;
@ -294,6 +298,38 @@ void Accessible::GetPositionAndSetSize(int32_t* aPosInSet, int32_t* aSetSize) {
}
}
#ifdef A11Y_LOG
void Accessible::DebugDescription(nsCString& aDesc) {
aDesc.Truncate();
aDesc.AppendPrintf("[%p] ", this);
nsAutoString role;
GetAccService()->GetStringRole(Role(), role);
aDesc.Append(NS_ConvertUTF16toUTF8(role));
if (nsAtom* tagAtom = TagName()) {
nsAutoCString tag;
tagAtom->ToUTF8String(tag);
aDesc.AppendPrintf(" %s", tag.get());
nsAutoString id;
DOMNodeID(id);
if (!id.IsEmpty()) {
aDesc.Append("#");
aDesc.Append(NS_ConvertUTF16toUTF8(id));
}
}
nsAutoString id;
nsAutoString name;
Name(name);
if (!name.IsEmpty()) {
aDesc.Append(" '");
aDesc.Append(NS_ConvertUTF16toUTF8(name));
aDesc.Append("'");
}
}
#endif
void Accessible::TranslateString(const nsString& aKey, nsAString& aStringOut) {
nsCOMPtr<nsIStringBundleService> stringBundleService =
components::StringBundle::Service();

View file

@ -453,6 +453,14 @@ class Accessible {
virtual TableAccessibleBase* AsTableBase() { return nullptr; }
virtual TableCellAccessibleBase* AsTableCellBase() { return nullptr; }
#ifdef A11Y_LOG
/**
* Provide a human readable description of the accessible,
* including memory address, role, name, DOM tag and DOM ID.
*/
void DebugDescription(nsCString& aDesc);
#endif
/**
* Return the localized string for the given key.
*/

View file

@ -641,6 +641,11 @@ already_AddRefed<AccAttributes> RemoteAccessibleBase<Derived>::Attributes() {
}
}
nsAutoString name;
if (Name(name) != eNameFromSubtree && !name.IsVoid()) {
attributes->SetAttribute(nsGkAtoms::explicit_name, true);
}
return attributes.forget();
}

View file

@ -685,6 +685,10 @@ void RemoteAccessible::TableUnselectRow(uint32_t aRow) {
}
bool RemoteAccessible::TableIsProbablyForLayout() {
if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
return RemoteAccessibleBase<RemoteAccessible>::TableIsProbablyForLayout();
}
bool forLayout = false;
Unused << mDoc->SendTableIsProbablyForLayout(mID, &forLayout);
return forLayout;

View file

@ -231,3 +231,31 @@ addAccessibleTask(
},
{ chrome: true, topLevel: true, iframe: true, remoteIframe: true }
);
/**
* Test caching of the explicit-name attribute.
*/
addAccessibleTask(
`
<h1 id="h1">content</h1>
<button id="buttonContent">content</button>
<button id="buttonLabel" aria-label="label">content</button>
`,
async function(browser, docAcc) {
const h1 = findAccessibleChildByID(docAcc, "h1");
testAbsentAttrs(h1, { "explicit-name": "" });
const buttonContent = findAccessibleChildByID(docAcc, "buttonContent");
testAbsentAttrs(buttonContent, { "explicit-name": "" });
const buttonLabel = findAccessibleChildByID(docAcc, "buttonLabel");
testAttrs(buttonLabel, { "explicit-name": "true" }, true);
info("Setting aria-label on h1");
let nameChanged = waitForEvent(EVENT_NAME_CHANGE, h1);
await invokeContentTask(browser, [], () => {
content.document.getElementById("h1").setAttribute("aria-label", "label");
});
await nameChanged;
testAttrs(h1, { "explicit-name": "true" }, true);
},
{ chrome: true, topLevel: true, iframe: true, remoteIframe: true }
);

View file

@ -94,6 +94,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
TelemetryUtils: "resource://gre/modules/TelemetryUtils.jsm",
TRRRacer: "resource:///modules/TRRPerformance.jsm",
UIState: "resource://services-sync/UIState.jsm",
UpdateListener: "resource://gre/modules/UpdateListener.jsm",
UrlbarQuickSuggest: "resource:///modules/UrlbarQuickSuggest.jsm",
UrlbarPrefs: "resource:///modules/UrlbarPrefs.jsm",
WebChannel: "resource://gre/modules/WebChannel.jsm",
@ -106,23 +107,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
PluginManager: "resource:///actors/PluginParent.jsm",
});
// Modules requiring an initialization method call.
let initializedModules = {};
[
[
"ContentPrefServiceParent",
"resource://gre/modules/ContentPrefServiceParent.jsm",
"alwaysInit",
],
["UpdateListener", "resource://gre/modules/UpdateListener.jsm", "init"],
].forEach(([name, resource, init]) => {
XPCOMUtils.defineLazyGetter(this, name, () => {
ChromeUtils.import(resource, initializedModules);
initializedModules[name][init]();
return initializedModules[name];
});
});
XPCOMUtils.defineLazyServiceGetters(this, {
BrowserHandler: ["@mozilla.org/browser/clh;1", "nsIBrowserHandler"],
PushService: ["@mozilla.org/push/Service;1", "nsIPushService"],
@ -1979,14 +1963,9 @@ BrowserGlue.prototype = {
() => Normandy.uninit(),
() => RFPHelper.uninit(),
() => ASRouterNewTabHook.destroy(),
() => UpdateListener.reset(),
];
tasks.push(
...Object.values(initializedModules)
.filter(m => m.uninit)
.map(m => () => m.uninit())
);
for (let task of tasks) {
try {
task();
@ -2745,6 +2724,12 @@ BrowserGlue.prototype = {
},
},
{
task: () => {
UpdateListener.maybeShowUnsupportedNotification();
},
},
// WebDriver components (Marionette) need to be
// initialized as very last step.
{

View file

@ -45,6 +45,13 @@ XPCOMUtils.defineLazyGetter(this, "log", () => {
});
XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
// This maximum length was originally based on how much space we have in the PE
// file header that we store attribution codes in for full and stub installers.
// Windows Store builds instead use a "Campaign ID" passed through URLs to send
// attribution information, which Microsoft's documentation claims must be no
// longer than 100 characters. In our own testing, we've been able to retrieve
// the first 208 characters of the Campaign ID. Either way, the "max" length
// for Microsoft Store builds is much lower than this limit implies.
const ATTR_CODE_MAX_LENGTH = 1010;
const ATTR_CODE_VALUE_REGEX = /[a-zA-Z0-9_%\\-\\.\\(\\)]*/;
const ATTR_CODE_FIELD_SEPARATOR = "%26"; // URL-encoded &
@ -58,6 +65,7 @@ const ATTR_CODE_KEYS = [
"variation",
"ua",
"dltoken",
"msstoresignedin",
];
let gCachedAttrData = null;
@ -147,7 +155,17 @@ var AttributionCode = {
let [key, value] = param.split(ATTR_CODE_KEY_VALUE_SEPARATOR, 2);
if (key && ATTR_CODE_KEYS.includes(key)) {
if (value && ATTR_CODE_VALUE_REGEX.test(value)) {
parsed[key] = value;
if (key === "msstoresignedin") {
if (value === "true") {
parsed[key] = true;
} else if (value === "false") {
parsed[key] = false;
} else {
throw new Error("Couldn't parse msstoresignedin");
}
} else {
parsed[key] = value;
}
}
} else {
log.debug(
@ -314,7 +332,29 @@ var AttributionCode = {
let bytes;
try {
bytes = await AttributionIOUtils.read(attributionFile.path);
if (
AppConstants.platform === "win" &&
Services.sysinfo.getProperty("hasWinPackageId")
) {
// This comes out of windows-package-manager _not_ URL encoded or in an ArrayBuffer,
// but the parsing code wants it that way. It's easier to just provide that
// than have the parsing code support both.
log.debug(
`winPackageFamilyName is: ${Services.sysinfo.getProperty(
"winPackageFamilyName"
)}`
);
let encoder = new TextEncoder();
bytes = encoder.encode(
encodeURIComponent(
Cc["@mozilla.org/windows-package-manager;1"]
.createInstance(Ci.nsIWindowsPackageManager)
.getCampaignId()
)
);
} else {
bytes = await AttributionIOUtils.read(attributionFile.path);
}
} catch (ex) {
if (DOMException.isInstance(ex) && ex.name == "NotFoundError") {
log.debug(
@ -324,6 +364,13 @@ var AttributionCode = {
);
return gCachedAttrData;
}
log.debug(
`other error trying to read attribution data:
attributionFile.path is: ${attributionFile.path}`
);
log.debug("Full exception is:");
log.debug(ex);
Services.telemetry
.getHistogramById("BROWSER_ATTRIBUTION_ERRORS")
.add("read_error");
@ -333,7 +380,7 @@ var AttributionCode = {
let decoder = new TextDecoder();
let code = decoder.decode(bytes);
log.debug(
`getAttrDataAsync: ${attributionFile.path} deserializes to ${code}`
`getAttrDataAsync: attribution bytes deserializes to ${code}`
);
if (AppConstants.platform == "macosx" && !code) {
// On macOS, an empty attribution code is fine. (On Windows, that

View file

@ -0,0 +1,26 @@
========================
Installation Attribution
========================
---------------
Microsoft Store
---------------
Firefox installs done through the Microsoft Store support extracting campaign IDs that may be embedded into them. This allows us to attribute installs through different channels by providing particular links to the Microsoft Store with attribution data included. For example:
`ms-windows-store://pdp/?productid=9NZVDKPMR9RD&cid=source%3Dgoogle.com%26medium%3Dorganic%26campaign%3D(not%20set)%26content%3D(not%20set) <ms-windows-store://pdp/?productid=9NZVDKPMR9RD&cid=source%3Dgoogle.com%26medium%3Dorganic%26campaign%3D(not%20set)%26content%3D(not%20set)>`_
`https://www.microsoft.com/store/apps/9NZVDKPMR9RD?cid=source%3Dgoogle.com%26medium%3Dorganic%26campaign%3D(not%20set)%26content%3D(not%20set) <https://www.microsoft.com/store/apps/9NZVDKPMR9RD?cid=source%3Dgoogle.com%26medium%3Dorganic%26campaign%3D(not%20set)%26content%3D(not%20set)>`_
For more on how custom campaign IDs work in general in the Microsoft Store environment, `see Microsoft's documentation <https://docs.microsoft.com/en-us/windows/uwp/publish/create-a-custom-app-promotion-campaign>`_.
The Microsoft Store provides a single `cid` (Campaign ID). Their documentation claims it is limited to 100 characters, although in our own testing we've been able to retrieve the first 208 characters of Campaign IDs. Firefox expects this Campaign ID to follow the same format as stub and full installer attribution codes, which have a maximum of length of 1010 characters. Since Campaign IDs are more limited than what Firefox allows, we need to be a bit more thoughtful about what we include in them vs. stub and full installer attribution. At the time of writing, we've yet to be able to test whether we can reliably pull more than the advertised 100 characters of a Campaign ID in the real world -- something that we should do before we send any crucial information past the first 100 characters.
In addition to the attribution data retrieved through the campaign ID, we also add an extra key to it to indicate whether or not the user was signed into the Microsoft Store when they installed. This `msstoresignedin` key can have a value of `true` or `false`.
There are a couple of other caveats to keep in mind:
* A campaign ID is only set the *first* time a user installs Firefox through the Store. Subsequent installs will inherit the original campaign ID (even if it was an empty string). This means that only brand new installs will be attributed -- not reinstalls.
* At the time of writing, it is not clear whether or not installs done without being signed into the Microsoft Store will be able to find their campaign ID. Microsoft's documentation claims they can, but our own testing has not been able to verify this.

View file

@ -15,6 +15,8 @@ EXTRA_JS_MODULES += [
"AttributionCode.jsm",
]
SPHINX_TREES["docs"] = "docs"
if CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa":
XPIDL_SOURCES += [
"nsIMacAttribution.idl",

View file

@ -18,6 +18,18 @@ let validAttrCodes = [
content: "(not%20set)",
},
},
{
code:
"source%3Dgoogle.com%26medium%3Dorganic%26campaign%3D(not%20set)%26content%3D(not%20set)%26msstoresignedin%3Dtrue",
parsed: {
source: "google.com",
medium: "organic",
campaign: "(not%20set)",
content: "(not%20set)",
msstoresignedin: true,
},
platforms: ["win"],
},
{
code: "source%3Dgoogle.com%26medium%3Dorganic%26campaign%3D%26content%3D",
parsed: { source: "google.com", medium: "organic" },

View file

@ -19,6 +19,10 @@ add_task(async function testValidAttrCodes() {
);
for (let entry of validAttrCodes) {
if (entry.platforms && !entry.platforms.includes("mac")) {
continue;
}
// Set a url referrer. In the macOS quarantine database, the
// referrer URL has components that areURI-encoded. Our test data
// URI-encodes the components and also the separators (?, &, =).

View file

@ -19,7 +19,11 @@ XPCOMUtils.defineLazyModuleGetters(this, {
const DEFAULT_WELCOME_CONTENT = {
id: "DEFAULT_ABOUTWELCOME_PROTON",
template: "multistage",
transitions: true,
// Allow tests to easily disable transitions.
transitions: Services.prefs.getBoolPref(
"browser.aboutwelcome.transitions",
true
),
backdrop:
"#F9F9FB url('chrome://activity-stream/content/data/content/assets/fx100-noodles.svg') center/cover no-repeat fixed",
screens: [

View file

@ -210,7 +210,7 @@
"description": "Show transitions within and between screens"
}
},
"additionalProperties": false,
"additionalProperties": true,
"if": {
"properties": {
"logoImageURL": { "type": "null" }

View file

@ -418,6 +418,7 @@ const MESSAGES = () => [
groups: ["panel-test-provider"],
template: "spotlight",
content: {
id: "control",
template: "multistage",
backdrop: "transparent",
transitions: true,

View file

@ -39,10 +39,11 @@ async function spyOnTelemetryButtonClicks(browser) {
}
async function openAboutWelcome() {
await pushPrefs([
"intl.multilingual.aboutWelcome.languageMismatchEnabled",
true,
]);
await pushPrefs(
// Speed up the tests by disabling transitions.
["browser.aboutwelcome.transitions", false],
["intl.multilingual.aboutWelcome.languageMismatchEnabled", true]
);
await setAboutWelcomePref(true);
// Stub out the doesAppNeedPin to false so the about:welcome pages do not attempt
@ -81,7 +82,12 @@ async function clickVisibleButton(browser, selector) {
return null;
}
await ContentTaskUtils.waitForCondition(getVisibleElement, selector);
await ContentTaskUtils.waitForCondition(
getVisibleElement,
selector,
200, // interval
100 // maxTries
);
getVisibleElement().click();
});
}

View file

@ -14,6 +14,8 @@ add_task(async function test_setup() {
// state opens a new tab for signing in.
// We'll clean those up here for now.
gBrowser.removeAllTabsBut(gBrowser.tabs[0]);
// Stop the load in the last tab that remains.
gBrowser.stop();
Services.prefs.clearUserPref("identity.fxaccounts.toolbar.accessed");
});
});

View file

@ -21,14 +21,11 @@ support-files =
[browser_privatebrowsing_DownloadLastDirWithCPS.js]
[browser_privatebrowsing_about_nimbus.js]
[browser_privatebrowsing_about_messaging.js]
skip-if =
verify # This was already broken on mozilla-central, skipping for now
# to prevent it from causing issues with people who want to use
# verify with the whole directory.
# https://bugzilla.mozilla.org/show_bug.cgi?id=1754536
[browser_privatebrowsing_about_nimbus_messaging.js]
[browser_privatebrowsing_about_nimbus_impressions.js]
[browser_privatebrowsing_about_nimbus_dismiss.js]
[browser_privatebrowsing_about.js]
skip-if =
skip-if =
verify
os == 'linux' || os == 'win' # Bug 1700527
tags = trackingprotection

View file

@ -1,238 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const { ExperimentFakes } = ChromeUtils.import(
"resource://testing-common/NimbusTestUtils.jsm"
);
const { TelemetryTestUtils } = ChromeUtils.import(
"resource://testing-common/TelemetryTestUtils.jsm"
);
const { ASRouter } = ChromeUtils.import(
"resource://activity-stream/lib/ASRouter.jsm"
);
async function setupMSExperimentWithMessage(message) {
let doExperimentCleanup = await ExperimentFakes.enrollWithFeatureConfig({
featureId: "pbNewtab",
enabled: true,
value: message,
});
Services.prefs.setStringPref(
"browser.newtabpage.activity-stream.asrouter.providers.messaging-experiments",
'{"id":"messaging-experiments","enabled":true,"type":"remote-experiments","messageGroups":["pbNewtab"],"updateCycleInMs":0}'
);
// Reload the provider
await ASRouter._updateMessageProviders();
// Wait to load the messages from the messaging-experiments provider
await ASRouter.loadMessagesFromAllProviders();
registerCleanupFunction(async () => {
// Reload the provider again at cleanup to remove the experiment message
await ASRouter._updateMessageProviders();
// Wait to load the messages from the messaging-experiments provider
await ASRouter.loadMessagesFromAllProviders();
Services.prefs.clearUserPref(
"browser.newtabpage.activity-stream.asrouter.providers.messaging-experiments"
);
});
Assert.ok(
ASRouter.state.messages.find(m => m.id.includes(message.id)),
"Experiment message found in ASRouter state"
);
return doExperimentCleanup;
}
add_setup(async function() {
let { win } = await openAboutPrivateBrowsing();
await BrowserTestUtils.closeWindow(win);
});
add_task(async function test_experiment_messaging_system() {
const LOCALE = Services.locale.appLocaleAsBCP47;
let doExperimentCleanup = await setupMSExperimentWithMessage({
id: "PB_NEWTAB_MESSAGING_SYSTEM",
template: "pb_newtab",
content: {
promoEnabled: true,
infoEnabled: true,
promoHeader: "fluent:about-private-browsing-focus-promo-header-c",
infoBody: "fluent:about-private-browsing-info-title",
promoLinkText: "fluent:about-private-browsing-prominent-cta",
infoLinkUrl: "http://foo.example.com/%LOCALE%",
promoLinkUrl: "http://bar.example.com/%LOCALE%",
},
// Priority ensures this message is picked over the one in
// OnboardingMessageProvider
priority: 5,
targeting: "true",
});
Services.telemetry.clearEvents();
let { win, tab } = await openTabAndWaitForRender();
await SpecialPowers.spawn(tab, [LOCALE], async function(locale) {
const infoBody = content.document.getElementById("info-body");
const promoHeader = content.document.getElementById("promo-header");
const promoLink = content.document.getElementById(
"private-browsing-vpn-link"
);
// Check experiment values are rendered
is(
promoHeader.textContent,
"Next-level privacy on mobile",
"should render promo header with fluent"
);
is(
infoBody.textContent,
"Youre in a Private Window",
"should render infoBody with fluent"
);
is(
promoLink.textContent,
"Stay private with Mozilla VPN",
"should render promoLinkText with fluent"
);
is(
content.document.querySelector(".info a").getAttribute("href"),
"http://foo.example.com/" + locale,
"should format the infoLinkUrl url"
);
is(
content.document.querySelector(".info a").getAttribute("target"),
"_blank",
"should open info url in new tab"
);
is(
content.document.querySelector(".promo button").getAttribute("target"),
"_blank",
"should open promo url in new tab"
);
});
// There's something buggy here, disabling for now to prevent intermittent failures
// until we fix it in bug 1754536.
//
// TelemetryTestUtils.assertEvents(
// [
// {
//
// method: "expose",
// extra: {
// featureId: "pbNewtab",
// },
// },
// ],
// { category: "normandy" }
// );
await BrowserTestUtils.closeWindow(win);
await doExperimentCleanup();
});
add_task(async function test_experiment_messaging_system_impressions() {
const LOCALE = Services.locale.appLocaleAsBCP47;
let doExperimentCleanup = await setupMSExperimentWithMessage({
id: `PB_NEWTAB_MESSAGING_SYSTEM_${Math.random()}`,
template: "pb_newtab",
content: {
promoEnabled: true,
infoEnabled: true,
infoBody: "fluent:about-private-browsing-info-title",
promoLinkText: "fluent:about-private-browsing-prominent-cta",
infoLinkUrl: "http://foo.example.com/%LOCALE%",
promoLinkUrl: "http://bar.example.com/%LOCALE%",
},
frequency: {
lifetime: 2,
},
// Priority ensures this message is picked over the one in
// OnboardingMessageProvider
priority: 5,
targeting: "true",
});
let { win: win1, tab: tab1 } = await openTabAndWaitForRender();
await SpecialPowers.spawn(tab1, [LOCALE], async function(locale) {
is(
content.document.querySelector(".promo button").getAttribute("href"),
"http://bar.example.com/" + locale,
"should format the promoLinkUrl url"
);
});
let { win: win2, tab: tab2 } = await openTabAndWaitForRender();
await SpecialPowers.spawn(tab2, [LOCALE], async function(locale) {
is(
content.document.querySelector(".promo button").getAttribute("href"),
"http://bar.example.com/" + locale,
"should format the promoLinkUrl url"
);
});
let { win: win3, tab: tab3 } = await openTabAndWaitForRender();
await SpecialPowers.spawn(tab3, [], async function() {
is(
content.document.querySelector(".promo button"),
null,
"should no longer render the experiment message after 2 impressions"
);
});
await BrowserTestUtils.closeWindow(win1);
await BrowserTestUtils.closeWindow(win2);
await BrowserTestUtils.closeWindow(win3);
await doExperimentCleanup();
});
add_task(async function test_experiment_messaging_system_dismiss() {
const LOCALE = Services.locale.appLocaleAsBCP47;
let doExperimentCleanup = await setupMSExperimentWithMessage({
id: `PB_NEWTAB_MESSAGING_SYSTEM_${Math.random()}`,
template: "pb_newtab",
content: {
promoEnabled: true,
infoEnabled: true,
infoBody: "fluent:about-private-browsing-info-title",
promoLinkText: "fluent:about-private-browsing-prominent-cta",
infoLinkUrl: "http://foo.example.com/%LOCALE%",
promoLinkUrl: "http://bar.example.com/%LOCALE%",
},
// Priority ensures this message is picked over the one in
// OnboardingMessageProvider
priority: 5,
targeting: "true",
});
let { win: win1, tab: tab1 } = await openTabAndWaitForRender();
await SpecialPowers.spawn(tab1, [LOCALE], async function(locale) {
is(
content.document.querySelector(".promo button").getAttribute("href"),
"http://bar.example.com/" + locale,
"should format the promoLinkUrl url"
);
content.document.querySelector("#dismiss-btn").click();
});
let { win: win2, tab: tab2 } = await openTabAndWaitForRender();
await SpecialPowers.spawn(tab2, [], async function() {
is(
content.document.querySelector(".promo button"),
null,
"should no longer render the experiment message after dismissing"
);
});
await BrowserTestUtils.closeWindow(win1);
await BrowserTestUtils.closeWindow(win2);
await doExperimentCleanup();
});

View file

@ -2,36 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const { ExperimentFakes } = ChromeUtils.import(
"resource://testing-common/NimbusTestUtils.jsm"
);
const { ExperimentAPI } = ChromeUtils.import(
"resource://nimbus/ExperimentAPI.jsm"
);
const { PanelTestProvider } = ChromeUtils.import(
"resource://activity-stream/lib/PanelTestProvider.jsm"
);
const { ASRouter } = ChromeUtils.import(
"resource://activity-stream/lib/ASRouter.jsm"
);
function waitForTelemetryEvent(category) {
info("waiting for telemetry event");
return TestUtils.waitForCondition(() => {
let events = Services.telemetry.snapshotEvents(
Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
false
).content;
if (!events) {
return null;
}
events = events.filter(e => e[1] == category);
if (events.length) {
return events[0];
}
return null;
}, "waiting for telemetry event");
}
/* import-globals-from head.js */
add_task(async function test_experiment_plain_text() {
const defaultMessageContent = (await PanelTestProvider.getMessages()).find(
@ -214,10 +185,11 @@ add_task(async function test_experiment_click_info_telemetry() {
},
});
let { win, tab } = await openTabAndWaitForRender();
// Required for `mach test --verify`
Services.telemetry.clearEvents();
let { win, tab } = await openTabAndWaitForRender();
await SpecialPowers.spawn(tab, [], () => {
const el = content.document.querySelector(".info a");
el.click();
@ -318,7 +290,6 @@ add_task(async function test_experiment_bottom_promo() {
});
await BrowserTestUtils.closeWindow(win);
await doExperimentCleanup();
});
@ -378,7 +349,6 @@ add_task(async function test_experiment_below_search_promo() {
});
await BrowserTestUtils.closeWindow(win);
await doExperimentCleanup();
});
@ -435,6 +405,5 @@ add_task(async function test_experiment_top_promo() {
});
await BrowserTestUtils.closeWindow(win);
await doExperimentCleanup();
});

View file

@ -0,0 +1,59 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* import-globals-from head.js */
add_task(async function test_experiment_messaging_system_dismiss() {
const LOCALE = Services.locale.appLocaleAsBCP47;
let doExperimentCleanup = await setupMSExperimentWithMessage({
id: `PB_NEWTAB_MESSAGING_SYSTEM_${Math.random()}`,
template: "pb_newtab",
content: {
promoEnabled: true,
infoEnabled: true,
infoBody: "fluent:about-private-browsing-info-title",
promoLinkText: "fluent:about-private-browsing-prominent-cta",
infoLinkUrl: "http://foo.example.com/%LOCALE%",
promoLinkUrl: "http://bar.example.com/%LOCALE%",
},
// Priority ensures this message is picked over the one in
// OnboardingMessageProvider
priority: 5,
targeting: "true",
});
let { win: win1, tab: tab1 } = await openTabAndWaitForRender();
await SpecialPowers.spawn(tab1, [LOCALE], async function(locale) {
is(
content.document.querySelector(".promo button").getAttribute("href"),
"http://bar.example.com/" + locale,
"should format the promoLinkUrl url"
);
content.document.querySelector("#dismiss-btn").click();
info("button clicked");
});
let telemetryEvent = await waitForTelemetryEvent("aboutprivatebrowsing");
ok(
telemetryEvent[2] == "click" && telemetryEvent[3] == "dismiss_button",
"recorded the dismiss button click"
);
let { win: win2, tab: tab2 } = await openTabAndWaitForRender();
await SpecialPowers.spawn(tab2, [], async function() {
is(
content.document.querySelector(".promo button"),
null,
"should no longer render the experiment message after dismissing"
);
});
await BrowserTestUtils.closeWindow(win1);
await BrowserTestUtils.closeWindow(win2);
await doExperimentCleanup();
});

View file

@ -0,0 +1,102 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* import-globals-from head.js */
/* Tests that use TelemetryTestUtils.assertEvents (at the very least, those with
* `{ process: "content" }`) seem to be super flaky and intermittent-prone when they
* share a file with other telemetry tests, so each one gets its own file.
*/
add_task(async function test_experiment_messaging_system_impressions() {
const LOCALE = Services.locale.appLocaleAsBCP47;
let doExperimentCleanup = await setupMSExperimentWithMessage({
id: `PB_NEWTAB_MESSAGING_SYSTEM_${Math.random()}`,
template: "pb_newtab",
content: {
promoEnabled: true,
infoEnabled: true,
infoBody: "fluent:about-private-browsing-info-title",
promoLinkText: "fluent:about-private-browsing-prominent-cta",
infoLinkUrl: "http://foo.example.com/%LOCALE%",
promoLinkUrl: "http://bar.example.com/%LOCALE%",
},
frequency: {
lifetime: 2,
},
// Priority ensures this message is picked over the one in
// OnboardingMessageProvider
priority: 5,
targeting: "true",
});
Services.telemetry.clearEvents();
let { win: win1, tab: tab1 } = await openTabAndWaitForRender();
await SpecialPowers.spawn(tab1, [LOCALE], async function(locale) {
is(
content.document.querySelector(".promo button").getAttribute("href"),
"http://bar.example.com/" + locale,
"should format the promoLinkUrl url"
);
});
await waitForTelemetryEvent("normandy");
TelemetryTestUtils.assertEvents(
[
{
method: "expose",
extra: {
featureId: "pbNewtab",
},
},
],
{ category: "normandy" },
{ process: "content" }
);
let { win: win2, tab: tab2 } = await openTabAndWaitForRender();
await SpecialPowers.spawn(tab2, [LOCALE], async function(locale) {
is(
content.document.querySelector(".promo button").getAttribute("href"),
"http://bar.example.com/" + locale,
"should format the promoLinkUrl url"
);
});
await waitForTelemetryEvent("normandy");
TelemetryTestUtils.assertEvents(
[
{
method: "expose",
extra: {
featureId: "pbNewtab",
},
},
],
{ category: "normandy" },
{ process: "content" }
);
let { win: win3, tab: tab3 } = await openTabAndWaitForRender();
await SpecialPowers.spawn(tab3, [], async function() {
is(
content.document.querySelector(".promo button"),
null,
"should no longer render the experiment message after 2 impressions"
);
});
TelemetryTestUtils.assertNumberOfEvents(0, {
category: "normandy",
method: "expose",
});
await BrowserTestUtils.closeWindow(win1);
await BrowserTestUtils.closeWindow(win2);
await BrowserTestUtils.closeWindow(win3);
await doExperimentCleanup();
});

View file

@ -0,0 +1,102 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* import-globals-from head.js */
/* Tests that use TelemetryTestUtils.assertEvents (at the very least, those with
* `{ process: "content" }`) seem to be super flaky and intermittent-prone when they
* share a file with other telemetry tests, so each one gets its own file.
*/
add_task(async function test_experiment_messaging_system() {
const LOCALE = Services.locale.appLocaleAsBCP47;
let doExperimentCleanup = await setupMSExperimentWithMessage({
id: "PB_NEWTAB_MESSAGING_SYSTEM",
template: "pb_newtab",
content: {
promoEnabled: true,
infoEnabled: true,
infoBody: "fluent:about-private-browsing-info-title",
promoLinkText: "fluent:about-private-browsing-prominent-cta",
infoLinkUrl: "http://foo.example.com/%LOCALE%",
promoLinkUrl: "http://bar.example.com/%LOCALE%",
},
// Priority ensures this message is picked over the one in
// OnboardingMessageProvider
priority: 5,
targeting: "true",
});
await TestUtils.waitForCondition(() => {
Services.telemetry.clearEvents();
let events = Services.telemetry.snapshotEvents(
Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
true
).content;
info("waiting for events to clear, but:");
info(JSON.stringify(events));
return !events || !events.length;
}, "Waiting for telemetry events to get cleared");
Services.telemetry.snapshotEvents(
Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
true
);
Services.telemetry.clearEvents();
let { win, tab } = await openTabAndWaitForRender();
await SpecialPowers.spawn(tab, [LOCALE], async function(locale) {
const infoBody = content.document.getElementById("info-body");
const promoLink = content.document.getElementById(
"private-browsing-vpn-link"
);
// Check experiment values are rendered
is(
infoBody.textContent,
"Youre in a Private Window",
"should render infoBody with fluent"
);
is(
promoLink.textContent,
"Stay private with Mozilla VPN",
"should render promoLinkText with fluent"
);
is(
content.document.querySelector(".info a").getAttribute("href"),
"http://foo.example.com/" + locale,
"should format the infoLinkUrl url"
);
is(
content.document.querySelector(".info a").getAttribute("target"),
"_blank",
"should open info url in new tab"
);
is(
content.document.querySelector(".promo button").getAttribute("target"),
"_blank",
"should open promo url in new tab"
);
});
await waitForTelemetryEvent("normandy");
TelemetryTestUtils.assertEvents(
[
{
method: "expose",
extra: {
featureId: "pbNewtab",
},
},
],
{ category: "normandy" },
{ process: "content" }
);
Services.telemetry.clearEvents();
await BrowserTestUtils.closeWindow(win);
await doExperimentCleanup();
});

View file

@ -26,6 +26,14 @@ ChromeUtils.defineModuleGetter(
"resource://gre/modules/FileUtils.jsm"
);
XPCOMUtils.defineLazyModuleGetters(this, {
ASRouter: "resource://activity-stream/lib/ASRouter.jsm",
ExperimentAPI: "resource://nimbus/ExperimentAPI.jsm",
ExperimentFakes: "resource://testing-common/NimbusTestUtils.jsm",
PanelTestProvider: "resource://activity-stream/lib/PanelTestProvider.jsm",
TelemetryTestUtils: "resource://testing-common/TelemetryTestUtils.jsm",
});
function whenNewWindowLoaded(aOptions, aCallback) {
let win = OpenBrowserWindow(aOptions);
let focused = SimpleTest.promiseFocus(win);
@ -104,4 +112,76 @@ function _initTest() {
);
}
function waitForTelemetryEvent(category) {
info("waiting for telemetry event");
return TestUtils.waitForCondition(() => {
let events = Services.telemetry.snapshotEvents(
Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
false
).content;
if (!events) {
return null;
}
events = events.filter(e => e[1] == category);
info(JSON.stringify(events));
if (events.length) {
return events[0];
}
return null;
}, "waiting for telemetry event");
}
async function setupMSExperimentWithMessage(message) {
Services.telemetry.clearEvents();
Services.telemetry.snapshotEvents(
Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
true
);
let doExperimentCleanup = await ExperimentFakes.enrollWithFeatureConfig({
featureId: "pbNewtab",
enabled: true,
value: message,
});
await SpecialPowers.pushPrefEnv({
set: [
// Disable onboarding, so we don't have default stuff showing up on the
// private browsing surface.
[
"browser.newtabpage.activity-stream.asrouter.providers.onboarding",
'{"id":"onboarding","type":"local","localProvider":"OnboardingMessageProvider","enabled":false,"exclude":[]}',
],
[
"browser.newtabpage.activity-stream.asrouter.providers.messaging-experiments",
'{"id":"messaging-experiments","enabled":true,"type":"remote-experiments","messageGroups":["pbNewtab"],"updateCycleInMs":0}',
],
],
});
// Reload the provider
await ASRouter._updateMessageProviders();
// Wait to load the messages from the messaging-experiments provider
await ASRouter.loadMessagesFromAllProviders();
// XXX this only runs at the end of the file, so some of this stuff (eg unblockAll) should be run
// at the bottom of various test functions too. Quite possibly other stuff beside unblockAll too.
registerCleanupFunction(async () => {
// Clear telemetry side effects
Services.telemetry.clearEvents();
// Make sure the side-effects from dismisses are cleared.
ASRouter.unblockAll();
// put the disabled providers back
SpecialPowers.popPrefEnv();
// Reload the provider again at cleanup to remove the experiment message
await ASRouter._updateMessageProviders();
// Wait to load the messages from the messaging-experiments provider
await ASRouter.loadMessagesFromAllProviders();
});
Assert.ok(
ASRouter.state.messages.find(m => m.id.includes(message.id)),
"Experiment message found in ASRouter state"
);
return doExperimentCleanup;
}
_initTest();

View file

@ -5,4 +5,4 @@ tags = local
[test_restore_windows_after_windows_shutdown.py]
skip-if =
os != "win"
win10_2004 && !debug && !asan # Bug 1727691
win10_2004 # Bug 1727691

View file

@ -52,6 +52,7 @@ PSSTDAPI PropVariantToString(REFPROPVARIANT propvar, PWSTR psz, UINT cch);
# include <Lmcons.h> // For UNLEN
#endif
#include <comutil.h>
#include <objbase.h>
#include <shlobj.h>
#include <knownfolders.h>
@ -93,6 +94,11 @@ PSSTDAPI PropVariantToString(REFPROPVARIANT propvar, PWSTR psz, UINT cch);
using mozilla::IsWin8OrLater;
using namespace mozilla;
struct SysFreeStringDeleter {
void operator()(BSTR aPtr) { ::SysFreeString(aPtr); }
};
using BStrPtr = mozilla::UniquePtr<OLECHAR, SysFreeStringDeleter>;
NS_IMPL_ISUPPORTS(nsWindowsShellService, nsIToolkitShellService,
nsIShellService, nsIWindowsShellService)
@ -1042,47 +1048,84 @@ static nsresult GetMatchingShortcut(int aCSIDL, const nsAutoString& aAUMID,
}
static nsresult PinCurrentAppToTaskbarWin7(bool aCheckOnly,
nsAutoString aShortcutPath) {
// Currently disabled due to
// https://bugzilla.mozilla.org/show_bug.cgi?id=1763573 At the time of
// writing, the only callers eat any errors from here so it's safe to return
// one. This is a short term workaround to confirm that this code is indeed
// causing the bug above. If it is, we'll rework this code to avoid it.
return NS_ERROR_ABORT;
// This is a less generalized version of the code from the NSIS
// Invoke Shell Verb plugin.
// https://nsis.sourceforge.io/Invoke_Shell_Verb_plugin
VARIANT dir;
RefPtr<Folder> folder = nullptr;
RefPtr<FolderItem> folderItem = nullptr;
RefPtr<FolderItemVerbs> verbs = nullptr;
RefPtr<FolderItemVerb> fiVerb = nullptr;
RefPtr<IShellDispatch> shellDisp = nullptr;
nsModuleHandle shellInst(LoadLibraryW(L"shell32.dll"));
wchar_t shortcutDir[MAX_PATH + 1], linkName[MAX_PATH + 1];
WCHAR verbName[100];
// Pull the actual verb name that we need to use later
int rv = LoadStringW(shellInst.get(), PIN_TO_TASKBAR_SHELL_VERB, verbName,
sizeof(verbName) / sizeof(verbName[0]));
if (!rv) return NS_ERROR_NOT_AVAILABLE;
HRESULT hr = CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER,
IID_IShellDispatch, getter_AddRefs(shellDisp));
RefPtr<IShellWindows> shellWindows;
HRESULT hr =
::CoCreateInstance(CLSID_ShellWindows, nullptr, CLSCTX_LOCAL_SERVER,
IID_IShellWindows, getter_AddRefs(shellWindows));
if (FAILED(hr)) return NS_ERROR_FAILURE;
wcscpy_s(shortcutDir, MAX_PATH + 1, aShortcutPath.get());
PathRemoveFileSpecW(shortcutDir);
// 1. Find the shell view for the desktop.
_variant_t loc(int(CSIDL_DESKTOP));
_variant_t empty;
long hwnd;
RefPtr<IDispatch> dispDesktop;
hr = shellWindows->FindWindowSW(&loc, &empty, SWC_DESKTOP, &hwnd,
SWFO_NEEDDISPATCH,
getter_AddRefs(dispDesktop));
if (FAILED(hr)) return NS_ERROR_FAILURE;
RefPtr<IServiceProvider> servProv;
hr = dispDesktop->QueryInterface(IID_IServiceProvider,
getter_AddRefs(servProv));
if (FAILED(hr)) return NS_ERROR_FAILURE;
RefPtr<IShellBrowser> browser;
hr = servProv->QueryService(SID_STopLevelBrowser, IID_IShellBrowser,
getter_AddRefs(browser));
if (FAILED(hr)) return NS_ERROR_FAILURE;
RefPtr<IShellView> activeShellView;
hr = browser->QueryActiveShellView(getter_AddRefs(activeShellView));
if (FAILED(hr)) return NS_ERROR_FAILURE;
// 2. Get the automation object for the desktop.
RefPtr<IDispatch> dispView;
hr = activeShellView->GetItemObject(SVGIO_BACKGROUND, IID_IDispatch,
getter_AddRefs(dispView));
if (FAILED(hr)) return NS_ERROR_FAILURE;
RefPtr<IShellFolderViewDual> folderView;
hr = dispView->QueryInterface(IID_IShellFolderViewDual,
getter_AddRefs(folderView));
if (FAILED(hr)) return NS_ERROR_FAILURE;
// 3. Get the interface to IShellDispatch
RefPtr<IDispatch> dispShell;
hr = folderView->get_Application(getter_AddRefs(dispShell));
if (FAILED(hr)) return NS_ERROR_FAILURE;
RefPtr<IShellDispatch2> shellDisp;
hr =
dispShell->QueryInterface(IID_IShellDispatch2, getter_AddRefs(shellDisp));
if (FAILED(hr)) return NS_ERROR_FAILURE;
wchar_t shortcutDir[MAX_PATH + 1];
wcscpy_s(shortcutDir, MAX_PATH + 1, aShortcutPath.get());
if (!PathRemoveFileSpecW(shortcutDir)) return NS_ERROR_FAILURE;
VARIANT dir;
dir.vt = VT_BSTR;
dir.bstrVal = shortcutDir;
BStrPtr bstrShortcutDir = BStrPtr(SysAllocString(shortcutDir));
if (bstrShortcutDir.get() == NULL) return NS_ERROR_FAILURE;
dir.bstrVal = bstrShortcutDir.get();
RefPtr<Folder> folder;
hr = shellDisp->NameSpace(dir, getter_AddRefs(folder));
if (FAILED(hr)) return NS_ERROR_FAILURE;
wchar_t linkName[MAX_PATH + 1];
wcscpy_s(linkName, MAX_PATH + 1, aShortcutPath.get());
PathStripPathW(linkName);
hr = folder->ParseName(linkName, getter_AddRefs(folderItem));
BStrPtr bstrLinkName = BStrPtr(SysAllocString(linkName));
if (bstrLinkName.get() == NULL) return NS_ERROR_FAILURE;
RefPtr<FolderItem> folderItem;
hr = folder->ParseName(bstrLinkName.get(), getter_AddRefs(folderItem));
if (FAILED(hr)) return NS_ERROR_FAILURE;
RefPtr<FolderItemVerbs> verbs;
hr = folderItem->Verbs(getter_AddRefs(verbs));
if (FAILED(hr)) return NS_ERROR_FAILURE;
@ -1090,22 +1133,29 @@ static nsresult PinCurrentAppToTaskbarWin7(bool aCheckOnly,
hr = verbs->get_Count(&count);
if (FAILED(hr)) return NS_ERROR_FAILURE;
int i;
WCHAR verbName[100];
if (!LoadStringW(shellInst.get(), PIN_TO_TASKBAR_SHELL_VERB, verbName,
ARRAYSIZE(verbName))) {
return NS_ERROR_NOT_AVAILABLE;
}
VARIANT v;
v.vt = VT_I4;
BSTR name;
for (i = 0; i < count; ++i) {
BStrPtr name;
for (long i = 0; i < count; ++i) {
RefPtr<FolderItemVerb> fiVerb;
v.lVal = i;
hr = verbs->Item(v, getter_AddRefs(fiVerb));
if (FAILED(hr) || fiVerb == nullptr) {
continue;
}
hr = fiVerb->get_Name(&name);
if (FAILED(hr)) {
continue;
}
if (!wcscmp((WCHAR*)name, verbName)) {
BSTR tmpName;
hr = fiVerb->get_Name(&tmpName);
if (FAILED(hr)) {
continue;
}
name = BStrPtr(tmpName);
if (!wcscmp((WCHAR*)name.get(), verbName)) {
if (aCheckOnly) {
// we've done as much as we can without actually
// changing anything

View file

@ -15,6 +15,7 @@ This is the nascent documentation of the Firefox front-end code.
extensions/formautofill/docs/index
components/newtab/docs/index
installer/windows/installer/index
components/attribution/docs/index
/toolkit/mozapps/defaultagent/default-browser-agent/index
components/pagedata/docs/index
places/index

View file

@ -34,7 +34,7 @@ add_warning("-Wall", when=not_clang_cl)
add_warning("-W3", when=depends(c_compiler)(lambda c: c.type == "clang-cl"))
# catch implicit truncation of enum values assigned to smaller bit fields
check_and_add_warning("-Wbitfield-enum-conversion", when=not_clang_cl)
check_and_add_warning("-Wbitfield-enum-conversion")
# catches deprecated implicit capture of `this` in lambdas.
check_and_add_warning("-Wdeprecated-this-capture", cxx_compiler)
@ -64,8 +64,8 @@ add_warning("-Wtype-limits")
check_and_add_warning("-Wno-error=tautological-type-limit-compare")
# catches some dead code
add_warning("-Wunreachable-code", when=not_clang_cl)
check_and_add_warning("-Wunreachable-code-return", when=not_clang_cl)
add_warning("-Wunreachable-code")
check_and_add_warning("-Wunreachable-code-return")
# catches parameters that are set but not read
# Only enable on clang because gcc reports false positives.

View file

@ -2415,7 +2415,7 @@
"byName": {},
"byBlocks": {},
"usedIds": {
"1": 1
"0": 0
}
}
}
@ -2436,7 +2436,7 @@
"byName": {},
"byBlocks": {},
"usedIds": {
"1": 1
"0": 0
}
}
}

View file

@ -14609,43 +14609,30 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj &&
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
let symbolDeclarations = new Map();
function getUniqueIdentifiers(identifiers) {
const newIdentifiers = [];
const locationKeys = new Set();
function extractFunctionSymbol(path, state, symbols) {
const name = (0, _getFunctionName.default)(path.node, path.parent);
for (const newId of identifiers) {
const key = (0, _helpers.nodeLocationKey)(newId);
if (!locationKeys.has(key)) {
locationKeys.add(key);
newIdentifiers.push(newId);
}
if (!state.fnCounts[name]) {
state.fnCounts[name] = 0;
}
return newIdentifiers;
} // eslint-disable-next-line complexity
const index = state.fnCounts[name]++;
symbols.functions.push({
name,
klass: (0, _inferClassName.inferClassName)(path),
location: path.node.loc,
parameterNames: (0, _helpers.getFunctionParameterNames)(path),
identifier: path.node.id,
// indicates the occurence of the function in a file
// e.g { name: foo, ... index: 4 } is the 4th foo function
// in the file
index
});
}
function extractSymbol(path, symbols, state) {
if ((0, _helpers.isFunction)(path)) {
const name = (0, _getFunctionName.default)(path.node, path.parent);
if (!state.fnCounts[name]) {
state.fnCounts[name] = 0;
}
const index = state.fnCounts[name]++;
symbols.functions.push({
name,
klass: (0, _inferClassName.inferClassName)(path),
location: path.node.loc,
parameterNames: (0, _helpers.getFunctionParameterNames)(path),
identifier: path.node.id,
// indicates the occurence of the function in a file
// e.g { name: foo, ... index: 4 } is the 4th foo function
// in the file
index
});
extractFunctionSymbol(path, state, symbols);
}
if (t.isJSXElement(path)) {
@ -14657,58 +14644,19 @@ function extractSymbol(path, symbols, state) {
}
if (t.isClassDeclaration(path)) {
const {
loc,
superClass
} = path.node;
symbols.classes.push({
name: path.node.id.name,
parent: superClass ? {
name: t.isMemberExpression(superClass) ? (0, _helpers.getCode)(superClass) : superClass.name,
location: superClass.loc
} : null,
location: loc
});
symbols.classes.push(getClassDeclarationSymbol(path.node));
}
if (t.isImportDeclaration(path)) {
symbols.imports.push({
source: path.node.source.value,
location: path.node.loc,
specifiers: (0, _helpers.getSpecifiers)(path.node.specifiers)
});
symbols.imports.push(getImportDeclarationSymbol(path.node));
}
if (t.isObjectProperty(path)) {
const {
start,
end,
identifierName
} = path.node.key.loc;
symbols.objectProperties.push({
name: identifierName,
location: {
start,
end
},
expression: getSnippet(path)
});
symbols.objectProperties.push(getObjectPropertySymbol(path));
}
if (t.isMemberExpression(path) || t.isOptionalMemberExpression(path)) {
const {
start,
end
} = path.node.property.loc;
symbols.memberExpressions.push({
name: t.isPrivateName(path.node.property) ? `#${path.node.property.id.name}` : path.node.property.name,
location: {
start,
end
},
expression: getSnippet(path),
computed: path.node.computed
});
symbols.memberExpressions.push(getMemberExpressionSymbol(path));
}
if ((t.isStringLiteral(path) || t.isNumericLiteral(path)) && t.isMemberExpression(path.parentPath)) {
@ -14728,114 +14676,10 @@ function extractSymbol(path, symbols, state) {
}
if (t.isCallExpression(path)) {
const {
callee
} = path.node;
const args = path.node.arguments;
if (t.isMemberExpression(callee)) {
const {
property: {
name,
loc
}
} = callee;
symbols.callExpressions.push({
name,
values: args.filter(arg => arg.value).map(arg => arg.value),
location: loc
});
} else {
const {
start,
end,
identifierName
} = callee.loc;
symbols.callExpressions.push({
name: identifierName,
values: args.filter(arg => arg.value).map(arg => arg.value),
location: {
start,
end
}
});
}
symbols.callExpressions.push(getCallExpressionSymbol(path.node));
}
if (t.isStringLiteral(path) && t.isProperty(path.parentPath)) {
const {
start,
end
} = path.node.loc;
return symbols.identifiers.push({
name: path.node.value,
expression: (0, _helpers.getObjectExpressionValue)(path.parent),
location: {
start,
end
}
});
}
if (t.isIdentifier(path) && !t.isGenericTypeAnnotation(path.parent)) {
let {
start,
end
} = path.node.loc; // We want to include function params, but exclude the function name
if (t.isClassMethod(path.parent) && !path.inList) {
return;
}
if (t.isProperty(path.parentPath) && !(0, _helpers.isObjectShorthand)(path.parent)) {
return symbols.identifiers.push({
name: path.node.name,
expression: (0, _helpers.getObjectExpressionValue)(path.parent),
location: {
start,
end
}
});
}
if (path.node.typeAnnotation) {
const {
column
} = path.node.typeAnnotation.loc.start;
end = { ...end,
column
};
}
symbols.identifiers.push({
name: path.node.name,
expression: path.node.name,
location: {
start,
end
}
});
}
if (t.isThisExpression(path.node)) {
const {
start,
end
} = path.node.loc;
symbols.identifiers.push({
name: "this",
location: {
start,
end
},
expression: "this"
});
}
if (t.isVariableDeclarator(path)) {
const nodeId = path.node.id;
symbols.identifiers.push(...(0, _helpers.getPatternIdentifiers)(nodeId));
}
symbols.identifiers.push(...getIdentifierSymbols(path));
}
function extractSymbols(sourceId) {
@ -15058,6 +14902,218 @@ function getSymbols(sourceId) {
return symbols;
}
function getUniqueIdentifiers(identifiers) {
const newIdentifiers = [];
const locationKeys = new Set();
for (const newId of identifiers) {
const key = (0, _helpers.nodeLocationKey)(newId);
if (!locationKeys.has(key)) {
locationKeys.add(key);
newIdentifiers.push(newId);
}
}
return newIdentifiers;
}
function getMemberExpressionSymbol(path) {
const {
start,
end
} = path.node.property.loc;
return {
name: t.isPrivateName(path.node.property) ? `#${path.node.property.id.name}` : path.node.property.name,
location: {
start,
end
},
expression: getSnippet(path),
computed: path.node.computed
};
}
function getImportDeclarationSymbol(node) {
return {
source: node.source.value,
location: node.loc,
specifiers: (0, _helpers.getSpecifiers)(node.specifiers)
};
}
function getObjectPropertySymbol(path) {
const {
start,
end,
identifierName
} = path.node.key.loc;
return {
name: identifierName,
location: {
start,
end
},
expression: getSnippet(path)
};
}
function getCallExpressionSymbol(node) {
const {
callee,
arguments: args
} = node;
const values = args.filter(arg => arg.value).map(arg => arg.value);
if (t.isMemberExpression(callee)) {
const {
property: {
name,
loc
}
} = callee;
return {
name,
values,
location: loc
};
}
const {
start,
end,
identifierName
} = callee.loc;
return {
name: identifierName,
values,
location: {
start,
end
}
};
}
function getClassParentName(superClass) {
return t.isMemberExpression(superClass) ? (0, _helpers.getCode)(superClass) : superClass.name;
}
function getClassParentSymbol(superClass) {
if (!superClass) {
return null;
}
return {
name: getClassParentName(superClass),
location: superClass.loc
};
}
function getClassDeclarationSymbol(node) {
const {
loc,
superClass
} = node;
return {
name: node.id.name,
parent: getClassParentSymbol(superClass),
location: loc
};
}
/**
* Get a list of identifiers that are part of the given path.
*
* @param {Object} path
* @returns {Array.<Object>} a list of identifiers
*/
function getIdentifierSymbols(path) {
if (t.isStringLiteral(path) && t.isProperty(path.parentPath)) {
const {
start,
end
} = path.node.loc;
return [{
name: path.node.value,
expression: (0, _helpers.getObjectExpressionValue)(path.parent),
location: {
start,
end
}
}];
}
const identifiers = [];
if (t.isIdentifier(path) && !t.isGenericTypeAnnotation(path.parent)) {
// We want to include function params, but exclude the function name
if (t.isClassMethod(path.parent) && !path.inList) {
return [];
}
if (t.isProperty(path.parentPath) && !(0, _helpers.isObjectShorthand)(path.parent)) {
const {
start,
end
} = path.node.loc;
return [{
name: path.node.name,
expression: (0, _helpers.getObjectExpressionValue)(path.parent),
location: {
start,
end
}
}];
}
let {
start,
end
} = path.node.loc;
if (path.node.typeAnnotation) {
const {
column
} = path.node.typeAnnotation.loc.start;
end = { ...end,
column
};
}
identifiers.push({
name: path.node.name,
expression: path.node.name,
location: {
start,
end
}
});
}
if (t.isThisExpression(path.node)) {
const {
start,
end
} = path.node.loc;
identifiers.push({
name: "this",
location: {
start,
end
},
expression: "this"
});
}
if (t.isVariableDeclarator(path)) {
const nodeId = path.node.id;
identifiers.push(...(0, _helpers.getPatternIdentifiers)(nodeId));
}
return identifiers;
}
/***/ }),
/* 649 */
/***/ (function(module, exports, __webpack_require__) {

View file

@ -25,41 +25,29 @@ import { getFramework } from "./frameworks";
let symbolDeclarations = new Map();
function getUniqueIdentifiers(identifiers) {
const newIdentifiers = [];
const locationKeys = new Set();
for (const newId of identifiers) {
const key = nodeLocationKey(newId);
if (!locationKeys.has(key)) {
locationKeys.add(key);
newIdentifiers.push(newId);
}
}
function extractFunctionSymbol(path, state, symbols) {
const name = getFunctionName(path.node, path.parent);
return newIdentifiers;
if (!state.fnCounts[name]) {
state.fnCounts[name] = 0;
}
const index = state.fnCounts[name]++;
symbols.functions.push({
name,
klass: inferClassName(path),
location: path.node.loc,
parameterNames: getFunctionParameterNames(path),
identifier: path.node.id,
// indicates the occurence of the function in a file
// e.g { name: foo, ... index: 4 } is the 4th foo function
// in the file
index,
});
}
// eslint-disable-next-line complexity
function extractSymbol(path, symbols, state) {
if (isFunction(path)) {
const name = getFunctionName(path.node, path.parent);
if (!state.fnCounts[name]) {
state.fnCounts[name] = 0;
}
const index = state.fnCounts[name]++;
symbols.functions.push({
name,
klass: inferClassName(path),
location: path.node.loc,
parameterNames: getFunctionParameterNames(path),
identifier: path.node.id,
// indicates the occurence of the function in a file
// e.g { name: foo, ... index: 4 } is the 4th foo function
// in the file
index,
});
extractFunctionSymbol(path, state, symbols);
}
if (t.isJSXElement(path)) {
@ -71,48 +59,19 @@ function extractSymbol(path, symbols, state) {
}
if (t.isClassDeclaration(path)) {
const { loc, superClass } = path.node;
symbols.classes.push({
name: path.node.id.name,
parent: superClass
? {
name: t.isMemberExpression(superClass)
? getCode(superClass)
: superClass.name,
location: superClass.loc,
}
: null,
location: loc,
});
symbols.classes.push(getClassDeclarationSymbol(path.node));
}
if (t.isImportDeclaration(path)) {
symbols.imports.push({
source: path.node.source.value,
location: path.node.loc,
specifiers: getSpecifiers(path.node.specifiers),
});
symbols.imports.push(getImportDeclarationSymbol(path.node));
}
if (t.isObjectProperty(path)) {
const { start, end, identifierName } = path.node.key.loc;
symbols.objectProperties.push({
name: identifierName,
location: { start, end },
expression: getSnippet(path),
});
symbols.objectProperties.push(getObjectPropertySymbol(path));
}
if (t.isMemberExpression(path) || t.isOptionalMemberExpression(path)) {
const { start, end } = path.node.property.loc;
symbols.memberExpressions.push({
name: t.isPrivateName(path.node.property)
? `#${path.node.property.id.name}`
: path.node.property.name,
location: { start, end },
expression: getSnippet(path),
computed: path.node.computed,
});
symbols.memberExpressions.push(getMemberExpressionSymbol(path));
}
if (
@ -129,78 +88,10 @@ function extractSymbol(path, symbols, state) {
}
if (t.isCallExpression(path)) {
const { callee } = path.node;
const args = path.node.arguments;
if (t.isMemberExpression(callee)) {
const {
property: { name, loc },
} = callee;
symbols.callExpressions.push({
name,
values: args.filter(arg => arg.value).map(arg => arg.value),
location: loc,
});
} else {
const { start, end, identifierName } = callee.loc;
symbols.callExpressions.push({
name: identifierName,
values: args.filter(arg => arg.value).map(arg => arg.value),
location: { start, end },
});
}
symbols.callExpressions.push(getCallExpressionSymbol(path.node));
}
if (t.isStringLiteral(path) && t.isProperty(path.parentPath)) {
const { start, end } = path.node.loc;
return symbols.identifiers.push({
name: path.node.value,
expression: getObjectExpressionValue(path.parent),
location: { start, end },
});
}
if (t.isIdentifier(path) && !t.isGenericTypeAnnotation(path.parent)) {
let { start, end } = path.node.loc;
// We want to include function params, but exclude the function name
if (t.isClassMethod(path.parent) && !path.inList) {
return;
}
if (t.isProperty(path.parentPath) && !isObjectShorthand(path.parent)) {
return symbols.identifiers.push({
name: path.node.name,
expression: getObjectExpressionValue(path.parent),
location: { start, end },
});
}
if (path.node.typeAnnotation) {
const { column } = path.node.typeAnnotation.loc.start;
end = { ...end, column };
}
symbols.identifiers.push({
name: path.node.name,
expression: path.node.name,
location: { start, end },
});
}
if (t.isThisExpression(path.node)) {
const { start, end } = path.node.loc;
symbols.identifiers.push({
name: "this",
location: { start, end },
expression: "this",
});
}
if (t.isVariableDeclarator(path)) {
const nodeId = path.node.id;
symbols.identifiers.push(...getPatternIdentifiers(nodeId));
}
symbols.identifiers.push(...getIdentifierSymbols(path));
}
function extractSymbols(sourceId) {
@ -425,3 +316,159 @@ export function getSymbols(sourceId) {
symbolDeclarations.set(sourceId, symbols);
return symbols;
}
function getUniqueIdentifiers(identifiers) {
const newIdentifiers = [];
const locationKeys = new Set();
for (const newId of identifiers) {
const key = nodeLocationKey(newId);
if (!locationKeys.has(key)) {
locationKeys.add(key);
newIdentifiers.push(newId);
}
}
return newIdentifiers;
}
function getMemberExpressionSymbol(path) {
const { start, end } = path.node.property.loc;
return {
name: t.isPrivateName(path.node.property)
? `#${path.node.property.id.name}`
: path.node.property.name,
location: { start, end },
expression: getSnippet(path),
computed: path.node.computed,
};
}
function getImportDeclarationSymbol(node) {
return {
source: node.source.value,
location: node.loc,
specifiers: getSpecifiers(node.specifiers),
};
}
function getObjectPropertySymbol(path) {
const { start, end, identifierName } = path.node.key.loc;
return {
name: identifierName,
location: { start, end },
expression: getSnippet(path),
};
}
function getCallExpressionSymbol(node) {
const { callee, arguments: args } = node;
const values = args.filter(arg => arg.value).map(arg => arg.value);
if (t.isMemberExpression(callee)) {
const {
property: { name, loc },
} = callee;
return {
name,
values,
location: loc,
};
}
const { start, end, identifierName } = callee.loc;
return {
name: identifierName,
values,
location: { start, end },
};
}
function getClassParentName(superClass) {
return t.isMemberExpression(superClass)
? getCode(superClass)
: superClass.name;
}
function getClassParentSymbol(superClass) {
if (!superClass) {
return null;
}
return {
name: getClassParentName(superClass),
location: superClass.loc,
};
}
function getClassDeclarationSymbol(node) {
const { loc, superClass } = node;
return {
name: node.id.name,
parent: getClassParentSymbol(superClass),
location: loc,
};
}
/**
* Get a list of identifiers that are part of the given path.
*
* @param {Object} path
* @returns {Array.<Object>} a list of identifiers
*/
function getIdentifierSymbols(path) {
if (t.isStringLiteral(path) && t.isProperty(path.parentPath)) {
const { start, end } = path.node.loc;
return [
{
name: path.node.value,
expression: getObjectExpressionValue(path.parent),
location: { start, end },
},
];
}
const identifiers = [];
if (t.isIdentifier(path) && !t.isGenericTypeAnnotation(path.parent)) {
// We want to include function params, but exclude the function name
if (t.isClassMethod(path.parent) && !path.inList) {
return [];
}
if (t.isProperty(path.parentPath) && !isObjectShorthand(path.parent)) {
const { start, end } = path.node.loc;
return [
{
name: path.node.name,
expression: getObjectExpressionValue(path.parent),
location: { start, end },
},
];
}
let { start, end } = path.node.loc;
if (path.node.typeAnnotation) {
const { column } = path.node.typeAnnotation.loc.start;
end = { ...end, column };
}
identifiers.push({
name: path.node.name,
expression: path.node.name,
location: { start, end },
});
}
if (t.isThisExpression(path.node)) {
const { start, end } = path.node.loc;
identifiers.push({
name: "this",
location: { start, end },
expression: "this",
});
}
if (t.isVariableDeclarator(path)) {
const nodeId = path.node.id;
identifiers.push(...getPatternIdentifiers(nodeId));
}
return identifiers;
}

View file

@ -23,6 +23,8 @@ prefs =
[browser_browser_toolbox.js]
[browser_browser_toolbox_debugger.js]
skip-if =
os == "linux" && bits == 64 && debug # Bug 1756616
[browser_browser_toolbox_evaluation_context.js]
[browser_browser_toolbox_fission_contentframe_inspector.js]
skip-if =

View file

@ -17,7 +17,6 @@ const EventEmitter = require("devtools/shared/event-emitter");
const {
getString,
text,
wire,
showFilePicker,
optionsPopupMenu,
} = require("resource://devtools/client/styleeditor/StyleEditorUtil.jsm");
@ -159,18 +158,24 @@ StyleEditorUI.prototype = {
createUI: function() {
this._view = new SplitView(this._root);
wire(this._view.rootElement, ".style-editor-newButton", async () => {
const stylesheetsFront = await this.currentTarget.getFront("stylesheets");
stylesheetsFront.addStyleSheet(null);
});
this._root
.querySelector(".style-editor-newButton")
.addEventListener("click", async () => {
const stylesheetsFront = await this.currentTarget.getFront(
"stylesheets"
);
stylesheetsFront.addStyleSheet(null);
});
wire(this._view.rootElement, ".style-editor-importButton", () => {
this._importFromFile(this._mockImportFile || null, this._window);
});
this._root
.querySelector(".style-editor-importButton")
.addEventListener("click", () => {
this._importFromFile(this._mockImportFile || null, this._window);
});
wire(this._view.rootElement, "#style-editor-options", event => {
this._onOptionsButtonClick(event);
});
this._root
.querySelector("#style-editor-options")
.addEventListener("click", this._onOptionsButtonClick);
this._panelDoc.addEventListener(
"contextmenu",
@ -616,29 +621,31 @@ StyleEditorUI.prototype = {
createdEditor.summary = summary;
createdEditor.details = details;
wire(summary, ".stylesheet-enabled", function onToggleDisabled(event) {
event.stopPropagation();
event.target.blur();
summary
.querySelector(".stylesheet-enabled")
.addEventListener("click", event => {
event.stopPropagation();
event.target.blur();
createdEditor.toggleDisabled();
});
createdEditor.toggleDisabled();
});
wire(summary, ".stylesheet-name", {
events: {
keypress: event => {
if (event.keyCode == KeyCodes.DOM_VK_RETURN) {
this._view.setActiveSummary(summary);
}
},
},
});
summary
.querySelector(".stylesheet-name")
.addEventListener("keypress", event => {
if (event.keyCode == KeyCodes.DOM_VK_RETURN) {
this._view.setActiveSummary(summary);
}
});
wire(summary, ".stylesheet-saveButton", function onSaveButton(event) {
event.stopPropagation();
event.target.blur();
summary
.querySelector(".stylesheet-saveButton")
.addEventListener("click", event => {
event.stopPropagation();
event.target.blur();
createdEditor.saveToFile(createdEditor.savedFile);
});
createdEditor.saveToFile(createdEditor.savedFile);
});
this._updateSummaryForEditor(createdEditor, summary);

View file

@ -12,7 +12,6 @@ const EXPORTED_SYMBOLS = [
"assert",
"log",
"text",
"wire",
"showFilePicker",
"optionsPopupMenu",
];
@ -128,50 +127,6 @@ function log() {
console.logStringMessage(Array.prototype.slice.call(arguments).join(" "));
}
/**
* Wire up element(s) matching selector with attributes, event listeners, etc.
*
* @param DOMElement root
* The element to use for querySelectorAll.
* Can be null if selector is a DOMElement.
* @param string|DOMElement selectorOrElement
* Selector string or DOMElement for the element(s) to wire up.
* @param object descriptor
* An object describing how to wire matching selector,
* supported properties are "events" and "attributes" taking
* objects themselves.
* Each key of properties above represents the name of the event or
* attribute, with the value being a function used as an event handler or
* string to use as attribute value.
* If descriptor is a function, the argument is equivalent to :
* {events: {'click': descriptor}}
*/
function wire(root, selectorOrElement, descriptor) {
let matches;
if (typeof selectorOrElement == "string") {
// selector
matches = root.querySelectorAll(selectorOrElement);
if (!matches.length) {
return;
}
} else {
// element
matches = [selectorOrElement];
}
if (typeof descriptor == "function") {
descriptor = { events: { click: descriptor } };
}
for (let i = 0; i < matches.length; i++) {
const element = matches[i];
forEach(descriptor.events, function(name, handler) {
element.addEventListener(name, handler);
});
forEach(descriptor.attributes, element.setAttribute);
}
}
/**
* Show file picker and return the file user selected.
*

View file

@ -1,10 +1,13 @@
reject-osfile
=============================
=============
Rejects calls into ``OS.File``. This is configured as a warning.
You should use |IOUtils|_ for new code.
If modifying old code, please consider swapping it in if possible; if this is tricky please ensure
a bug is on file.
Rejects calls into ``OS.File`` and ``OS.Path``. This is configured as a warning.
You should use |IOUtils|_ and |PathUtils|_ respectively for new code. If
modifying old code, please consider swapping it in if possible; if this is
tricky please ensure a bug is on file.
.. |IOUtils| replace:: ``IOUtils``
.. _IOUtils: https://searchfox.org/mozilla-central/source/dom/chrome-webidl/IOUtils.webidl
.. |PathUtils| replace:: ``PathUtils``
.. _PathUtils: https://searchfox.org/mozilla-central/source/dom/chrome-webidl/PathUtils.webidl

View file

@ -25,11 +25,14 @@
#include "mozilla/dom/ContentPlaybackController.h"
#include "mozilla/dom/SessionStorageManager.h"
#include "mozilla/ipc/ProtocolUtils.h"
#include "mozilla/layout/RemotePrintJobParent.h"
#include "mozilla/net/DocumentLoadListener.h"
#include "mozilla/NullPrincipal.h"
#include "mozilla/StaticPrefs_docshell.h"
#include "mozilla/StaticPrefs_fission.h"
#include "mozilla/Telemetry.h"
#include "nsIPrintSettings.h"
#include "nsIPrintSettingsService.h"
#include "nsISupports.h"
#include "nsIWebNavigation.h"
#include "mozilla/MozPromiseInlines.h"
@ -51,10 +54,6 @@
#include "nsImportModule.h"
#include "UnitTransforms.h"
#ifdef NS_PRINTING
# include "mozilla/embedding/printingui/PrintingParent.h"
#endif
using namespace mozilla::ipc;
extern mozilla::LazyLogModule gAutoplayPermissionLog;
@ -708,17 +707,39 @@ already_AddRefed<Promise> CanonicalBrowsingContext::Print(
return promise.forget();
}
RefPtr<embedding::PrintingParent> printingParent =
browserParent->Manager()->GetPrintingParent();
nsCOMPtr<nsIPrintSettingsService> printSettingsSvc =
do_GetService("@mozilla.org/gfx/printsettings-service;1");
if (NS_WARN_IF(!printSettingsSvc)) {
promise->MaybeReject(ErrorResult(NS_ERROR_FAILURE));
return promise.forget();
}
nsresult rv;
nsCOMPtr<nsIPrintSettings> printSettings = aPrintSettings;
if (!printSettings) {
rv = printSettingsSvc->GetNewPrintSettings(getter_AddRefs(printSettings));
if (NS_WARN_IF(NS_FAILED(rv))) {
promise->MaybeReject(ErrorResult(rv));
return promise.forget();
}
}
embedding::PrintData printData;
nsresult rv = printingParent->SerializeAndEnsureRemotePrintJob(
aPrintSettings, listener, nullptr, &printData);
rv = printSettingsSvc->SerializeToPrintData(printSettings, &printData);
if (NS_WARN_IF(NS_FAILED(rv))) {
promise->MaybeReject(ErrorResult(rv));
return promise.forget();
}
layout::RemotePrintJobParent* remotePrintJob =
new layout::RemotePrintJobParent(printSettings);
printData.remotePrintJobParent() =
browserParent->Manager()->SendPRemotePrintJobConstructor(remotePrintJob);
if (listener) {
remotePrintJob->RegisterListener(listener);
}
if (NS_WARN_IF(!browserParent->SendPrint(this, printData))) {
promise->MaybeReject(ErrorResult(NS_ERROR_FAILURE));
}

View file

@ -2889,6 +2889,10 @@ class Document : public nsINode,
*/
Document* GetTemplateContentsOwner();
Document* GetTemplateContentsOwnerIfExists() const {
return mTemplateContentsOwner.get();
}
/**
* Returns true if this document is a static clone of a normal document.
*

View file

@ -608,11 +608,6 @@ int32_t DocumentOrShadowRoot::StyleOrderIndexOfSheet(
return mStyleSheets.IndexOf(&aSheet);
}
void DocumentOrShadowRoot::GetAdoptedStyleSheets(
nsTArray<RefPtr<StyleSheet>>& aAdoptedStyleSheets) const {
aAdoptedStyleSheets = mAdoptedStyleSheets.Clone();
}
void DocumentOrShadowRoot::TraverseSheetRefInStylesIfApplicable(
StyleSheet& aSheet, nsCycleCollectionTraversalCallback& cb) {
if (!aSheet.IsApplicable()) {

View file

@ -89,8 +89,6 @@ class DocumentOrShadowRoot : public RadioGroupManager {
StyleSheetList* StyleSheets();
void GetAdoptedStyleSheets(nsTArray<RefPtr<StyleSheet>>&) const;
void RemoveStyleSheet(StyleSheet&);
Element* GetElementById(const nsAString& aElementId);

View file

@ -101,6 +101,17 @@ JSObject* ShadowRoot::WrapNode(JSContext* aCx,
return mozilla::dom::ShadowRoot_Binding::Wrap(aCx, this, aGivenProto);
}
void ShadowRoot::NodeInfoChanged(Document* aOldDoc) {
DocumentFragment::NodeInfoChanged(aOldDoc);
Document* newDoc = OwnerDoc();
const bool fromOrToTemplate =
aOldDoc->GetTemplateContentsOwnerIfExists() == newDoc ||
newDoc->GetTemplateContentsOwnerIfExists() == aOldDoc;
if (!fromOrToTemplate) {
ClearAdoptedStyleSheets();
}
}
void ShadowRoot::CloneInternalDataFrom(ShadowRoot* aOther) {
if (aOther->IsUAWidget()) {
SetIsUAWidget();

View file

@ -185,10 +185,7 @@ class ShadowRoot final : public DocumentFragment,
JSObject* WrapNode(JSContext*, JS::Handle<JSObject*> aGivenProto) final;
void NodeInfoChanged(Document* aOldDoc) override {
DocumentFragment::NodeInfoChanged(aOldDoc);
ClearAdoptedStyleSheets();
}
void NodeInfoChanged(Document* aOldDoc) override;
void AddToIdTable(Element* aElement, nsAtom* aId);
void RemoveFromIdTable(Element* aElement, nsAtom* aId);

View file

@ -1571,17 +1571,17 @@ nsDOMWindowUtils::CompareCanvases(nsISupports* aCanvas1, nsISupports* aCanvas2,
DataSourceSurface::ScopedMap map1(img1, DataSourceSurface::READ);
DataSourceSurface::ScopedMap map2(img2, DataSourceSurface::READ);
if (NS_WARN_IF(!map1.IsMapped()) || NS_WARN_IF(!map2.IsMapped()) ||
NS_WARN_IF(map1.GetStride() != map2.GetStride())) {
if (NS_WARN_IF(!map1.IsMapped()) || NS_WARN_IF(!map2.IsMapped())) {
return NS_ERROR_FAILURE;
}
int v;
IntSize size = img1->GetSize();
int32_t stride = map1.GetStride();
int32_t stride1 = map1.GetStride();
int32_t stride2 = map2.GetStride();
// we can optimize for the common all-pass case
if (stride == size.width * 4) {
if (stride1 == stride2 && stride1 == size.width * 4) {
v = memcmp(map1.GetData(), map2.GetData(), size.width * size.height * 4);
if (v == 0) {
if (aMaxDifference) *aMaxDifference = 0;
@ -1594,9 +1594,9 @@ nsDOMWindowUtils::CompareCanvases(nsISupports* aCanvas1, nsISupports* aCanvas2,
uint32_t different = 0;
for (int j = 0; j < size.height; j++) {
unsigned char* p1 = map1.GetData() + j * stride;
unsigned char* p2 = map2.GetData() + j * stride;
v = memcmp(p1, p2, stride);
unsigned char* p1 = map1.GetData() + j * stride1;
unsigned char* p2 = map2.GetData() + j * stride2;
v = memcmp(p1, p2, size.width * 4);
if (v) {
for (int i = 0; i < size.width; i++) {

View file

@ -136,7 +136,6 @@
#include "nsXULPopupManager.h"
#ifdef NS_PRINTING
# include "mozilla/embedding/printingui/PrintingParent.h"
# include "nsIWebBrowserPrint.h"
#endif

View file

@ -630,6 +630,12 @@ DOMInterfaces = {
'headerFile': 'PeerConnectionImpl.h',
},
'Performance' : {
'implicitJSContext': [
'mark'
],
},
'PerformanceResourceTiming' : {
'concrete': True,
},

View file

@ -1242,10 +1242,9 @@ class EventStateManager : public nsSupportsWeakReference, public nsIObserver {
// Click and double-click events need to be handled even for content that
// has no frame. This is required for Web compatibility.
#define NS_EVENT_NEEDS_FRAME(event) \
(!(event)->HasPluginActivationEventMessage() && \
(event)->mMessage != eMouseClick && \
(event)->mMessage != eMouseDoubleClick && \
#define NS_EVENT_NEEDS_FRAME(event) \
((event)->mMessage != eMouseClick && \
(event)->mMessage != eMouseDoubleClick && \
(event)->mMessage != eMouseAuxClick)
#endif // mozilla_EventStateManager_h_

View file

@ -109,6 +109,7 @@
#include "mozilla/layers/CompositorManagerChild.h"
#include "mozilla/layers/ContentProcessController.h"
#include "mozilla/layers/ImageBridgeChild.h"
#include "mozilla/layout/RemotePrintJobChild.h"
#include "mozilla/loader/ScriptCacheActors.h"
#include "mozilla/media/MediaChild.h"
#include "mozilla/net/CaptivePortalService.h"
@ -194,9 +195,6 @@
#include "nsThreadManager.h"
#include "nsVariant.h"
#include "nsXULAppAPI.h"
#ifdef NS_PRINTING
# include "nsPrintingProxy.h"
#endif
#include "IHistory.h"
#include "ReferrerInfo.h"
#include "base/message_loop.h"
@ -801,12 +799,6 @@ void ContentChild::Init(base::ProcessId aParentPid, const char* aParentBuildID,
mID = aChildID;
mIsForBrowser = aIsForBrowser;
#ifdef NS_PRINTING
// Force the creation of the nsPrintingProxy so that it's IPC counterpart,
// PrintingParent, is always available for printing initiated from the parent.
RefPtr<nsPrintingProxy> printingProxy = nsPrintingProxy::GetInstance();
#endif
SetProcessName("Web Content"_ns);
#ifdef NIGHTLY_BUILD
@ -2025,17 +2017,8 @@ mozilla::ipc::IPCResult ContentChild::RecvSocketProcessCrashed() {
return IPC_OK();
}
PPrintingChild* ContentChild::AllocPPrintingChild() {
// The ContentParent should never attempt to allocate the nsPrintingProxy,
// which implements PPrintingChild. Instead, the nsPrintingProxy service is
// requested and instantiated via XPCOM, and the constructor of
// nsPrintingProxy sets up the IPC connection.
MOZ_CRASH("Should never get here!");
return nullptr;
}
bool ContentChild::DeallocPPrintingChild(PPrintingChild* printing) {
return true;
PRemotePrintJobChild* ContentChild::AllocPRemotePrintJobChild() {
return new RemotePrintJobChild();
}
PChildToParentStreamChild* ContentChild::SendPChildToParentStreamConstructor(

View file

@ -230,9 +230,7 @@ class ContentChild final : public PContentChild,
PScriptCacheChild*, const FileDescOrError& cacheFile,
const bool& wantCacheData) override;
PPrintingChild* AllocPPrintingChild();
bool DeallocPPrintingChild(PPrintingChild*);
PRemotePrintJobChild* AllocPRemotePrintJobChild();
PChildToParentStreamChild* AllocPChildToParentStreamChild();
bool DeallocPChildToParentStreamChild(PChildToParentStreamChild*);

View file

@ -134,7 +134,6 @@
#include "mozilla/dom/nsMixedContentBlocker.h"
#include "mozilla/dom/power/PowerManagerService.h"
#include "mozilla/dom/quota/QuotaManagerService.h"
#include "mozilla/embedding/printingui/PrintingParent.h"
#include "mozilla/extensions/ExtensionsParent.h"
#include "mozilla/extensions/StreamFilterParent.h"
#include "mozilla/gfx/GPUProcessManager.h"
@ -4328,51 +4327,6 @@ already_AddRefed<PNeckoParent> ContentParent::AllocPNeckoParent() {
return actor.forget();
}
PPrintingParent* ContentParent::AllocPPrintingParent() {
#ifdef NS_PRINTING
if (mPrintingParent) {
// Only one PrintingParent should be created per process.
return nullptr;
}
// Create the printing singleton for this process.
mPrintingParent = new PrintingParent();
// Take another reference for IPDL code.
mPrintingParent.get()->AddRef();
return mPrintingParent.get();
#else
MOZ_ASSERT_UNREACHABLE("Should never be created if no printing.");
return nullptr;
#endif
}
bool ContentParent::DeallocPPrintingParent(PPrintingParent* printing) {
#ifdef NS_PRINTING
MOZ_RELEASE_ASSERT(
mPrintingParent == printing,
"Only one PrintingParent should have been created per process.");
// Release reference taken for IPDL code.
static_cast<PrintingParent*>(printing)->Release();
mPrintingParent = nullptr;
#else
MOZ_ASSERT_UNREACHABLE("Should never have been created if no printing.");
#endif
return true;
}
#ifdef NS_PRINTING
already_AddRefed<embedding::PrintingParent> ContentParent::GetPrintingParent() {
MOZ_ASSERT(mPrintingParent);
RefPtr<embedding::PrintingParent> printingParent = mPrintingParent;
return printingParent.forget();
}
#endif
mozilla::ipc::IPCResult ContentParent::RecvInitStreamFilter(
const uint64_t& aChannelId, const nsString& aAddonId,
InitStreamFilterResolver&& aResolver) {

View file

@ -74,10 +74,6 @@ class BenchmarkStorageParent;
using mozilla::loader::PScriptCacheParent;
namespace embedding {
class PrintingParent;
}
namespace ipc {
class CrashReporterHost;
class PFileDescriptorSetParent;
@ -450,17 +446,6 @@ class ContentParent final
return PContentParent::RecvPNeckoConstructor(aActor);
}
PPrintingParent* AllocPPrintingParent();
bool DeallocPPrintingParent(PPrintingParent* aActor);
#if defined(NS_PRINTING)
/**
* @return the PrintingParent for this ContentParent.
*/
already_AddRefed<embedding::PrintingParent> GetPrintingParent();
#endif
mozilla::ipc::IPCResult RecvInitStreamFilter(
const uint64_t& aChannelId, const nsString& aAddonId,
InitStreamFilterResolver&& aResolver);
@ -1615,10 +1600,6 @@ class ContentParent final
sSandboxBrokerPolicyFactory;
#endif
#ifdef NS_PRINTING
RefPtr<embedding::PrintingParent> mPrintingParent;
#endif
// This hashtable is used to run GetFilesHelper objects in the parent process.
// GetFilesHelper can be aborted by receiving RecvDeleteGetFilesRequest.
nsRefPtrHashtable<nsIDHashKey, GetFilesHelper> mGetFilesPendingRequests;

View file

@ -19,6 +19,7 @@ include protocol PHeapSnapshotTempFileHelper;
include protocol PProcessHangMonitor;
include protocol PImageBridge;
include protocol PRemoteLazyInputStream;
include protocol PRemotePrintJob;
include protocol PLoginReputation;
include protocol PMedia;
include protocol PNecko;
@ -26,7 +27,6 @@ include protocol PStreamFilter;
include protocol PGMPContent;
include protocol PGMPService;
include protocol PGMP;
include protocol PPrinting;
include protocol PChildToParentStream;
include protocol PParentToChildStream;
#ifdef MOZ_WEBSPEECH
@ -468,9 +468,9 @@ sync protocol PContent
manages PHandlerService;
manages PHeapSnapshotTempFileHelper;
manages PRemoteLazyInputStream;
manages PRemotePrintJob;
manages PMedia;
manages PNecko;
manages PPrinting;
manages PChildToParentStream;
manages PParentToChildStream;
#ifdef MOZ_WEBSPEECH
@ -1035,6 +1035,8 @@ child:
// details.
async InitNextGenLocalStorageEnabled(bool enabled);
async PRemotePrintJob();
parent:
async SynchronizeLayoutHistoryState(MaybeDiscardedBrowsingContext aContext,
@ -1085,8 +1087,6 @@ parent:
async PNecko();
async PPrinting();
async PChildToParentStream();
#ifdef MOZ_WEBSPEECH

View file

@ -0,0 +1,14 @@
<html>
<head>
<script>
document.addEventListener('DOMContentLoaded', async () => {
const peer = new RTCPeerConnection({ 'iceServers': [{ 'urls': 'stun:23.21.150.121' }] })
const offer = await peer.createOffer({ 'offerToReceiveVideo': true })
await peer.setRemoteDescription(offer)
const senders = peer.getSenders()
setTimeout(async () => await senders[1].setParameters({}), 334)
await peer.setRemoteDescription(offer)
})
</script>
</head>
</html>

View file

@ -38,3 +38,4 @@ load 1594136.html
load 1749308.html
load 1764915.html
load 1764933.html
load 1764940.html

View file

@ -302,6 +302,7 @@ struct ParamTraits<mozilla::dom::RTCInboundRtpStreamStats> {
WriteParam(aWriter, aParam.mNackCount);
WriteParam(aWriter, aParam.mFirCount);
WriteParam(aWriter, aParam.mPliCount);
WriteParam(aWriter, aParam.mFramesPerSecond);
WriteRTCReceivedRtpStreamStats(aWriter, aParam);
}
@ -312,6 +313,7 @@ struct ParamTraits<mozilla::dom::RTCInboundRtpStreamStats> {
ReadParam(aReader, &(aResult->mNackCount)) &&
ReadParam(aReader, &(aResult->mFirCount)) &&
ReadParam(aReader, &(aResult->mPliCount)) &&
ReadParam(aReader, &(aResult->mFramesPerSecond)) &&
ReadRTCReceivedRtpStreamStats(aReader, aResult);
}
};

View file

@ -8,6 +8,7 @@
#define _ENCODING_CONSTRAINTS_H_
#include <algorithm>
#include "mozilla/Maybe.h"
namespace mozilla {
class EncodingConstraints {
@ -15,7 +16,6 @@ class EncodingConstraints {
EncodingConstraints()
: maxWidth(0),
maxHeight(0),
maxFps(0),
maxFs(0),
maxBr(0),
maxPps(0),
@ -44,7 +44,7 @@ class EncodingConstraints {
uint32_t maxWidth;
uint32_t maxHeight;
uint32_t maxFps;
Maybe<double> maxFps;
uint32_t maxFs;
uint32_t maxBr;
uint32_t maxPps;

View file

@ -685,7 +685,7 @@ class ConfigureCodec {
}
}
videoCodec.mConstraints.maxFs = mVP8MaxFs;
videoCodec.mConstraints.maxFps = mVP8MaxFr;
videoCodec.mConstraints.maxFps = Some(mVP8MaxFr);
}
if (mUseTmmbr) {

View file

@ -430,11 +430,11 @@ nsTArray<RefPtr<RTCStatsPromise>> RTCRtpReceiver::GetStatsInternal() {
// Lastly, fill in video decoder stats
local.mFramesDecoded.Construct(videoStats->frames_decoded);
local.mFramesPerSecond.Construct(videoStats->decode_frame_rate);
/*
* Potential new stats that are now available upstream.
local.mFrameWidth.Construct(videoStats->width);
local.mFrameheight.Construct(videoStats->height);
local.mFramesPerSecond.Construct(videoStats->decode_frame_rate);
if (videoStats->qp_sum) {
local.mQpSum.Construct(*videoStats->qp_sum.value);
}

View file

@ -430,6 +430,13 @@ already_AddRefed<Promise> RTCRtpSender::SetParameters(
}
uniqueRids.insert(encoding.mRid.Value());
}
if (encoding.mMaxFramerate.WasPassed()) {
if (encoding.mMaxFramerate.Value() < 0.0f) {
p->MaybeRejectWithRangeError("maxFramerate must be non-negative");
return p.forget();
}
}
}
// TODO(bug 1401592): transaction ids, timing changes
@ -466,6 +473,9 @@ void RTCRtpSender::ApplyParameters(const RTCRtpParameters& aParameters) {
if (encoding.mMaxBitrate.WasPassed()) {
constraint.constraints.maxBr = encoding.mMaxBitrate.Value();
}
if (encoding.mMaxFramerate.WasPassed()) {
constraint.constraints.maxFps = Some(encoding.mMaxFramerate.Value());
}
constraint.constraints.scaleDownBy = encoding.mScaleResolutionDownBy;
constraints.push_back(constraint);
}

View file

@ -5,6 +5,7 @@
#ifndef _JSEPCODECDESCRIPTION_H_
#define _JSEPCODECDESCRIPTION_H_
#include <cmath>
#include <string>
#include "sdp/SdpMediaSection.h"
#include "sdp/SdpHelper.h"
@ -369,7 +370,7 @@ class JsepVideoCodecDescription : public JsepCodecDescription {
auto codec = MakeUnique<JsepVideoCodecDescription>("120", "VP8", 90000);
// Defaults for mandatory params
codec->mConstraints.maxFs = 12288; // Enough for 2048x1536
codec->mConstraints.maxFps = 60;
codec->mConstraints.maxFps = Some(60);
if (aUseRtx) {
codec->EnableRtx("124");
}
@ -380,7 +381,7 @@ class JsepVideoCodecDescription : public JsepCodecDescription {
auto codec = MakeUnique<JsepVideoCodecDescription>("121", "VP9", 90000);
// Defaults for mandatory params
codec->mConstraints.maxFs = 12288; // Enough for 2048x1536
codec->mConstraints.maxFps = 60;
codec->mConstraints.maxFps = Some(60);
if (aUseRtx) {
codec->EnableRtx("125");
}
@ -526,7 +527,12 @@ class JsepVideoCodecDescription : public JsepCodecDescription {
GetVP8Parameters(mDefaultPt, msection));
vp8Params.max_fs = mConstraints.maxFs;
vp8Params.max_fr = mConstraints.maxFps;
if (mConstraints.maxFps.isSome()) {
vp8Params.max_fr =
static_cast<unsigned int>(std::round(*mConstraints.maxFps));
} else {
vp8Params.max_fr = 60;
}
msection.SetFmtp(SdpFmtpAttributeList::Fmtp(mDefaultPt, vp8Params));
}
}
@ -736,7 +742,11 @@ class JsepVideoCodecDescription : public JsepCodecDescription {
GetVP8Parameters(mDefaultPt, remoteMsection));
mConstraints.maxFs = vp8Params.max_fs;
mConstraints.maxFps = vp8Params.max_fr;
// Right now, we treat max-fr=0 (or the absence of max-fr) as no limit.
// We will eventually want to stop doing this (bug 1762600).
if (vp8Params.max_fr) {
mConstraints.maxFps = Some(vp8Params.max_fr);
}
}
}

View file

@ -49,7 +49,6 @@
# include <netinet/in.h>
#endif
#define DEFAULT_VIDEO_MAX_FRAMERATE 30u
#define INVALID_RTP_PAYLOAD 255 // valid payload types are 0 to 127
namespace mozilla {
@ -138,11 +137,12 @@ unsigned int SelectSendFrameRate(const VideoCodecConfig& codecConfig,
cur_fs = mb_width * mb_height;
if (cur_fs > 0) { // in case no frames have been sent
new_framerate = codecConfig.mEncodingConstraints.maxMbps / cur_fs;
new_framerate =
MinIgnoreZero(new_framerate, codecConfig.mEncodingConstraints.maxFps);
}
}
new_framerate =
std::min(new_framerate, WebrtcVideoConduit::ToLibwebrtcMaxFramerate(
codecConfig.mEncodingConstraints.maxFps));
return new_framerate;
}
@ -242,7 +242,8 @@ bool operator==(const rtc::VideoSinkWants& aThis,
const rtc::VideoSinkWants& aOther) {
// This would have to be expanded should we make use of more members of
// rtc::VideoSinkWants.
return aThis.max_pixel_count == aOther.max_pixel_count;
return aThis.max_pixel_count == aOther.max_pixel_count &&
aThis.max_framerate_fps == aOther.max_framerate_fps;
}
// TODO: Make this a defaulted operator when we have c++20 (bug 1731036).
@ -385,7 +386,7 @@ WebrtcVideoConduit::WebrtcVideoConduit(
mBufferPool(false, SCALER_BUFFER_POOL_SIZE),
mEngineTransmitting(false),
mEngineReceiving(false),
mSendingFramerate(DEFAULT_VIDEO_MAX_FRAMERATE),
mMaxFramerateForAllStreams(std::numeric_limits<unsigned int>::max()),
mVideoLatencyTestEnable(aOptions.mVideoLatencyTestEnable),
mMinBitrate(aOptions.mMinBitrate),
mStartBitrate(aOptions.mStartBitrate),
@ -676,13 +677,12 @@ void WebrtcVideoConduit::OnControlConfigChange() {
this, streamCount);
{
const unsigned max_framerate =
codecConfig->mEncodingConstraints.maxFps > 0
? codecConfig->mEncodingConstraints.maxFps
: DEFAULT_VIDEO_MAX_FRAMERATE;
// maxFps inside codecConfig applies to all streams.
const unsigned maxFramerate =
ToLibwebrtcMaxFramerate(codecConfig->mEncodingConstraints.maxFps);
// apply restrictions from maxMbps/etc
mSendingFramerate = SelectSendFrameRate(*codecConfig, max_framerate,
mLastWidth, mLastHeight);
mMaxFramerateForAllStreams = SelectSendFrameRate(
*codecConfig, maxFramerate, mLastWidth, mLastHeight);
}
// So we can comply with b=TIAS/b=AS/maxbr=X when input resolution
@ -702,7 +702,7 @@ void WebrtcVideoConduit::OnControlConfigChange() {
mVideoStreamFactory = new rtc::RefCountedObject<VideoStreamFactory>(
*codecConfig, mControl.mCodecMode, mMinBitrate, mStartBitrate,
mPrefMaxBitrate, mNegotiatedMaxBitrate, mSendingFramerate);
mPrefMaxBitrate, mNegotiatedMaxBitrate, mMaxFramerateForAllStreams);
mEncoderConfig.video_stream_factory = mVideoStreamFactory.get();
// Reset the VideoAdapter. SelectResolution will ensure limits are set.
@ -1101,6 +1101,25 @@ void WebrtcVideoConduit::UnsetRemoteSSRC(uint32_t aSsrc) {
SetRemoteSSRCAndRestartAsNeeded(our_ssrc, 0);
}
/*static*/
unsigned WebrtcVideoConduit::ToLibwebrtcMaxFramerate(
const Maybe<double>& aMaxFramerate) {
Maybe<unsigned> negotiatedMaxFps;
if (aMaxFramerate.isSome()) {
// libwebrtc does not handle non-integer max framerate.
unsigned integerMaxFps = static_cast<unsigned>(std::round(*aMaxFramerate));
// libwebrtc crashes with a max framerate of 0, even though the
// spec says this is valid. For now, we treat this as no limit.
if (integerMaxFps) {
negotiatedMaxFps = Some(integerMaxFps);
}
}
// We do not use DEFAULT_VIDEO_MAX_FRAMERATE here; that is used at the very
// end in VideoStreamFactory, once codec-wide and per-encoding limits are
// known.
return negotiatedMaxFps.refOr(std::numeric_limits<unsigned int>::max());
}
Maybe<Ssrc> WebrtcVideoConduit::GetRemoteSSRC() const {
MOZ_ASSERT(mCallThread->IsOnCurrentThread());
// libwebrtc uses 0 to mean a lack of SSRC. That is not to spec.
@ -1308,17 +1327,32 @@ void WebrtcVideoConduit::SelectSendResolution(unsigned short width,
static_cast<int>(mCurSendCodecConfig->mEncodingConstraints.maxFs *
(16 * 16)));
}
mVideoAdapter->OnOutputFormatRequest(absl::optional<std::pair<int, int>>(),
max_fs, absl::optional<int>());
}
unsigned int framerate = SelectSendFrameRate(
mCurSendCodecConfig.ref(), mSendingFramerate, width, height);
if (mSendingFramerate != framerate) {
CSFLogDebug(LOGTAG, "%s: framerate changing to %u (from %u)", __FUNCTION__,
framerate, mSendingFramerate);
mSendingFramerate = framerate;
mVideoStreamFactory->SetSendingFramerate(mSendingFramerate);
unsigned int framerate_all_streams = SelectSendFrameRate(
mCurSendCodecConfig.ref(), mMaxFramerateForAllStreams, width, height);
if (mMaxFramerateForAllStreams != framerate_all_streams) {
CSFLogDebug(LOGTAG, "%s: framerate changing to %u (from %u)",
__FUNCTION__, framerate_all_streams,
mMaxFramerateForAllStreams);
mMaxFramerateForAllStreams = framerate_all_streams;
mVideoStreamFactory->SetMaxFramerateForAllStreams(
mMaxFramerateForAllStreams);
}
int framerate_with_wants;
if (framerate_all_streams > std::numeric_limits<int>::max()) {
framerate_with_wants = std::numeric_limits<int>::max();
} else {
framerate_with_wants = static_cast<int>(framerate_all_streams);
}
framerate_with_wants = std::min(
framerate_with_wants, mVideoBroadcaster.wants().max_framerate_fps);
CSFLogDebug(LOGTAG,
"%s: Calling OnOutputFormatRequest, max_fs=%d, max_fps=%d",
__FUNCTION__, max_fs, framerate_with_wants);
mVideoAdapter->OnOutputFormatRequest(absl::optional<std::pair<int, int>>(),
max_fs, framerate_with_wants);
}
}
@ -1821,8 +1855,10 @@ void WebrtcVideoConduit::DumpCodecDB() const {
CSFLogDebug(LOGTAG, "Payload Type: %d", entry.mType);
CSFLogDebug(LOGTAG, "Payload Max Frame Size: %d",
entry.mEncodingConstraints.maxFs);
CSFLogDebug(LOGTAG, "Payload Max Frame Rate: %d",
entry.mEncodingConstraints.maxFps);
if (entry.mEncodingConstraints.maxFps.isSome()) {
CSFLogDebug(LOGTAG, "Payload Max Frame Rate: %f",
*entry.mEncodingConstraints.maxFps);
}
}
}

View file

@ -161,6 +161,8 @@ class WebrtcVideoConduit
// Call thread.
void UnsetRemoteSSRC(uint32_t aSsrc) override;
static unsigned ToLibwebrtcMaxFramerate(const Maybe<double>& aMaxFramerate);
private:
void NotifyUnsetCurrentRemoteSSRC();
void SetRemoteSSRCConfig(uint32_t aSsrc, uint32_t aRtxSsrc);
@ -406,7 +408,7 @@ class WebrtcVideoConduit
Maybe<uint32_t> mLastRTPTimestampReceive;
// Accessed under mMutex.
unsigned int mSendingFramerate;
unsigned int mMaxFramerateForAllStreams;
// Accessed from any thread under mRendererMonitor.
uint64_t mVideoLatencyAvg = 0;

View file

@ -17,6 +17,8 @@ namespace mozilla {
#endif
#define LOGTAG "WebrtcVideoSessionConduit"
#define DEFAULT_VIDEO_MAX_FRAMERATE 30u
#define MB_OF(w, h) \
((unsigned int)((((w + 15) >> 4)) * ((unsigned int)((h + 15) >> 4))))
// For now, try to set the max rates well above the knee in the curve.
@ -115,8 +117,9 @@ void VideoStreamFactory::SetCodecMode(webrtc::VideoCodecMode aCodecMode) {
mCodecMode = aCodecMode;
}
void VideoStreamFactory::SetSendingFramerate(unsigned int aSendingFramerate) {
mSendingFramerate = aSendingFramerate;
void VideoStreamFactory::SetMaxFramerateForAllStreams(
unsigned int aMaxFramerate) {
mMaxFramerateForAllStreams = aMaxFramerate;
}
std::vector<webrtc::VideoStream> VideoStreamFactory::CreateEncoderStreams(
@ -210,8 +213,25 @@ std::vector<webrtc::VideoStream> VideoStreamFactory::CreateEncoderStreams(
continue;
}
// We want to ensure this picks up the current framerate, so indirect
video_stream.max_framerate = mSendingFramerate;
// mMaxFramerateForAllStreams is based on codec-wide stuff like fmtp, and
// hard-coded limits based on the source resolution.
// mCodecConfig.mEncodingConstraints.maxFps does not take the hard-coded
// limits into account, so we have mMaxFramerateForAllStreams which
// incorporates those. Per-encoding max framerate is based on parameters
// from JS, and maybe rid
unsigned int max_framerate = mMaxFramerateForAllStreams;
max_framerate = std::min(WebrtcVideoConduit::ToLibwebrtcMaxFramerate(
encoding.constraints.maxFps),
max_framerate);
if (max_framerate >= std::numeric_limits<int>::max()) {
// If nothing has specified any kind of limit (uncommon), pick something
// reasonable.
max_framerate = DEFAULT_VIDEO_MAX_FRAMERATE;
}
video_stream.max_framerate = static_cast<int>(max_framerate);
CSFLogInfo(LOGTAG, "%s Stream with RID %s maxFps=%d (global max fps = %u)",
__FUNCTION__, encoding.rid.c_str(), video_stream.max_framerate,
(unsigned)mMaxFramerateForAllStreams);
SelectBitrates(video_stream.width, video_stream.height, mMinBitrate,
mStartBitrate, encoding.constraints.maxBr, mPrefMaxBitrate,

View file

@ -34,9 +34,9 @@ class VideoStreamFactory
VideoStreamFactory(VideoCodecConfig aConfig,
webrtc::VideoCodecMode aCodecMode, int aMinBitrate,
int aStartBitrate, int aPrefMaxBitrate,
int aNegotiatedMaxBitrate, unsigned int aSendingFramerate)
int aNegotiatedMaxBitrate, unsigned int aMaxFramerate)
: mCodecMode(aCodecMode),
mSendingFramerate(aSendingFramerate),
mMaxFramerateForAllStreams(aMaxFramerate),
mCodecConfig(std::forward<VideoCodecConfig>(aConfig)),
mMinBitrate(aMinBitrate),
mStartBitrate(aStartBitrate),
@ -45,7 +45,7 @@ class VideoStreamFactory
mSimulcastAdapter(MakeUnique<cricket::VideoAdapter>()) {}
void SetCodecMode(webrtc::VideoCodecMode aCodecMode);
void SetSendingFramerate(unsigned int aSendingFramerate);
void SetMaxFramerateForAllStreams(unsigned int aMaxFramerate);
// This gets called off-main thread and may hold internal webrtc.org
// locks. May *NOT* lock the conduit's mutex, to avoid deadlocks.
@ -57,7 +57,7 @@ class VideoStreamFactory
Atomic<webrtc::VideoCodecMode> mCodecMode;
// The framerate we're currently sending at.
Atomic<unsigned int> mSendingFramerate;
Atomic<unsigned int> mMaxFramerateForAllStreams;
// The current send codec config, containing simulcast layer configs.
const VideoCodecConfig mCodecConfig;

View file

@ -609,6 +609,10 @@ void WebrtcGmpVideoEncoder::Encoded(
unit.capture_time_ms_ = capture_time.ms();
unit._completeFrame = true;
// Parse QP.
mH264BitstreamParser.ParseBitstream(unit.data(), unit.size());
mH264BitstreamParser.GetLastSliceQp(&unit.qp_);
// TODO: Currently the OpenH264 codec does not preserve any codec
// specific info passed into it and just returns default values.
// If this changes in the future, it would be nice to get rid of

View file

@ -46,6 +46,7 @@
#include "VideoConduit.h"
#include "api/video/video_frame_type.h"
#include "modules/video_coding/include/video_codec_interface.h"
#include "common_video/h264/h264_bitstream_parser.h"
#include "gmp-video-host.h"
#include "GMPVideoDecoderProxy.h"
@ -278,6 +279,7 @@ class WebrtcGmpVideoEncoder : public GMPVideoEncoderCallbackProxy,
GMPVideoCodec mCodecParams;
uint32_t mMaxPayloadSize;
webrtc::CodecSpecificInfo mCodecSpecificInfo;
webrtc::H264BitstreamParser mH264BitstreamParser;
// Protects mCallback
Mutex mCallbackMutex MOZ_UNANNOTATED;
webrtc::EncodedImageCallback* mCallback;

View file

@ -1188,7 +1188,12 @@ void RsdparsaSdpAttributeList::LoadRids(RustAttributeList* attributeList) {
EncodingConstraints parameters;
parameters.maxWidth = rid.params.max_width;
parameters.maxHeight = rid.params.max_height;
parameters.maxFps = rid.params.max_fps;
// Right now, we treat max-fps=0 and the absence of max-fps as no limit.
// We will eventually want to treat max-fps=0 as 0 frames per second, and
// the absence of max-fps as no limit (bug 1762632).
if (rid.params.max_fps) {
parameters.maxFps = Some(rid.params.max_fps);
}
parameters.maxFs = rid.params.max_fs;
parameters.maxBr = rid.params.max_br;
parameters.maxPps = rid.params.max_pps;

View file

@ -813,10 +813,11 @@ bool SdpRidAttributeList::Rid::ParseParameters(std::istream& is,
return false;
}
} else if (key == "max-fps") {
if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &constraints.maxFps,
error)) {
uint32_t maxFps;
if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &maxFps, error)) {
return false;
}
constraints.maxFps = Some(maxFps);
} else if (key == "max-fs") {
if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &constraints.maxFs,
error)) {

View file

@ -197,6 +197,7 @@ skip-if =
[test_peerConnection_setParameters.html]
[test_peerConnection_setParameters_scaleResolutionDownBy.html]
skip-if = (os == 'win' && processor == 'aarch64') # aarch64 due to bug 1537567
[test_peerConnection_setParameters_maxFramerate.html]
[test_peerConnection_setRemoteAnswerInHaveRemoteOffer.html]
[test_peerConnection_setRemoteAnswerInStable.html]
[test_peerConnection_setRemoteOfferInHaveLocalOffer.html]

View file

@ -26,6 +26,7 @@ const statsExpectedByType = {
"pliCount",
"framesDecoded",
"discardedPackets",
"framesPerSecond",
],
unimplemented: [
"mediaTrackId",
@ -504,6 +505,12 @@ function pedanticChecks(report) {
`${stat.type}.discardedPackets is a sane number for a short test. ` +
`value=${stat.discardedPackets}`
);
// framesPerSecond
ok(
stat.framesPerSecond > 0 && stat.framesPerSecond < 70,
`${stat.type}.framesPerSecond is a sane number for a short ` +
`${stat.kind} test. value=${stat.framesPerSecond}`
);
// framesDecoded
ok(
stat.framesDecoded > 0 && stat.framesDecoded < 1000000,
@ -603,12 +610,27 @@ function pedanticChecks(report) {
// qpSum
// This is supported for all of our vpx codecs (on the encode side, see
// bug 1519590)
if (report.get(stat.codecId).mimeType.includes("VP")) {
const mimeType = report.get(stat.codecId).mimeType;
if (mimeType.includes("VP")) {
ok(
stat.qpSum >= 0,
`${stat.type}.qpSum is a sane number (${stat.kind}) ` +
`for ${report.get(stat.codecId).mimeType}. value=${stat.qpSum}`
);
} else if (mimeType.includes("H264")) {
// OpenH264 encoder records QP so we check for either condition.
if (!stat.qpSum && !("qpSum" in stat)) {
ok(
!stat.qpSum && !("qpSum" in stat),
`${stat.type}.qpSum absent for ${report.get(stat.codecId).mimeType}`
);
} else {
ok(
stat.qpSum >= 0,
`${stat.type}.qpSum is a sane number (${stat.kind}) ` +
`for ${report.get(stat.codecId).mimeType}. value=${stat.qpSum}`
);
}
} else {
ok(
!stat.qpSum && !("qpSum" in stat),

View file

@ -23,6 +23,7 @@ function parameterstest(pc) {
var validateEncoding = (a, b) => {
is(a.rid, b.rid, "same rid");
is(a.maxBitrate, b.maxBitrate, "same maxBitrate");
is(a.maxFramerate, b.maxFramerate, "same maxFramerate");
is(a.scaleResolutionDownBy, b.scaleResolutionDownBy,
"same scaleResolutionDownBy");
};
@ -51,6 +52,9 @@ function parameterstest(pc) {
{ rid: "bar", maxBitrate: 10000, scaleResolutionDownBy: 4 }]
}],
[{ encodings: [{ maxBitrate: 10000, scaleResolutionDownBy: 4 }]}],
[{ encodings: [{ maxFramerate: 0.0, scaleResolutionDownBy: 1 }]}],
[{ encodings: [{ maxFramerate: 30.5, scaleResolutionDownBy: 1 }]}],
[{ encodings: [{ maxFramerate: -1, scaleResolutionDownBy: 1 }]}, "RangeError", "maxFramerate must be non-negative"],
[{ encodings: [{ maxBitrate: 40000 },
{ rid: "bar", maxBitrate: 10000 }] }, "TypeError", "Missing rid"],
[{ encodings: [{ rid: "foo", maxBitrate: 40000 },

View file

@ -0,0 +1,60 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "1611957",
title: "Live-updating maxFramerate"
});
let sender, receiver;
async function checkMaxFrameRate(rate) {
sender.setParameters({ encodings: [{ maxFramerate: rate }] });
await wait(2000);
const stats = Array.from((await receiver.getStats()).values());
const inboundRtp = stats.find(stat => stat.type == "inbound-rtp");
info(`inbound-rtp stats: ${JSON.stringify(inboundRtp)}`);
const fps = inboundRtp.framesPerSecond;
ok(fps <= (rate * 1.1) + 1, `fps is an appropriate value (${fps}) for rate (${rate})`);
}
runNetworkTest(async function (options) {
let test = new PeerConnectionTest(options);
test.setMediaConstraints([{video: true}], []);
test.chain.append([
function CHECK_PRECONDITIONS() {
is(test.pcLocal._pc.getSenders().length, 1,
"Should have 1 local sender");
is(test.pcRemote._pc.getReceivers().length, 1,
"Should have 1 remote receiver");
sender = test.pcLocal._pc.getSenders()[0];
receiver = test.pcRemote._pc.getReceivers()[0];
},
function PC_LOCAL_SET_MAX_FRAMERATE_2() {
return checkMaxFrameRate(2);
},
function PC_LOCAL_SET_MAX_FRAMERATE_4() {
return checkMaxFrameRate(4);
},
function PC_LOCAL_SET_MAX_FRAMERATE_15() {
return checkMaxFrameRate(15);
},
function PC_LOCAL_SET_MAX_FRAMERATE_8() {
return checkMaxFrameRate(8);
},
function PC_LOCAL_SET_MAX_FRAMERATE_1() {
return checkMaxFrameRate(1);
},
]);
await test.run();
});
</script>
</pre>
</body>
</html>

View file

@ -27,6 +27,7 @@
#include "mozilla/Preferences.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/WorkerRunnable.h"
#include "mozilla/dom/WorkerScope.h"
#define PERFLOG(msg, ...) printf_stderr(msg, ##__VA_ARGS__)
@ -64,6 +65,27 @@ already_AddRefed<Performance> Performance::CreateForWorker(
return performance.forget();
}
/* static */
already_AddRefed<Performance> Performance::Get(JSContext* aCx,
nsIGlobalObject* aGlobal) {
RefPtr<Performance> performance;
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal);
if (window) {
performance = window->GetPerformance();
} else {
const WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
if (!workerPrivate) {
return nullptr;
}
WorkerGlobalScope* scope = workerPrivate->GlobalScope();
MOZ_ASSERT(scope);
performance = scope->GetPerformance();
}
return performance.forget();
}
Performance::Performance(nsIGlobalObject* aGlobal, bool aSystemPrincipal)
: DOMEventTargetHelper(aGlobal),
mResourceTimingBufferSize(kDefaultResourceTimingBufferSize),
@ -315,20 +337,38 @@ struct UserTimingMarker {
}
};
void Performance::Mark(const nsAString& aName, ErrorResult& aRv) {
// We add nothing when 'privacy.resistFingerprinting' is on.
if (nsContentUtils::ShouldResistFingerprinting()) {
return;
already_AddRefed<PerformanceMark> Performance::Mark(
JSContext* aCx, const nsAString& aName,
const PerformanceMarkOptions& aMarkOptions, ErrorResult& aRv) {
nsCOMPtr<nsIGlobalObject> parent = GetParentObject();
if (!parent || parent->IsDying() || !parent->HasJSGlobal()) {
aRv.ThrowInvalidStateError("Global object is unavailable");
return nullptr;
}
if (IsPerformanceTimingAttribute(aName)) {
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
return;
GlobalObject global(aCx, parent->GetGlobalJSObject());
if (global.Failed()) {
aRv.ThrowInvalidStateError("Global object is unavailable");
return nullptr;
}
RefPtr<PerformanceMark> performanceMark =
new PerformanceMark(GetParentObject(), aName, Now());
InsertUserEntry(performanceMark);
PerformanceMark::Constructor(global, aName, aMarkOptions, aRv);
if (aRv.Failed()) {
return nullptr;
}
// To avoid fingerprinting in User Timing L2, we didn't add marks to the
// buffer so the user could not get timing data (which can be used to
// fingerprint) from the API. This may no longer be necessary (since
// performance.now() has reduced precision to protect against fingerprinting
// and performance.mark's primary fingerprinting issue is probably this timing
// data) but we need to do a more thorough reanalysis before we remove the
// fingerprinting protection. For now, we preserve the User Timing L2 behavior
// while supporting User Timing L3.
if (!nsContentUtils::ShouldResistFingerprinting()) {
InsertUserEntry(performanceMark);
}
if (profiler_thread_is_being_profiled_for_markers()) {
Maybe<uint64_t> innerWindowId;
@ -339,6 +379,8 @@ void Performance::Mark(const nsAString& aName, ErrorResult& aRv) {
MarkerInnerWindowId(innerWindowId), UserTimingMarker{},
aName, /* aIsMeasure */ false, Nothing{}, Nothing{});
}
return performanceMark.forget();
}
void Performance::ClearMarks(const Optional<nsAString>& aName) {

View file

@ -22,6 +22,8 @@ class ErrorResult;
namespace dom {
class PerformanceEntry;
class PerformanceMark;
struct PerformanceMarkOptions;
class PerformanceNavigation;
class PerformancePaintTiming;
class PerformanceObserver;
@ -47,6 +49,10 @@ class Performance : public DOMEventTargetHelper {
static already_AddRefed<Performance> CreateForWorker(
WorkerPrivate* aWorkerPrivate);
// This will return nullptr if called outside of a Window or Worker.
static already_AddRefed<Performance> Get(JSContext* aCx,
nsIGlobalObject* aGlobal);
JSObject* WrapObject(JSContext* cx,
JS::Handle<JSObject*> aGivenProto) override;
@ -72,7 +78,9 @@ class Performance : public DOMEventTargetHelper {
DOMHighResTimeStamp TimeOrigin();
void Mark(const nsAString& aName, ErrorResult& aRv);
already_AddRefed<PerformanceMark> Mark(
JSContext* aCx, const nsAString& aName,
const PerformanceMarkOptions& aMarkOptions, ErrorResult& aRv);
void ClearMarks(const Optional<nsAString>& aName);
@ -138,6 +146,10 @@ class Performance : public DOMEventTargetHelper {
void QueueNotificationObserversTask();
virtual bool IsPerformanceTimingAttribute(const nsAString& aName) {
return false;
}
protected:
Performance(nsIGlobalObject* aGlobal, bool aSystemPrincipal);
Performance(nsPIDOMWindowInner* aWindow, bool aSystemPrincipal);
@ -156,10 +168,6 @@ class Performance : public DOMEventTargetHelper {
virtual DOMHighResTimeStamp CreationTime() const = 0;
virtual bool IsPerformanceTimingAttribute(const nsAString& aName) {
return false;
}
virtual DOMHighResTimeStamp GetPerformanceTimingFromString(
const nsAString& aTimingName) {
return 0;

View file

@ -6,21 +6,106 @@
#include "PerformanceMark.h"
#include "MainThreadUtils.h"
#include "nsContentUtils.h"
#include "Performance.h"
#include "mozilla/dom/MessagePortBinding.h"
#include "mozilla/dom/PerformanceBinding.h"
#include "mozilla/dom/PerformanceMarkBinding.h"
using namespace mozilla::dom;
PerformanceMark::PerformanceMark(nsISupports* aParent, const nsAString& aName,
DOMHighResTimeStamp aStartTime)
: PerformanceEntry(aParent, aName, u"mark"_ns), mStartTime(aStartTime) {}
DOMHighResTimeStamp aStartTime,
const JS::Handle<JS::Value>& aDetail)
: PerformanceEntry(aParent, aName, u"mark"_ns),
mStartTime(aStartTime),
mDetail(aDetail) {
mozilla::HoldJSObjects(this);
}
PerformanceMark::~PerformanceMark() = default;
already_AddRefed<PerformanceMark> PerformanceMark::Constructor(
const GlobalObject& aGlobal, const nsAString& aMarkName,
const PerformanceMarkOptions& aMarkOptions, ErrorResult& aRv) {
const nsCOMPtr<nsIGlobalObject> global =
do_QueryInterface(aGlobal.GetAsSupports());
return PerformanceMark::Constructor(aGlobal.Context(), global, aMarkName,
aMarkOptions, aRv);
}
already_AddRefed<PerformanceMark> PerformanceMark::Constructor(
JSContext* aCx, nsIGlobalObject* aGlobal, const nsAString& aMarkName,
const PerformanceMarkOptions& aMarkOptions, ErrorResult& aRv) {
RefPtr<Performance> performance = Performance::Get(aCx, aGlobal);
if (!performance) {
// This is similar to the message that occurs when accessing `performance`
// from outside a valid global.
aRv.ThrowTypeError(
"can't access PerformanceMark constructor, performance is null");
return nullptr;
}
if (performance->IsPerformanceTimingAttribute(aMarkName)) {
aRv.ThrowSyntaxError("markName cannot be a performance timing attribute");
return nullptr;
}
DOMHighResTimeStamp startTime = aMarkOptions.mStartTime.WasPassed()
? aMarkOptions.mStartTime.Value()
: performance->Now();
if (startTime < 0) {
aRv.ThrowTypeError("Expected startTime >= 0");
return nullptr;
}
JS::Rooted<JS::Value> detail(aCx);
if (aMarkOptions.mDetail.isNullOrUndefined()) {
detail.setNull();
} else {
StructuredSerializeOptions serializeOptions;
JS::Rooted<JS::Value> valueToClone(aCx, aMarkOptions.mDetail);
nsContentUtils::StructuredClone(aCx, aGlobal, valueToClone,
serializeOptions, &detail, aRv);
if (aRv.Failed()) {
return nullptr;
}
}
return do_AddRef(new PerformanceMark(aGlobal, aMarkName, startTime, detail));
}
PerformanceMark::~PerformanceMark() { mozilla::DropJSObjects(this); }
NS_IMPL_CYCLE_COLLECTION_CLASS(PerformanceMark)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PerformanceMark,
PerformanceEntry)
tmp->mDetail.setUndefined();
mozilla::DropJSObjects(tmp);
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PerformanceMark,
PerformanceEntry)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PerformanceMark,
PerformanceEntry)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mDetail)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(PerformanceMark,
PerformanceEntry)
JSObject* PerformanceMark::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) {
return PerformanceMark_Binding::Wrap(aCx, this, aGivenProto);
}
void PerformanceMark::GetDetail(JSContext* aCx,
JS::MutableHandle<JS::Value> aRetval) {
// Return a copy so that the PerformanceMark.detail reference always returns
// the same value. However, the contents of detail can be mutated. The spec
// isn't clear if this is okay but it matches Chrome's behavior.
aRetval.set(mDetail);
}
size_t PerformanceMark::SizeOfIncludingThis(
mozilla::MallocSizeOf aMallocSizeOf) const {
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);

View file

@ -12,23 +12,45 @@
namespace mozilla {
namespace dom {
struct PerformanceMarkOptions;
// http://www.w3.org/TR/user-timing/#performancemark
class PerformanceMark final : public PerformanceEntry {
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(PerformanceMark,
PerformanceEntry);
private:
PerformanceMark(nsISupports* aParent, const nsAString& aName,
DOMHighResTimeStamp aStartTime);
DOMHighResTimeStamp aStartTime,
const JS::Handle<JS::Value>& aDetail);
public:
static already_AddRefed<PerformanceMark> Constructor(
const GlobalObject& aGlobal, const nsAString& aMarkName,
const PerformanceMarkOptions& aMarkOptions, ErrorResult& aRv);
static already_AddRefed<PerformanceMark> Constructor(
JSContext* aCx, nsIGlobalObject* aGlobal, const nsAString& aMarkName,
const PerformanceMarkOptions& aMarkOptions, ErrorResult& aRv);
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
virtual DOMHighResTimeStamp StartTime() const override { return mStartTime; }
void GetDetail(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval);
size_t SizeOfIncludingThis(
mozilla::MallocSizeOf aMallocSizeOf) const override;
protected:
virtual ~PerformanceMark();
DOMHighResTimeStamp mStartTime;
private:
JS::Heap<JS::Value> mDetail;
};
} // namespace dom

View file

@ -13,6 +13,7 @@ support-files =
[test_performance_observer.html]
[test_performance_user_timing.html]
[test_performance_user_timing_dying_global.html]
[test_performance_navigation_timing.html]
[test_performance_paint_timing.html]
support-files =

View file

@ -0,0 +1,57 @@
<!DOCTYPE html>
<html>
<head>
<title>Test for User Timing APIs on dying globals</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript">
// We must wait for the iframe to load.
SimpleTest.waitForExplicitFinish();
window.addEventListener('load', () => {
const dyingWindow = initTest();
ok(true, 'Initialization complete');
testDoesNotCrash(dyingWindow);
SimpleTest.finish();
});
function initTest() {
// We create a dying global by creating an iframe, keeping a
// reference to it, and removing it.
const iframe = document.querySelector('iframe');
const iframeWindow = iframe.contentWindow;
// We want to call the User Timing functions in the context of
// the dying global. However, we can't call constructors
// directly on a reference to a window so we have to wrap it.
iframeWindow.newPerformanceMark = () => {
new PerformanceMark('constructor', {detail: 'constructorDetail'});
};
// Send the global to a dying state.
iframe.remove();
return iframeWindow;
}
function testDoesNotCrash(dyingWindow) {
ok(true, 'Running testDoesNotCrash');
dyingWindow.newPerformanceMark();
ok(true, 'new PerformanceMark() on dying global did not crash');
try {
dyingWindow.performance.mark('markMethod', {detail: 'markMethodDetail'});
} catch (e) {
is(e.code, e.INVALID_STATE_ERR, 'performance.mark on dying global threw expected exception');
}
ok(true, 'performance.mark on dying global did not crash');
dyingWindow.performance.measure('measureMethod');
ok(true, 'performance.measure on dying global did not crash');
}
</script>
</head>
<body>
<iframe width="200" height="200" src="about:blank"></iframe>
</body>
</html>

View file

@ -71,10 +71,12 @@ static bool DoWindowsPathCheck() {
#ifdef XP_WIN
# ifdef DEBUG
return true;
# endif // DEBUG
# else // DEBUG
return xpc::IsInAutomation();
#endif // XP_WIN
# endif // DEBUG
#else // XP_WIN
return false;
#endif // XP_WIN
}
/* static */

View file

@ -22,6 +22,7 @@ interface MessagePort : EventTarget {
attribute EventHandler onmessageerror;
};
// Used to declare which objects should be transferred.
dictionary StructuredSerializeOptions {
sequence<object> transfer = [];
};

View file

@ -65,11 +65,17 @@ partial interface Performance {
readonly attribute object mozMemory;
};
// https://w3c.github.io/user-timing/#extensions-performance-interface
dictionary PerformanceMarkOptions {
any detail;
DOMHighResTimeStamp startTime;
};
// https://w3c.github.io/user-timing/#extensions-performance-interface
[Exposed=(Window,Worker)]
partial interface Performance {
[Throws]
void mark(DOMString markName);
PerformanceMark mark(DOMString markName, optional PerformanceMarkOptions markOptions = {});
void clearMarks(optional DOMString markName);
[Throws]
void measure(DOMString measureName, optional DOMString startMark, optional DOMString endMark);
@ -81,4 +87,3 @@ partial interface Performance {
[Pref="dom.enable_event_timing", SameObject]
readonly attribute EventCounts eventCounts;
};

View file

@ -10,4 +10,7 @@
[Exposed=(Window,Worker)]
interface PerformanceMark : PerformanceEntry
{
[Throws]
constructor(DOMString markName, optional PerformanceMarkOptions markOptions = {});
readonly attribute any detail;
};

View file

@ -38,6 +38,8 @@ dictionary RTCRtpEncodingParameters {
RTCDegradationPreference degradationPreference = "balanced";
DOMString rid;
float scaleResolutionDownBy = 1.0;
// From https://w3c.github.io/webrtc-extensions/#rtcrtpencodingparameters-dictionary
double maxFramerate;
};
dictionary RTCRtpHeaderExtensionParameters {

View file

@ -68,6 +68,7 @@ dictionary RTCInboundRtpStreamStats : RTCReceivedRtpStreamStats {
unsigned long nackCount;
unsigned long firCount;
unsigned long pliCount;
double framesPerSecond;
};
dictionary RTCRemoteInboundRtpStreamStats : RTCReceivedRtpStreamStats {

View file

@ -111,12 +111,6 @@ namespace workerinternals {
static_assert(MAX_WORKERS_PER_DOMAIN >= 1,
"We should allow at least one worker per domain.");
// The number of seconds that idle threads can hang around before being killed.
#define IDLE_THREAD_TIMEOUT_SEC 30
// The maximum number of threads that can be idle at one time.
#define MAX_IDLE_THREADS 20
#define PREF_WORKERS_PREFIX "dom.workers."
#define PREF_WORKERS_MAX_PER_DOMAIN PREF_WORKERS_PREFIX "maxPerDomain"
@ -1307,22 +1301,12 @@ bool RuntimeService::ScheduleWorker(WorkerPrivate& aWorkerPrivate) {
return true;
}
SafeRefPtr<WorkerThread> thread;
{
MutexAutoLock lock(mMutex);
if (!mIdleThreadArray.IsEmpty()) {
thread = std::move(mIdleThreadArray.PopLastElement().mThread);
}
}
const WorkerThreadFriendKey friendKey;
SafeRefPtr<WorkerThread> thread = WorkerThread::Create(friendKey);
if (!thread) {
thread = WorkerThread::Create(friendKey);
if (!thread) {
UnregisterWorker(aWorkerPrivate);
return false;
}
UnregisterWorker(aWorkerPrivate);
return false;
}
if (NS_FAILED(thread->SetPriority(nsISupportsPriority::PRIORITY_NORMAL))) {
@ -1342,54 +1326,6 @@ bool RuntimeService::ScheduleWorker(WorkerPrivate& aWorkerPrivate) {
return true;
}
// static
void RuntimeService::ShutdownIdleThreads(nsITimer* aTimer,
void* /* aClosure */) {
AssertIsOnMainThread();
RuntimeService* runtime = RuntimeService::GetService();
NS_ASSERTION(runtime, "This should never be null!");
NS_ASSERTION(aTimer == runtime->mIdleThreadTimer, "Wrong timer!");
// Cheat a little and grab all threads that expire within one second of now.
const TimeStamp now = TimeStamp::NowLoRes() + TimeDuration::FromSeconds(1);
TimeStamp nextExpiration;
AutoTArray<SafeRefPtr<WorkerThread>, 20> expiredThreads;
{
MutexAutoLock lock(runtime->mMutex);
for (auto& info : runtime->mIdleThreadArray) {
if (info.mExpirationTime > now) {
nextExpiration = info.mExpirationTime;
break;
}
expiredThreads.AppendElement(std::move(info.mThread));
}
runtime->mIdleThreadArray.RemoveElementsAt(0, expiredThreads.Length());
}
if (!nextExpiration.IsNull()) {
const TimeDuration delta = nextExpiration - TimeStamp::NowLoRes();
const uint32_t delay = delta > TimeDuration{} ? delta.ToMilliseconds() : 0;
// Reschedule the timer.
MOZ_ALWAYS_SUCCEEDS(aTimer->InitWithNamedFuncCallback(
ShutdownIdleThreads, nullptr, delay, nsITimer::TYPE_ONE_SHOT,
"RuntimeService::ShutdownIdleThreads"));
}
for (const auto& expiredThread : expiredThreads) {
if (NS_FAILED(expiredThread->Shutdown())) {
NS_WARNING("Failed to shutdown thread!");
}
}
}
nsresult RuntimeService::Init() {
AssertIsOnMainThread();
@ -1408,9 +1344,6 @@ nsresult RuntimeService::Init() {
do_GetService(kStreamTransportServiceCID, &rv);
NS_ENSURE_TRUE(sts, NS_ERROR_FAILURE);
mIdleThreadTimer = NS_NewTimer();
NS_ENSURE_STATE(mIdleThreadTimer);
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
@ -1659,13 +1592,6 @@ void RuntimeService::Cleanup() {
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
NS_WARNING_ASSERTION(obs, "Failed to get observer service?!");
if (mIdleThreadTimer) {
if (NS_FAILED(mIdleThreadTimer->Cancel())) {
NS_WARNING("Failed to cancel idle timer!");
}
mIdleThreadTimer = nullptr;
}
{
MutexAutoLock lock(mMutex);
@ -1676,33 +1602,6 @@ void RuntimeService::Cleanup() {
nsIThread* currentThread = NS_GetCurrentThread();
NS_ASSERTION(currentThread, "This should never be null!");
// Shut down any idle threads.
if (!mIdleThreadArray.IsEmpty()) {
AutoTArray<SafeRefPtr<WorkerThread>, 20> idleThreads;
idleThreads.SetCapacity(mIdleThreadArray.Length());
#ifdef DEBUG
const bool anyNullThread = std::any_of(
mIdleThreadArray.begin(), mIdleThreadArray.end(),
[](const auto& entry) { return entry.mThread == nullptr; });
MOZ_ASSERT(!anyNullThread);
#endif
std::transform(mIdleThreadArray.begin(), mIdleThreadArray.end(),
MakeBackInserter(idleThreads),
[](auto& entry) { return std::move(entry.mThread); });
mIdleThreadArray.Clear();
MutexAutoUnlock unlock(mMutex);
for (const auto& idleThread : idleThreads) {
if (NS_FAILED(idleThread->Shutdown())) {
NS_WARNING("Failed to shutdown thread!");
}
}
}
// And make sure all their final messages have run and all their threads
// have joined.
while (mDomainMap.Count()) {
@ -1861,47 +1760,6 @@ void RuntimeService::PropagateStorageAccessPermissionGranted(
}
}
void RuntimeService::NoteIdleThread(SafeRefPtr<WorkerThread> aThread) {
AssertIsOnMainThread();
MOZ_ASSERT(aThread);
bool shutdownThread = mShuttingDown;
bool scheduleTimer = false;
if (!shutdownThread) {
static TimeDuration timeout =
TimeDuration::FromSeconds(IDLE_THREAD_TIMEOUT_SEC);
const TimeStamp expirationTime = TimeStamp::NowLoRes() + timeout;
MutexAutoLock lock(mMutex);
const uint32_t previousIdleCount = mIdleThreadArray.Length();
if (previousIdleCount < MAX_IDLE_THREADS) {
IdleThreadInfo* const info = mIdleThreadArray.AppendElement();
info->mThread = std::move(aThread);
info->mExpirationTime = expirationTime;
scheduleTimer = previousIdleCount == 0;
} else {
shutdownThread = true;
}
}
MOZ_ASSERT_IF(shutdownThread, !scheduleTimer);
MOZ_ASSERT_IF(scheduleTimer, !shutdownThread);
// Too many idle threads, just shut this one down.
if (shutdownThread) {
MOZ_ALWAYS_SUCCEEDS(aThread->Shutdown());
} else if (scheduleTimer) {
MOZ_ALWAYS_SUCCEEDS(mIdleThreadTimer->InitWithNamedFuncCallback(
ShutdownIdleThreads, nullptr, IDLE_THREAD_TIMEOUT_SEC * 1000,
nsITimer::TYPE_ONE_SHOT, "RuntimeService::ShutdownIdleThreads"));
}
}
template <typename Func>
void RuntimeService::BroadcastAllWorkers(const Func& aFunc) {
AssertIsOnMainThread();
@ -2255,11 +2113,7 @@ WorkerThreadPrimaryRunnable::FinishedRunnable::Run() {
AssertIsOnMainThread();
SafeRefPtr<WorkerThread> thread = std::move(mThread);
RuntimeService* rts = RuntimeService::GetService();
if (rts) {
rts->NoteIdleThread(std::move(thread));
} else if (thread->ShutdownRequired()) {
if (thread->ShutdownRequired()) {
MOZ_ALWAYS_SUCCEEDS(thread->Shutdown());
}

View file

@ -22,7 +22,6 @@
#include "nsHashKeys.h"
#include "nsTArray.h"
class nsITimer;
class nsPIDOMWindowInner;
namespace mozilla {
@ -55,28 +54,17 @@ class RuntimeService final : public nsIObserver {
}
};
struct IdleThreadInfo {
SafeRefPtr<WorkerThread> mThread;
mozilla::TimeStamp mExpirationTime;
};
mozilla::Mutex mMutex;
// Protected by mMutex.
nsClassHashtable<nsCStringHashKey, WorkerDomainInfo> mDomainMap
GUARDED_BY(mMutex);
// Protected by mMutex.
nsTArray<IdleThreadInfo> mIdleThreadArray GUARDED_BY(mMutex);
// *Not* protected by mMutex.
nsClassHashtable<nsPtrHashKey<const nsPIDOMWindowInner>,
nsTArray<WorkerPrivate*> >
mWindowMap;
// Only used on the main thread.
nsCOMPtr<nsITimer> mIdleThreadTimer;
static UniquePtr<workerinternals::JSSettings> sDefaultJSSettings;
public:
@ -127,8 +115,6 @@ class RuntimeService final : public nsIObserver {
return mNavigatorProperties;
}
void NoteIdleThread(SafeRefPtr<WorkerThread> aThread);
static void GetDefaultJSSettings(workerinternals::JSSettings& aSettings) {
AssertIsOnMainThread();
aSettings = *sDefaultJSSettings;
@ -203,8 +189,6 @@ class RuntimeService final : public nsIObserver {
bool ScheduleWorker(WorkerPrivate& aWorkerPrivate);
static void ShutdownIdleThreads(nsITimer* aTimer, void* aClosure);
template <typename Func>
void BroadcastAllWorkers(const Func& aFunc);
};

View file

@ -299,7 +299,6 @@ struct ScriptLoadInfo {
nsCOMPtr<nsIInputStream> mCacheReadStream;
nsCOMPtr<nsIChannel> mChannel;
Maybe<ClientInfo> mReservedClientInfo;
nsresult mLoadResult = NS_ERROR_NOT_INITIALIZED;
// If |mScriptIsUTF8|, then |mUTF8| is active, otherwise |mUTF16| is active.
@ -360,8 +359,6 @@ struct ScriptLoadInfo {
CacheStatus mCacheStatus = Uncached;
nsLoadFlags mLoadFlags = nsIRequest::LOAD_NORMAL;
Maybe<bool> mMutedErrorFlag;
bool Finished() const {
@ -374,13 +371,14 @@ class ScriptLoaderRunnable;
class ScriptExecutorRunnable final : public MainThreadWorkerSyncRunnable {
ScriptLoaderRunnable& mScriptLoader;
const bool mIsWorkerScript;
const Span<ScriptLoadInfo> mLoadInfosAlreadyExecuted, mLoadInfosToExecute;
const Span<ScriptLoadInfo> mLoadInfosToExecute;
const bool mAllScriptsExecutable;
public:
ScriptExecutorRunnable(ScriptLoaderRunnable& aScriptLoader,
nsIEventTarget* aSyncLoopTarget, bool aIsWorkerScript,
Span<ScriptLoadInfo> aLoadInfosAlreadyExecuted,
Span<ScriptLoadInfo> aLoadInfosToExecute);
Span<ScriptLoadInfo> aLoadInfosToExecute,
bool aAllScriptsExecutable);
private:
~ScriptExecutorRunnable() = default;
@ -400,6 +398,9 @@ class ScriptExecutorRunnable final : public MainThreadWorkerSyncRunnable {
void ShutdownScriptLoader(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
bool aResult, bool aMutedError);
bool EvaluateLoadInfo(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
ScriptLoadInfo& aLoadInfo);
void LogExceptionToConsole(JSContext* aCx, WorkerPrivate* WorkerPrivate);
bool AllScriptsExecutable() const;
@ -669,6 +670,8 @@ class ScriptLoaderRunnable final : public nsIRunnable, public nsINamed {
WorkerScriptType mWorkerScriptType;
bool mCanceledMainThread;
ErrorResult& mRv;
bool mExecutionAborted = false;
bool mMutedErrorFlag = false;
public:
NS_DECL_THREADSAFE_ISUPPORTS
@ -1022,7 +1025,7 @@ class ScriptLoaderRunnable final : public nsIRunnable, public nsINamed {
nsresult& rv = aLoadInfo.mLoadResult;
nsLoadFlags loadFlags = aLoadInfo.mLoadFlags;
nsLoadFlags loadFlags = mWorkerPrivate->GetLoadFlags();
// Get the top-level worker.
WorkerPrivate* topWorkerPrivate = mWorkerPrivate;
@ -1101,7 +1104,7 @@ class ScriptLoaderRunnable final : public nsIRunnable, public nsINamed {
}
if (IsMainWorkerScript()) {
MOZ_DIAGNOSTIC_ASSERT(aLoadInfo.mReservedClientInfo.isSome());
MOZ_DIAGNOSTIC_ASSERT(mClientInfo.isSome());
// In order to get the correct foreign partitioned prinicpal, we need to
// set the `IsThirdPartyContextToTopWindow` to the channel's loadInfo.
@ -1111,9 +1114,9 @@ class ScriptLoaderRunnable final : public nsIRunnable, public nsINamed {
loadInfo->SetIsThirdPartyContextToTopWindow(
mWorkerPrivate->IsThirdPartyContextToTopWindow());
rv = AddClientChannelHelper(
channel, std::move(aLoadInfo.mReservedClientInfo),
Maybe<ClientInfo>(), mWorkerPrivate->HybridEventTarget());
rv = AddClientChannelHelper(channel, std::move(mClientInfo),
Maybe<ClientInfo>(),
mWorkerPrivate->HybridEventTarget());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -1532,8 +1535,8 @@ class ScriptLoaderRunnable final : public nsIRunnable, public nsINamed {
RefPtr<ScriptExecutorRunnable> runnable = new ScriptExecutorRunnable(
*this, mSyncLoopTarget, IsMainWorkerScript(),
Span{begin, maybeRangeToExecute->first},
Span{maybeRangeToExecute->first, maybeRangeToExecute->second});
Span{maybeRangeToExecute->first, maybeRangeToExecute->second},
/* AllLoadInfosExecutable = */ end == maybeRangeToExecute->second);
if (!runnable->Dispatch()) {
MOZ_ASSERT(false, "This should never fail!");
}
@ -2039,21 +2042,14 @@ class ChannelGetterRunnable final : public WorkerMainThreadRunnable {
ScriptExecutorRunnable::ScriptExecutorRunnable(
ScriptLoaderRunnable& aScriptLoader, nsIEventTarget* aSyncLoopTarget,
bool aIsWorkerScript, Span<ScriptLoadInfo> aLoadInfosAlreadyExecuted,
Span<ScriptLoadInfo> aLoadInfosToExecute)
bool aIsWorkerScript, Span<ScriptLoadInfo> aLoadInfosToExecute,
bool aAllScriptsExecutable)
: MainThreadWorkerSyncRunnable(aScriptLoader.mWorkerPrivate,
aSyncLoopTarget),
mScriptLoader(aScriptLoader),
mIsWorkerScript(aIsWorkerScript),
mLoadInfosAlreadyExecuted(aLoadInfosAlreadyExecuted),
mLoadInfosToExecute(aLoadInfosToExecute) {
// If there are load infos for scripts that have already been executed, the
// load infos for the scripts to execute must immediate follow them.
MOZ_ASSERT_IF(mLoadInfosAlreadyExecuted.Length(),
mLoadInfosAlreadyExecuted.Elements() +
mLoadInfosAlreadyExecuted.Length() ==
mLoadInfosToExecute.Elements());
}
mLoadInfosToExecute(aLoadInfosToExecute),
mAllScriptsExecutable(aAllScriptsExecutable) {}
bool ScriptExecutorRunnable::IsDebuggerRunnable() const {
// ScriptExecutorRunnable is used to execute both worker and debugger scripts.
@ -2073,7 +2069,6 @@ bool ScriptExecutorRunnable::PreRun(WorkerPrivate* aWorkerPrivate) {
return false;
}
MOZ_ASSERT(mLoadInfosAlreadyExecuted.Length() == 0);
MOZ_ASSERT(!mScriptLoader.mRv.Failed());
// Move the CSP from the workerLoadInfo in the corresponding Client
@ -2106,20 +2101,73 @@ static bool EvaluateScriptData(JSContext* aCx,
return Evaluate(aCx, aOptions, srcBuf, &unused);
}
bool ScriptExecutorRunnable::EvaluateLoadInfo(JSContext* aCx,
WorkerPrivate* aWorkerPrivate,
ScriptLoadInfo& aLoadInfo) {
NS_ASSERTION(!aLoadInfo.mChannel, "Should no longer have a channel!");
NS_ASSERTION(aLoadInfo.mExecutionScheduled, "Should be scheduled!");
NS_ASSERTION(!aLoadInfo.mExecutionResult, "Should not have executed yet!");
MOZ_ASSERT(!mScriptLoader.mRv.Failed(), "Who failed it and why?");
mScriptLoader.mRv.MightThrowJSException();
if (NS_FAILED(aLoadInfo.mLoadResult)) {
workerinternals::ReportLoadError(mScriptLoader.mRv, aLoadInfo.mLoadResult,
aLoadInfo.mURL);
return false;
}
// If this is a top level script that succeeded, then mark the
// Client execution ready and possible controlled by a service worker.
if (mIsWorkerScript) {
if (mScriptLoader.mController.isSome()) {
MOZ_ASSERT(mScriptLoader.mWorkerScriptType == WorkerScript,
"Debugger clients can't be controlled.");
aWorkerPrivate->GlobalScope()->Control(mScriptLoader.mController.ref());
}
aWorkerPrivate->ExecutionReady();
}
NS_ConvertUTF16toUTF8 filename(aLoadInfo.mURL);
JS::CompileOptions options(aCx);
options.setFileAndLine(filename.get(), 1).setNoScriptRval(true);
MOZ_ASSERT(aLoadInfo.mMutedErrorFlag.isSome());
options.setMutedErrors(aLoadInfo.mMutedErrorFlag.valueOr(true));
if (aLoadInfo.mSourceMapURL) {
options.setSourceMapURL(aLoadInfo.mSourceMapURL->get());
}
// Our ErrorResult still shouldn't be a failure.
MOZ_ASSERT(!mScriptLoader.mRv.Failed(), "Who failed it and why?");
// Transfer script length to a local variable, encoding-agnostically.
size_t scriptLength = 0;
std::swap(scriptLength, aLoadInfo.mScriptLength);
// This transfers script data out of the active arm of |aLoadInfo.mScript|.
bool successfullyEvaluated =
aLoadInfo.mScriptIsUTF8
? EvaluateScriptData(aCx, options, aLoadInfo.mScript.mUTF8,
scriptLength)
: EvaluateScriptData(aCx, options, aLoadInfo.mScript.mUTF16,
scriptLength);
MOZ_ASSERT(aLoadInfo.ScriptTextIsNull());
if (!successfullyEvaluated) {
mScriptLoader.mRv.StealExceptionFromJSContext(aCx);
return false;
}
aLoadInfo.mExecutionResult = true;
return true;
}
bool ScriptExecutorRunnable::WorkerRun(JSContext* aCx,
WorkerPrivate* aWorkerPrivate) {
aWorkerPrivate->AssertIsOnWorkerThread();
// Don't run if something else has already failed.
if (std::any_of(
mLoadInfosAlreadyExecuted.cbegin(), mLoadInfosAlreadyExecuted.cend(),
[](const ScriptLoadInfo& loadInfo) {
NS_ASSERTION(!loadInfo.mChannel,
"Should no longer have a channel!");
NS_ASSERTION(loadInfo.mExecutionScheduled, "Should be scheduled!");
return !loadInfo.mExecutionResult;
})) {
if (mScriptLoader.mExecutionAborted) {
return true;
}
@ -2132,62 +2180,13 @@ bool ScriptExecutorRunnable::WorkerRun(JSContext* aCx,
MOZ_ASSERT(global);
for (ScriptLoadInfo& loadInfo : mLoadInfosToExecute) {
NS_ASSERTION(!loadInfo.mChannel, "Should no longer have a channel!");
NS_ASSERTION(loadInfo.mExecutionScheduled, "Should be scheduled!");
NS_ASSERTION(!loadInfo.mExecutionResult, "Should not have executed yet!");
MOZ_ASSERT(!mScriptLoader.mRv.Failed(), "Who failed it and why?");
mScriptLoader.mRv.MightThrowJSException();
if (NS_FAILED(loadInfo.mLoadResult)) {
workerinternals::ReportLoadError(mScriptLoader.mRv, loadInfo.mLoadResult,
loadInfo.mURL);
return true;
if (mScriptLoader.mExecutionAborted) {
break;
}
// If this is a top level script that succeeded, then mark the
// Client execution ready and possible controlled by a service worker.
if (mIsWorkerScript) {
if (mScriptLoader.mController.isSome()) {
MOZ_ASSERT(mScriptLoader.mWorkerScriptType == WorkerScript,
"Debugger clients can't be controlled.");
aWorkerPrivate->GlobalScope()->Control(mScriptLoader.mController.ref());
}
aWorkerPrivate->ExecutionReady();
if (!EvaluateLoadInfo(aCx, aWorkerPrivate, loadInfo)) {
mScriptLoader.mExecutionAborted = true;
mScriptLoader.mMutedErrorFlag = loadInfo.mMutedErrorFlag.valueOr(true);
}
NS_ConvertUTF16toUTF8 filename(loadInfo.mURL);
JS::CompileOptions options(aCx);
options.setFileAndLine(filename.get(), 1).setNoScriptRval(true);
MOZ_ASSERT(loadInfo.mMutedErrorFlag.isSome());
options.setMutedErrors(loadInfo.mMutedErrorFlag.valueOr(true));
if (loadInfo.mSourceMapURL) {
options.setSourceMapURL(loadInfo.mSourceMapURL->get());
}
// Our ErrorResult still shouldn't be a failure.
MOZ_ASSERT(!mScriptLoader.mRv.Failed(), "Who failed it and why?");
// Transfer script length to a local variable, encoding-agnostically.
size_t scriptLength = 0;
std::swap(scriptLength, loadInfo.mScriptLength);
// This transfers script data out of the active arm of |loadInfo.mScript|.
bool successfullyEvaluated =
loadInfo.mScriptIsUTF8
? EvaluateScriptData(aCx, options, loadInfo.mScript.mUTF8,
scriptLength)
: EvaluateScriptData(aCx, options, loadInfo.mScript.mUTF16,
scriptLength);
MOZ_ASSERT(loadInfo.ScriptTextIsNull());
if (!successfullyEvaluated) {
mScriptLoader.mRv.StealExceptionFromJSContext(aCx);
return true;
}
loadInfo.mExecutionResult = true;
}
return true;
@ -2200,23 +2199,16 @@ void ScriptExecutorRunnable::PostRun(JSContext* aCx,
MOZ_ASSERT(!JS_IsExceptionPending(aCx), "Who left an exception on there?");
if (AllScriptsExecutable()) {
// All done. If anything failed then return false.
bool result = true;
bool mutedError = false;
for (const auto& loadInfo : mScriptLoader.mLoadInfos) {
if (!loadInfo.mExecutionResult) {
mutedError = loadInfo.mMutedErrorFlag.valueOr(true);
result = false;
break;
}
}
// The only way we can get here with "result" false but without
// The only way we can get here with an aborted execution but without
// mScriptLoader.mRv being a failure is if we're loading the main worker
// script and GetOrCreateGlobalScope() fails. In that case we would have
// returned false from WorkerRun, so assert that.
MOZ_ASSERT_IF(!result && !mScriptLoader.mRv.Failed(), !aRunResult);
ShutdownScriptLoader(aCx, aWorkerPrivate, result, mutedError);
MOZ_ASSERT_IF(
mScriptLoader.mExecutionAborted && !mScriptLoader.mRv.Failed(),
!aRunResult);
// All done.
ShutdownScriptLoader(aCx, aWorkerPrivate, !mScriptLoader.mExecutionAborted,
mScriptLoader.mMutedErrorFlag);
}
}
@ -2302,16 +2294,15 @@ void ScriptExecutorRunnable::LogExceptionToConsole(
}
bool ScriptExecutorRunnable::AllScriptsExecutable() const {
return mScriptLoader.mLoadInfos.Length() ==
mLoadInfosAlreadyExecuted.Length() + mLoadInfosToExecute.Length();
return mAllScriptsExecutable;
}
void LoadAllScripts(WorkerPrivate* aWorkerPrivate,
UniquePtr<SerializedStackHolder> aOriginStack,
nsTArray<ScriptLoadInfo> aLoadInfos, bool aIsMainScript,
const nsTArray<nsString>& aScriptURLs, bool aIsMainScript,
WorkerScriptType aWorkerScriptType, ErrorResult& aRv) {
aWorkerPrivate->AssertIsOnWorkerThread();
NS_ASSERTION(!aLoadInfos.IsEmpty(), "Bad arguments!");
NS_ASSERTION(!aScriptURLs.IsEmpty(), "Bad arguments!");
AutoSyncLoopHolder syncLoop(aWorkerPrivate, Canceling);
nsCOMPtr<nsIEventTarget> syncLoopTarget = syncLoop.GetEventTarget();
@ -2322,15 +2313,20 @@ void LoadAllScripts(WorkerPrivate* aWorkerPrivate,
Maybe<ClientInfo> clientInfo;
Maybe<ServiceWorkerDescriptor> controller;
if (!aIsMainScript) {
nsIGlobalObject* global =
aWorkerScriptType == WorkerScript
? static_cast<nsIGlobalObject*>(aWorkerPrivate->GlobalScope())
: aWorkerPrivate->DebuggerGlobalScope();
nsIGlobalObject* global =
aWorkerScriptType == WorkerScript
? static_cast<nsIGlobalObject*>(aWorkerPrivate->GlobalScope())
: aWorkerPrivate->DebuggerGlobalScope();
clientInfo = global->GetClientInfo();
controller = global->GetController();
}
clientInfo = global->GetClientInfo();
controller = global->GetController();
nsTArray<ScriptLoadInfo> aLoadInfos =
TransformIntoNewArray(aScriptURLs, [](const auto& scriptURL) {
ScriptLoadInfo res;
res.mURL = scriptURL;
return res;
});
RefPtr<ScriptLoaderRunnable> loader = new ScriptLoaderRunnable(
aWorkerPrivate, std::move(aOriginStack), syncLoopTarget,
@ -2456,23 +2452,12 @@ void LoadMainScript(WorkerPrivate* aWorkerPrivate,
UniquePtr<SerializedStackHolder> aOriginStack,
const nsAString& aScriptURL,
WorkerScriptType aWorkerScriptType, ErrorResult& aRv) {
nsTArray<ScriptLoadInfo> loadInfos;
nsTArray<nsString> scriptURLs;
ScriptLoadInfo* info = loadInfos.AppendElement();
info->mURL = aScriptURL;
info->mLoadFlags = aWorkerPrivate->GetLoadFlags();
scriptURLs.AppendElement(aScriptURL);
// We are loading the main script, so the worker's Client must be
// reserved.
if (aWorkerScriptType == WorkerScript) {
info->mReservedClientInfo = aWorkerPrivate->GlobalScope()->GetClientInfo();
} else {
info->mReservedClientInfo =
aWorkerPrivate->DebuggerGlobalScope()->GetClientInfo();
}
LoadAllScripts(aWorkerPrivate, std::move(aOriginStack), std::move(loadInfos),
true, aWorkerScriptType, aRv);
LoadAllScripts(aWorkerPrivate, std::move(aOriginStack), scriptURLs, true,
aWorkerScriptType, aRv);
}
void Load(WorkerPrivate* aWorkerPrivate,
@ -2490,17 +2475,8 @@ void Load(WorkerPrivate* aWorkerPrivate,
return;
}
nsTArray<ScriptLoadInfo> loadInfos = TransformIntoNewArray(
aScriptURLs,
[loadFlags = aWorkerPrivate->GetLoadFlags()](const auto& scriptURL) {
ScriptLoadInfo res;
res.mURL = scriptURL;
res.mLoadFlags = loadFlags;
return res;
});
LoadAllScripts(aWorkerPrivate, std::move(aOriginStack), std::move(loadInfos),
false, aWorkerScriptType, aRv);
LoadAllScripts(aWorkerPrivate, std::move(aOriginStack), aScriptURLs, false,
aWorkerScriptType, aRv);
}
} // namespace workerinternals

View file

@ -286,7 +286,13 @@ already_AddRefed<gfx::DataSourceSurface> CanvasChild::GetDataSurface(
return nullptr;
}
mTransactionsSinceGetDataSurface = 0;
// mTransactionsSinceGetDataSurface is used to determine if we want to prepare
// a DataSourceSurface in the GPU process up front at the end of the
// transaction, but that only makes sense if the canvas JS is requesting data
// in between transactions.
if (!mIsInTransaction) {
mTransactionsSinceGetDataSurface = 0;
}
EnsureBeginTransaction();
mRecorder->RecordEvent(RecordedPrepareDataForSurface(aSurface));
uint32_t checkpoint = mRecorder->CreateCheckpoint();

View file

@ -1116,6 +1116,7 @@ Family* FontList::FindFamily(const nsCString& aName, bool aPrimaryNameOnly) {
// the "real" family names will have been found in AliasFamilies() above.
if (aName.Contains(' ')) {
auto pfl = gfxPlatformFontList::PlatformFontList();
pfl->mLock.AssertCurrentThreadIn();
if (header.mAliasCount) {
// Aliases have been fully loaded by the parent process, so just discard
// any stray mAliasTable and mLocalNameTable entries from earlier calls

View file

@ -936,6 +936,8 @@ gfxFontEntry* gfxDWriteFontList::LookupLocalFont(
nsPresContext* aPresContext, const nsACString& aFontName,
WeightRange aWeightForEntry, StretchRange aStretchForEntry,
SlantStyleRange aStyleForEntry) {
AutoLock lock(mLock);
if (SharedFontList()) {
return LookupInSharedFaceNameList(aPresContext, aFontName, aWeightForEntry,
aStretchForEntry, aStyleForEntry);

View file

@ -63,7 +63,8 @@ class gfxDWriteFontFamily final : public gfxFontFamily {
mForceGDIClassic(false) {}
virtual ~gfxDWriteFontFamily();
void FindStyleVariationsLocked(FontInfoData* aFontInfoData = nullptr) final;
void FindStyleVariationsLocked(FontInfoData* aFontInfoData = nullptr)
REQUIRES(mLock) final;
void LocalizedName(nsACString& aLocalizedName) final;
@ -206,10 +207,10 @@ class gfxDWriteFontEntry final : public gfxFontEntry {
void SetForceGDIClassic(bool aForce) { mForceGDIClassic = aForce; }
bool GetForceGDIClassic() { return mForceGDIClassic; }
virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontListSizes* aSizes) const;
virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontListSizes* aSizes) const;
void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontListSizes* aSizes) const override;
void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontListSizes* aSizes) const override;
protected:
friend class gfxDWriteFont;
@ -374,8 +375,8 @@ class gfxDWriteFontList final : public gfxPlatformFontList {
}
// initialize font lists
nsresult InitFontListForPlatform() override;
void InitSharedFontListForPlatform() override;
nsresult InitFontListForPlatform() REQUIRES(mLock) override;
void InitSharedFontListForPlatform() REQUIRES(mLock) override;
FontVisibility GetVisibilityForFamily(const nsACString& aName) const;
@ -419,7 +420,8 @@ class gfxDWriteFontList final : public gfxPlatformFontList {
nsPresContext* aPresContext, mozilla::StyleGenericFontFamily aGeneric,
const nsACString& aFamily, nsTArray<FamilyAndGeneric>* aOutput,
FindFamiliesFlags aFlags, gfxFontStyle* aStyle = nullptr,
nsAtom* aLanguage = nullptr, gfxFloat aDevToCssSize = 1.0) override;
nsAtom* aLanguage = nullptr, gfxFloat aDevToCssSize = 1.0)
REQUIRES(mLock) override;
gfxFloat GetForceGDIClassicMaxFontSize() {
return mForceGDIClassicMaxFontSize;
@ -433,7 +435,8 @@ class gfxDWriteFontList final : public gfxPlatformFontList {
protected:
FontFamily GetDefaultFontForPlatform(nsPresContext* aPresContext,
const gfxFontStyle* aStyle,
nsAtom* aLanguage = nullptr) override;
nsAtom* aLanguage = nullptr)
REQUIRES(mLock) override;
// attempt to use platform-specific fallback for the given character,
// return null if no usable result found
@ -441,23 +444,25 @@ class gfxDWriteFontList final : public gfxPlatformFontList {
const uint32_t aCh,
Script aRunScript,
const gfxFontStyle* aMatchStyle,
FontFamily& aMatchedFamily) override;
FontFamily& aMatchedFamily)
REQUIRES(mLock) override;
private:
friend class gfxDWriteFontFamily;
nsresult GetFontSubstitutes();
nsresult GetFontSubstitutes() REQUIRES(mLock);
void GetDirectWriteSubstitutes();
void GetDirectWriteSubstitutes() REQUIRES(mLock);
virtual bool UsesSystemFallback() { return true; }
void GetFontsFromCollection(IDWriteFontCollection* aCollection);
void GetFontsFromCollection(IDWriteFontCollection* aCollection)
REQUIRES(mLock);
void AppendFamiliesFromCollection(
IDWriteFontCollection* aCollection,
nsTArray<mozilla::fontlist::Family::InitData>& aFamilies,
const nsTArray<nsCString>* aForceClassicFams = nullptr);
const nsTArray<nsCString>* aForceClassicFams = nullptr) REQUIRES(mLock);
#ifdef MOZ_BUNDLED_FONTS
already_AddRefed<IDWriteFontCollection> CreateBundledFontsCollection(

View file

@ -111,10 +111,7 @@ GDIFontEntry::GDIFontEntry(const nsACString& aFaceName,
gfxWindowsFontType aFontType, SlantStyleRange aStyle,
WeightRange aWeight, StretchRange aStretch,
gfxUserFontData* aUserFontData)
: gfxFontEntry(aFaceName),
mFontType(aFontType),
mForceGDI(false),
mUnicodeRanges() {
: gfxFontEntry(aFaceName), mFontType(aFontType), mForceGDI(false) {
mUserFontData.reset(aUserFontData);
mStyleRange = aStyle;
mWeightRange = aWeight;
@ -372,7 +369,7 @@ GDIFontEntry* GDIFontEntry::CreateFontEntry(const nsACString& aName,
WeightRange aWeight,
StretchRange aStretch,
gfxUserFontData* aUserFontData) {
// jtdfix - need to set charset, unicode ranges, pitch/family
// jtdfix - need to set charset, pitch/family
return new GDIFontEntry(aName, aFontType, aStyle, aWeight, aStretch,
aUserFontData);
@ -403,10 +400,11 @@ static bool ShouldIgnoreItalicStyle(const nsACString& aName) {
int CALLBACK GDIFontFamily::FamilyAddStylesProc(
const ENUMLOGFONTEXW* lpelfe, const NEWTEXTMETRICEXW* nmetrics,
DWORD fontType, LPARAM data) {
DWORD fontType, LPARAM data) NO_THREAD_SAFETY_ANALYSIS {
const NEWTEXTMETRICW& metrics = nmetrics->ntmTm;
LOGFONTW logFont = lpelfe->elfLogFont;
GDIFontFamily* ff = reinterpret_cast<GDIFontFamily*>(data);
MOZ_ASSERT(ff->mLock.LockedForWritingByCurrentThread());
if (logFont.lfItalic && ShouldIgnoreItalicStyle(ff->mName)) {
return 1;
@ -458,23 +456,8 @@ int CALLBACK GDIFontFamily::FamilyAddStylesProc(
return 1;
}
MOZ_ASSERT(ff->mLock.LockedForWritingByCurrentThread());
ff->AddFontEntryLocked(fe);
if (nmetrics->ntmFontSig.fsUsb[0] != 0x00000000 &&
nmetrics->ntmFontSig.fsUsb[1] != 0x00000000 &&
nmetrics->ntmFontSig.fsUsb[2] != 0x00000000 &&
nmetrics->ntmFontSig.fsUsb[3] != 0x00000000) {
// set the unicode ranges
uint32_t x = 0;
for (uint32_t i = 0; i < 4; ++i) {
DWORD range = nmetrics->ntmFontSig.fsUsb[i];
for (uint32_t k = 0; k < 32; ++k) {
fe->mUnicodeRanges.set(x++, (range & (1 << k)) != 0);
}
}
}
if (LOG_FONTLIST_ENABLED()) {
LOG_FONTLIST(
("(fontlist) added (%s) to family (%s)"
@ -649,6 +632,7 @@ int CALLBACK gfxGDIFontList::EnumFontFamExProc(ENUMLOGFONTEXW* lpelfe,
NS_ConvertUTF16toUTF8 key(name);
gfxGDIFontList* fontList = PlatformFontList();
fontList->mLock.AssertCurrentThreadIn();
if (!fontList->mFontFamilies.Contains(key)) {
NS_ConvertUTF16toUTF8 faceName(lf.lfFaceName);
@ -683,9 +667,9 @@ gfxFontEntry* gfxGDIFontList::LookupLocalFont(nsPresContext* aPresContext,
WeightRange aWeightForEntry,
StretchRange aStretchForEntry,
SlantStyleRange aStyleForEntry) {
gfxFontEntry* lookup;
AutoLock lock(mLock);
lookup = LookupInFaceNameLists(aFontName);
gfxFontEntry* lookup = LookupInFaceNameLists(aFontName);
if (!lookup) {
return nullptr;
}

View file

@ -129,18 +129,14 @@ class GDIFontEntry final : public gfxFontEntry {
mFontType == GFX_FONT_TYPE_TT_OPENTYPE);
}
virtual bool SupportsRange(uint8_t range) {
return mUnicodeRanges.test(range);
}
virtual bool SkipDuringSystemFallback() {
bool SkipDuringSystemFallback() override {
return !HasCmapTable(); // explicitly skip non-SFNT fonts
}
virtual bool TestCharacterMap(uint32_t aCh);
bool TestCharacterMap(uint32_t aCh) override;
virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontListSizes* aSizes) const;
void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontListSizes* aSizes) const override;
gfxFontEntry* Clone() const override;
@ -160,8 +156,6 @@ class GDIFontEntry final : public gfxFontEntry {
gfxWindowsFontType mFontType;
bool mForceGDI;
gfxSparseBitSet mUnicodeRanges;
protected:
friend class gfxGDIFont;
@ -193,8 +187,8 @@ class GDIFontFamily final : public gfxFontFamily {
mWindowsPitch(0),
mCharset() {}
void FindStyleVariationsLocked(
FontInfoData* aFontInfoData = nullptr) override;
void FindStyleVariationsLocked(FontInfoData* aFontInfoData = nullptr)
REQUIRES(mLock) override;
bool FilterForFontList(nsAtom* aLangGroup,
const nsACString& aGeneric) const final {
@ -300,7 +294,7 @@ class gfxGDIFontList final : public gfxPlatformFontList {
}
// initialize font lists
virtual nsresult InitFontListForPlatform() override;
nsresult InitFontListForPlatform() REQUIRES(mLock) override;
gfxFontFamily* CreateFontFamily(const nsACString& aName,
FontVisibility aVisibility) const override;
@ -309,43 +303,45 @@ class gfxGDIFontList final : public gfxPlatformFontList {
nsPresContext* aPresContext, mozilla::StyleGenericFontFamily aGeneric,
const nsACString& aFamily, nsTArray<FamilyAndGeneric>* aOutput,
FindFamiliesFlags aFlags, gfxFontStyle* aStyle = nullptr,
nsAtom* aLanguage = nullptr, gfxFloat aDevToCssSize = 1.0) override;
nsAtom* aLanguage = nullptr, gfxFloat aDevToCssSize = 1.0)
REQUIRES(mLock) override;
virtual gfxFontEntry* LookupLocalFont(nsPresContext* aPresContext,
const nsACString& aFontName,
WeightRange aWeightForEntry,
StretchRange aStretchForEntry,
SlantStyleRange aStyleForEntry);
gfxFontEntry* LookupLocalFont(nsPresContext* aPresContext,
const nsACString& aFontName,
WeightRange aWeightForEntry,
StretchRange aStretchForEntry,
SlantStyleRange aStyleForEntry) override;
virtual gfxFontEntry* MakePlatformFont(const nsACString& aFontName,
WeightRange aWeightForEntry,
StretchRange aStretchForEntry,
SlantStyleRange aStyleForEntry,
const uint8_t* aFontData,
uint32_t aLength);
gfxFontEntry* MakePlatformFont(const nsACString& aFontName,
WeightRange aWeightForEntry,
StretchRange aStretchForEntry,
SlantStyleRange aStyleForEntry,
const uint8_t* aFontData,
uint32_t aLength) override;
virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontListSizes* aSizes) const;
virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontListSizes* aSizes) const;
void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontListSizes* aSizes) const override;
void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontListSizes* aSizes) const override;
protected:
FontFamily GetDefaultFontForPlatform(nsPresContext* aPresContext,
const gfxFontStyle* aStyle,
nsAtom* aLanguage = nullptr) override;
nsAtom* aLanguage = nullptr)
REQUIRES(mLock) override;
private:
friend class gfxWindowsPlatform;
gfxGDIFontList();
nsresult GetFontSubstitutes();
nsresult GetFontSubstitutes() REQUIRES(mLock);
static int CALLBACK EnumFontFamExProc(ENUMLOGFONTEXW* lpelfe,
NEWTEXTMETRICEXW* lpntme,
DWORD fontType, LPARAM lParam);
virtual already_AddRefed<FontInfoData> CreateFontInfoData();
already_AddRefed<FontInfoData> CreateFontInfoData() override;
#ifdef MOZ_BUNDLED_FONTS
void ActivateBundledFonts();

View file

@ -475,8 +475,6 @@ bool Channel::ChannelImpl::ProcessIncomingMessages(
bytes_read = 0; // Get more data.
}
return true;
}
bool Channel::ChannelImpl::ProcessOutgoingMessages(

View file

@ -144,8 +144,14 @@ JS_PUBLIC_API void js::gc::AssertGCThingHasType(js::gc::Cell* cell,
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
JS::AutoAssertNoGC::AutoAssertNoGC(JSContext* maybecx)
: cx_(maybecx ? maybecx : TlsContext.get()) {
JS::AutoAssertNoGC::AutoAssertNoGC(JSContext* maybecx) {
if (maybecx) {
cx_ = maybecx;
} else if (TlsContext.initialized()) {
cx_ = TlsContext.get();
} else {
cx_ = nullptr;
}
if (cx_) {
cx_->inUnsafeRegion++;
}

View file

@ -178,11 +178,11 @@ class Range : public TempObject {
static const int64_t NoInt32UpperBound = int64_t(JSVAL_INT_MAX) + 1;
static const int64_t NoInt32LowerBound = int64_t(JSVAL_INT_MIN) - 1;
enum FractionalPartFlag {
enum FractionalPartFlag : bool {
ExcludesFractionalParts = false,
IncludesFractionalParts = true
};
enum NegativeZeroFlag {
enum NegativeZeroFlag : bool {
ExcludesNegativeZero = false,
IncludesNegativeZero = true
};

View file

@ -44,8 +44,13 @@ enum RegisterID : uint8_t {
enum HRegisterID { ah = rsp, ch = rbp, dh = rsi, bh = rdi };
enum XMMRegisterID {
xmm0,
enum XMMRegisterID
// GCC < 8.0 has a bug with bitfields of enums with an underlying type.
#if defined(__clang__) || __GNUC__ > 7
: uint8_t
#endif
{
xmm0 = 0,
xmm1,
xmm2,
xmm3,

View file

@ -9615,9 +9615,10 @@ void nsLayoutUtils::ComputeSystemFont(nsFont* aSystemFont,
aFontID == LookAndFeel::FontID::MozList) {
const bool isWindowsOrNonNativeTheme =
#ifdef XP_WIN
true ||
#endif
true;
#else
aDocument->ShouldAvoidNativeTheme();
#endif
if (isWindowsOrNonNativeTheme) {
// For textfields, buttons and selects, we use whatever font is defined by

View file

@ -6467,16 +6467,30 @@ nsIFrame::SizeComputationResult nsIFrame::ComputeSize(
// aspect-ratio to the other side to preserve the aspect ratio to the extent
// that they can without violating any sizes specified explicitly on that
// affected axis.
//
// FIXME: The spec words may not be correct, so we may have to update this
// tentative solution once this spec issue gets resolved. Here, we clamp the
// flex base size by the transferred min and max sizes, and don't include
// the transferred min & max sizes into its used min & max sizes. So this
// lets us match other browsers' current behaviors.
// https://github.com/w3c/csswg-drafts/issues/6071
//
// Note: This may make more sense if we clamp the flex base size in
// FlexItem::ResolveFlexBaseSizeFromAspectRatio(). However, the result should
// be identical. FlexItem::ResolveFlexBaseSizeFromAspectRatio() only handles
// the case of the definite cross size, and the definite cross size is clamped
// by the min & max cross sizes below in this function. This means its flex
// base size has been clamped by the transferred min & max size already after
// generating the flex items. So here we make the code more general for both
// definite cross size and indefinite cross size.
const bool isDefiniteISize = styleISize.IsLengthPercentage();
const bool isFlexItemInlineAxisMainAxis =
isFlexItem && flexMainAxis == eLogicalAxisInline;
const auto& minBSizeCoord = stylePos->MinBSize(aWM);
const auto& maxBSizeCoord = stylePos->MaxBSize(aWM);
const bool isAutoMinBSize =
nsLayoutUtils::IsAutoBSize(minBSizeCoord, aCBSize.BSize(aWM));
const bool isAutoMaxBSize =
nsLayoutUtils::IsAutoBSize(maxBSizeCoord, aCBSize.BSize(aWM));
if (aspectRatio && !isDefiniteISize && !isFlexItemInlineAxisMainAxis) {
if (aspectRatio && !isDefiniteISize) {
const MinMaxSize minMaxBSize{
isAutoMinBSize ? 0
: nsLayoutUtils::ComputeBSizeValue(
@ -6496,6 +6510,8 @@ nsIFrame::SizeComputationResult nsIFrame::ComputeSize(
// Flex items ignore their min & max sizing properties in their
// flex container's main-axis. (Those properties get applied later in
// the flexbox algorithm.)
const bool isFlexItemInlineAxisMainAxis =
isFlexItem && flexMainAxis == eLogicalAxisInline;
const auto& maxISizeCoord = stylePos->MaxISize(aWM);
nscoord maxISize = NS_UNCONSTRAINEDSIZE;
if (!maxISizeCoord.IsNone() && !isFlexItemInlineAxisMainAxis) {

View file

@ -4,7 +4,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
include protocol PPrinting;
include protocol PContent;
namespace mozilla {
namespace layout {
@ -12,7 +12,7 @@ namespace layout {
[ChildImpl=virtual, ParentImpl=virtual]
async protocol PRemotePrintJob
{
manager PPrinting;
manager PContent;
both:
// Tell either side to abort printing and clean up.

View file

@ -1695,7 +1695,7 @@ TEST_F(JsepTrackTest, NonDefaultVideoSdpFmtpLine) {
auto* video = static_cast<JsepVideoCodecDescription*>(codec.get());
video->mConstraints.maxFs = 1200;
if (codec->mName == "VP8") {
video->mConstraints.maxFps = 15;
video->mConstraints.maxFps = Some(15);
} else {
video->mConstraints.maxDpb = 6400;
video->mConstraints.maxBr = 1000;
@ -1710,7 +1710,7 @@ TEST_F(JsepTrackTest, NonDefaultVideoSdpFmtpLine) {
auto* video = static_cast<JsepVideoCodecDescription*>(codec.get());
video->mConstraints.maxFs = 32400;
if (codec->mName == "VP8") {
video->mConstraints.maxFps = 60;
video->mConstraints.maxFps = Some(60);
} else {
video->mConstraints.maxMbps = 1944000;
video->mConstraints.maxCpb = 800000;

View file

@ -5132,7 +5132,7 @@ TEST(NewSdpTestNoFixture, CheckRidValidParse)
ASSERT_EQ(0U, rid.formats.size());
ASSERT_EQ(0U, rid.constraints.maxWidth);
ASSERT_EQ(0U, rid.constraints.maxHeight);
ASSERT_EQ(0U, rid.constraints.maxFps);
ASSERT_FALSE(rid.constraints.maxFps.isSome());
ASSERT_EQ(0U, rid.constraints.maxFs);
ASSERT_EQ(0U, rid.constraints.maxBr);
ASSERT_EQ(0U, rid.constraints.maxPps);
@ -5147,7 +5147,7 @@ TEST(NewSdpTestNoFixture, CheckRidValidParse)
ASSERT_EQ(96U, rid.formats[0]);
ASSERT_EQ(800U, rid.constraints.maxWidth);
ASSERT_EQ(0U, rid.constraints.maxHeight);
ASSERT_EQ(0U, rid.constraints.maxFps);
ASSERT_FALSE(rid.constraints.maxFps.isSome());
ASSERT_EQ(0U, rid.constraints.maxFs);
ASSERT_EQ(0U, rid.constraints.maxBr);
ASSERT_EQ(0U, rid.constraints.maxPps);
@ -5164,7 +5164,8 @@ TEST(NewSdpTestNoFixture, CheckRidValidParse)
ASSERT_EQ(98U, rid.formats[2]);
ASSERT_EQ(800U, rid.constraints.maxWidth);
ASSERT_EQ(0U, rid.constraints.maxHeight);
ASSERT_EQ(0U, rid.constraints.maxFps);
ASSERT_FALSE(rid.constraints.maxFps.isSome());
ASSERT_FALSE(rid.constraints.maxFps.isSome());
ASSERT_EQ(0U, rid.constraints.maxFs);
ASSERT_EQ(0U, rid.constraints.maxBr);
ASSERT_EQ(0U, rid.constraints.maxPps);
@ -5178,7 +5179,7 @@ TEST(NewSdpTestNoFixture, CheckRidValidParse)
ASSERT_EQ(0U, rid.formats.size());
ASSERT_EQ(800U, rid.constraints.maxWidth);
ASSERT_EQ(0U, rid.constraints.maxHeight);
ASSERT_EQ(0U, rid.constraints.maxFps);
ASSERT_FALSE(rid.constraints.maxFps.isSome());
ASSERT_EQ(0U, rid.constraints.maxFs);
ASSERT_EQ(0U, rid.constraints.maxBr);
ASSERT_EQ(0U, rid.constraints.maxPps);
@ -5190,7 +5191,7 @@ TEST(NewSdpTestNoFixture, CheckRidValidParse)
ASSERT_EQ(0U, rid.formats.size());
ASSERT_EQ(0U, rid.constraints.maxWidth);
ASSERT_EQ(0U, rid.constraints.maxHeight);
ASSERT_EQ(0U, rid.constraints.maxFps);
ASSERT_FALSE(rid.constraints.maxFps.isSome());
ASSERT_EQ(0U, rid.constraints.maxFs);
ASSERT_EQ(0U, rid.constraints.maxBr);
ASSERT_EQ(0U, rid.constraints.maxPps);
@ -5203,7 +5204,7 @@ TEST(NewSdpTestNoFixture, CheckRidValidParse)
ASSERT_EQ(96U, rid.formats[0]);
ASSERT_EQ(0U, rid.constraints.maxWidth);
ASSERT_EQ(0U, rid.constraints.maxHeight);
ASSERT_EQ(0U, rid.constraints.maxFps);
ASSERT_FALSE(rid.constraints.maxFps.isSome());
ASSERT_EQ(0U, rid.constraints.maxFs);
ASSERT_EQ(0U, rid.constraints.maxBr);
ASSERT_EQ(0U, rid.constraints.maxPps);
@ -5219,7 +5220,7 @@ TEST(NewSdpTestNoFixture, CheckRidValidParse)
ASSERT_EQ(96U, rid.formats[0]);
ASSERT_EQ(0U, rid.constraints.maxWidth);
ASSERT_EQ(0U, rid.constraints.maxHeight);
ASSERT_EQ(0U, rid.constraints.maxFps);
ASSERT_FALSE(rid.constraints.maxFps.isSome());
ASSERT_EQ(0U, rid.constraints.maxFs);
ASSERT_EQ(30000U, rid.constraints.maxBr);
ASSERT_EQ(0U, rid.constraints.maxPps);
@ -5234,7 +5235,7 @@ TEST(NewSdpTestNoFixture, CheckRidValidParse)
ASSERT_EQ(98U, rid.formats[2]);
ASSERT_EQ(0U, rid.constraints.maxWidth);
ASSERT_EQ(0U, rid.constraints.maxHeight);
ASSERT_EQ(0U, rid.constraints.maxFps);
ASSERT_FALSE(rid.constraints.maxFps.isSome());
ASSERT_EQ(0U, rid.constraints.maxFs);
ASSERT_EQ(0U, rid.constraints.maxBr);
ASSERT_EQ(0U, rid.constraints.maxPps);
@ -5246,7 +5247,7 @@ TEST(NewSdpTestNoFixture, CheckRidValidParse)
ASSERT_EQ(0U, rid.formats.size());
ASSERT_EQ(800U, rid.constraints.maxWidth);
ASSERT_EQ(0U, rid.constraints.maxHeight);
ASSERT_EQ(0U, rid.constraints.maxFps);
ASSERT_FALSE(rid.constraints.maxFps.isSome());
ASSERT_EQ(0U, rid.constraints.maxFs);
ASSERT_EQ(0U, rid.constraints.maxBr);
ASSERT_EQ(0U, rid.constraints.maxPps);
@ -5258,7 +5259,7 @@ TEST(NewSdpTestNoFixture, CheckRidValidParse)
ASSERT_EQ(0U, rid.formats.size());
ASSERT_EQ(0U, rid.constraints.maxWidth);
ASSERT_EQ(640U, rid.constraints.maxHeight);
ASSERT_EQ(0U, rid.constraints.maxFps);
ASSERT_FALSE(rid.constraints.maxFps.isSome());
ASSERT_EQ(0U, rid.constraints.maxFs);
ASSERT_EQ(0U, rid.constraints.maxBr);
ASSERT_EQ(0U, rid.constraints.maxPps);
@ -5270,7 +5271,7 @@ TEST(NewSdpTestNoFixture, CheckRidValidParse)
ASSERT_EQ(0U, rid.formats.size());
ASSERT_EQ(0U, rid.constraints.maxWidth);
ASSERT_EQ(0U, rid.constraints.maxHeight);
ASSERT_EQ(30U, rid.constraints.maxFps);
ASSERT_EQ(30.0, *rid.constraints.maxFps);
ASSERT_EQ(0U, rid.constraints.maxFs);
ASSERT_EQ(0U, rid.constraints.maxBr);
ASSERT_EQ(0U, rid.constraints.maxPps);
@ -5282,7 +5283,7 @@ TEST(NewSdpTestNoFixture, CheckRidValidParse)
ASSERT_EQ(0U, rid.formats.size());
ASSERT_EQ(0U, rid.constraints.maxWidth);
ASSERT_EQ(0U, rid.constraints.maxHeight);
ASSERT_EQ(0U, rid.constraints.maxFps);
ASSERT_FALSE(rid.constraints.maxFps.isSome());
ASSERT_EQ(3600U, rid.constraints.maxFs);
ASSERT_EQ(0U, rid.constraints.maxBr);
ASSERT_EQ(0U, rid.constraints.maxPps);
@ -5294,7 +5295,7 @@ TEST(NewSdpTestNoFixture, CheckRidValidParse)
ASSERT_EQ(0U, rid.formats.size());
ASSERT_EQ(0U, rid.constraints.maxWidth);
ASSERT_EQ(0U, rid.constraints.maxHeight);
ASSERT_EQ(0U, rid.constraints.maxFps);
ASSERT_FALSE(rid.constraints.maxFps.isSome());
ASSERT_EQ(0U, rid.constraints.maxFs);
ASSERT_EQ(30000U, rid.constraints.maxBr);
ASSERT_EQ(0U, rid.constraints.maxPps);
@ -5306,7 +5307,7 @@ TEST(NewSdpTestNoFixture, CheckRidValidParse)
ASSERT_EQ(0U, rid.formats.size());
ASSERT_EQ(0U, rid.constraints.maxWidth);
ASSERT_EQ(0U, rid.constraints.maxHeight);
ASSERT_EQ(0U, rid.constraints.maxFps);
ASSERT_FALSE(rid.constraints.maxFps.isSome());
ASSERT_EQ(0U, rid.constraints.maxFs);
ASSERT_EQ(0U, rid.constraints.maxBr);
ASSERT_EQ(9216000U, rid.constraints.maxPps);
@ -5318,7 +5319,7 @@ TEST(NewSdpTestNoFixture, CheckRidValidParse)
ASSERT_EQ(0U, rid.formats.size());
ASSERT_EQ(0U, rid.constraints.maxWidth);
ASSERT_EQ(0U, rid.constraints.maxHeight);
ASSERT_EQ(0U, rid.constraints.maxFps);
ASSERT_FALSE(rid.constraints.maxFps.isSome());
ASSERT_EQ(0U, rid.constraints.maxFs);
ASSERT_EQ(0U, rid.constraints.maxBr);
ASSERT_EQ(0U, rid.constraints.maxPps);
@ -5331,7 +5332,7 @@ TEST(NewSdpTestNoFixture, CheckRidValidParse)
ASSERT_EQ(0U, rid.formats.size());
ASSERT_EQ(0U, rid.constraints.maxWidth);
ASSERT_EQ(0U, rid.constraints.maxHeight);
ASSERT_EQ(0U, rid.constraints.maxFps);
ASSERT_FALSE(rid.constraints.maxFps.isSome());
ASSERT_EQ(0U, rid.constraints.maxFs);
ASSERT_EQ(0U, rid.constraints.maxBr);
ASSERT_EQ(0U, rid.constraints.maxPps);
@ -5343,7 +5344,7 @@ TEST(NewSdpTestNoFixture, CheckRidValidParse)
ASSERT_EQ(0U, rid.formats.size());
ASSERT_EQ(0U, rid.constraints.maxWidth);
ASSERT_EQ(0U, rid.constraints.maxHeight);
ASSERT_EQ(0U, rid.constraints.maxFps);
ASSERT_FALSE(rid.constraints.maxFps.isSome());
ASSERT_EQ(0U, rid.constraints.maxFs);
ASSERT_EQ(0U, rid.constraints.maxBr);
ASSERT_EQ(0U, rid.constraints.maxPps);
@ -5358,7 +5359,7 @@ TEST(NewSdpTestNoFixture, CheckRidValidParse)
ASSERT_EQ(0U, rid.formats.size());
ASSERT_EQ(800U, rid.constraints.maxWidth);
ASSERT_EQ(600U, rid.constraints.maxHeight);
ASSERT_EQ(0U, rid.constraints.maxFps);
ASSERT_FALSE(rid.constraints.maxFps.isSome());
ASSERT_EQ(0U, rid.constraints.maxFs);
ASSERT_EQ(0U, rid.constraints.maxBr);
ASSERT_EQ(0U, rid.constraints.maxPps);
@ -5373,7 +5374,7 @@ TEST(NewSdpTestNoFixture, CheckRidValidParse)
ASSERT_EQ(97U, rid.formats[1]);
ASSERT_EQ(800U, rid.constraints.maxWidth);
ASSERT_EQ(600U, rid.constraints.maxHeight);
ASSERT_EQ(0U, rid.constraints.maxFps);
ASSERT_FALSE(rid.constraints.maxFps.isSome());
ASSERT_EQ(0U, rid.constraints.maxFs);
ASSERT_EQ(0U, rid.constraints.maxBr);
ASSERT_EQ(0U, rid.constraints.maxPps);
@ -5386,7 +5387,7 @@ TEST(NewSdpTestNoFixture, CheckRidValidParse)
ASSERT_EQ(0U, rid.formats.size());
ASSERT_EQ(800U, rid.constraints.maxWidth);
ASSERT_EQ(600U, rid.constraints.maxHeight);
ASSERT_EQ(0U, rid.constraints.maxFps);
ASSERT_FALSE(rid.constraints.maxFps.isSome());
ASSERT_EQ(0U, rid.constraints.maxFs);
ASSERT_EQ(0U, rid.constraints.maxBr);
ASSERT_EQ(0U, rid.constraints.maxPps);
@ -5401,7 +5402,7 @@ TEST(NewSdpTestNoFixture, CheckRidValidParse)
ASSERT_EQ(0U, rid.formats.size());
ASSERT_EQ(800U, rid.constraints.maxWidth);
ASSERT_EQ(600U, rid.constraints.maxHeight);
ASSERT_EQ(0U, rid.constraints.maxFps);
ASSERT_FALSE(rid.constraints.maxFps.isSome());
ASSERT_EQ(0U, rid.constraints.maxFs);
ASSERT_EQ(0U, rid.constraints.maxBr);
ASSERT_EQ(0U, rid.constraints.maxPps);
@ -5502,7 +5503,7 @@ TEST(NewSdpTestNoFixture, CheckRidSerialize)
SdpRidAttributeList::Rid rid;
rid.id = "foo";
rid.direction = sdp::kSend;
rid.constraints.maxFps = 30;
rid.constraints.maxFps = Some(30);
std::ostringstream os;
rid.Serialize(os);
ASSERT_EQ("foo send max-fps=30", os.str());

Some files were not shown because too many files have changed in this diff Show more