Merge inbound to mozilla-central. a=merge

This commit is contained in:
Tiberius Oros 2018-03-16 11:53:43 +02:00
Родитель d01a762799 027c306ce3
Коммит a4a2be52e8
40 изменённых файлов: 4366 добавлений и 447 удалений

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

@ -23,21 +23,16 @@ var gSafeBrowsing = {
document.getElementById("menu_HelpPopup_reportPhishingErrortoolmenu") document.getElementById("menu_HelpPopup_reportPhishingErrortoolmenu")
.hidden = !isPhishingPage; .hidden = !isPhishingPage;
var broadcasterId = isPhishingPage
? "reportPhishingErrorBroadcaster"
: "reportPhishingBroadcaster";
var broadcaster = document.getElementById(broadcasterId);
if (!broadcaster)
return;
// Now look at the currentURI to learn which page we were trying // Now look at the currentURI to learn which page we were trying
// to browse to. // to browse to.
let uri = gBrowser.currentURI; const uri = gBrowser.currentURI;
if (uri && (uri.schemeIs("http") || uri.schemeIs("https"))) const isReportablePage = uri && (uri.schemeIs("http") || uri.schemeIs("https"));
broadcaster.removeAttribute("disabled");
else const disabledByPolicy = !Services.policies.isAllowed("feedbackCommands");
broadcaster.setAttribute("disabled", true); document.getElementById("reportPhishingBroadcaster")
.disabled = disabledByPolicy || isPhishingPage || !isReportablePage;
document.getElementById("reportPhishingErrorBroadcaster")
.disabled = disabledByPolicy || !isPhishingPage || !isReportablePage;
}, },
/** /**

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

@ -831,6 +831,12 @@ function openTourPage() {
} }
function buildHelpMenu() { function buildHelpMenu() {
document.getElementById("feedbackPage")
.disabled = !Services.policies.isAllowed("feedbackCommands");
document.getElementById("helpSafeMode")
.disabled = !Services.policies.isAllowed("safeMode");
// Enable/disable the "Report Web Forgery" menu item. // Enable/disable the "Report Web Forgery" menu item.
if (typeof gSafeBrowsing != "undefined") { if (typeof gSafeBrowsing != "undefined") {
gSafeBrowsing.setReportPhishingMenu(); gSafeBrowsing.setReportPhishingMenu();

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

@ -135,6 +135,14 @@ var Policies = {
} }
}, },
"DisableBuiltinPDFViewer": {
onBeforeUIStartup(manager, param) {
if (param) {
manager.disallowFeature("PDF.js");
}
}
},
"DisableDeveloperTools": { "DisableDeveloperTools": {
onBeforeAddons(manager, param) { onBeforeAddons(manager, param) {
if (param) { if (param) {
@ -149,6 +157,14 @@ var Policies = {
} }
}, },
"DisableFeedbackCommands": {
onBeforeUIStartup(manager, param) {
if (param) {
manager.disallowFeature("feedbackCommands");
}
}
},
"DisableFirefoxAccounts": { "DisableFirefoxAccounts": {
onBeforeAddons(manager, param) { onBeforeAddons(manager, param) {
if (param) { if (param) {
@ -199,6 +215,14 @@ var Policies = {
} }
}, },
"DisableSafeMode": {
onBeforeUIStartup(manager, param) {
if (param) {
manager.disallowFeature("safeMode");
}
}
},
"DisableSysAddonUpdate": { "DisableSysAddonUpdate": {
onBeforeAddons(manager, param) { onBeforeAddons(manager, param) {
if (param) { if (param) {

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

@ -107,6 +107,13 @@
"type": "boolean" "type": "boolean"
}, },
"DisableBuiltinPDFViewer": {
"description": "Disables PDF.js, which displays PDFs within Firefox.",
"first_available": "60.0",
"type": "boolean"
},
"DisableDeveloperTools": { "DisableDeveloperTools": {
"description": "Prevents access to developer tools.", "description": "Prevents access to developer tools.",
"first_available": "60.0", "first_available": "60.0",
@ -114,6 +121,13 @@
"type": "boolean" "type": "boolean"
}, },
"DisableFeedbackCommands": {
"description": "Prevents ability to send feedback from the help menu (\"Submit Feedback\" and \"Report Deceptive Site\").",
"first_available": "60.0",
"type": "boolean"
},
"DisableFirefoxAccounts": { "DisableFirefoxAccounts": {
"description": "Disables Firefox Account based services, including Sync.", "description": "Disables Firefox Account based services, including Sync.",
"first_available": "60.0", "first_available": "60.0",
@ -156,6 +170,13 @@
"type": "boolean" "type": "boolean"
}, },
"DisableSafeMode": {
"description": "Prevents ability to restart in safe mode.",
"first_available": "60.0",
"type": "boolean"
},
"DisableSysAddonUpdate": { "DisableSysAddonUpdate": {
"description": "Prevent the browser from installing and updating system addons.", "description": "Prevent the browser from installing and updating system addons.",
"first_available": "60.0", "first_available": "60.0",

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

@ -24,12 +24,15 @@ support-files =
[browser_policy_bookmarks.js] [browser_policy_bookmarks.js]
[browser_policy_clear_blocked_cookies.js] [browser_policy_clear_blocked_cookies.js]
[browser_policy_default_browser_check.js] [browser_policy_default_browser_check.js]
[browser_policy_disable_feedback_commands.js]
[browser_policy_disable_formhistory.js] [browser_policy_disable_formhistory.js]
[browser_policy_disable_fxaccounts.js] [browser_policy_disable_fxaccounts.js]
[browser_policy_disable_fxscreenshots.js] [browser_policy_disable_fxscreenshots.js]
[browser_policy_disable_masterpassword.js] [browser_policy_disable_masterpassword.js]
[browser_policy_disable_pdfjs.js]
[browser_policy_disable_pocket.js] [browser_policy_disable_pocket.js]
[browser_policy_disable_privatebrowsing.js] [browser_policy_disable_privatebrowsing.js]
[browser_policy_disable_safemode.js]
[browser_policy_disable_shield.js] [browser_policy_disable_shield.js]
[browser_policy_display_bookmarks.js] [browser_policy_display_bookmarks.js]
[browser_policy_display_menu.js] [browser_policy_display_menu.js]

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

@ -0,0 +1,49 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/* the buidHelpMenu() function comes from browser/base/content/utilityOverlay.js */
const NORMAL_PAGE = "http://example.com";
const PHISH_PAGE = "http://www.itisatrap.org/firefox/its-a-trap.html";
async function checkItemsAreDisabled(url) {
await BrowserTestUtils.withNewTab({
gBrowser,
url,
// The phishing page doesn't send a load notification
waitForLoad: false,
waitForStateStop: true,
}, async function checkItems() {
buildHelpMenu();
let reportMenu = document.getElementById("menu_HelpPopup_reportPhishingtoolmenu");
is(reportMenu.getAttribute("disabled"), "true",
"The `Report Deceptive Site` item should be disabled");
let errorMenu = document.getElementById("menu_HelpPopup_reportPhishingErrortoolmenu");
is(errorMenu.getAttribute("disabled"), "true",
"The `This isnt a deceptive site` item should be disabled");
});
}
add_task(async function test_policy_feedback_commands() {
await setupPolicyEngineWithJson({
"policies": {
"DisableFeedbackCommands": true
}
});
/* from browser/base/content/utilityOverlay.js */
buildHelpMenu();
let feedbackPageMenu = document.getElementById("feedbackPage");
is(feedbackPageMenu.getAttribute("disabled"), "true",
"The `Submit Feedback...` item should be disabled");
await checkItemsAreDisabled(NORMAL_PAGE);
await checkItemsAreDisabled(PHISH_PAGE);
});

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

@ -0,0 +1,17 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const {PdfJs} = ChromeUtils.import("resource://pdf.js/PdfJs.jsm", {});
add_task(async function test_disable_pdfjs() {
is(PdfJs.enabled, true, "PDFjs should be enabled before policy runs");
await setupPolicyEngineWithJson({
"policies": {
"DisableBuiltinPDFViewer": true
}
});
is(PdfJs.enabled, false, "PDFjs should be disabled after policy runs");
});

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

@ -0,0 +1,42 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
add_task(async function setup() {
await setupPolicyEngineWithJson({
"policies": {
"DisableSafeMode": true
}
});
});
add_task(async function test_help_menu() {
buildHelpMenu();
let safeModeMenu = document.getElementById("helpSafeMode");
is(safeModeMenu.getAttribute("disabled"), "true",
"The `Restart with Add-ons Disabled...` item should be disabled");
});
add_task(async function test_safemode_from_about_support() {
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:support");
await ContentTask.spawn(tab.linkedBrowser, null, async function() {
let button = content.document.getElementById("restart-in-safe-mode-button");
is(button.getAttribute("disabled"), "true",
"The `Restart with Add-ons Disabled...` button should be disabled");
});
await BrowserTestUtils.removeTab(tab);
});
add_task(async function test_safemode_from_about_profiles() {
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:profiles");
await ContentTask.spawn(tab.linkedBrowser, null, async function() {
let button = content.document.getElementById("restart-in-safe-mode-button");
is(button.getAttribute("disabled"), "true",
"The `Restart with Add-ons Disabled...` button should be disabled");
});
await BrowserTestUtils.removeTab(tab);
});

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

@ -51,6 +51,7 @@ skip-if = os == 'linux'
[browser_ext_browserAction_pageAction_icon_permissions.js] [browser_ext_browserAction_pageAction_icon_permissions.js]
[browser_ext_browserAction_popup.js] [browser_ext_browserAction_popup.js]
skip-if = (debug && os == 'linux' && bits == 32) || (os == 'win' && !debug) # Bug 1313372, win: Bug 1285500 skip-if = (debug && os == 'linux' && bits == 32) || (os == 'win' && !debug) # Bug 1313372, win: Bug 1285500
[browser_ext_browserAction_popup_port.js]
[browser_ext_browserAction_popup_preload.js] [browser_ext_browserAction_popup_preload.js]
skip-if = (os == 'win' && !debug) # bug 1352668 skip-if = (os == 'win' && !debug) # bug 1352668
[browser_ext_browserAction_popup_resize.js] [browser_ext_browserAction_popup_resize.js]

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

@ -0,0 +1,52 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
let scriptPage = url => `<html><head><meta charset="utf-8"><script src="${url}"></script></head><body>${url}</body></html>`;
// Tests that message ports still function correctly after a browserAction popup
// <browser> has been reparented.
add_task(async function test_browserActionPort() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"browser_action": {
"default_popup": "popup.html",
"browser_style": true,
},
},
background() {
new Promise(resolve => {
browser.runtime.onConnect.addListener(port => {
resolve(Promise.all([
new Promise(r => port.onMessage.addListener(r)),
new Promise(r => port.onDisconnect.addListener(r)),
]));
});
}).then(([msg]) => {
browser.test.assertEq("Hallo.", msg, "Got expected message");
browser.test.notifyPass("browserAction-popup-port");
});
},
files: {
"popup.html": scriptPage("popup.js"),
"popup.js"() {
let port = browser.runtime.connect();
window.onload = () => {
setTimeout(() => {
port.postMessage("Hallo.");
window.close();
}, 0);
};
},
},
});
await extension.startup();
await clickBrowserAction(extension);
await extension.awaitFinish("browserAction-popup-port");
await extension.unload();
});

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

@ -0,0 +1,48 @@
.. _browsererrorreporter:
=======================
Browser Error Reporter
=======================
The `BrowserErrorReporter.jsm <https://dxr.mozilla.org/mozilla-central/source/browser/modules/BrowserErrorReporter.jsm>`_ module collects errors logged to the Browser Console and sends them to a remote error aggregation service.
.. note::
This module and the related service is a prototype and will be removed from Firefox in later 2018.
Opt-out
=======
Collection is enabled by default in the Nightly channel, except for local builds, where it is disabled. It is not available outside of the Nightly channel.
To opt-out of collection:
1. Open ``about:preferences``.
2. Select the Privacy and Security panel and go to the Nightly Data Collection and Use section.
3. Uncheck "Allow Nightly to send browser error reports (including error messages) to Mozilla".
Collected Error Data
====================
Errors are first sampled at the rate specified by the ``browser.chrome.errorReporter.sampleRate`` preference.
The payload sent to the remote collection service contains the following info:
- Firefox version number
- Firefox update channel (usually "Nightly")
- Firefox build ID
- Revision used to build Firefox
- Timestamp when the error occurred
- A project ID specified by the ``browser.chrome.errorReporter.projectId`` preference
- The error message
- The filename that the error was thrown from
- A stacktrace, if available, of the code being executed when the error was thrown
Privacy-sensitive info
======================
Error reports may contain sensitive information about the user:
- Error messages may inadvertently contain personal info depending on the code that generated the error.
- Filenames in the stack trace may contain add-on IDs of currently-installed add-ons. They may also contain local filesystem paths.
.. seealso::
`Browser Error Collection wiki page <https://wiki.mozilla.org/Firefox/BrowserErrorCollection>`_
Wiki page with up-to-date information on error collection and how we restrict access to the collected data.

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

@ -9,3 +9,4 @@ This is the nascent documentation of the Firefox front-end code.
UITelemetry UITelemetry
BrowserUsageTelemetry BrowserUsageTelemetry
BrowserErrorReporter

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

@ -337,6 +337,10 @@ var PdfJs = {
* @return {boolean} Whether or not it's enabled. * @return {boolean} Whether or not it's enabled.
*/ */
get enabled() { get enabled() {
if (!Services.policies.isAllowed("PDF.js")) {
return false;
}
if (!Services.prefs.getBoolPref(PREF_ENABLED_CACHE_INITIALIZED, false)) { if (!Services.prefs.getBoolPref(PREF_ENABLED_CACHE_INITIALIZED, false)) {
// If we just updated, and the cache hasn't been initialized, then we // If we just updated, and the cache hasn't been initialized, then we
// can't assume a default state, and need to synchronously initialize // can't assume a default state, and need to synchronously initialize

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

@ -55,9 +55,10 @@ const REPORTED_CATEGORIES = new Set([
* traces; see bug 1426482 for privacy review and server-side mitigation. * traces; see bug 1426482 for privacy review and server-side mitigation.
*/ */
class BrowserErrorReporter { class BrowserErrorReporter {
constructor(fetchMethod = this._defaultFetch) { constructor(fetchMethod = this._defaultFetch, chromeOnly = true) {
// A fake fetch is passed by the tests to avoid network connections // Test arguments for mocks and changing behavior
this.fetch = fetchMethod; this.fetch = fetchMethod;
this.chromeOnly = chromeOnly;
// Values that don't change between error reports. // Values that don't change between error reports.
this.requestBodyTemplate = { this.requestBodyTemplate = {
@ -133,7 +134,7 @@ class BrowserErrorReporter {
const isWarning = message.flags & message.warningFlag; const isWarning = message.flags & message.warningFlag;
const isFromChrome = REPORTED_CATEGORIES.has(message.category); const isFromChrome = REPORTED_CATEGORIES.has(message.category);
if (!isFromChrome || isWarning) { if ((this.chromeOnly && !isFromChrome) || isWarning) {
return; return;
} }
@ -143,6 +144,22 @@ class BrowserErrorReporter {
return; return;
} }
const extensions = new Map();
for (let extension of WebExtensionPolicy.getActiveExtensions()) {
extensions.set(extension.mozExtensionHostname, extension);
}
// Replaces any instances of moz-extension:// URLs with internal UUIDs to use
// the add-on ID instead.
function mangleExtURL(string, anchored = true) {
let re = new RegExp(`${anchored ? "^" : ""}moz-extension://([^/]+)/`, "g");
return string.replace(re, (m0, m1) => {
let id = extensions.has(m1) ? extensions.get(m1).id : m1;
return `moz-extension://${id}/`;
});
}
// Parse the error type from the message if present (e.g. "TypeError: Whoops"). // Parse the error type from the message if present (e.g. "TypeError: Whoops").
let errorMessage = message.errorMessage; let errorMessage = message.errorMessage;
let errorName = "Error"; let errorName = "Error";
@ -156,7 +173,9 @@ class BrowserErrorReporter {
let frame = message.stack; let frame = message.stack;
// Avoid an infinite loop by limiting traces to 100 frames. // Avoid an infinite loop by limiting traces to 100 frames.
while (frame && frames.length < 100) { while (frame && frames.length < 100) {
frames.push(await this.normalizeStackFrame(frame)); const normalizedFrame = await this.normalizeStackFrame(frame);
normalizedFrame.module = mangleExtURL(normalizedFrame.module, false);
frames.push(normalizedFrame);
frame = frame.parent; frame = frame.parent;
} }
// Frames are sent in order from oldest to newest. // Frames are sent in order from oldest to newest.
@ -169,7 +188,7 @@ class BrowserErrorReporter {
values: [ values: [
{ {
type: errorName, type: errorName,
value: errorMessage, value: mangleExtURL(errorMessage),
module: message.sourceName, module: message.sourceName,
stacktrace: { stacktrace: {
frames, frames,

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

@ -2,6 +2,7 @@
* http://creativecommons.org/publicdomain/zero/1.0/ */ * http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict"; "use strict";
ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm", this);
ChromeUtils.import("resource:///modules/BrowserErrorReporter.jsm", this); ChromeUtils.import("resource:///modules/BrowserErrorReporter.jsm", this);
/* global sinon */ /* global sinon */
@ -406,3 +407,45 @@ add_task(async function testFetchArguments() {
reporter.uninit(); reporter.uninit();
resetConsole(); resetConsole();
}); });
add_task(async function testAddonIDMangle() {
const fetchSpy = sinon.spy();
// Passing false here disables category checks on errors, which would
// otherwise block errors directly from extensions.
const reporter = new BrowserErrorReporter(fetchSpy, false);
await SpecialPowers.pushPrefEnv({set: [
[PREF_ENABLED, true],
[PREF_SAMPLE_RATE, "1.0"],
]});
reporter.init();
// Create and install test add-on
const id = "browsererrorcollection@example.com";
const extension = ExtensionTestUtils.loadExtension({
manifest: {
applications: {
gecko: { id },
},
},
background() {
throw new Error("testAddonIDMangle error");
},
});
await extension.startup();
// Just in case the error hasn't been thrown before add-on startup.
const call = await TestUtils.waitForCondition(
() => fetchCallForMessage(fetchSpy, "testAddonIDMangle error"),
`Wait for error from ${id} to be logged`,
);
const body = JSON.parse(call.args[1].body);
const stackFrame = body.exception.values[0].stacktrace.frames[0];
ok(
stackFrame.module.startsWith(`moz-extension://${id}/`),
"Stack frame filenames use the proper add-on ID instead of internal UUIDs.",
);
await extension.unload();
reporter.uninit();
resetConsole();
});

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

@ -217,7 +217,7 @@ BrowserAddonActor.prototype = {
} }
if (global instanceof Ci.nsIDOMWindow) { if (global instanceof Ci.nsIDOMWindow) {
return global.document.nodePrincipal.addonId; return global.document.nodePrincipal.addonId == this.id;
} }
return false; return false;

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

@ -15,6 +15,7 @@
#include "mozilla/dom/MessageEventBinding.h" #include "mozilla/dom/MessageEventBinding.h"
#include "mozilla/dom/ScriptSettings.h" #include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/WorkerPrivate.h" #include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/WorkerRef.h"
#include "mozilla/dom/WorkerRunnable.h" #include "mozilla/dom/WorkerRunnable.h"
#include "mozilla/dom/WorkerScope.h" #include "mozilla/dom/WorkerScope.h"
#include "mozilla/UniquePtrExtensions.h" #include "mozilla/UniquePtrExtensions.h"
@ -143,8 +144,8 @@ public:
void AddRefObject(); void AddRefObject();
void ReleaseObject(); void ReleaseObject();
bool RegisterWorkerHolder(); bool CreateWorkerRef(WorkerPrivate* aWorkerPrivate);
void UnregisterWorkerHolder(); void ReleaseWorkerRef();
void AssertIsOnTargetThread() const void AssertIsOnTargetThread() const
{ {
@ -282,11 +283,8 @@ public:
nsString mLastFieldValue; nsString mLastFieldValue;
// EventSourceImpl internal states. // EventSourceImpl internal states.
// The worker private where the EventSource is created. nullptr if created on // WorkerRef to keep the worker alive. (accessed on worker thread only)
// main thread. (accessed on worker thread only) RefPtr<ThreadSafeWorkerRef> mWorkerRef;
WorkerPrivate* mWorkerPrivate;
// Holder to worker to keep worker alive. (accessed on worker thread only)
nsAutoPtr<WorkerHolder> mWorkerHolder;
// This mutex protects mFrozen and mEventSource->mReadyState that are used in // This mutex protects mFrozen and mEventSource->mReadyState that are used in
// different threads. // different threads.
mozilla::Mutex mMutex; mozilla::Mutex mMutex;
@ -353,8 +351,6 @@ EventSourceImpl::EventSourceImpl(EventSource* aEventSource)
{ {
MOZ_ASSERT(mEventSource); MOZ_ASSERT(mEventSource);
if (!mIsMainThread) { if (!mIsMainThread) {
mWorkerPrivate = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(mWorkerPrivate);
mEventSource->mIsMainThread = false; mEventSource->mIsMainThread = false;
} }
SetReadyState(CONNECTING); SetReadyState(CONNECTING);
@ -364,11 +360,11 @@ class CleanupRunnable final : public WorkerMainThreadRunnable
{ {
public: public:
explicit CleanupRunnable(EventSourceImpl* aEventSourceImpl) explicit CleanupRunnable(EventSourceImpl* aEventSourceImpl)
: WorkerMainThreadRunnable(aEventSourceImpl->mWorkerPrivate, : WorkerMainThreadRunnable(GetCurrentThreadWorkerPrivate(),
NS_LITERAL_CSTRING("EventSource :: Cleanup")) NS_LITERAL_CSTRING("EventSource :: Cleanup"))
, mImpl(aEventSourceImpl) , mImpl(aEventSourceImpl)
{ {
mImpl->mWorkerPrivate->AssertIsOnWorkerThread(); mWorkerPrivate->AssertIsOnWorkerThread();
} }
bool MainThreadRun() override bool MainThreadRun() override
@ -413,7 +409,7 @@ EventSourceImpl::CloseInternal()
RefPtr<CleanupRunnable> runnable = new CleanupRunnable(this); RefPtr<CleanupRunnable> runnable = new CleanupRunnable(this);
runnable->Dispatch(Killing, rv); runnable->Dispatch(Killing, rv);
MOZ_ASSERT(!rv.Failed()); MOZ_ASSERT(!rv.Failed());
UnregisterWorkerHolder(); ReleaseWorkerRef();
} }
while (mMessagesToDispatch.GetSize() != 0) { while (mMessagesToDispatch.GetSize() != 0) {
@ -452,20 +448,22 @@ void EventSourceImpl::CleanupOnMainThread()
class InitRunnable final : public WorkerMainThreadRunnable class InitRunnable final : public WorkerMainThreadRunnable
{ {
public: public:
explicit InitRunnable(EventSourceImpl* aEventSourceImpl, InitRunnable(WorkerPrivate* aWorkerPrivate,
const nsAString& aURL) EventSourceImpl* aEventSourceImpl,
: WorkerMainThreadRunnable(aEventSourceImpl->mWorkerPrivate, const nsAString& aURL)
: WorkerMainThreadRunnable(aWorkerPrivate,
NS_LITERAL_CSTRING("EventSource :: Init")) NS_LITERAL_CSTRING("EventSource :: Init"))
, mImpl(aEventSourceImpl) , mImpl(aEventSourceImpl)
, mURL(aURL) , mURL(aURL)
{ {
mImpl->mWorkerPrivate->AssertIsOnWorkerThread(); MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
} }
bool MainThreadRun() override bool MainThreadRun() override
{ {
// Get principal from worker's owner document or from worker. // Get principal from worker's owner document or from worker.
WorkerPrivate* wp = mImpl->mWorkerPrivate; WorkerPrivate* wp = mWorkerPrivate;
while (wp->GetParent()) { while (wp->GetParent()) {
wp = wp->GetParent(); wp = wp->GetParent();
} }
@ -485,13 +483,36 @@ public:
nsresult ErrorCode() const { return mRv; } nsresult ErrorCode() const { return mRv; }
protected: private:
// Raw pointer because this runnable is sync. // Raw pointer because this runnable is sync.
EventSourceImpl* mImpl; EventSourceImpl* mImpl;
const nsAString& mURL; const nsAString& mURL;
nsresult mRv; nsresult mRv;
}; };
class ConnectRunnable final : public WorkerMainThreadRunnable
{
public:
explicit ConnectRunnable(WorkerPrivate* aWorkerPrivate,
EventSourceImpl* aEventSourceImpl)
: WorkerMainThreadRunnable(aWorkerPrivate,
NS_LITERAL_CSTRING("EventSource :: Connect"))
, mImpl(aEventSourceImpl)
{
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
}
bool MainThreadRun() override
{
mImpl->InitChannelAndRequestEventSource();
return true;
}
private:
RefPtr<EventSourceImpl> mImpl;
};
nsresult nsresult
EventSourceImpl::ParseURL(const nsAString& aURL) EventSourceImpl::ParseURL(const nsAString& aURL)
{ {
@ -585,11 +606,6 @@ EventSourceImpl::Init(nsIPrincipal* aPrincipal,
DEFAULT_RECONNECTION_TIME_VALUE); DEFAULT_RECONNECTION_TIME_VALUE);
mUnicodeDecoder = UTF_8_ENCODING->NewDecoderWithBOMRemoval(); mUnicodeDecoder = UTF_8_ENCODING->NewDecoderWithBOMRemoval();
// the constructor should throw a SYNTAX_ERROR only if it fails resolving the
// url parameter, so we don't care about the InitChannelAndRequestEventSource
// result.
InitChannelAndRequestEventSource();
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -1131,11 +1147,11 @@ class CallRestartConnection final : public WorkerMainThreadRunnable
public: public:
explicit CallRestartConnection(EventSourceImpl* aEventSourceImpl) explicit CallRestartConnection(EventSourceImpl* aEventSourceImpl)
: WorkerMainThreadRunnable( : WorkerMainThreadRunnable(
aEventSourceImpl->mWorkerPrivate, aEventSourceImpl->mWorkerRef->Private(),
NS_LITERAL_CSTRING("EventSource :: RestartConnection")) NS_LITERAL_CSTRING("EventSource :: RestartConnection"))
, mImpl(aEventSourceImpl) , mImpl(aEventSourceImpl)
{ {
mImpl->mWorkerPrivate->AssertIsOnWorkerThread(); mWorkerPrivate->AssertIsOnWorkerThread();
} }
bool MainThreadRun() override bool MainThreadRun() override
@ -1476,8 +1492,8 @@ EventSourceImpl::DispatchAllMessageEvents()
return; return;
} }
} else { } else {
MOZ_ASSERT(mWorkerPrivate); MOZ_ASSERT(mWorkerRef);
if (NS_WARN_IF(!jsapi.Init(mWorkerPrivate->GlobalScope()))) { if (NS_WARN_IF(!jsapi.Init(mWorkerRef->Private()->GlobalScope()))) {
return; return;
} }
} }
@ -1777,28 +1793,6 @@ EventSourceImpl::ReleaseObject()
} }
namespace { namespace {
class EventSourceWorkerHolder final : public WorkerHolder
{
public:
explicit EventSourceWorkerHolder(EventSourceImpl* aEventSourceImpl)
: WorkerHolder("EventSourceWorkerHolder")
, mEventSourceImpl(aEventSourceImpl)
{
}
bool Notify(WorkerStatus aStatus) override
{
MOZ_ASSERT(aStatus > Running);
if (aStatus >= Canceling) {
mEventSourceImpl->Close();
}
return true;
}
private:
// Raw pointer because the EventSourceImpl object keeps alive the holder.
EventSourceImpl* mEventSourceImpl;
};
class WorkerRunnableDispatcher final : public WorkerRunnable class WorkerRunnableDispatcher final : public WorkerRunnable
{ {
@ -1849,30 +1843,32 @@ private:
} // namespace } // namespace
bool EventSourceImpl::RegisterWorkerHolder() bool EventSourceImpl::CreateWorkerRef(WorkerPrivate* aWorkerPrivate)
{ {
MOZ_ASSERT(!IsShutDown()); MOZ_ASSERT(!IsShutDown());
MOZ_ASSERT(mWorkerPrivate); MOZ_ASSERT(!mWorkerRef);
mWorkerPrivate->AssertIsOnWorkerThread(); MOZ_ASSERT(aWorkerPrivate);
MOZ_ASSERT(!mWorkerHolder); aWorkerPrivate->AssertIsOnWorkerThread();
mWorkerHolder = new EventSourceWorkerHolder(this);
if (NS_WARN_IF(!mWorkerHolder->HoldWorker(mWorkerPrivate, Canceling))) { RefPtr<EventSourceImpl> self = this;
mWorkerHolder = nullptr; RefPtr<StrongWorkerRef> workerRef =
StrongWorkerRef::Create(aWorkerPrivate, "EventSource", [self]() {
self->Close();
});
if (NS_WARN_IF(!workerRef)) {
return false; return false;
} }
mWorkerRef = new ThreadSafeWorkerRef(workerRef);
return true; return true;
} }
void EventSourceImpl::UnregisterWorkerHolder() void EventSourceImpl::ReleaseWorkerRef()
{ {
// RegisterWorkerHolder fail will destroy EventSourceImpl and invoke
// UnregisterWorkerHolder.
MOZ_ASSERT(IsClosed()); MOZ_ASSERT(IsClosed());
MOZ_ASSERT(mWorkerPrivate); MOZ_ASSERT(IsCurrentThreadRunningWorker());
mWorkerPrivate->AssertIsOnWorkerThread(); mWorkerRef = nullptr;
// The DTOR of this WorkerHolder will release the worker for us.
mWorkerHolder = nullptr;
mWorkerPrivate = nullptr;
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -1896,11 +1892,11 @@ EventSourceImpl::Dispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags)
if (IsShutDown()) { if (IsShutDown()) {
return NS_OK; return NS_OK;
} }
MOZ_ASSERT(mWorkerPrivate);
// If the target is a worker, we have to use a custom WorkerRunnableDispatcher // If the target is a worker, we have to use a custom WorkerRunnableDispatcher
// runnable. // runnable.
RefPtr<WorkerRunnableDispatcher> event = RefPtr<WorkerRunnableDispatcher> event =
new WorkerRunnableDispatcher(this, mWorkerPrivate, event_ref.forget()); new WorkerRunnableDispatcher(this, mWorkerRef->Private(), event_ref.forget());
if (!event->Dispatch()) { if (!event->Dispatch()) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
@ -1982,23 +1978,53 @@ EventSource::Constructor(const GlobalObject& aGlobal, const nsAString& aURL,
return nullptr; return nullptr;
} }
eventSourceImp->Init(principal, aURL, aRv); eventSourceImp->Init(principal, aURL, aRv);
} else {
// In workers we have to keep the worker alive using a WorkerHolder in order
// to dispatch messages correctly.
if (!eventSourceImp->RegisterWorkerHolder()) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
RefPtr<InitRunnable> runnable = new InitRunnable(eventSourceImp, aURL);
runnable->Dispatch(Terminating, aRv);
if (NS_WARN_IF(aRv.Failed())) { if (NS_WARN_IF(aRv.Failed())) {
return nullptr; return nullptr;
} }
aRv = runnable->ErrorCode();
eventSourceImp->InitChannelAndRequestEventSource();
return eventSource.forget();
} }
// Worker side.
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(workerPrivate);
RefPtr<InitRunnable> initRunnable =
new InitRunnable(workerPrivate, eventSourceImp, aURL);
initRunnable->Dispatch(Terminating, aRv);
if (NS_WARN_IF(aRv.Failed())) { if (NS_WARN_IF(aRv.Failed())) {
return nullptr; return nullptr;
} }
aRv = initRunnable->ErrorCode();
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
// In workers we have to keep the worker alive using a WorkerRef in order
// to dispatch messages correctly.
if (!eventSourceImp->CreateWorkerRef(workerPrivate)) {
// The worker is already shutting down. Let's return an already closed
// object, but marked as Connecting.
eventSource->Close();
// EventSourceImpl must be released before returning the object, otherwise
// it will set EventSource to a CLOSED state in its DTOR.
eventSourceImp = nullptr;
eventSource->mReadyState = EventSourceImpl::CONNECTING;
return eventSource.forget();
}
// Let's connect to the server.
RefPtr<ConnectRunnable> connectRunnable =
new ConnectRunnable(workerPrivate, eventSourceImp);
connectRunnable->Dispatch(Terminating, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return eventSource.forget(); return eventSource.forget();
} }

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

@ -85,7 +85,7 @@ struct LinkedProgramInfo final
: public RefCounted<LinkedProgramInfo> : public RefCounted<LinkedProgramInfo>
, public SupportsWeakPtr<LinkedProgramInfo> , public SupportsWeakPtr<LinkedProgramInfo>
{ {
friend class WebGLProgram; friend class mozilla::WebGLProgram;
MOZ_DECLARE_REFCOUNTED_TYPENAME(LinkedProgramInfo) MOZ_DECLARE_REFCOUNTED_TYPENAME(LinkedProgramInfo)
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(LinkedProgramInfo) MOZ_DECLARE_WEAKREFERENCE_TYPENAME(LinkedProgramInfo)

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

@ -1,4 +1,4 @@
52547 52568
0/nm 0/nm
0th/pt 0th/pt
1/n1 1/n1
@ -14713,6 +14713,7 @@ anticyclonic
antidemocratic antidemocratic
antidepressant/MS antidepressant/MS
antidote/MS antidote/MS
antifa
antifascist/MS antifascist/MS
antifreeze/M antifreeze/M
antigen/SM antigen/SM
@ -16936,6 +16937,7 @@ blockader/M
blockage/MS blockage/MS
blockbuster/SM blockbuster/SM
blockbusting/M blockbusting/M
blockchain/S
blocker/MS blocker/MS
blockhead/SM blockhead/SM
blockhouse/MS blockhouse/MS
@ -22428,6 +22430,7 @@ demonstration/M
demonstrative/MYSP demonstrative/MYSP
demonstrativeness/M demonstrativeness/M
demonstrator/MS demonstrator/MS
demonym/S
demote/GD demote/GD
demotic demotic
demount demount
@ -24433,6 +24436,7 @@ ember/SM
embezzle/ZGLDRS embezzle/ZGLDRS
embezzlement/M embezzlement/M
embezzler/M embezzler/M
embiggen
embitter/GLDS embitter/GLDS
embitterment/M embitterment/M
emblazon/GDLS emblazon/GDLS
@ -27921,6 +27925,7 @@ glamorization/M
glamorize/DSG glamorize/DSG
glamorous/Y glamorous/Y
glamour/GMDS glamour/GMDS
glamping
glance/DSMG glance/DSMG
gland/SM gland/SM
glandes glandes
@ -28761,6 +28766,7 @@ hacktivist/MS
hackwork/M hackwork/M
had had
haddock/SM haddock/SM
hadith/S
hadn't hadn't
hadst hadst
hafnium/M hafnium/M
@ -28996,6 +29002,7 @@ harelip/SM
harelipped harelipped
harem/SM harem/SM
haricot/S haricot/S
harissa
hark/DGS hark/DGS
harlequin/SM harlequin/SM
harlot/SM harlot/SM
@ -29634,6 +29641,7 @@ hobnobbed
hobnobbing hobnobbing
hobo/MS hobo/MS
hoboes hoboes
hoc
hock/MDSG hock/MDSG
hockey/M hockey/M
hockshop/MS hockshop/MS
@ -32138,6 +32146,7 @@ kWh
kabbala kabbala
kabbalah kabbalah
kabob/SM kabob/SM
kabocha
kaboom kaboom
kabuki/M kabuki/M
kaddish/MS kaddish/MS
@ -32422,6 +32431,7 @@ kohl
kohlrabi/M kohlrabi/M
kohlrabies kohlrabies
kola/MS kola/MS
kombucha
kook/MS kook/MS
kookaburra/SM kookaburra/SM
kookiness/M kookiness/M
@ -34727,6 +34737,7 @@ microcode
microcomputer/MS microcomputer/MS
microcosm/MS microcosm/MS
microcosmic microcosmic
microcredit
microdot/SM microdot/SM
microeconomics/M microeconomics/M
microelectronic/S microelectronic/S
@ -34734,6 +34745,7 @@ microelectronics/M
microfiber/MS microfiber/MS
microfiche/M microfiche/M
microfilm/GMDS microfilm/GMDS
microfinance
microfloppies microfloppies
microgroove/SM microgroove/SM
microlight/MS microlight/MS
@ -35258,6 +35270,7 @@ moistness/M
moisture/M moisture/M
moisturize/ZGDRS moisturize/ZGDRS
moisturizer/M moisturizer/M
mojo/S
molar/SM molar/SM
molasses/M molasses/M
mold/MDRJSZG mold/MDRJSZG
@ -36143,6 +36156,7 @@ nelson/SM
nematode/SM nematode/SM
nemeses nemeses
nemesis/M nemesis/M
neoadjuvant
neoclassic neoclassic
neoclassical neoclassical
neoclassicism/M neoclassicism/M
@ -37995,6 +38009,7 @@ oxygenate/DSGN
oxygenation/M oxygenation/M
oxymora oxymora
oxymoron/M oxymoron/M
oy
oyes oyes
oyez oyez
oyster/SM oyster/SM
@ -42412,6 +42427,7 @@ respectful/EY
respectfulness/M respectfulness/M
respective/Y respective/Y
respell/SGD respell/SGD
respellings
respiration/M respiration/M
respirator/SM respirator/SM
respiratory respiratory
@ -44946,6 +44962,7 @@ site/MGDS
sitemap/SM sitemap/SM
sitter/SM sitter/SM
sitting/SM sitting/SM
situ
situate/DSXGN situate/DSXGN
situation/M situation/M
situational situational
@ -45404,7 +45421,7 @@ snarly/TR
snatch/ZGMDRS snatch/ZGMDRS
snatcher/M snatcher/M
snazzily snazzily
snazzy/TR snazzy/TRP
sneak/SMDRZG sneak/SMDRZG
sneaker/M sneaker/M
sneakily sneakily
@ -47117,6 +47134,7 @@ subtrahend/SM
subtropic/S subtropic/S
subtropical subtropical
subtropics/M subtropics/M
subtweet/S
suburb/MS suburb/MS
suburban/SM suburban/SM
suburbanite/SM suburbanite/SM
@ -49875,6 +49893,7 @@ tyrannous
tyranny/SM tyranny/SM
tyrant/SM tyrant/SM
tyro/MS tyro/MS
tzatziki
u/S u/S
ubiquitous/Y ubiquitous/Y
ubiquity/M ubiquity/M
@ -51565,6 +51584,7 @@ wellington/MS
wellness/M wellness/M
wellspring/MS wellspring/MS
welly/S welly/S
welp
welsh/ZGDRS welsh/ZGDRS
welsher/M welsher/M
welt/MDRSZG welt/MDRSZG
@ -52104,6 +52124,7 @@ word's
word/ADSG word/ADSG
wordage/M wordage/M
wordbook/SM wordbook/SM
wordie/S
wordily wordily
wordiness/M wordiness/M
wording/SM wording/SM

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

@ -195,7 +195,7 @@ struct nsGridContainerFrame::TrackSize
eMaxContentMinSizing = 0x4, eMaxContentMinSizing = 0x4,
eMinOrMaxContentMinSizing = eMinContentMinSizing | eMaxContentMinSizing, eMinOrMaxContentMinSizing = eMinContentMinSizing | eMaxContentMinSizing,
eIntrinsicMinSizing = eMinOrMaxContentMinSizing | eAutoMinSizing, eIntrinsicMinSizing = eMinOrMaxContentMinSizing | eAutoMinSizing,
// 0x8 is unused, feel free to take it! eModified = 0x8,
eAutoMaxSizing = 0x10, eAutoMaxSizing = 0x10,
eMinContentMaxSizing = 0x20, eMinContentMaxSizing = 0x20,
eMaxContentMaxSizing = 0x40, eMaxContentMaxSizing = 0x40,
@ -208,6 +208,7 @@ struct nsGridContainerFrame::TrackSize
eSkipGrowUnlimited = eSkipGrowUnlimited1 | eSkipGrowUnlimited2, eSkipGrowUnlimited = eSkipGrowUnlimited1 | eSkipGrowUnlimited2,
eBreakBefore = 0x800, eBreakBefore = 0x800,
eFitContent = 0x1000, eFitContent = 0x1000,
eInfinitelyGrowable = 0x2000,
}; };
StateBits Initialize(nscoord aPercentageBasis, StateBits Initialize(nscoord aPercentageBasis,
@ -1139,6 +1140,61 @@ struct nsGridContainerFrame::Tracks
*/ */
void AlignBaselineSubtree(const GridItemInfo& aGridItem) const; void AlignBaselineSubtree(const GridItemInfo& aGridItem) const;
enum class TrackSizingPhase
{
eIntrinsicMinimums,
eContentBasedMinimums,
eMaxContentMinimums,
eIntrinsicMaximums,
eMaxContentMaximums,
};
// Some data we collect on each item for Step 2 of the Track Sizing Algorithm
// in ResolveIntrinsicSize below.
struct Step2ItemData final
{
uint32_t mSpan;
TrackSize::StateBits mState;
LineRange mLineRange;
nscoord mMinSize;
nscoord mMinContentContribution;
nscoord mMaxContentContribution;
nsIFrame* mFrame;
static bool IsSpanLessThan(const Step2ItemData& a, const Step2ItemData& b)
{
return a.mSpan < b.mSpan;
}
template<TrackSizingPhase phase>
nscoord SizeContributionForPhase() const
{
switch (phase) {
case TrackSizingPhase::eIntrinsicMinimums:
case TrackSizingPhase::eIntrinsicMaximums:
return mMinSize;
case TrackSizingPhase::eContentBasedMinimums:
return mMinContentContribution;
case TrackSizingPhase::eMaxContentMinimums:
case TrackSizingPhase::eMaxContentMaximums:
return mMaxContentContribution;
}
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected phase");
}
};
using FitContentClamper =
std::function<bool(uint32_t aTrack, nscoord aMinSize, nscoord* aSize)>;
// Helper method for ResolveIntrinsicSize.
template<TrackSizingPhase phase>
bool GrowSizeForSpanningItems(nsTArray<Step2ItemData>::iterator aIter,
const nsTArray<Step2ItemData>::iterator aEnd,
nsTArray<uint32_t>& aTracks,
nsTArray<TrackSize>& aPlan,
nsTArray<TrackSize>& aItemPlan,
TrackSize::StateBits aSelector,
const FitContentClamper& aClamper = nullptr,
bool aNeedInfinitelyGrowableFlag = false);
/** /**
* Resolve Intrinsic Track Sizes. * Resolve Intrinsic Track Sizes.
* http://dev.w3.org/csswg/css-grid/#algo-content * http://dev.w3.org/csswg/css-grid/#algo-content
@ -1161,66 +1217,117 @@ struct nsGridContainerFrame::Tracks
SizingConstraint aConstraint, SizingConstraint aConstraint,
const LineRange& aRange, const LineRange& aRange,
const GridItemInfo& aGridItem); const GridItemInfo& aGridItem);
// Helper method that returns the track size to use in §11.5.1.2
// https://drafts.csswg.org/css-grid/#extra-space
template<TrackSizingPhase phase> static
nscoord StartSizeInDistribution(const TrackSize& aSize)
{
switch (phase) {
case TrackSizingPhase::eIntrinsicMinimums:
case TrackSizingPhase::eContentBasedMinimums:
case TrackSizingPhase::eMaxContentMinimums:
return aSize.mBase;
case TrackSizingPhase::eIntrinsicMaximums:
case TrackSizingPhase::eMaxContentMaximums:
if (aSize.mLimit == NS_UNCONSTRAINEDSIZE) {
return aSize.mBase;
}
return aSize.mLimit;
}
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected phase");
}
/** /**
* Collect the tracks which are growable (matching aSelector) into * Collect the tracks which are growable (matching aSelector) into
* aGrowableTracks, and return the amount of space that can be used * aGrowableTracks, and return the amount of space that can be used
* to grow those tracks. Specifically, we return aAvailableSpace minus * to grow those tracks. This method implements CSS Grid §11.5.1.2.
* the sum of mBase's (and corresponding grid gaps) in aPlan (clamped to 0) * https://drafts.csswg.org/css-grid/#extra-space
* for the tracks in aRange, or zero when there are no growable tracks.
* @note aPlan[*].mBase represents a planned new base or limit.
*/ */
nscoord CollectGrowable(nscoord aAvailableSpace, template<TrackSizingPhase phase>
const nsTArray<TrackSize>& aPlan, nscoord CollectGrowable(nscoord aAvailableSpace,
const LineRange& aRange, const LineRange& aRange,
TrackSize::StateBits aSelector, TrackSize::StateBits aSelector,
nsTArray<uint32_t>& aGrowableTracks) const nsTArray<uint32_t>& aGrowableTracks) const
{ {
MOZ_ASSERT(aAvailableSpace > 0, "why call me?"); MOZ_ASSERT(aAvailableSpace > 0, "why call me?");
nscoord space = aAvailableSpace - mGridGap * (aRange.Extent() - 1); nscoord space = aAvailableSpace - mGridGap * (aRange.Extent() - 1);
const uint32_t start = aRange.mStart; const uint32_t start = aRange.mStart;
const uint32_t end = aRange.mEnd; const uint32_t end = aRange.mEnd;
for (uint32_t i = start; i < end; ++i) { for (uint32_t i = start; i < end; ++i) {
const TrackSize& sz = aPlan[i]; const TrackSize& sz = mSizes[i];
space -= sz.mBase; space -= StartSizeInDistribution<phase>(sz);
if (space <= 0) { if (space <= 0) {
return 0; return 0;
} }
if ((sz.mState & aSelector) && !sz.IsFrozen()) { if (sz.mState & aSelector) {
aGrowableTracks.AppendElement(i); aGrowableTracks.AppendElement(i);
} }
} }
return aGrowableTracks.IsEmpty() ? 0 : space; return aGrowableTracks.IsEmpty() ? 0 : space;
} }
void SetupGrowthPlan(nsTArray<TrackSize>& aPlan, template<TrackSizingPhase phase>
const nsTArray<uint32_t>& aTracks) const void InitializeItemPlan(nsTArray<TrackSize>& aItemPlan,
const nsTArray<uint32_t>& aTracks) const
{ {
for (uint32_t track : aTracks) { for (uint32_t track : aTracks) {
aPlan[track] = mSizes[track]; auto& plan = aItemPlan[track];
const TrackSize& sz = mSizes[track];
plan.mBase = StartSizeInDistribution<phase>(sz);
bool unlimited = sz.mState & TrackSize::eInfinitelyGrowable;
plan.mLimit = unlimited ? NS_UNCONSTRAINEDSIZE : sz.mLimit;
plan.mState = sz.mState;
} }
} }
void CopyPlanToBase(const nsTArray<TrackSize>& aPlan, template<TrackSizingPhase phase>
const nsTArray<uint32_t>& aTracks) void InitializePlan(nsTArray<TrackSize>& aPlan) const
{ {
for (uint32_t track : aTracks) { for (size_t i = 0, len = aPlan.Length(); i < len; ++i) {
MOZ_ASSERT(mSizes[track].mBase <= aPlan[track].mBase); auto& plan = aPlan[i];
mSizes[track].mBase = aPlan[track].mBase; const auto& sz = mSizes[i];
plan.mBase = StartSizeInDistribution<phase>(sz);
MOZ_ASSERT(phase == TrackSizingPhase::eMaxContentMaximums ||
!(sz.mState & TrackSize::eInfinitelyGrowable),
"forgot to reset the eInfinitelyGrowable bit?");
plan.mState = sz.mState;
} }
} }
void CopyPlanToLimit(const nsTArray<TrackSize>& aPlan, template<TrackSizingPhase phase>
const nsTArray<uint32_t>& aTracks) void CopyPlanToSize(const nsTArray<TrackSize>& aPlan,
bool aNeedInfinitelyGrowableFlag = false)
{ {
for (uint32_t track : aTracks) { for (size_t i = 0, len = mSizes.Length(); i < len; ++i) {
MOZ_ASSERT(mSizes[track].mLimit == NS_UNCONSTRAINEDSIZE || const auto& plan = aPlan[i];
mSizes[track].mLimit <= aPlan[track].mBase); MOZ_ASSERT(plan.mBase >= 0);
mSizes[track].mLimit = aPlan[track].mBase; auto& sz = mSizes[i];
switch (phase) {
case TrackSizingPhase::eIntrinsicMinimums:
case TrackSizingPhase::eContentBasedMinimums:
case TrackSizingPhase::eMaxContentMinimums:
sz.mBase = plan.mBase;
break;
case TrackSizingPhase::eIntrinsicMaximums:
if (plan.mState & TrackSize::eModified) {
if (sz.mLimit == NS_UNCONSTRAINEDSIZE &&
aNeedInfinitelyGrowableFlag) {
sz.mState |= TrackSize::eInfinitelyGrowable;
}
sz.mLimit = plan.mBase;
}
break;
case TrackSizingPhase::eMaxContentMaximums:
if (plan.mState & TrackSize::eModified) {
sz.mLimit = plan.mBase;
}
sz.mState &= ~TrackSize::eInfinitelyGrowable;
break;
}
} }
} }
using FitContentClamper =
std::function<bool(uint32_t aTrack, nscoord aMinSize, nscoord* aSize)>;
/** /**
* Grow the planned size for tracks in aGrowableTracks up to their limit * Grow the planned size for tracks in aGrowableTracks up to their limit
* and then freeze them (all aGrowableTracks must be unfrozen on entry). * and then freeze them (all aGrowableTracks must be unfrozen on entry).
@ -1280,12 +1387,13 @@ struct nsGridContainerFrame::Tracks
* assumed that aPlan have no aSkipFlag set for tracks in aGrowableTracks * assumed that aPlan have no aSkipFlag set for tracks in aGrowableTracks
* on entry to this method. * on entry to this method.
*/ */
uint32_t MarkExcludedTracks(nsTArray<TrackSize>& aPlan, static uint32_t
uint32_t aNumGrowable, MarkExcludedTracks(nsTArray<TrackSize>& aPlan,
const nsTArray<uint32_t>& aGrowableTracks, uint32_t aNumGrowable,
TrackSize::StateBits aMinSizingSelector, const nsTArray<uint32_t>& aGrowableTracks,
TrackSize::StateBits aMaxSizingSelector, TrackSize::StateBits aMinSizingSelector,
TrackSize::StateBits aSkipFlag) const TrackSize::StateBits aMaxSizingSelector,
TrackSize::StateBits aSkipFlag)
{ {
bool foundOneSelected = false; bool foundOneSelected = false;
bool foundOneGrowable = false; bool foundOneGrowable = false;
@ -1315,41 +1423,60 @@ struct nsGridContainerFrame::Tracks
} }
/** /**
* Increase the planned size for tracks in aGrowableTracks that match * Mark all tracks in aGrowableTracks with an eSkipGrowUnlimited bit if
* aSelector (or all tracks if aSelector is zero) beyond their limit. * they *shouldn't* grow unlimited in §11.5.1.2.3 "Distribute space beyond
* growth limits" https://drafts.csswg.org/css-grid/#extra-space
* Return the number of tracks that are still growable.
*/
template<TrackSizingPhase phase>
static uint32_t
MarkExcludedTracks(nsTArray<TrackSize>& aPlan,
const nsTArray<uint32_t>& aGrowableTracks,
TrackSize::StateBits aSelector)
{
uint32_t numGrowable = aGrowableTracks.Length();
if (phase == TrackSizingPhase::eIntrinsicMaximums ||
phase == TrackSizingPhase::eMaxContentMaximums) {
// "when handling any intrinsic growth limit: all affected tracks"
return numGrowable;
}
MOZ_ASSERT(aSelector == (aSelector & TrackSize::eIntrinsicMinSizing) &&
(aSelector & TrackSize::eMaxContentMinSizing),
"Should only get here for track sizing steps 2.1 to 2.3");
// Note that eMaxContentMinSizing is always included. We do those first:
numGrowable = MarkExcludedTracks(aPlan, numGrowable, aGrowableTracks,
TrackSize::eMaxContentMinSizing,
TrackSize::eMaxContentMaxSizing,
TrackSize::eSkipGrowUnlimited1);
// Now mark min-content/auto min-sizing tracks if requested.
auto minOrAutoSelector = aSelector & ~TrackSize::eMaxContentMinSizing;
if (minOrAutoSelector) {
numGrowable = MarkExcludedTracks(aPlan, numGrowable, aGrowableTracks,
minOrAutoSelector,
TrackSize::eIntrinsicMaxSizing,
TrackSize::eSkipGrowUnlimited2);
}
return numGrowable;
}
/**
* Increase the planned size for tracks in aGrowableTracks that aren't
* marked with a eSkipGrowUnlimited flag beyond their limit.
* This implements the "Distribute space beyond growth limits" step in * This implements the "Distribute space beyond growth limits" step in
* https://drafts.csswg.org/css-grid/#distribute-extra-space * https://drafts.csswg.org/css-grid/#distribute-extra-space
*/ */
void GrowSelectedTracksUnlimited(nscoord aAvailableSpace, void GrowSelectedTracksUnlimited(nscoord aAvailableSpace,
nsTArray<TrackSize>& aPlan, nsTArray<TrackSize>& aPlan,
const nsTArray<uint32_t>& aGrowableTracks, const nsTArray<uint32_t>& aGrowableTracks,
TrackSize::StateBits aSelector, uint32_t aNumGrowable,
const FitContentClamper& aFitContentClamper) const const FitContentClamper& aFitContentClamper) const
{ {
MOZ_ASSERT(aAvailableSpace > 0 && aGrowableTracks.Length() > 0); MOZ_ASSERT(aAvailableSpace > 0 && aGrowableTracks.Length() > 0 &&
uint32_t numGrowable = aGrowableTracks.Length(); aNumGrowable <= aGrowableTracks.Length());
if (aSelector) {
MOZ_ASSERT(aSelector == (aSelector & TrackSize::eIntrinsicMinSizing) &&
(aSelector & TrackSize::eMaxContentMinSizing),
"Should only get here for track sizing steps 2.1 to 2.3");
// Note that eMaxContentMinSizing is always included. We do those first:
numGrowable = MarkExcludedTracks(aPlan, numGrowable, aGrowableTracks,
TrackSize::eMaxContentMinSizing,
TrackSize::eMaxContentMaxSizing,
TrackSize::eSkipGrowUnlimited1);
// Now mark min-content/auto min-sizing tracks if requested.
auto minOrAutoSelector = aSelector & ~TrackSize::eMaxContentMinSizing;
if (minOrAutoSelector) {
numGrowable = MarkExcludedTracks(aPlan, numGrowable, aGrowableTracks,
minOrAutoSelector,
TrackSize::eIntrinsicMaxSizing,
TrackSize::eSkipGrowUnlimited2);
}
}
nscoord space = aAvailableSpace; nscoord space = aAvailableSpace;
DebugOnly<bool> didClamp = false; DebugOnly<bool> didClamp = false;
while (numGrowable) { while (aNumGrowable) {
nscoord spacePerTrack = std::max<nscoord>(space / numGrowable, 1); nscoord spacePerTrack = std::max<nscoord>(space / aNumGrowable, 1);
for (uint32_t track : aGrowableTracks) { for (uint32_t track : aGrowableTracks) {
TrackSize& sz = aPlan[track]; TrackSize& sz = aPlan[track];
if (sz.mState & TrackSize::eSkipGrowUnlimited) { if (sz.mState & TrackSize::eSkipGrowUnlimited) {
@ -1365,7 +1492,7 @@ struct nsGridContainerFrame::Tracks
delta = newBase - sz.mBase; delta = newBase - sz.mBase;
MOZ_ASSERT(delta >= 0, "track size shouldn't shrink"); MOZ_ASSERT(delta >= 0, "track size shouldn't shrink");
sz.mState |= TrackSize::eSkipGrowUnlimited1; sz.mState |= TrackSize::eSkipGrowUnlimited1;
--numGrowable; --aNumGrowable;
} }
} }
sz.mBase = newBase; sz.mBase = newBase;
@ -1384,46 +1511,30 @@ struct nsGridContainerFrame::Tracks
* Distribute aAvailableSpace to the planned base size for aGrowableTracks * Distribute aAvailableSpace to the planned base size for aGrowableTracks
* up to their limits, then distribute the remaining space beyond the limits. * up to their limits, then distribute the remaining space beyond the limits.
*/ */
void DistributeToTrackBases(nscoord aAvailableSpace, template<TrackSizingPhase phase>
void DistributeToTrackSizes(nscoord aAvailableSpace,
nsTArray<TrackSize>& aPlan, nsTArray<TrackSize>& aPlan,
nsTArray<TrackSize>& aItemPlan,
nsTArray<uint32_t>& aGrowableTracks, nsTArray<uint32_t>& aGrowableTracks,
TrackSize::StateBits aSelector) TrackSize::StateBits aSelector,
const FitContentClamper& aFitContentClamper)
{ {
SetupGrowthPlan(aPlan, aGrowableTracks); InitializeItemPlan<phase>(aItemPlan, aGrowableTracks);
nscoord space = GrowTracksToLimit(aAvailableSpace, aPlan, aGrowableTracks, nullptr); nscoord space = GrowTracksToLimit(aAvailableSpace, aItemPlan, aGrowableTracks,
aFitContentClamper);
if (space > 0) { if (space > 0) {
GrowSelectedTracksUnlimited(space, aPlan, aGrowableTracks, aSelector, nullptr); uint32_t numGrowable =
MarkExcludedTracks<phase>(aItemPlan, aGrowableTracks, aSelector);
GrowSelectedTracksUnlimited(space, aItemPlan, aGrowableTracks,
numGrowable, aFitContentClamper);
} }
CopyPlanToBase(aPlan, aGrowableTracks); for (uint32_t track : aGrowableTracks) {
} nscoord& plannedSize = aPlan[track].mBase;
nscoord itemIncurredSize = aItemPlan[track].mBase;
/** if (plannedSize < itemIncurredSize) {
* Distribute aAvailableSpace to the planned limits for aGrowableTracks. plannedSize = itemIncurredSize;
*/
void DistributeToTrackLimits(nscoord aAvailableSpace,
nsTArray<TrackSize>& aPlan,
nsTArray<uint32_t>& aGrowableTracks,
const TrackSizingFunctions& aFunctions,
nscoord aPercentageBasis)
{
auto fitContentClamper = [&aFunctions, aPercentageBasis] (uint32_t aTrack,
nscoord aMinSize,
nscoord* aSize) {
nscoord fitContentLimit =
::ResolveToDefiniteSize(aFunctions.MaxSizingFor(aTrack), aPercentageBasis);
if (*aSize > fitContentLimit) {
*aSize = std::max(aMinSize, fitContentLimit);
return true;
} }
return false;
};
nscoord space = GrowTracksToLimit(aAvailableSpace, aPlan, aGrowableTracks,
fitContentClamper);
if (space > 0) {
GrowSelectedTracksUnlimited(aAvailableSpace, aPlan, aGrowableTracks,
TrackSize::StateBits(0), fitContentClamper);
} }
CopyPlanToLimit(aPlan, aGrowableTracks);
} }
/** /**
@ -4136,6 +4247,55 @@ nsGridContainerFrame::Tracks::AlignBaselineSubtree(
} }
} }
template<nsGridContainerFrame::Tracks::TrackSizingPhase phase>
bool
nsGridContainerFrame::Tracks::GrowSizeForSpanningItems(
nsTArray<Step2ItemData>::iterator aIter,
const nsTArray<Step2ItemData>::iterator aIterEnd,
nsTArray<uint32_t>& aTracks,
nsTArray<TrackSize>& aPlan,
nsTArray<TrackSize>& aItemPlan,
TrackSize::StateBits aSelector,
const FitContentClamper& aFitContentClamper,
bool aNeedInfinitelyGrowableFlag)
{
constexpr bool isMaxSizingPhase =
phase == TrackSizingPhase::eIntrinsicMaximums ||
phase == TrackSizingPhase::eMaxContentMaximums;
bool needToUpdateSizes = false;
InitializePlan<phase>(aPlan);
for (; aIter != aIterEnd; ++aIter) {
const Step2ItemData& item = *aIter;
if (!(item.mState & aSelector)) {
continue;
}
if (isMaxSizingPhase) {
for (auto j = item.mLineRange.mStart, end = item.mLineRange.mEnd; j < end; ++j) {
aPlan[j].mState |= TrackSize::eModified;
}
}
nscoord space = item.SizeContributionForPhase<phase>();
if (space <= 0) {
continue;
}
aTracks.ClearAndRetainStorage();
space = CollectGrowable<phase>(space, item.mLineRange, aSelector,
aTracks);
if (space > 0) {
DistributeToTrackSizes<phase>(space, aPlan, aItemPlan, aTracks, aSelector,
aFitContentClamper);
needToUpdateSizes = true;
}
}
if (isMaxSizingPhase) {
needToUpdateSizes = true;
}
if (needToUpdateSizes) {
CopyPlanToSize<phase>(aPlan, aNeedInfinitelyGrowableFlag);
}
return needToUpdateSizes;
}
void void
nsGridContainerFrame::Tracks::ResolveIntrinsicSize( nsGridContainerFrame::Tracks::ResolveIntrinsicSize(
GridReflowInput& aState, GridReflowInput& aState,
@ -4145,22 +4305,6 @@ nsGridContainerFrame::Tracks::ResolveIntrinsicSize(
nscoord aPercentageBasis, nscoord aPercentageBasis,
SizingConstraint aConstraint) SizingConstraint aConstraint)
{ {
// Some data we collect on each item for Step 2 of the algorithm below.
struct Step2ItemData
{
uint32_t mSpan;
TrackSize::StateBits mState;
LineRange mLineRange;
nscoord mMinSize;
nscoord mMinContentContribution;
nscoord mMaxContentContribution;
nsIFrame* mFrame;
static bool IsSpanLessThan(const Step2ItemData& a, const Step2ItemData& b)
{
return a.mSpan < b.mSpan;
}
};
// Resolve Intrinsic Track Sizes // Resolve Intrinsic Track Sizes
// http://dev.w3.org/csswg/css-grid/#algo-content // http://dev.w3.org/csswg/css-grid/#algo-content
// We're also setting eIsFlexing on the item state here to speed up // We're also setting eIsFlexing on the item state here to speed up
@ -4270,6 +4414,19 @@ nsGridContainerFrame::Tracks::ResolveIntrinsicSize(
// Step 2. // Step 2.
if (maxSpan) { if (maxSpan) {
auto fitContentClamper = [&aFunctions, aPercentageBasis] (uint32_t aTrack,
nscoord aMinSize,
nscoord* aSize)
{
nscoord fitContentLimit =
::ResolveToDefiniteSize(aFunctions.MaxSizingFor(aTrack), aPercentageBasis);
if (*aSize > fitContentLimit) {
*aSize = std::max(aMinSize, fitContentLimit);
return true;
}
return false;
};
// Sort the collected items on span length, shortest first. // Sort the collected items on span length, shortest first.
std::stable_sort(step2Items.begin(), step2Items.end(), std::stable_sort(step2Items.begin(), step2Items.end(),
Step2ItemData::IsSpanLessThan); Step2ItemData::IsSpanLessThan);
@ -4277,85 +4434,44 @@ nsGridContainerFrame::Tracks::ResolveIntrinsicSize(
nsTArray<uint32_t> tracks(maxSpan); nsTArray<uint32_t> tracks(maxSpan);
nsTArray<TrackSize> plan(mSizes.Length()); nsTArray<TrackSize> plan(mSizes.Length());
plan.SetLength(mSizes.Length()); plan.SetLength(mSizes.Length());
for (uint32_t i = 0, len = step2Items.Length(); i < len; ) { nsTArray<TrackSize> itemPlan(mSizes.Length());
// Start / end index for items of the same span length: itemPlan.SetLength(mSizes.Length());
const uint32_t spanGroupStartIndex = i; // Start / end iterator for items of the same span length:
uint32_t spanGroupEndIndex = len; auto spanGroupStart = step2Items.begin();
const uint32_t span = step2Items[i].mSpan; auto spanGroupEnd = spanGroupStart;
for (++i; i < len; ++i) { const auto end = step2Items.end();
if (step2Items[i].mSpan != span) { for (; spanGroupStart != end; spanGroupStart = spanGroupEnd) {
spanGroupEndIndex = i; while (spanGroupEnd != end &&
break; !Step2ItemData::IsSpanLessThan(*spanGroupStart, *spanGroupEnd)) {
} ++spanGroupEnd;
} }
const uint32_t span = spanGroupStart->mSpan;
bool updatedBase = false; // Did we update any mBase in step 2.1 - 2.3? bool updatedBase = false; // Did we update any mBase in step 2.1 - 2.3?
TrackSize::StateBits selector(TrackSize::eIntrinsicMinSizing); TrackSize::StateBits selector(TrackSize::eIntrinsicMinSizing);
if (stateBitsPerSpan[span] & selector) { if (stateBitsPerSpan[span] & selector) {
// Step 2.1 MinSize to intrinsic min-sizing. // Step 2.1 MinSize to intrinsic min-sizing.
for (i = spanGroupStartIndex; i < spanGroupEndIndex; ++i) { updatedBase =
Step2ItemData& item = step2Items[i]; GrowSizeForSpanningItems<TrackSizingPhase::eIntrinsicMinimums>(
if (!(item.mState & selector)) { spanGroupStart, spanGroupEnd, tracks, plan, itemPlan, selector);
continue;
}
nscoord space = item.mMinSize;
if (space <= 0) {
continue;
}
tracks.ClearAndRetainStorage();
space = CollectGrowable(space, mSizes, item.mLineRange, selector,
tracks);
if (space > 0) {
DistributeToTrackBases(space, plan, tracks, selector);
updatedBase = true;
}
}
} }
selector = contentBasedMinSelector; selector = contentBasedMinSelector;
if (stateBitsPerSpan[span] & selector) { if (stateBitsPerSpan[span] & selector) {
// Step 2.2 MinContentContribution to min-/max-content (and 'auto' when // Step 2.2 MinContentContribution to min-/max-content (and 'auto' when
// sizing under a min-content constraint) min-sizing. // sizing under a min-content constraint) min-sizing.
for (i = spanGroupStartIndex; i < spanGroupEndIndex; ++i) { updatedBase |=
Step2ItemData& item = step2Items[i]; GrowSizeForSpanningItems<TrackSizingPhase::eContentBasedMinimums>(
if (!(item.mState & selector)) { spanGroupStart, spanGroupEnd, tracks, plan, itemPlan, selector);
continue;
}
nscoord space = item.mMinContentContribution;
if (space <= 0) {
continue;
}
tracks.ClearAndRetainStorage();
space = CollectGrowable(space, mSizes, item.mLineRange, selector,
tracks);
if (space > 0) {
DistributeToTrackBases(space, plan, tracks, selector);
updatedBase = true;
}
}
} }
selector = maxContentMinSelector; selector = maxContentMinSelector;
if (stateBitsPerSpan[span] & selector) { if (stateBitsPerSpan[span] & selector) {
// Step 2.3 MaxContentContribution to max-content (and 'auto' when // Step 2.3 MaxContentContribution to max-content (and 'auto' when
// sizing under a max-content constraint) min-sizing. // sizing under a max-content constraint) min-sizing.
for (i = spanGroupStartIndex; i < spanGroupEndIndex; ++i) { updatedBase |=
Step2ItemData& item = step2Items[i]; GrowSizeForSpanningItems<TrackSizingPhase::eMaxContentMinimums>(
if (!(item.mState & selector)) { spanGroupStart, spanGroupEnd, tracks, plan, itemPlan, selector);
continue;
}
nscoord space = item.mMaxContentContribution;
if (space <= 0) {
continue;
}
tracks.ClearAndRetainStorage();
space = CollectGrowable(space, mSizes, item.mLineRange, selector,
tracks);
if (space > 0) {
DistributeToTrackBases(space, plan, tracks, selector);
updatedBase = true;
}
}
} }
if (updatedBase) { if (updatedBase) {
@ -4366,63 +4482,22 @@ nsGridContainerFrame::Tracks::ResolveIntrinsicSize(
} }
} }
} }
if (stateBitsPerSpan[span] & TrackSize::eIntrinsicMaxSizing) {
plan = mSizes;
for (TrackSize& sz : plan) {
if (sz.mLimit == NS_UNCONSTRAINEDSIZE) {
// use mBase as the planned limit
} else {
sz.mBase = sz.mLimit;
}
}
selector = TrackSize::eIntrinsicMaxSizing;
if (stateBitsPerSpan[span] & selector) {
const bool willRunStep2_6 =
stateBitsPerSpan[span] & TrackSize::eAutoOrMaxContentMaxSizing;
// Step 2.5 MinSize to intrinsic max-sizing. // Step 2.5 MinSize to intrinsic max-sizing.
for (i = spanGroupStartIndex; i < spanGroupEndIndex; ++i) { GrowSizeForSpanningItems<TrackSizingPhase::eIntrinsicMaximums>(
Step2ItemData& item = step2Items[i]; spanGroupStart, spanGroupEnd, tracks, plan, itemPlan, selector,
if (!(item.mState & TrackSize::eIntrinsicMaxSizing)) { fitContentClamper, willRunStep2_6);
continue;
}
nscoord space = item.mMinSize;
if (space <= 0) {
continue;
}
tracks.ClearAndRetainStorage();
space = CollectGrowable(space, plan, item.mLineRange,
TrackSize::eIntrinsicMaxSizing,
tracks);
if (space > 0) {
DistributeToTrackLimits(space, plan, tracks, aFunctions,
aPercentageBasis);
}
}
for (size_t j = 0, len = mSizes.Length(); j < len; ++j) {
TrackSize& sz = plan[j];
sz.mState &= ~(TrackSize::eFrozen | TrackSize::eSkipGrowUnlimited);
if (sz.mLimit != NS_UNCONSTRAINEDSIZE) {
sz.mLimit = sz.mBase; // collect the results from 2.5
}
}
if (stateBitsPerSpan[span] & TrackSize::eAutoOrMaxContentMaxSizing) { if (willRunStep2_6) {
// Step 2.6 MaxContentContribution to max-content max-sizing. // Step 2.6 MaxContentContribution to max-content max-sizing.
for (i = spanGroupStartIndex; i < spanGroupEndIndex; ++i) { selector = TrackSize::eAutoOrMaxContentMaxSizing;
Step2ItemData& item = step2Items[i]; GrowSizeForSpanningItems<TrackSizingPhase::eMaxContentMaximums>(
if (!(item.mState & TrackSize::eAutoOrMaxContentMaxSizing)) { spanGroupStart, spanGroupEnd, tracks, plan, itemPlan, selector,
continue; fitContentClamper);
}
nscoord space = item.mMaxContentContribution;
if (space <= 0) {
continue;
}
tracks.ClearAndRetainStorage();
space = CollectGrowable(space, plan, item.mLineRange,
TrackSize::eAutoOrMaxContentMaxSizing,
tracks);
if (space > 0) {
DistributeToTrackLimits(space, plan, tracks, aFunctions,
aPercentageBasis);
}
}
} }
} }
} }
@ -6944,6 +7019,9 @@ nsGridContainerFrame::TrackSize::Dump() const
if (mState & eFrozen) { if (mState & eFrozen) {
printf("frozen "); printf("frozen ");
} }
if (mState & eModified) {
printf("modified ");
}
if (mState & eBreakBefore) { if (mState & eBreakBefore) {
printf("break-before "); printf("break-before ");
} }

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

@ -142,3 +142,7 @@ support-files = file_scroll_position_restore.html
[test_scroll_position_restore_after_stop.html] [test_scroll_position_restore_after_stop.html]
[test_scroll_animation_restore.html] [test_scroll_animation_restore.html]
[test_scroll_position_iframe.html] [test_scroll_position_iframe.html]
[test_grid_track_sizing_algo_001.html]
skip-if = debug == true # the test is slow
[test_grid_track_sizing_algo_002.html]
skip-if = debug == true # the test is slow

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -17,9 +17,9 @@ body,html { color:black; background:white; font-size:16px; padding:0; margin:0;
clear:left; clear:left;
} }
.c1 { min-width:40px; margin-bottom: 2px; margin-right: 47px; } .c1 { width:40px; margin-bottom: 2px; margin-right: 47px; }
.r1 { min-width:70px; margin-left: 38px; margin-top: 2px; } .r1 { min-width:70px; margin-left: 38px; margin-top: 2px; }
.c3 { min-width:0; margin: 2px 18px 1px 85px; } .c3 { width:10px; margin: 2px 18px 1px 71px; }
span { span {
display: block; display: block;
@ -52,21 +52,22 @@ x { display:inline-block; width:10px; height:18px; }
<span class="r1"><x>&nbsp;</x></span> <span class="r1"><x>&nbsp;</x></span>
<span class="r1"><x>&nbsp;</x></span> <span class="r1"><x>&nbsp;</x></span>
</div> </div>
<div class="grid" style="width:436px"> <div class="grid" style="width:436px">
<span class="c1" style="margin-right:41px"><x>&nbsp;</x></span> <span class="c1" style="width:374px; margin-right:41px"><x>&nbsp;</x></span>
<span class="r1" style="margin-left:5px"><x>&nbsp;</x></span> <span class="r1" style="margin-left:5px"><x>&nbsp;</x></span>
<span class="c3" style="margin-left:405px; float:left;margin-top:1px;"><x>&nbsp;</x></span> <span class="c3" style="margin-left:405px; float:left;margin-top:1px;"><x>&nbsp;</x></span>
</div> </div>
<div class="grid" style="width:500px;"> <div class="grid" style="width:500px;">
<span class="c1" style="min-width:20px;margin-right:448px"><x>&nbsp;</x></span> <span class="c1" style="width:20px;margin-right:448px"><x>&nbsp;</x></span>
<span class="r1" style="min-width:10px;margin-left:28px; margin-right:426px"><x>&nbsp;</x></span> <span class="r1" style="min-width:10px;margin-left:28px; margin-right:426px"><x>&nbsp;</x></span>
<span class="r1" style="min-width:30px;margin-left:28px; margin-right:426px"><x>&nbsp;</x></span> <span class="r1" style="min-width:30px;margin-left:28px; margin-right:426px"><x>&nbsp;</x></span>
<span class="r1" style="min-width:10px;margin-left:28px; margin-right:426px"><x>&nbsp;</x></span> <span class="r1" style="min-width:10px;margin-left:28px; margin-right:426px"><x>&nbsp;</x></span>
</div> </div>
<div class="grid" style="width:583px;"> <div class="grid" style="width:583px;">
<span class="c1" style="margin-right:55px"><x>&nbsp;</x></span> <span class="c1" style="width:507px; margin-right:55px"><x>&nbsp;</x></span>
<span class="r1"><x>&nbsp;</x></span> <span class="r1"><x>&nbsp;</x></span>
<span class="c3" style="margin-left:538px; float:left;margin-top:1px;"><x>&nbsp;</x></span> <span class="c3" style="margin-left:538px; float:left;margin-top:1px;"><x>&nbsp;</x></span>
</div> </div>
@ -74,13 +75,13 @@ x { display:inline-block; width:10px; height:18px; }
<div class="grid" style="width:389px;"> <div class="grid" style="width:389px;">
<span class="c1" style="width:100px"><x>&nbsp;</x></span> <span class="c1" style="width:100px"><x>&nbsp;</x></span>
<span class="r1" style="width:300px;margin-left:68px;"><x>&nbsp;</x></span> <span class="r1" style="width:300px;margin-left:68px;"><x>&nbsp;</x></span>
<span class="c3" style="margin-left:245px;float:left;margin-top:1px;"><x>&nbsp;</x></span> <span class="c3" style="margin-left:131px;float:left;margin-top:1px;"><x>&nbsp;</x></span>
</div> </div>
<div class="grid" style="width:389px;"> <div class="grid" style="width:389px;">
<span class="c1" style="width:100px"><x>&nbsp;</x></span> <span class="c1" style="width:100px"><x>&nbsp;</x></span>
<span class="r1" style="width:300px;margin-left:68px;"><x>&nbsp;</x></span> <span class="r1" style="width:300px;margin-left:68px;"><x>&nbsp;</x></span>
<span class="c3" style="margin-left:245px;float:left;margin-top:1px;"><x>&nbsp;</x></span> <span class="c3" style="margin-left:131px;float:left;margin-top:1px;"><x>&nbsp;</x></span>
</div> </div>

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

@ -21,9 +21,9 @@ body,html { color:black; background:white; font-size:16px; padding:0; margin:0;
clear:left; clear:left;
} }
.c1 { min-width:40px; margin-bottom: 2px; margin-right: 47px; } .c1 { width:40px; margin-bottom: 2px; margin-right: 47px; }
.r1 { min-width:70px; margin-left: 38px; margin-top: 2px; } .r1 { min-width:70px; margin-left: 38px; margin-top: 2px; }
.c3 { min-width:0; margin: 2px 18px 1px 85px; } .c3 { width:10px; margin: 2px 18px 1px 71px; }
span { span {
display: block; display: block;
@ -56,21 +56,22 @@ x { display:inline-block; width:10px; height:18px; }
<span class="r1"><x>&nbsp;</x></span> <span class="r1"><x>&nbsp;</x></span>
<span class="r1"><x>&nbsp;</x></span> <span class="r1"><x>&nbsp;</x></span>
</div></div> </div></div>
<div class="wrap"><div class="grid" style="width:436px"> <div class="wrap"><div class="grid" style="width:436px">
<span class="c1" style="margin-right:41px"><x>&nbsp;</x></span> <span class="c1" style="width:374px; margin-right:41px"><x>&nbsp;</x></span>
<span class="r1" style="margin-left:5px"><x>&nbsp;</x></span> <span class="r1" style="margin-left:5px"><x>&nbsp;</x></span>
<span class="c3" style="margin-left:405px; float:left;margin-top:1px;"><x>&nbsp;</x></span> <span class="c3" style="margin-left:405px; float:left;margin-top:1px;"><x>&nbsp;</x></span>
</div></div> </div></div>
<div class="wrap" style="float:left;"><div class="grid" style="width:500px;"> <div class="wrap" style="float:left;"><div class="grid" style="width:500px;">
<span class="c1" style="min-width:20px;margin-right:448px"><x>&nbsp;</x></span> <span class="c1" style="width:20px;margin-right:448px"><x>&nbsp;</x></span>
<span class="r1" style="min-width:10px;margin-left:28px; margin-right:426px"><x>&nbsp;</x></span> <span class="r1" style="min-width:10px;margin-left:28px; margin-right:426px"><x>&nbsp;</x></span>
<span class="r1" style="min-width:30px;margin-left:28px; margin-right:426px"><x>&nbsp;</x></span> <span class="r1" style="min-width:30px;margin-left:28px; margin-right:426px"><x>&nbsp;</x></span>
<span class="r1" style="min-width:10px;margin-left:28px; margin-right:426px"><x>&nbsp;</x></span> <span class="r1" style="min-width:10px;margin-left:28px; margin-right:426px"><x>&nbsp;</x></span>
</div></div> </div></div>
<div class="wrap"><div class="grid" style="width:583px;"> <div class="wrap"><div class="grid" style="width:583px;">
<span class="c1" style="margin-right:55px"><x>&nbsp;</x></span> <span class="c1" style="width:507px; margin-right:55px"><x>&nbsp;</x></span>
<span class="r1"><x>&nbsp;</x></span> <span class="r1"><x>&nbsp;</x></span>
<span class="c3" style="margin-left:538px; float:left;margin-top:1px;"><x>&nbsp;</x></span> <span class="c3" style="margin-left:538px; float:left;margin-top:1px;"><x>&nbsp;</x></span>
</div></div> </div></div>
@ -78,13 +79,13 @@ x { display:inline-block; width:10px; height:18px; }
<div class="wrap"><div class="grid" style="width:389px;"> <div class="wrap"><div class="grid" style="width:389px;">
<span class="c1" style="width:100px"><x>&nbsp;</x></span> <span class="c1" style="width:100px"><x>&nbsp;</x></span>
<span class="r1" style="width:300px;margin-left:68px;"><x>&nbsp;</x></span> <span class="r1" style="width:300px;margin-left:68px;"><x>&nbsp;</x></span>
<span class="c3" style="margin-left:245px;float:left;margin-top:1px;"><x>&nbsp;</x></span> <span class="c3" style="margin-left:131px;float:left;margin-top:1px;"><x>&nbsp;</x></span>
</div></div> </div></div>
<div class="wrap"><div class="grid" style="width:389px;"> <div class="wrap"><div class="grid" style="width:389px;">
<span class="c1" style="width:100px"><x>&nbsp;</x></span> <span class="c1" style="width:100px"><x>&nbsp;</x></span>
<span class="r1" style="width:300px;margin-left:68px;"><x>&nbsp;</x></span> <span class="r1" style="width:300px;margin-left:68px;"><x>&nbsp;</x></span>
<span class="c3" style="margin-left:245px;float:left;margin-top:1px;"><x>&nbsp;</x></span> <span class="c3" style="margin-left:131px;float:left;margin-top:1px;"><x>&nbsp;</x></span>
</div></div> </div></div>

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

@ -127,10 +127,10 @@ body,html { color:black; background:white; font-family:monospace; font-size:16px
} }
.gF { .gF {
grid-template-columns: 22px grid-template-columns: 2px
1px 20px
1px 2px
auto; 0;
} }

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

@ -51,8 +51,8 @@ x { display:inline-block; height:10px; width:18px; }
<div class="grid flex" style="width:1px;height:1px;"><span class="c1" style="margin-top:1px"><x></x></span></div> <div class="grid flex" style="width:1px;height:1px;"><span class="c1" style="margin-top:1px"><x></x></span></div>
--> -->
<div class="grid mm" style="width:0;height:0;"><span class="c1" style="min-width:23px;min-height:10px"><x></x></span><span class="c2" style="position:relative;left:14px;width:18px;min-width:0;z-index:-1"><x></x></span></div> <div class="grid mm" style="width:0;height:0;"><span class="c1" style="min-width:18px;min-height:10px"><x></x></span><span class="c2" style="position:relative;left:14px;width:18px;min-width:0;z-index:-1"><x></x></span></div>
<div class="grid mm" style="width:1px;height:1px;"><span class="c1" style="min-width:23px;min-height:10px"><x></x></span><span class="c2" style="position:relative;left:14px;width:18px;min-width:0;z-index:-1"><x></x></span></div> <div class="grid mm" style="width:1px;height:1px;"><span class="c1" style="min-width:18px;min-height:10px"><x></x></span><span class="c2" style="position:relative;left:14px;width:18px;min-width:0;z-index:-1"><x></x></span></div>
<!-- TODO: fails due to broken align:stretch <!-- TODO: fails due to broken align:stretch
<div class="grid zero" style="width:0;height:0;"><span class="c1"><x></x></span></div> <div class="grid zero" style="width:0;height:0;"><span class="c1"><x></x></span></div>
<div class="grid zero" style="width:1px;height:1px;"><span class="c1"><x></x></span></div> <div class="grid zero" style="width:1px;height:1px;"><span class="c1"><x></x></span></div>

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

@ -27,34 +27,34 @@ body,html { color:black; background:white; font-family:monospace; font-size:16px
background: grey; background: grey;
} }
.g1 .d1 { .g1 .d1 {
width: 52px; width: 0px;
} }
.g2 .d1 { .g2 .d1 {
width: 56px; width: 0px;
} }
.g2f .d1 { .g2f .d1 {
width: 69px; width: 0px;
} }
.g3 .d1 { .g3 .d1 {
width: 56px; width: 0px;
} }
.g4 .d1 { .g4 .d1 {
width: 96px; width: 80px;
} }
.g4f .d1 { .g4f .d1 {
width: 69px; width: 0px;
} }
.g5 .d1 { .g5 .d1 {
width: 96px; width: 80px;
} }
.g6 .d1 { .g6 .d1 {
width: 69px; width: 0px;
} }
.g6f .d1 { .g6f .d1 {
width: 69px; width: 0px;
} }
.g7 .d1 { .g7 .d1 {
width: 69px; width: 0px;
} }
.g8 .t { .g8 .t {
width: 196px; width: 196px;
@ -63,19 +63,19 @@ body,html { color:black; background:white; font-family:monospace; font-size:16px
width: 200px; width: 200px;
} }
.g9 .d1 { .g9 .d1 {
width: 69px; width: 0px;
} }
.gA .d1 { .gA .d1 {
width: 93px; width: 80px;
} }
.gB .d1 { .gB .d1 {
width: 93px; width: 80px;
} }
.gC .d1 { .gC .d1 {
width: 93px; width: 80px;
} }
.gD .d1 { .gD .d1 {
width: 93px; width: 80px;
} }
.t { grid-column: span 1; border:2px solid; } .t { grid-column: span 1; border:2px solid; }

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

@ -27,34 +27,34 @@ body,html { color:black; background:white; font-family:monospace; font-size:16px
background: grey; background: grey;
} }
.g1 .d1 { .g1 .d1 {
width: 52px; width: 0px;
} }
.g2 .d1 { .g2 .d1 {
width: 56px; width: 0px;
} }
.g2f .d1 { .g2f .d1 {
width: 69px; width: 69px;
} }
.g3 .d1 { .g3 .d1 {
width: 56px; width: 0px;
} }
.g4 .d1 { .g4 .d1 {
width: 96px; width: 80px;
} }
.g4f .d1 { .g4f .d1 {
width: 104px; width: 104px;
} }
.g5 .d1 { .g5 .d1 {
width: 96px; width: 80px;
} }
.g6 .d1 { .g6 .d1 {
width: 69px; width: 0px;
} }
.g6f .d1 { .g6f .d1 {
width: 89px; width: 89px;
} }
.g7 .d1 { .g7 .d1 {
width: 69px; width: 0px;
} }
.g8 .t { .g8 .t {
width: 196px; width: 196px;
@ -63,19 +63,19 @@ body,html { color:black; background:white; font-family:monospace; font-size:16px
width: 200px; width: 200px;
} }
.g9 .d1 { .g9 .d1 {
width: 69px; width: 0px;
} }
.gA .d1 { .gA .d1 {
width: 93px; width: 80px;
} }
.gB .d1 { .gB .d1 {
width: 93px; width: 80px;
} }
.gC .d1 { .gC .d1 {
width: 93px; width: 80px;
} }
.gD .d1 { .gD .d1 {
width: 93px; width: 80px;
} }
.d2 { .d2 {
position: absolute; position: absolute;
@ -84,10 +84,10 @@ body,html { color:black; background:white; font-family:monospace; font-size:16px
background: blue; background: blue;
} }
.g1 .d2 { .g1 .d2 {
width: 448px; width: 500px;
} }
.g2 .d2 { .g2 .d2 {
width: 444px; width: 500px;
} }
.g2f .d2 { .g2f .d2 {
right: auto; right: auto;
@ -95,10 +95,10 @@ body,html { color:black; background:white; font-family:monospace; font-size:16px
width: 35px; width: 35px;
} }
.g3 .d2 { .g3 .d2 {
width: 444px; width: 500px;
} }
.g4 .d2 { .g4 .d2 {
width: 404px; width: 420px;
} }
.g4f .d2 { .g4f .d2 {
right: auto; right: auto;
@ -106,10 +106,10 @@ body,html { color:black; background:white; font-family:monospace; font-size:16px
width: 35px; width: 35px;
} }
.g5 .d2 { .g5 .d2 {
width: 404px; width: 420px;
} }
.g6 .d2 { .g6 .d2 {
width: 431px; width: 500px;
} }
.g6f .d2 { .g6f .d2 {
right: auto; right: auto;
@ -117,25 +117,25 @@ body,html { color:black; background:white; font-family:monospace; font-size:16px
width: 35px; width: 35px;
} }
.g7 .d2 { .g7 .d2 {
width: 431px; width: 500px;
} }
.g8 .d2 { .g8 .d2 {
width: 300px; width: 300px;
} }
.g9 .d2 { .g9 .d2 {
width: 431px; width: 500px;
} }
.gA .d2 { .gA .d2 {
width: 407px; width: 420px;
} }
.gB .d2 { .gB .d2 {
width: 407px; width: 420px;
} }
.gC .d2 { .gC .d2 {
width: 407px; width: 420px;
} }
.gD .d2 { .gD .d2 {
width: 407px; width: 420px;
} }
.t { grid-column: span 1; border:2px solid; } .t { grid-column: span 1; border:2px solid; }

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

@ -65,7 +65,14 @@ static const uint32_t kGoldenRatioU32 = 0x9E3779B9U;
namespace detail { namespace detail {
inline uint32_t MOZ_NO_SANITIZE_UNSIGNED_OVERFLOW
constexpr uint32_t
RotateLeft5(uint32_t aValue)
{
return (aValue << 5) | (aValue >> 27);
}
constexpr uint32_t
AddU32ToHash(uint32_t aHash, uint32_t aValue) AddU32ToHash(uint32_t aHash, uint32_t aValue)
{ {
/* /*
@ -89,7 +96,7 @@ AddU32ToHash(uint32_t aHash, uint32_t aValue)
* Otherwise, if |aHash| is 0 (as it often is for the beginning of a * Otherwise, if |aHash| is 0 (as it often is for the beginning of a
* message), the expression * message), the expression
* *
* mozilla::WrappingMultiply(kGoldenRatioU32, RotateBitsLeft(aHash, 5)) * mozilla::WrappingMultiply(kGoldenRatioU32, RotateLeft5(aHash))
* |xor| * |xor|
* aValue * aValue
* *
@ -110,14 +117,14 @@ AddU32ToHash(uint32_t aHash, uint32_t aValue)
* more than enough for our purposes.) * more than enough for our purposes.)
*/ */
return mozilla::WrappingMultiply(kGoldenRatioU32, return mozilla::WrappingMultiply(kGoldenRatioU32,
RotateLeft(aHash, 5) ^ aValue); RotateLeft5(aHash) ^ aValue);
} }
/** /**
* AddUintptrToHash takes sizeof(uintptr_t) as a template parameter. * AddUintptrToHash takes sizeof(uintptr_t) as a template parameter.
*/ */
template<size_t PtrSize> template<size_t PtrSize>
inline uint32_t constexpr uint32_t
AddUintptrToHash(uint32_t aHash, uintptr_t aValue) AddUintptrToHash(uint32_t aHash, uintptr_t aValue)
{ {
return AddU32ToHash(aHash, static_cast<uint32_t>(aValue)); return AddU32ToHash(aHash, static_cast<uint32_t>(aValue));
@ -173,7 +180,7 @@ AddToHash(uint32_t aHash, A* aA)
// implicitly converted to 32 bits and then passed to AddUintptrToHash() to be hashed. // implicitly converted to 32 bits and then passed to AddUintptrToHash() to be hashed.
template<typename T, template<typename T,
typename U = typename mozilla::EnableIf<mozilla::IsIntegral<T>::value>::Type> typename U = typename mozilla::EnableIf<mozilla::IsIntegral<T>::value>::Type>
MOZ_MUST_USE inline uint32_t MOZ_MUST_USE constexpr uint32_t
AddToHash(uint32_t aHash, T aA) AddToHash(uint32_t aHash, T aA)
{ {
return detail::AddUintptrToHash<sizeof(T)>(aHash, aA); return detail::AddUintptrToHash<sizeof(T)>(aHash, aA);
@ -213,6 +220,19 @@ HashUntilZero(const T* aStr)
return hash; return hash;
} }
// This is a `constexpr` alternative to HashUntilZero(const T*). It should
// only be used for compile-time computation because it uses recursion.
// XXX: once support for GCC 4.9 is dropped, this function should be removed
// and HashUntilZero(const T*) should be made `constexpr`.
template<typename T>
constexpr uint32_t
ConstExprHashUntilZero(const T* aStr, uint32_t aHash)
{
return !*aStr
? aHash
: ConstExprHashUntilZero(aStr + 1, AddToHash(aHash, *aStr));
}
template<typename T> template<typename T>
uint32_t uint32_t
HashKnownLength(const T* aStr, size_t aLength) HashKnownLength(const T* aStr, size_t aLength)
@ -257,6 +277,21 @@ HashString(const char16_t* aStr)
return detail::HashUntilZero(aStr); return detail::HashUntilZero(aStr);
} }
// This is a `constexpr` alternative to HashString(const char16_t*). It should
// only be used for compile-time computation because it uses recursion.
//
// You may need to use the
// MOZ_{PUSH,POP}_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING macros if you use
// this function. See the comment on those macros' definitions for more detail.
//
// XXX: once support for GCC 4.9 is dropped, this function should be removed
// and HashString(const char16_t*) should be made `constexpr`.
MOZ_MUST_USE constexpr uint32_t
ConstExprHashString(const char16_t* aStr)
{
return detail::ConstExprHashUntilZero(aStr, 0);
}
MOZ_MUST_USE inline uint32_t MOZ_MUST_USE inline uint32_t
HashString(const char16_t* aStr, size_t aLength) HashString(const char16_t* aStr, size_t aLength)
{ {

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

@ -94,29 +94,16 @@ struct WrapToSignedHelper
* happening. * happening.
*/ */
template<typename UnsignedType> template<typename UnsignedType>
inline constexpr typename detail::WrapToSignedHelper<UnsignedType>::SignedType constexpr typename detail::WrapToSignedHelper<UnsignedType>::SignedType
WrapToSigned(UnsignedType aValue) WrapToSigned(UnsignedType aValue)
{ {
return detail::WrapToSignedHelper<UnsignedType>::compute(aValue); return detail::WrapToSignedHelper<UnsignedType>::compute(aValue);
} }
// The |mozilla::Wrapping*| functions aren't constexpr because MSVC warns about
// well-defined unsigned integer overflows that may occur within the constexpr
// math. If/when MSVC fix this bug, we should make them all constexpr.
//
// https://msdn.microsoft.com/en-us/library/4kze989h.aspx (C4307)
// https://developercommunity.visualstudio.com/content/problem/211134/unsigned-integer-overflows-in-constexpr-functionsa.html (bug report)
//
// For now there's no practical, readable way to avoid such overflows in pure
// C++. And we can't add narrow #pragmas where overflow can occur to disable
// the warnings, because constexpr apparently causes the warning to be emitted
// at the outermost call *sites* (so every user of |mozilla::Wrapping*| would
// have to add them).
namespace detail { namespace detail {
template<typename T> template<typename T>
inline constexpr T constexpr T
ToResult(typename MakeUnsigned<T>::Type aUnsigned) ToResult(typename MakeUnsigned<T>::Type aUnsigned)
{ {
// We could *always* return WrapToSigned and rely on unsigned conversion to // We could *always* return WrapToSigned and rely on unsigned conversion to
@ -132,7 +119,7 @@ private:
public: public:
MOZ_NO_SANITIZE_UNSIGNED_OVERFLOW MOZ_NO_SANITIZE_UNSIGNED_OVERFLOW
static T compute(T aX, T aY) static constexpr T compute(T aX, T aY)
{ {
return ToResult<T>(static_cast<UnsignedT>(aX) + static_cast<UnsignedT>(aY)); return ToResult<T>(static_cast<UnsignedT>(aX) + static_cast<UnsignedT>(aY));
} }
@ -165,7 +152,7 @@ public:
* permissibly -- triggers different behavior. * permissibly -- triggers different behavior.
*/ */
template<typename T> template<typename T>
inline T constexpr T
WrappingAdd(T aX, T aY) WrappingAdd(T aX, T aY)
{ {
return detail::WrappingAddHelper<T>::compute(aX, aY); return detail::WrappingAddHelper<T>::compute(aX, aY);
@ -181,7 +168,7 @@ private:
public: public:
MOZ_NO_SANITIZE_UNSIGNED_OVERFLOW MOZ_NO_SANITIZE_UNSIGNED_OVERFLOW
static T compute(T aX, T aY) static constexpr T compute(T aX, T aY)
{ {
return ToResult<T>(static_cast<UnsignedT>(aX) - static_cast<UnsignedT>(aY)); return ToResult<T>(static_cast<UnsignedT>(aX) - static_cast<UnsignedT>(aY));
} }
@ -215,7 +202,7 @@ public:
* permissibly -- triggers different behavior. * permissibly -- triggers different behavior.
*/ */
template<typename T> template<typename T>
inline T constexpr T
WrappingSubtract(T aX, T aY) WrappingSubtract(T aX, T aY)
{ {
return detail::WrappingSubtractHelper<T>::compute(aX, aY); return detail::WrappingSubtractHelper<T>::compute(aX, aY);
@ -231,7 +218,7 @@ private:
public: public:
MOZ_NO_SANITIZE_UNSIGNED_OVERFLOW MOZ_NO_SANITIZE_UNSIGNED_OVERFLOW
static T compute(T aX, T aY) static constexpr T compute(T aX, T aY)
{ {
// Begin with |1U| to ensure the overall operation chain is never promoted // Begin with |1U| to ensure the overall operation chain is never promoted
// to signed integer operations that might have *signed* integer overflow. // to signed integer operations that might have *signed* integer overflow.
@ -277,12 +264,36 @@ public:
* or similar -- quite permissibly -- triggers different behavior. * or similar -- quite permissibly -- triggers different behavior.
*/ */
template<typename T> template<typename T>
inline T constexpr T
WrappingMultiply(T aX, T aY) WrappingMultiply(T aX, T aY)
{ {
return detail::WrappingMultiplyHelper<T>::compute(aX, aY); return detail::WrappingMultiplyHelper<T>::compute(aX, aY);
} }
// The |mozilla::Wrapping*| functions are constexpr. Unfortunately, MSVC warns
// about well-defined unsigned integer overflows that may occur within the
// constexpr math.
//
// https://msdn.microsoft.com/en-us/library/4kze989h.aspx (C4307)
// https://developercommunity.visualstudio.com/content/problem/211134/unsigned-integer-overflows-in-constexpr-functionsa.html (bug report)
//
// So we need a way to suppress these warnings. Unfortunately, the warnings are
// issued at the very top of the `constexpr` chain, which is often some
// distance from the triggering Wrapping*() operation. So we can't suppress
// them within this file. Instead, callers have to do it with these macros.
//
// If/when MSVC fix this bug, we should remove these macros.
#ifdef _MSC_VER
#define MOZ_PUSH_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING \
__pragma(warning(push)) \
__pragma(warning(disable:4307))
#define MOZ_POP_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING \
__pragma(warning(pop))
#else
#define MOZ_PUSH_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING
#define MOZ_POP_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING
#endif
} /* namespace mozilla */ } /* namespace mozilla */
#endif /* mozilla_WrappingOperations_h */ #endif /* mozilla_WrappingOperations_h */

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

@ -5,6 +5,14 @@
from __future__ import absolute_import from __future__ import absolute_import
import copy import copy
import platform
try:
import winreg
except ImportError:
try:
import _winreg as winreg
except ImportError:
pass
from marionette_harness import MarionetteTestCase from marionette_harness import MarionetteTestCase
@ -34,9 +42,36 @@ class TestCommandLineArguments(MarionetteTestCase):
return Services.appinfo.inSafeMode; return Services.appinfo.inSafeMode;
""") """)
self.assertTrue(safe_mode, "Safe Mode has not been enabled") self.assertTrue(safe_mode, "Safe Mode has not been enabled")
def test_safe_mode_blocked_by_policy(self):
if platform.system() != 'Windows':
return
reg_policies = winreg.OpenKeyEx(winreg.HKEY_CURRENT_USER, "SOFTWARE\\Policies", 0, winreg.KEY_WRITE)
reg_mozilla = winreg.CreateKeyEx(reg_policies, "Mozilla", 0, winreg.KEY_WRITE)
reg_firefox = winreg.CreateKeyEx(reg_mozilla, "Firefox", 0, winreg.KEY_WRITE)
winreg.SetValueEx(reg_firefox, "DisableSafeMode", 0, winreg.REG_DWORD, 1)
self.marionette.instance.app_args.append("-safe-mode")
self.marionette.quit()
self.marionette.start_session()
with self.marionette.using_context("chrome"):
safe_mode = self.marionette.execute_script("""
Cu.import("resource://gre/modules/Services.jsm");
return Services.appinfo.inSafeMode;
""")
self.assertFalse(safe_mode, "Safe Mode has been enabled")
winreg.CloseKey(reg_firefox)
winreg.DeleteKey(reg_mozilla, "Firefox")
winreg.CloseKey(reg_mozilla)
winreg.DeleteKey(reg_policies, "Mozilla")
winreg.CloseKey(reg_policies)
def test_startup_timeout(self): def test_startup_timeout(self):
startup_timeout = self.marionette.startup_timeout startup_timeout = self.marionette.startup_timeout

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

@ -319484,6 +319484,12 @@
{} {}
] ]
], ],
"eventsource/dedicated-worker/eventsource-close2.htm": [
[
"/eventsource/dedicated-worker/eventsource-close2.htm",
{}
]
],
"eventsource/dedicated-worker/eventsource-constructor-non-same-origin.htm": [ "eventsource/dedicated-worker/eventsource-constructor-non-same-origin.htm": [
[ [
"/eventsource/dedicated-worker/eventsource-constructor-non-same-origin.htm", "/eventsource/dedicated-worker/eventsource-constructor-non-same-origin.htm",
@ -390178,7 +390184,7 @@
"manual" "manual"
], ],
"FileAPI/FileReader/workers.html": [ "FileAPI/FileReader/workers.html": [
"7e9f00c9af5bb982a1b549ebc9f5a3d0b5cf4387", "d7894a0abb064411d4811d8cfb9c3ce65f99babd",
"testharness" "testharness"
], ],
"FileAPI/FileReaderSync.worker.js": [ "FileAPI/FileReaderSync.worker.js": [
@ -542317,6 +542323,10 @@
"700107771158b22fa280f30a5a52d1aac617ff6e", "700107771158b22fa280f30a5a52d1aac617ff6e",
"testharness" "testharness"
], ],
"eventsource/dedicated-worker/eventsource-close2.htm": [
"a3d13b0261b05eba56effb9ca3f6c31e312e777a",
"testharness"
],
"eventsource/dedicated-worker/eventsource-constructor-non-same-origin.htm": [ "eventsource/dedicated-worker/eventsource-constructor-non-same-origin.htm": [
"9614ac5ce1967bbbcae6a1cc8d64465579f6410d", "9614ac5ce1967bbbcae6a1cc8d64465579f6410d",
"testharness" "testharness"
@ -546826,7 +546836,7 @@
"testharness" "testharness"
], ],
"html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-non-integer-height.html": [ "html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-non-integer-height.html": [
"caaede75e5c16cc78023ce410f48e37e612cffbb", "6da68164fdba8986d4dd217ad48198f675e83165",
"testharness" "testharness"
], ],
"html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-non-integer-innerheight.html": [ "html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-non-integer-innerheight.html": [
@ -594606,7 +594616,7 @@
"testharness" "testharness"
], ],
"websockets/Create-on-worker-shutdown.html": [ "websockets/Create-on-worker-shutdown.html": [
"6c1e57e92b617fdb3d8dd86528af7073d21ece03", "e710493c0cd84630a1c853ada23c37908bece9cb",
"testharness" "testharness"
], ],
"websockets/Create-protocol-with-space.htm": [ "websockets/Create-protocol-with-space.htm": [

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

@ -0,0 +1,29 @@
<!--
self.close()
var source = new EventSource("../resources/message.py")
postMessage(source.readyState)
/*-->
<!DOCTYPE html>
<html>
<head>
<title>dedicated worker - EventSource created after: worker.close()</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<div id="log"></div>
<script>
var test = async_test();
test.step(function() {
var worker = new Worker('#')
worker.onmessage = function(e) {
test.step(function() {
assert_equals(e.data, EventSource.CONNECTING, 'this.readyState')
})
test.done()
}
})
</script>
</body>
</html>
<!--*/ //-->

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

@ -85,7 +85,11 @@ function refreshUI() {
createButton.onclick = createProfileWizard; createButton.onclick = createProfileWizard;
let restartSafeModeButton = document.getElementById("restart-in-safe-mode-button"); let restartSafeModeButton = document.getElementById("restart-in-safe-mode-button");
restartSafeModeButton.onclick = function() { restart(true); }; if (!Services.policies || Services.policies.isAllowed("safeMode")) {
restartSafeModeButton.onclick = function() { restart(true); };
} else {
restartSafeModeButton.setAttribute("disabled", "true");
}
let restartNormalModeButton = document.getElementById("restart-button"); let restartNormalModeButton = document.getElementById("restart-button");
restartNormalModeButton.onclick = function() { restart(false); }; restartNormalModeButton.onclick = function() { restart(false); };

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

@ -1206,6 +1206,10 @@ function populateActionBox() {
if (!Services.appinfo.inSafeMode && AppConstants.platform !== "android") { if (!Services.appinfo.inSafeMode && AppConstants.platform !== "android") {
$("safe-mode-box").style.display = "block"; $("safe-mode-box").style.display = "block";
$("action-box").style.display = "block"; $("action-box").style.display = "block";
if (Services.policies && !Services.policies.isAllowed("safeMode")) {
$("restart-in-safe-mode-button").setAttribute("disabled", "true");
}
} }
} }

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

@ -2778,17 +2778,12 @@ var gDetailView = {
desc.textContent = aAddon.description; desc.textContent = aAddon.description;
var fullDesc = document.getElementById("detail-fulldesc"); var fullDesc = document.getElementById("detail-fulldesc");
if (aAddon.fullDescription) { if (aAddon.getFullDescription) {
// The following is part of an awful hack to include the licenses for GMP fullDesc.textContent = "";
// plugins without having bug 624602 fixed yet, and intentionally ignores fullDesc.append(aAddon.getFullDescription(document));
// localisation. fullDesc.hidden = false;
if (aAddon.isGMPlugin) { } else if (aAddon.fullDescription) {
// eslint-disable-next-line no-unsanitized/property fullDesc.textContent = aAddon.fullDescription;
fullDesc.unsafeSetInnerHTML(aAddon.fullDescription);
} else {
fullDesc.textContent = aAddon.fullDescription;
}
fullDesc.hidden = false; fullDesc.hidden = false;
} else { } else {
fullDesc.hidden = true; fullDesc.hidden = true;

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

@ -29,6 +29,8 @@ const SEC_IN_A_DAY = 24 * 60 * 60;
// How long to wait after a user enabled EME before attempting to download CDMs. // How long to wait after a user enabled EME before attempting to download CDMs.
const GMP_CHECK_DELAY = 10 * 1000; // milliseconds const GMP_CHECK_DELAY = 10 * 1000; // milliseconds
const XHTML = "http://www.w3.org/1999/xhtml";
const NS_GRE_DIR = "GreD"; const NS_GRE_DIR = "GreD";
const CLEARKEY_PLUGIN_ID = "gmp-clearkey"; const CLEARKEY_PLUGIN_ID = "gmp-clearkey";
const CLEARKEY_VERSION = "0.1"; const CLEARKEY_VERSION = "0.1";
@ -95,8 +97,9 @@ function configureLogging() {
* The GMPWrapper provides the info for the various GMP plugins to public * The GMPWrapper provides the info for the various GMP plugins to public
* callers through the API. * callers through the API.
*/ */
function GMPWrapper(aPluginInfo) { function GMPWrapper(aPluginInfo, aRawPluginInfo) {
this._plugin = aPluginInfo; this._plugin = aPluginInfo;
this._rawPlugin = aRawPluginInfo;
this._log = this._log =
Log.repository.getLoggerWithMessagePrefix("Toolkit.GMP", Log.repository.getLoggerWithMessagePrefix("Toolkit.GMP",
"GMPWrapper(" + "GMPWrapper(" +
@ -140,7 +143,31 @@ GMPWrapper.prototype = {
get homepageURL() { return this._plugin.homepageURL; }, get homepageURL() { return this._plugin.homepageURL; },
get description() { return this._plugin.description; }, get description() { return this._plugin.description; },
get fullDescription() { return this._plugin.fullDescription; }, get fullDescription() { return null; },
getFullDescription(doc) {
let plugin = this._rawPlugin;
let frag = doc.createDocumentFragment();
for (let [urlProp, labelId] of [["learnMoreURL", GMP_LEARN_MORE],
["licenseURL", this.id == WIDEVINE_ID ?
GMP_PRIVACY_INFO : GMP_LICENSE_INFO]]) {
if (plugin[urlProp]) {
let a = doc.createElementNS(XHTML, "a");
a.href = plugin[urlProp];
a.target = "_blank";
a.textContent = pluginsBundle.GetStringFromName(labelId);
if (frag.childElementCount) {
frag.append(doc.createElementNS(XHTML, "br"),
doc.createElementNS(XHTML, "br"));
}
frag.append(a);
}
}
return frag;
},
get version() { get version() {
return GMPPrefs.getString(GMPPrefs.KEY_PLUGIN_VERSION, null, this._plugin.id); return GMPPrefs.getString(GMPPrefs.KEY_PLUGIN_VERSION, null, this._plugin.id);
@ -633,19 +660,6 @@ var GMPProvider = {
return GMPPrefs.getBool(GMPPrefs.KEY_PROVIDER_ENABLED, false); return GMPPrefs.getBool(GMPPrefs.KEY_PROVIDER_ENABLED, false);
}, },
generateFullDescription(aPlugin) {
let rv = [];
for (let [urlProp, labelId] of [["learnMoreURL", GMP_LEARN_MORE],
["licenseURL", aPlugin.id == WIDEVINE_ID ?
GMP_PRIVACY_INFO : GMP_LICENSE_INFO]]) {
if (aPlugin[urlProp]) {
let label = pluginsBundle.GetStringFromName(labelId);
rv.push(`<xhtml:a href="${aPlugin[urlProp]}" target="_blank">${label}</xhtml:a>.`);
}
}
return rv.length ? rv.join("<xhtml:br /><xhtml:br />") : undefined;
},
buildPluginList() { buildPluginList() {
this._plugins = new Map(); this._plugins = new Map();
for (let aPlugin of GMP_PLUGINS) { for (let aPlugin of GMP_PLUGINS) {
@ -658,8 +672,7 @@ var GMPProvider = {
wrapper: null, wrapper: null,
isEME: aPlugin.isEME, isEME: aPlugin.isEME,
}; };
plugin.fullDescription = this.generateFullDescription(aPlugin); plugin.wrapper = new GMPWrapper(plugin, aPlugin);
plugin.wrapper = new GMPWrapper(plugin);
this._plugins.set(plugin.id, plugin); this._plugins.set(plugin.id, plugin);
} }
}, },

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

@ -511,7 +511,10 @@ add_task(async function test_permissions_prompt() {
return Promise.resolve(); return Promise.resolve();
}; };
await promiseCompleteInstall(install); await Promise.all([
promiseCompleteInstall(install),
promiseWebExtensionStartup(),
]);
notEqual(perminfo, undefined, "Permission handler was invoked"); notEqual(perminfo, undefined, "Permission handler was invoked");
equal(perminfo.existingAddon, null, "Permission info does not include an existing addon"); equal(perminfo.existingAddon, null, "Permission info does not include an existing addon");

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

@ -3154,6 +3154,40 @@ public:
#endif #endif
}; };
#ifdef XP_WIN
namespace {
bool PolicyHasRegValue(HKEY aKey, LPCTSTR aName, DWORD* aValue)
{
HKEY hkey = NULL;
LONG ret = RegOpenKeyExW(aKey,
L"SOFTWARE\\Policies\\Mozilla\\Firefox", 0, KEY_READ, &hkey);
if (ret != ERROR_SUCCESS) {
return false;
}
nsAutoRegKey key(hkey);
DWORD len = sizeof(aValue);
ret = RegQueryValueExW(hkey, aName, 0, NULL, (LPBYTE)aValue, &len);
RegCloseKey(key);
return ret == ERROR_SUCCESS;
}
bool SafeModeBlockedByPolicy()
{
LPCTSTR policyName = L"DisableSafeMode";
DWORD value;
if (PolicyHasRegValue(HKEY_LOCAL_MACHINE, policyName, &value)) {
return value == 1;
}
if (PolicyHasRegValue(HKEY_CURRENT_USER, policyName, &value)) {
return value == 1;
}
return false;
}
} // anonymous namespace
#endif // XP_WIN
/* /*
* XRE_mainInit - Initial setup and command line parameter processing. * XRE_mainInit - Initial setup and command line parameter processing.
* Main() will exit early if either return value != 0 or if aExitFlag is * Main() will exit early if either return value != 0 or if aExitFlag is
@ -3490,12 +3524,6 @@ XREMain::XRE_mainInit(bool* aExitFlag)
gRestartArgv[gRestartArgc] = nullptr; gRestartArgv[gRestartArgc] = nullptr;
if (EnvHasValue("MOZ_SAFE_MODE_RESTART")) {
gSafeMode = true;
// unset the env variable
SaveToEnv("MOZ_SAFE_MODE_RESTART=");
}
ar = CheckArg("safe-mode", true); ar = CheckArg("safe-mode", true);
if (ar == ARG_BAD) { if (ar == ARG_BAD) {
PR_fprintf(PR_STDERR, "Error: argument --safe-mode is invalid when argument --osint is specified\n"); PR_fprintf(PR_STDERR, "Error: argument --safe-mode is invalid when argument --osint is specified\n");
@ -3525,6 +3553,20 @@ XREMain::XRE_mainInit(bool* aExitFlag)
gSafeMode = true; gSafeMode = true;
#endif #endif
#ifdef XP_WIN
if (gSafeMode && SafeModeBlockedByPolicy()) {
gSafeMode = false;
}
#endif
// The Safe Mode Policy should not be enforced for the env var case
// (used by updater and crash-recovery).
if (EnvHasValue("MOZ_SAFE_MODE_RESTART")) {
gSafeMode = true;
// unset the env variable
SaveToEnv("MOZ_SAFE_MODE_RESTART=");
}
#ifdef XP_WIN #ifdef XP_WIN
{ {
// Add CPU microcode version to the crash report as "CPUMicrocodeVersion". // Add CPU microcode version to the crash report as "CPUMicrocodeVersion".