Merge autoland to mozilla-central. a=merge

This commit is contained in:
Csoregi Natalia 2022-04-24 00:26:55 +03:00
Родитель dccf61761f 265e005ae0
Коммит 4365e6ab34
120 изменённых файлов: 3110 добавлений и 899 удалений

7
Cargo.lock сгенерированный
Просмотреть файл

@ -5392,6 +5392,12 @@ dependencies = [
"serde",
]
[[package]]
name = "topological-sort"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa7c7f42dea4b1b99439786f5633aeb9c14c1b53f75e282803c2ec2ad545873c"
[[package]]
name = "tower-service"
version = "0.3.1"
@ -5813,6 +5819,7 @@ dependencies = [
"svg_fmt",
"swgl",
"time",
"topological-sort",
"tracy-rs",
"webrender_api",
"webrender_build",

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

@ -304,6 +304,10 @@ nsAccessibilityService::ListenersChanged(nsIArray* aEventChanges) {
if (document) {
LocalAccessible* acc = document->GetAccessible(content);
if (!acc && (content == document->GetContent() ||
content == document->DocumentNode()->GetRootElement())) {
acc = document;
}
if (!acc && nsCoreUtils::HasClickListener(content)) {
// Create an accessible for a inaccessible element having click event
// handler.

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

@ -312,7 +312,9 @@ void Accessible::TranslateString(const nsString& aKey, nsAString& aStringOut) {
}
const Accessible* Accessible::ActionAncestor() const {
for (Accessible* parent = Parent(); parent && !parent->IsDoc();
// We do want to consider a click handler on the document. However, we don't
// want to walk outside of this document, so we stop if we see an OuterDoc.
for (Accessible* parent = Parent(); parent && !parent->IsOuterDoc();
parent = parent->Parent()) {
if (parent->HasPrimaryAction()) {
return parent;

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

@ -89,12 +89,6 @@ void LinkableAccessible::Value(nsString& aValue) const {
}
}
uint8_t LinkableAccessible::ActionCount() const {
bool isLink, isOnclick;
ActionWalk(&isLink, &isOnclick);
return (isLink || isOnclick) ? 1 : 0;
}
const LocalAccessible* LinkableAccessible::ActionWalk(bool* aIsLink,
bool* aIsOnclick) const {
if (aIsOnclick) {
@ -132,33 +126,6 @@ const LocalAccessible* LinkableAccessible::ActionWalk(bool* aIsLink,
return localAction;
}
void LinkableAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
aName.Truncate();
// Action 0 (default action): Jump to link
if (aIndex == eAction_Jump) {
bool isOnclick, isLink;
ActionWalk(&isLink, &isOnclick);
if (isLink) {
aName.AssignLiteral("jump");
} else if (isOnclick) {
aName.AssignLiteral("click");
}
}
}
bool LinkableAccessible::DoAction(uint8_t aIndex) const {
if (aIndex != eAction_Jump) {
return false;
}
if (const LocalAccessible* actionAcc = ActionWalk()) {
return actionAcc->DoAction(aIndex);
}
return AccessibleWrap::DoAction(aIndex);
}
KeyBinding LinkableAccessible::AccessKey() const {
if (const LocalAccessible* actionAcc =
const_cast<LinkableAccessible*>(this)->ActionWalk()) {

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

@ -45,13 +45,11 @@ class LeafAccessible : public AccessibleWrap {
/**
* Used for text or image accessible nodes contained by link accessibles or
* accessibles for nodes with registered click event handler. It knows how to
* report the state of the host link (traveled or not) and can activate (click)
* the host accessible programmatically.
* report the state of the host link (traveled or not) and can focus the host
* accessible programmatically.
*/
class LinkableAccessible : public AccessibleWrap {
public:
enum { eAction_Jump = 0 };
LinkableAccessible(nsIContent* aContent, DocAccessible* aDoc)
: AccessibleWrap(aContent, aDoc) {}
@ -63,9 +61,6 @@ class LinkableAccessible : public AccessibleWrap {
virtual void TakeFocus() const override;
// ActionAccessible
virtual uint8_t ActionCount() const override;
virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override;
virtual bool DoAction(uint8_t index) const override;
virtual KeyBinding AccessKey() const override;
// ActionAccessible helpers

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

@ -2616,3 +2616,26 @@ LocalAccessible* DocAccessible::GetAccessible(nsINode* aNode) const {
return aNode == mDocumentNode ? const_cast<DocAccessible*>(this)
: mNodeToAccessibleMap.Get(aNode);
}
bool DocAccessible::HasPrimaryAction() const {
if (HyperTextAccessible::HasPrimaryAction()) {
return true;
}
// mContent is normally the body, but there might be a click listener on the
// root.
dom::Element* root = mDocumentNode->GetRootElement();
if (mContent != root) {
return nsCoreUtils::HasClickListener(root);
}
return false;
}
void DocAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
aName.Truncate();
if (aIndex != 0) {
return;
}
if (HasPrimaryAction()) {
aName.AssignLiteral("click");
}
}

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

@ -84,6 +84,10 @@ class DocAccessible : public HyperTextAccessibleWrap,
virtual nsRect RelativeBounds(nsIFrame** aRelativeFrame) const override;
// ActionAccessible
virtual bool HasPrimaryAction() const override;
virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override;
// HyperTextAccessible
virtual already_AddRefed<EditorBase> GetEditor() const override;

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

@ -1821,7 +1821,7 @@ role LocalAccessible::ARIATransformRole(role aRole) const {
role LocalAccessible::NativeRole() const { return roles::NOTHING; }
uint8_t LocalAccessible::ActionCount() const {
return HasPrimaryAction() ? 1 : 0;
return HasPrimaryAction() || ActionAncestor() ? 1 : 0;
}
void LocalAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
@ -1888,12 +1888,17 @@ void LocalAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
}
return;
}
if (ActionAncestor()) {
aName.AssignLiteral("click ancestor");
return;
}
}
bool LocalAccessible::DoAction(uint8_t aIndex) const {
if (aIndex != 0) return false;
if (HasPrimaryAction()) {
if (HasPrimaryAction() || ActionAncestor()) {
DoCommand();
return true;
}

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

@ -695,8 +695,7 @@ template <class Derived>
uint8_t RemoteAccessibleBase<Derived>::ActionCount() const {
uint8_t actionCount = 0;
if (mCachedFields) {
if (HasPrimaryAction() ||
((IsTextLeaf() || IsImage()) && ActionAncestor())) {
if (HasPrimaryAction() || ActionAncestor()) {
actionCount++;
}
@ -715,25 +714,21 @@ void RemoteAccessibleBase<Derived>::ActionNameAt(uint8_t aIndex,
if (mCachedFields) {
aName.Truncate();
nsAtom* action = GetPrimaryAction();
if (!action && (IsTextLeaf() || IsImage())) {
const Accessible* actionAcc = ActionAncestor();
Derived* acc =
actionAcc ? const_cast<Accessible*>(actionAcc)->AsRemote() : nullptr;
if (acc) {
action = acc->GetPrimaryAction();
}
}
bool hasActionAncestor = !action && ActionAncestor();
switch (aIndex) {
case 0:
if (action) {
action->ToString(aName);
} else if (hasActionAncestor) {
aName.AssignLiteral("click ancestor");
} else if (mCachedFields->HasAttribute(nsGkAtoms::longdesc)) {
aName.AssignLiteral("showlongdesc");
}
break;
case 1:
if (action && mCachedFields->HasAttribute(nsGkAtoms::longdesc)) {
if ((action || hasActionAncestor) &&
mCachedFields->HasAttribute(nsGkAtoms::longdesc)) {
aName.AssignLiteral("showlongdesc");
}
break;

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

@ -20,6 +20,7 @@ const gActionDescrMap = {
expand: "Expand",
activate: "Activate",
cycle: "Cycle",
"click ancestor": "Click ancestor",
};
async function testActions(browser, docAcc, id, expectedActions, domEvents) {
@ -86,14 +87,23 @@ addAccessibleTask(
<a id="link1" href="#">linkable textleaf accessible</a>
<div id="link2" onclick="">linkable textleaf accessible</div>
<a id="link3" href="#">
<img id="link3img" alt="image in link"
src="http://example.com/a11y/accessible/tests/mochitest/moz.png">
</a>
<div>
<label for="TextBox_t2" id="label1">
<span>Explicit</span>
</label>
<input name="in2" id="TextBox_t2" type="text" maxlength="17">
</div>
<div onclick=""><p id="p_in_clickable_div">p in clickable div</p></div>
`,
async function(browser, docAcc) {
is(docAcc.actionCount, 0, "Doc should not have any actions");
const _testActions = async (id, expectedActions, domEvents) => {
await testActions(browser, docAcc, id, expectedActions, domEvents);
};
@ -105,7 +115,10 @@ addAccessibleTask(
await _testActions("onclick_img", ["click"], gClickEvents);
await _testActions("link1", ["jump"], gClickEvents);
await _testActions("link2", ["click"], gClickEvents);
await _testActions("link3", ["jump"], gClickEvents);
await _testActions("link3img", ["click ancestor"], gClickEvents);
await _testActions("label1", ["click"], gClickEvents);
await _testActions("p_in_clickable_div", ["click ancestor"], gClickEvents);
await invokeContentTask(browser, [], () => {
content.document
@ -157,18 +170,39 @@ addAccessibleTask(
await _testActions("onclick_img", ["showlongdesc"]);
// Remove 'href' from link and test linkable child
acc = findAccessibleChildByID(docAcc, "link1");
const link1Acc = findAccessibleChildByID(docAcc, "link1");
is(
acc.firstChild.getActionName(0),
"jump",
"linkable child has jump action"
link1Acc.firstChild.getActionName(0),
"click ancestor",
"linkable child has click ancestor action"
);
await invokeContentTask(browser, [], () => {
let link1 = content.document.getElementById("link1");
link1.removeAttribute("href");
});
await untilCacheIs(() => acc.actionCount, 0, "link has no actions");
is(acc.firstChild.actionCount, 0, "linkable child's actions removed");
await untilCacheIs(() => link1Acc.actionCount, 0, "link has no actions");
is(link1Acc.firstChild.actionCount, 0, "linkable child's actions removed");
// Add a click handler to the body. Ensure it propagates to descendants.
await invokeContentTask(browser, [], () => {
content.document.body.onclick = () => {};
});
await untilCacheIs(() => docAcc.actionCount, 1, "Doc has 1 action");
await _testActions("link1", ["click ancestor"]);
await invokeContentTask(browser, [], () => {
content.document.body.onclick = null;
});
await untilCacheIs(() => docAcc.actionCount, 0, "Doc has no actions");
is(link1Acc.actionCount, 0, "link has no actions");
// Add a click handler to the root element. Ensure it propagates to
// descendants.
await invokeContentTask(browser, [], () => {
content.document.documentElement.onclick = () => {};
});
await untilCacheIs(() => docAcc.actionCount, 1, "Doc has 1 action");
await _testActions("link1", ["click ancestor"]);
},
{
chrome: true,

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

@ -227,4 +227,5 @@ var gActionDescrMap = {
expand: "Expand",
activate: "Activate",
cycle: "Cycle",
"click ancestor": "Click ancestor",
};

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

@ -65,8 +65,9 @@
{
ID: "img1",
targetID: "link1",
actionName: "jump",
actionName: "click ancestor",
events: CLICK_EVENTS,
allowBubbling: true,
eventSeq: [
new linkChecker("link1"),
],
@ -79,8 +80,9 @@
{
ID: "img2",
targetID: "link2",
actionName: "jump",
actionName: "click ancestor",
events: CLICK_EVENTS,
allowBubbling: true,
},
{
ID: "link3",
@ -90,8 +92,9 @@
{
ID: "img3",
targetID: "link3",
actionName: "jump",
actionName: "click ancestor",
events: CLICK_EVENTS,
allowBubbling: true,
},
{
ID: "link4",
@ -101,8 +104,9 @@
{
ID: "img4",
targetID: "link4",
actionName: "jump",
actionName: "click ancestor",
events: CLICK_EVENTS,
allowBubbling: true,
},
];
testActions(actionsArray);

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

@ -32,11 +32,11 @@
states: STATE_LINKED,
actions: "jump",
interfaces: [ nsIAccessibleText, nsIAccessibleHyperText, nsIAccessibleHyperLink ],
children: [ // all kids inherits linked state and jump action
children: [ // all kids inherits linked state and action
{
role: ROLE_TEXT_LEAF,
states: STATE_LINKED,
actions: "jump",
actions: "click ancestor",
},
],
};

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

@ -109,7 +109,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=429659
testThis("nonLinkedImage", "moz.png", 89, 38);
// Test linked image
var actionNamesArray = ["jump"];
var actionNamesArray = ["click ancestor"];
testThis("linkedImage", "moz.png", 89, 38, 1,
actionNamesArray);

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

@ -70,10 +70,13 @@ add_task(async function test_downloads_panel_new_download() {
DownloadsCommon.openDownload = async () => {
ok(false, "openDownload was called when it was not expected");
};
let newTabPromise = BrowserTestUtils.openNewForegroundTab(
let newTabPromise = BrowserTestUtils.openNewForegroundTab({
gBrowser,
TEST_PATH + "foo.txt"
);
opening: TEST_PATH + "foo.txt",
waitForLoad: false,
waitForStateStop: true,
});
await promisePanelOpened();
let downloadsListBox = document.getElementById("downloadsListBox");

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

@ -355,7 +355,7 @@ __webpack_require__.r(__webpack_exports__);
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const CONFIGURABLE_STYLES = ["color", "fontSize"];
const CONFIGURABLE_STYLES = ["color", "fontSize", "fontWeight", "letterSpacing", "lineHeight", "marginBlock", "marginInline", "paddingBlock", "paddingInline"];
const ZAP_SIZE_THRESHOLD = 160;
/**
* Based on the .text prop, localizes an inner element if a string_id
@ -428,7 +428,7 @@ const Localized = ({
CONFIGURABLE_STYLES.forEach(style => {
if (text[style]) props.style[style] = text[style];
if (text[style] !== undefined) props.style[style] = text[style];
});
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().cloneElement( // Provide a default container for the text if necessary.
children ?? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null), props, // Conditionally pass in as void elements can't accept empty array.

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

@ -218,10 +218,10 @@ body[lwt-newtab-brighttext] {
.onboardingContainer .welcome-text h1,
.onboardingContainer .welcome-text h2 {
color: var(--in-content-page-color);
line-height: 1.5;
}
.onboardingContainer .welcome-text h1 {
font-size: 24px;
line-height: 1.5;
font-weight: 600;
margin: 0 6px;
letter-spacing: -0.02em;
@ -231,25 +231,17 @@ body[lwt-newtab-brighttext] {
font-size: 16px;
font-weight: normal;
margin: 10px 6px 0;
line-height: 24px;
max-width: 750px;
letter-spacing: -0.01em;
}
.onboardingContainer .welcome-text.larger h1 {
font-size: 36px;
}
.onboardingContainer .welcome-text.slim h1 {
font-weight: 276;
}
.onboardingContainer .welcome-text.fancy h1 {
background-image: linear-gradient(90deg, #9059FF, #FF4AA2, #FF8C00, #FF4AA2, #9059FF);
background-size: 400% auto;
background-clip: text;
animation: shine 50s linear infinite;
background-size: 200%;
}
@media (prefers-contrast: no-preference) {
.onboardingContainer .welcome-text.fancy h1 {
-webkit-text-fill-color: transparent;
color: transparent;
}
}
@media (prefers-color-scheme: dark) {
@ -257,9 +249,13 @@ body[lwt-newtab-brighttext] {
background-image: linear-gradient(90deg, #C688FF, #FF84C0, #FFBD4F, #FF84C0, #C688FF);
}
}
.onboardingContainer .welcome-text.shine h1 {
animation: shine 50s linear infinite;
background-size: 400%;
}
@keyframes shine {
to {
background-position: 400% center;
background-position: 400%;
}
}
.onboardingContainer .screen.light-text .welcome-text.fancy h1 {

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

@ -238,11 +238,11 @@ body {
h1,
h2 {
color: var(--in-content-page-color);
line-height: 1.5;
}
h1 {
font-size: 24px;
line-height: 1.5;
font-weight: 600;
margin: 0 6px;
letter-spacing: -0.02em;
@ -253,40 +253,33 @@ body {
font-size: 16px;
font-weight: normal;
margin: 10px 6px 0;
line-height: 24px;
max-width: 750px;
letter-spacing: -0.01em;
}
&.larger {
h1 {
font-size: 36px;
}
}
&.slim {
h1 {
font-weight: 276;
}
}
&.fancy {
h1 {
background-image: linear-gradient(90deg, #9059FF, #FF4AA2, #FF8C00, #FF4AA2, #9059FF);
background-size: 400% auto;
background-clip: text;
animation: shine 50s linear infinite;
background-size: 200%;
@media (prefers-contrast: no-preference) {
-webkit-text-fill-color: transparent;
color: transparent;
}
@media (prefers-color-scheme: dark) {
background-image: linear-gradient(90deg, #C688FF, #FF84C0, #FFBD4F, #FF84C0, #C688FF);
}
}
}
&.shine {
h1 {
animation: shine 50s linear infinite;
background-size: 400%;
}
@keyframes shine {
to {
background-position: 400% center;
background-position: 400%;
}
}
}

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

@ -3,7 +3,17 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
import React, { useEffect } from "react";
const CONFIGURABLE_STYLES = ["color", "fontSize"];
const CONFIGURABLE_STYLES = [
"color",
"fontSize",
"fontWeight",
"letterSpacing",
"lineHeight",
"marginBlock",
"marginInline",
"paddingBlock",
"paddingInline",
];
const ZAP_SIZE_THRESHOLD = 160;
/**
@ -77,7 +87,7 @@ export const Localized = ({ text, children }) => {
// Apply certain configurable styles.
CONFIGURABLE_STYLES.forEach(style => {
if (text[style]) props.style[style] = text[style];
if (text[style] !== undefined) props.style[style] = text[style];
});
return React.cloneElement(

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

@ -47,7 +47,7 @@ const PREFS_BEFORE_SECTIONS = () => [
: [];
},
},
icon: "topsites",
icon: "chrome://browser/skin/topsites.svg",
maxRows: 4,
rowsPref: "topSitesRows",
eventSource: "TOP_SITES",

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

@ -64,9 +64,10 @@ const ONBOARDING_MESSAGES = () => [
},
has_noodles: true,
title: {
fontSize: "36px",
string_id: "fx100-upgrade-thanks-header",
},
title_style: "fancy larger",
title_style: "fancy shine",
background:
"url('chrome://activity-stream/content/data/content/assets/confetti.svg') top / 100% no-repeat var(--in-content-page-background)",
subtitle: {

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

@ -466,9 +466,10 @@ const MESSAGES = () => [
height: "100px",
},
title: {
fontSize: "36px",
fontWeight: 276,
string_id: "mr1-onboarding-default-header",
},
title_style: "slim larger",
subtitle: {
string_id: "mr1-onboarding-default-subtitle",
},
@ -527,8 +528,12 @@ const MESSAGES = () => [
height: "200px",
imageURL: "",
},
title: "Peace of mind.",
title_style: "fancy slim larger",
title: {
fontSize: "36px",
fontWeight: 276,
raw: "Peace of mind.",
},
title_style: "fancy shine",
text_color: "dark",
subtitle:
"For the best privacy protection, keep Firefox in easy reach.",

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

@ -151,12 +151,17 @@ add_task(async function test_aboutwelcome_with_color_backdrop() {
});
/**
* Test rendering a screen with a title that's fancy, slim, and larger
* Test rendering a screen with a title with custom styles.
*/
add_task(async function test_aboutwelcome_with_title_styles() {
const TEST_TITLE_STYLE = "fancy slim larger";
const TEST_TITLE_STYLE_CONTENT = makeTestContent("TEST_TITLE_STYLE_STEP", {
title_style: TEST_TITLE_STYLE,
title: {
fontSize: "36px",
fontWeight: 276,
letterSpacing: 0,
raw: "test",
},
title_style: "fancy shine",
});
const TEST_TITLE_STYLE_JSON = JSON.stringify([TEST_TITLE_STYLE_CONTENT]);
@ -166,7 +171,7 @@ add_task(async function test_aboutwelcome_with_title_styles() {
browser,
"renders screen with customized title style",
// Expected selectors:
[`div.welcome-text.fancy.slim.larger`]
[`div.welcome-text.fancy.shine`]
);
await test_element_styles(
@ -177,6 +182,7 @@ add_task(async function test_aboutwelcome_with_title_styles() {
"font-weight": "276",
"font-size": "36px",
animation: "50s linear 0s infinite normal none running shine",
"letter-spacing": "normal",
}
);
});

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

@ -2073,6 +2073,9 @@ body.theme_dark .stp_tag_picker .stp_tag_picker_tag {
body.theme_dark .stp_tag_picker .stp_tag_picker_tag_remove {
color: #8F8F9D;
}
body.theme_dark .stp_tag_picker .stp_tag_picker_tag_remove:hover {
color: #fff;
}
body.theme_dark .stp_tag_picker .stp_tag_picker_tag_remove:focus {
outline: 2px solid #00DDFF;
}
@ -2096,20 +2099,18 @@ body.theme_dark .stp_tag_picker .stp_tag_picker_tag_duplicate {
border-end-start-radius: 4px;
}
.stp_tag_picker .stp_tag_picker_input:focus {
outline: 2px solid #0060df;
border: 1px solid #0060DF;
outline: 1px solid #0060DF;
}
body.theme_dark .stp_tag_picker .stp_tag_picker_input {
background: none;
color: #FBFBFB;
}
body.theme_dark .stp_tag_picker .stp_tag_picker_input:focus {
outline: 2px solid #00DDFF;
border: 1px solid #00DDFF;
outline: 1px solid #00DDFF;
}
.stp_tag_picker .stp_tag_picker_input:focus {
border: 1px solid #0060DF;
outline: 1px solid #0060DF;
}
.stp_tag_picker .stp_tag_picker_button {
font-size: 0.95rem;
line-height: 1.1rem;
@ -2130,6 +2131,20 @@ body.theme_dark .stp_tag_picker .stp_tag_picker_input:focus {
border: 1px solid #0060DF;
outline: 1px solid #0060DF;
}
body.theme_dark .stp_tag_picker .stp_tag_picker_button {
background-color: #2B2A33;
color: #FBFBFB;
}
body.theme_dark .stp_tag_picker .stp_tag_picker_button:disabled {
color: #666;
}
body.theme_dark .stp_tag_picker .stp_tag_picker_button:hover:enabled {
background-color: #53535d;
}
body.theme_dark .stp_tag_picker .stp_tag_picker_button:focus:enabled {
border: 1px solid #00DDFF;
outline: 1px solid #00DDFF;
}
.stp_popular_topics {
padding: 0;
@ -2173,6 +2188,9 @@ body.theme_dark .stp_popular_topics .stp_popular_topic .stp_popular_topic_link {
body.theme_dark .stp_popular_topics .stp_popular_topic .stp_popular_topic_link:focus {
outline: 2px solid #00DDFF;
}
body.theme_dark .stp_popular_topics .stp_popular_topic .stp_popular_topic_link:hover {
background: #53535d;
}
.stp_article_list {
padding: 0;
@ -2311,6 +2329,9 @@ body.theme_dark .stp_button.stp_button_primary {
background: #00DDFF;
color: #15141A;
}
body.theme_dark .stp_button.stp_button_primary:hover {
background: #80ebfe;
}
body.theme_dark .stp_button.stp_button_primary:focus {
outline: 2px solid #00DDFF;
}
@ -2345,6 +2366,9 @@ body.theme_dark .stp_button.stp_button_secondary {
body.theme_dark .stp_button.stp_button_secondary:focus {
outline: 2px solid #00DDFF;
}
body.theme_dark .stp_button.stp_button_secondary:hover {
background: #53535d;
}
.stp_button_wide .stp_button {
display: block;

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

@ -63,6 +63,10 @@
background: #00DDFF;
color: #15141A;
&:hover {
background: #80ebfe;
}
&:focus {
outline: 2px solid #00DDFF;
}
@ -102,6 +106,10 @@
&:focus {
outline: 2px solid #00DDFF;
}
&:hover {
background: #53535d;
}
}
}
}

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

@ -44,6 +44,10 @@
&:focus {
outline: 2px solid #00DDFF;
}
&:hover {
background: #53535d;
}
}
}
}

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

@ -51,6 +51,10 @@
@include theme_dark {
color: #8F8F9D;
&:hover {
color: #fff;
}
&:focus {
outline: 2px solid #00DDFF;
}
@ -78,7 +82,8 @@
border-end-start-radius: 4px;
&:focus {
outline: 2px solid #0060df;
border: 1px solid #0060DF;
outline: 1px solid #0060DF;
}
@include theme_dark {
@ -86,13 +91,10 @@
color: #FBFBFB;
&:focus {
outline: 2px solid #00DDFF;
border: 1px solid #00DDFF;
outline: 1px solid #00DDFF;
}
}
&:focus {
border: 1px solid #0060DF;
outline: 1px solid #0060DF;
}
}
.stp_tag_picker_button {
@ -114,5 +116,20 @@
border: 1px solid #0060DF;
outline: 1px solid #0060DF;
}
@include theme_dark {
background-color: #2B2A33;
color: #FBFBFB;
&:disabled {
color: #666;
}
&:hover:enabled {
background-color: #53535d;
}
&:focus:enabled {
border: 1px solid #00DDFF;
outline: 1px solid #00DDFF;
}
}
}
}

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

@ -2,8 +2,17 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
var EXPORTED_SYMBOLS = ["BuiltInThemeConfig"];
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
XPCOMUtils.defineLazyModuleGetters(this, {
AppConstants: "resource://gre/modules/AppConstants.jsm",
});
/**
* A Map of themes built in to the browser, alongwith a Map of collections those themes belong to. Params for the objects contained
* within the map:
@ -239,13 +248,13 @@ const ColorwayCollections = new Map([
[
"life-in-color",
{
expiry: "2022-02-08",
expiry: AppConstants.NIGHTLY_BUILD ? "2022-08-03" : "2022-02-08",
},
],
[
"true-colors",
{
expiry: "2022-05-03",
expiry: AppConstants.NIGHTLY_BUILD ? "2022-4-20" : "2022-05-03",
},
],
]);

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

@ -56,6 +56,7 @@ js_source_path = [
"../browser/components/uitour",
"../browser/components/urlbar",
"../remote/marionette",
"../toolkit/actors",
"../toolkit/components/extensions",
"../toolkit/components/extensions/parent",
"../toolkit/components/featuregates",

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

@ -4140,11 +4140,17 @@ already_AddRefed<nsIWidget> nsGlobalWindowOuter::GetMainWidget() {
nsIWidget* nsGlobalWindowOuter::GetNearestWidget() const {
nsIDocShell* docShell = GetDocShell();
NS_ENSURE_TRUE(docShell, nullptr);
if (!docShell) {
return nullptr;
}
PresShell* presShell = docShell->GetPresShell();
NS_ENSURE_TRUE(presShell, nullptr);
if (!presShell) {
return nullptr;
}
nsIFrame* rootFrame = presShell->GetRootFrame();
NS_ENSURE_TRUE(rootFrame, nullptr);
if (!rootFrame) {
return nullptr;
}
return rootFrame->GetView()->GetNearestWidget(nullptr);
}

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

@ -252,12 +252,14 @@ OffscreenCanvasDisplayHelper::GetSurfaceSnapshot() {
Maybe<int32_t> childId;
HTMLCanvasElement* canvasElement;
RefPtr<gfx::SourceSurface> surface;
layers::CompositableHandle handle;
{
MutexAutoLock lock(mMutex);
hasAlpha = !mData.mIsOpaque;
isAlphaPremult = mData.mIsAlphaPremult;
originPos = mData.mOriginPos;
handle = mData.mHandle;
managerId = mContextManagerId;
childId = mContextChildId;
canvasElement = mCanvasElement;
@ -311,7 +313,9 @@ OffscreenCanvasDisplayHelper::GetSurfaceSnapshot() {
// We don't have a usable surface, and the context lives in the compositor
// process.
return gfx::CanvasManagerChild::Get()->GetSnapshot(
managerId.value(), childId.value(), hasAlpha);
managerId.value(), childId.value(), handle,
hasAlpha ? gfx::SurfaceFormat::R8G8B8A8 : gfx::SurfaceFormat::R8G8B8X8,
hasAlpha && !isAlphaPremult, originPos == gl::OriginPos::BottomLeft);
}
// If we don't have any protocol IDs, or an existing surface, it is possible

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

@ -664,6 +664,42 @@ struct QueueParamTraits<std::pair<TypeA, TypeB>> {
}
};
// ---------------------------------------------------------------
template <>
struct QueueParamTraits<mozilla::ipc::Shmem> {
using ParamType = mozilla::ipc::Shmem;
template <typename U>
static bool Write(ProducerView<U>& aProducerView, ParamType&& aParam) {
if (!aProducerView.WriteParam(
aParam.Id(mozilla::ipc::Shmem::PrivateIPDLCaller()))) {
return false;
}
aParam.RevokeRights(mozilla::ipc::Shmem::PrivateIPDLCaller());
aParam.forget(mozilla::ipc::Shmem::PrivateIPDLCaller());
}
template <typename U>
static bool Read(ConsumerView<U>& aConsumerView, ParamType* aResult) {
ParamType::id_t id;
if (!aConsumerView.ReadParam(&id)) {
return false;
}
mozilla::ipc::Shmem::SharedMemory* rawmem =
aConsumerView.LookupSharedMemory(id);
if (!rawmem) {
return false;
}
*aResult = mozilla::ipc::Shmem(mozilla::ipc::Shmem::PrivateIPDLCaller(),
rawmem, id);
return true;
}
};
} // namespace webgl
} // namespace mozilla

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

@ -9,6 +9,7 @@
#include "mozilla/dom/Document.h"
#include "mozilla/PresShell.h"
#include "nsRefreshDriver.h"
#include "ImageContainer.h"
nsICanvasRenderingContextInternal::nsICanvasRenderingContextInternal() =
default;
@ -72,3 +73,12 @@ void nsICanvasRenderingContextInternal::DoSecurityCheck(
mOffscreenCanvas->SetWriteOnly();
}
}
already_AddRefed<mozilla::layers::Image>
nsICanvasRenderingContextInternal::GetAsImage() {
RefPtr<mozilla::gfx::SourceSurface> snapshot = GetFrontBufferSnapshot(true);
if (!snapshot) {
return nullptr;
}
return mozilla::MakeAndAddRef<mozilla::layers::SourceSurfaceImage>(snapshot);
}

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

@ -146,9 +146,7 @@ class nsICanvasRenderingContextInternal : public nsISupports,
// for possibly reinitializing with SetDimensions/InitializeWithSurface.
NS_IMETHOD Reset() = 0;
virtual already_AddRefed<mozilla::layers::Image> GetAsImage() {
return nullptr;
}
virtual already_AddRefed<mozilla::layers::Image> GetAsImage();
virtual bool UpdateWebRenderCanvasData(
mozilla::nsDisplayListBuilder* aBuilder,

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

@ -24,6 +24,7 @@
#include "mozilla/dom/UserActivation.h"
#include "mozilla/dom/MutationEventBinding.h"
#include "mozilla/dom/WheelEventBinding.h"
#include "mozilla/dom/WindowGlobalChild.h"
#include "mozilla/PresShell.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/TextUtils.h"
@ -5522,6 +5523,74 @@ void HTMLInputElement::SetSelectionDirection(const nsAString& aDirection,
state->SetSelectionDirection(aDirection, aRv);
}
// https://html.spec.whatwg.org/multipage/input.html#dom-input-showpicker
void HTMLInputElement::ShowPicker(ErrorResult& aRv) {
// Step 1. If this is not mutable, then throw an "InvalidStateError"
// DOMException.
if (!IsMutable()) {
return aRv.ThrowInvalidStateError(
"This input is either disabled or readonly.");
}
// Step 2. If this's relevant settings object's origin is not same origin with
// this's relevant settings object's top-level origin, and this's type
// attribute is not in the File Upload state or Color state, then throw a
// "SecurityError" DOMException.
if (mType != FormControlType::InputFile &&
mType != FormControlType::InputColor) {
nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow();
WindowGlobalChild* windowGlobalChild =
window ? window->GetWindowGlobalChild() : nullptr;
if (!windowGlobalChild || !windowGlobalChild->SameOriginWithTop()) {
return aRv.ThrowSecurityError(
"Call was blocked because the current origin isn't same-origin with "
"top.");
}
}
// Step 3. If this's relevant global object does not have transient
// activation, then throw a "NotAllowedError" DOMException.
if (!OwnerDoc()->HasValidTransientUserGestureActivation()) {
return aRv.ThrowNotAllowedError(
"Call was blocked due to lack of user activation.");
}
// Step 4. Show the picker, if applicable, for this.
//
// https://html.spec.whatwg.org/multipage/input.html#show-the-picker,-if-applicable
// To show the picker, if applicable for an input element element:
// Step 1. Assert: element's relevant global object has transient activation.
// Step 2. If element is not mutable, then return.
// (See above.)
// Step 3. If element's type attribute is in the File Upload state, then run
// these steps in parallel:
if (mType == FormControlType::InputFile) {
FilePickerType type = FILE_PICKER_FILE;
if (StaticPrefs::dom_webkitBlink_dirPicker_enabled() &&
HasAttr(nsGkAtoms::webkitdirectory)) {
type = FILE_PICKER_DIRECTORY;
}
InitFilePicker(type);
return;
}
// Step 4. Otherwise, the user agent should show any relevant user interface
// for selecting a value for element, in the way it normally would when the
// user interacts with the control
if (mType == FormControlType::InputColor) {
InitColorPicker();
return;
}
if (IsDateTimeInputType(mType) && IsInComposedDoc()) {
DateTimeValue value;
GetDateTimeInputBoxValue(value);
OpenDateTimePicker(value);
}
}
#ifdef ACCESSIBILITY
/*static*/ nsresult FireEventForAccessibility(HTMLInputElement* aTarget,
EventMessage aEventMessage) {

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

@ -699,6 +699,8 @@ class HTMLInputElement final : public TextControlElement,
SelectionMode aSelectMode,
ErrorResult& aRv);
void ShowPicker(ErrorResult& aRv);
bool WebkitDirectoryAttr() const {
return HasAttr(kNameSpaceID_None, nsGkAtoms::webkitdirectory);
}

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

@ -27,6 +27,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=885996
{ id: 'normal', type: 'prevent-default-1', result: false },
{ id: 'normal', type: 'prevent-default-2', result: false },
{ id: 'normal', type: 'click-method', result: true },
{ id: 'normal', type: 'show-picker', result: true },
{ id: 'normal', type: 'right-click', result: false },
{ id: 'normal', type: 'middle-click', result: false },
{ id: 'label-1', result: true },
@ -87,6 +88,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=885996
case 'click-method':
element.click();
break;
case 'show-picker':
SpecialPowers.wrap(document).notifyUserGestureActivation();
element.showPicker();
break;
case 'right-click':
synthesizeMouseAtCenter(element, { button: 2 });
break;

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

@ -62,6 +62,7 @@
<div id='div-click-on-demand' onclick="var i=document.createElement('input'); i.type='file'; i.click();" tabindex='1'>foo</div>
<div id='div-keydown' onkeydown="document.getElementById('by-button').click();" tabindex='1'>foo</div>
<a id='link-click' href="javascript:document.getElementById('by-button').click();" tabindex='1'>foo</a>
<input id='show-picker' type='file'>
</div>
<pre id="test">
<script type="application/javascript">
@ -135,6 +136,7 @@ var testData = [["a", 1, MockFilePicker.filterImages, 1],
["div-click-on-demand", 0, undefined, 0],
["div-keydown", 0, undefined, 0],
["link-click", 0, undefined, 0],
["show-picker", 0, undefined, 0],
];
var currentTest = 0;
@ -202,6 +204,9 @@ function launchNextTest() {
synthesizeMouseAtCenter(document.getElementById(testData[currentTest][0]), {});
} else if (testData[currentTest][0] == 'div-keydown') {
sendString("a");
} else if (testData[currentTest][0] == 'show-picker') {
SpecialPowers.wrap(document).notifyUserGestureActivation();
document.getElementById(testData[currentTest][0]).showPicker();
} else {
document.getElementById(testData[currentTest][0]).click();
}

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

@ -15,6 +15,10 @@ collapse= Collapse
expand = Expand
activate= Activate
cycle = Cycle
# An action provided to accessibility clients such as screen readers to allow
# them to click an element when the click will be handled by a container
# (ancestor) element. This is not normally reported to users.
click ancestor = Click ancestor
# Universal Access API support
# (Mac Only)

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

@ -15,3 +15,7 @@ collapse= Collapse
expand = Expand
activate= Activate
cycle = Cycle
# An action provided to accessibility clients such as screen readers to allow
# them to click an element when the click will be handled by a container
# (ancestor) element. This is not normally reported to users.
click ancestor = Click ancestor

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

@ -15,3 +15,7 @@ collapse= Collapse
expand = Expand
activate= Activate
cycle = Cycle
# An action provided to accessibility clients such as screen readers to allow
# them to click an element when the click will be handled by a container
# (ancestor) element. This is not normally reported to users.
click ancestor = Click ancestor

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

@ -273,6 +273,9 @@ PlanarYCbCrData ConstructPlanarYCbCrData(const VideoInfo& aInfo,
data.mStereoMode = aInfo.mStereoMode;
data.mYUVColorSpace = aBuffer.mYUVColorSpace;
data.mColorDepth = aBuffer.mColorDepth;
if (aInfo.mTransferFunction) {
data.mTransferFunction = *aInfo.mTransferFunction;
}
data.mColorRange = aBuffer.mColorRange;
data.mChromaSubsampling = aBuffer.mChromaSubsampling;
return data;

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

@ -244,8 +244,16 @@ class VideoInfo : public TrackInfo {
// Should be 8, 10 or 12. Default value is 8.
gfx::ColorDepth mColorDepth = gfx::ColorDepth::COLOR_8;
// Matrix coefficients (if specified by the video) imply a colorspace.
Maybe<gfx::YUVColorSpace> mColorSpace;
// Color primaries are assumed to match the colorspace.
// Transfer functions get their own member, since we support different
// values for the BT2020 primaries. For other colorspaces, this member
// is ignored.
Maybe<gfx::TransferFunction> mTransferFunction;
// True indicates no restriction on Y, U, V values (otherwise 16-235 for 8
// bits etc)
gfx::ColorRange mColorRange = gfx::ColorRange::LIMITED;

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

@ -33,6 +33,7 @@ struct ParamTraits<mozilla::VideoInfo> {
WriteParam(aWriter, aParam.mRotation);
WriteParam(aWriter, aParam.mColorDepth);
WriteParam(aWriter, aParam.mColorSpace);
WriteParam(aWriter, aParam.mTransferFunction);
WriteParam(aWriter, aParam.mColorRange);
WriteParam(aWriter, aParam.HasAlpha());
}
@ -50,6 +51,7 @@ struct ParamTraits<mozilla::VideoInfo> {
ReadParam(aReader, &aResult->mRotation) &&
ReadParam(aReader, &aResult->mColorDepth) &&
ReadParam(aReader, &aResult->mColorSpace) &&
ReadParam(aReader, &aResult->mTransferFunction) &&
ReadParam(aReader, &aResult->mColorRange) &&
ReadParam(aReader, &alphaPresent)) {
aResult->SetAlpha(alphaPresent);

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

@ -44,6 +44,8 @@ AppleVTDecoder::AppleVTDecoder(const VideoInfo& aConfig,
mColorSpace(aConfig.mColorSpace
? *aConfig.mColorSpace
: DefaultColorSpace({mPictureWidth, mPictureHeight})),
mTransferFunction(aConfig.mTransferFunction ? *aConfig.mTransferFunction
: gfx::TransferFunction::PQ),
mColorRange(aConfig.mColorRange),
mColorDepth(aConfig.mColorDepth),
mStreamType(MP4Decoder::IsH264(aConfig.mMimeType) ? StreamType::H264
@ -430,6 +432,8 @@ void AppleVTDecoder::OutputFrame(CVPixelBufferRef aImage,
} else {
#if !defined(MAC_OS_VERSION_10_13) || \
MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_VERSION_10_13
CFStringRef kCVImageBufferTransferFunction_ITU_R_2100_HLG =
CFSTR("ITU_R_2100_HLG");
CFStringRef kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ =
CFSTR("SMPTE_ST_2084_PQ");
#endif
@ -459,9 +463,12 @@ void AppleVTDecoder::OutputFrame(CVPixelBufferRef aImage,
CVBufferSetAttachment(aImage, kCVImageBufferColorPrimariesKey,
kCVImageBufferColorPrimaries_ITU_R_2020,
kCVAttachmentMode_ShouldPropagate);
CVBufferSetAttachment(aImage, kCVImageBufferTransferFunctionKey,
kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ,
kCVAttachmentMode_ShouldPropagate);
CVBufferSetAttachment(
aImage, kCVImageBufferTransferFunctionKey,
(mTransferFunction == gfx::TransferFunction::HLG)
? kCVImageBufferTransferFunction_ITU_R_2100_HLG
: kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ,
kCVAttachmentMode_ShouldPropagate);
}
CFTypeRefPtr<IOSurfaceRef> surface =

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

@ -91,6 +91,7 @@ class AppleVTDecoder : public MediaDataDecoder,
const uint32_t mDisplayWidth;
const uint32_t mDisplayHeight;
const gfx::YUVColorSpace mColorSpace;
const gfx::TransferFunction mTransferFunction;
const gfx::ColorRange mColorRange;
const gfx::ColorDepth mColorDepth;

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

@ -142,6 +142,11 @@ class H264ChangeMonitor : public MediaChangeMonitor::CodecChangeMonitor {
mCurrentConfig.mDisplay.height = spsdata.display_height;
mCurrentConfig.mColorDepth = spsdata.ColorDepth();
mCurrentConfig.mColorSpace = Some(spsdata.ColorSpace());
// spsdata.transfer_characteristics has the same values as
// gfx::CICP::TransferCharacteristics.
mCurrentConfig.mTransferFunction = gfxUtils::CicpToTransferFunction(
static_cast<gfx::CICP::TransferCharacteristics>(
spsdata.transfer_characteristics));
mCurrentConfig.mColorRange = spsdata.video_full_range_flag
? gfx::ColorRange::FULL
: gfx::ColorRange::LIMITED;
@ -267,6 +272,14 @@ class VPXChangeMonitor : public MediaChangeMonitor::CodecChangeMonitor {
mCurrentConfig.mColorDepth = gfx::ColorDepthForBitDepth(info.mBitDepth);
mCurrentConfig.mColorSpace = Some(info.ColorSpace());
// We don't update the transfer function here, because VPX bitstream
// doesn't specify the transfer function. Instead, we keep the transfer
// function (if any) that was set in mCurrentConfig when we were created.
// If a video changes colorspaces away from BT2020, we won't clear
// mTransferFunction, in case the video changes back to BT2020 and we
// need the value again.
mCurrentConfig.mColorRange = info.ColorRange();
if (mCodec == VPXDecoder::Codec::VP9) {
mCurrentConfig.mExtraData->ClearAndRetainStorage();
@ -333,6 +346,8 @@ class AV1ChangeMonitor : public MediaChangeMonitor::CodecChangeMonitor {
mCurrentConfig.mColorSpace = gfxUtils::CicpToColorSpace(
aInfo.mColorSpace.mMatrix, aInfo.mColorSpace.mPrimaries,
gMediaDecoderLog);
mCurrentConfig.mTransferFunction =
gfxUtils::CicpToTransferFunction(aInfo.mColorSpace.mTransfer);
mCurrentConfig.mColorRange = aInfo.mColorSpace.mRange;
if (mCurrentConfig.mImage != mInfo->mImage) {

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

@ -14,6 +14,7 @@
#include "WebMDemuxer.h"
#include "WebMBufferedParser.h"
#include "gfx2DGlue.h"
#include "gfxUtils.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/SharedThreadPool.h"
#include "MediaDataDemuxer.h"
@ -305,6 +306,15 @@ nsresult WebMDemuxer::ReadMetadata() {
NS_WARNING("Unknown WebM video codec");
return NS_ERROR_FAILURE;
}
// For VPX, this is our only chance to capture the transfer
// characteristics, which we can't get from a VPX bitstream later.
// We only need this value if the video is using the BT2020
// colorspace, which will be determined on a per-frame basis later.
mInfo.mVideo.mTransferFunction = gfxUtils::CicpToTransferFunction(
static_cast<gfx::CICP::TransferCharacteristics>(
params.transfer_characteristics));
// Picture region, taking into account cropping, before scaling
// to the display size.
unsigned int cropH = params.crop_right + params.crop_left;

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

@ -5,9 +5,11 @@
#include "mozilla/dom/WebGPUBinding.h"
#include "CanvasContext.h"
#include "nsDisplayList.h"
#include "gfxUtils.h"
#include "LayerUserData.h"
#include "nsDisplayList.h"
#include "mozilla/dom/HTMLCanvasElement.h"
#include "mozilla/gfx/CanvasManagerChild.h"
#include "mozilla/layers/CompositableInProcessManager.h"
#include "mozilla/layers/ImageDataSerializer.h"
#include "mozilla/layers/LayersSurfaces.h"
@ -78,6 +80,7 @@ void CanvasContext::Configure(const dom::GPUCanvasConfiguration& aDesc) {
} else if (mOffscreenCanvas) {
dom::OffscreenCanvasDisplayData data;
data.mSize = {mWidth, mHeight};
data.mIsOpaque = false;
data.mHandle = mHandle;
mOffscreenCanvas->UpdateDisplayData(data);
}
@ -123,5 +126,51 @@ void CanvasContext::SwapChainPresent() {
}
}
mozilla::UniquePtr<uint8_t[]> CanvasContext::GetImageBuffer(int32_t* aFormat) {
gfxAlphaType any;
RefPtr<gfx::SourceSurface> snapshot = GetSurfaceSnapshot(&any);
if (!snapshot) {
*aFormat = 0;
return nullptr;
}
RefPtr<gfx::DataSourceSurface> dataSurface = snapshot->GetDataSurface();
return gfxUtils::GetImageBuffer(dataSurface, /* aIsAlphaPremultiplied */ true,
aFormat);
}
NS_IMETHODIMP CanvasContext::GetInputStream(const char* aMimeType,
const nsAString& aEncoderOptions,
nsIInputStream** aStream) {
gfxAlphaType any;
RefPtr<gfx::SourceSurface> snapshot = GetSurfaceSnapshot(&any);
if (!snapshot) {
return NS_ERROR_FAILURE;
}
RefPtr<gfx::DataSourceSurface> dataSurface = snapshot->GetDataSurface();
return gfxUtils::GetInputStream(dataSurface, /* aIsAlphaPremultiplied */ true,
aMimeType, aEncoderOptions, aStream);
}
already_AddRefed<mozilla::gfx::SourceSurface> CanvasContext::GetSurfaceSnapshot(
gfxAlphaType* aOutAlphaType) {
if (aOutAlphaType) {
*aOutAlphaType = gfxAlphaType::Premult;
}
auto* const cm = gfx::CanvasManagerChild::Get();
if (!cm) {
return nullptr;
}
if (!mBridge || !mBridge->IsOpen() || !mHandle) {
return nullptr;
}
return cm->GetSnapshot(cm->Id(), mBridge->Id(), mHandle, mGfxFormat,
/* aPremultiply */ false, /* aYFlip */ false);
}
} // namespace webgpu
} // namespace mozilla

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

@ -54,20 +54,12 @@ class CanvasContext final : public nsICanvasRenderingContextInternal,
return NS_OK;
}
mozilla::UniquePtr<uint8_t[]> GetImageBuffer(int32_t* aFormat) override {
MOZ_CRASH("todo");
}
mozilla::UniquePtr<uint8_t[]> GetImageBuffer(int32_t* aFormat) override;
NS_IMETHOD GetInputStream(const char* aMimeType,
const nsAString& aEncoderOptions,
nsIInputStream** aStream) override {
*aStream = nullptr;
return NS_ERROR_NOT_IMPLEMENTED;
}
nsIInputStream** aStream) override;
already_AddRefed<mozilla::gfx::SourceSurface> GetSurfaceSnapshot(
gfxAlphaType* aOutAlphaType) override {
return nullptr;
}
gfxAlphaType* aOutAlphaType) override;
void SetOpaqueValueFromOpaqueAttr(bool aOpaqueAttrValue) override {}
bool GetIsOpaque() override { return true; }

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

@ -640,6 +640,36 @@ static void PresentCallback(ffi::WGPUBufferMapAsyncStatus status,
delete req;
}
ipc::IPCResult WebGPUParent::GetFrontBufferSnapshot(
IProtocol* aProtocol, const CompositableHandle& aHandle,
Maybe<Shmem>& aShmem, gfx::IntSize& aSize) {
const auto& lookup = mCanvasMap.find(aHandle.Value());
if (lookup == mCanvasMap.end()) {
return IPC_OK();
}
RefPtr<PresentationData> data = lookup->second.get();
aSize = data->mTextureHost->GetSize();
uint32_t stride =
aSize.width * BytesPerPixel(data->mTextureHost->GetFormat());
uint32_t len = data->mRowCount * stride;
Shmem shmem;
if (!AllocShmem(len, ipc::Shmem::SharedMemory::TYPE_BASIC, &shmem)) {
return IPC_OK();
}
uint8_t* dst = shmem.get<uint8_t>();
uint8_t* src = data->mTextureHost->GetBuffer();
for (uint32_t row = 0; row < data->mRowCount; ++row) {
memcpy(dst, src, stride);
src += data->mTargetPitch;
dst += stride;
}
aShmem.emplace(std::move(shmem));
return IPC_OK();
}
ipc::IPCResult WebGPUParent::RecvSwapChainPresent(
const CompositableHandle& aHandle, RawId aTextureId,
RawId aCommandEncoderId) {

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

@ -11,8 +11,7 @@
#include "WebGPUTypes.h"
#include "base/timer.h"
namespace mozilla {
namespace webgpu {
namespace mozilla::webgpu {
class ErrorBuffer;
class PresentationData;
@ -90,6 +89,11 @@ class WebGPUParent final : public PWebGPUParent {
ipc::IPCResult RecvDevicePopErrorScope(
RawId aSelfId, DevicePopErrorScopeResolver&& aResolver);
ipc::IPCResult GetFrontBufferSnapshot(IProtocol* aProtocol,
const CompositableHandle& aHandle,
Maybe<Shmem>& aShmem,
gfx::IntSize& aSize);
void ActorDestroy(ActorDestroyReason aWhy) override;
private:
@ -109,7 +113,6 @@ class WebGPUParent final : public PWebGPUParent {
std::unordered_map<uint64_t, ErrorScopeStack> mErrorScopeMap;
};
} // namespace webgpu
} // namespace mozilla
} // namespace mozilla::webgpu
#endif // WEBGPU_PARENT_H_

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

@ -134,6 +134,9 @@ interface HTMLInputElement : HTMLElement {
[Throws]
void setSelectionRange(unsigned long start, unsigned long end, optional DOMString direction);
[Throws, Pref="dom.input.showPicker"]
void showPicker();
// also has obsolete members
};

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

@ -82,6 +82,6 @@ partial interface mixin WindowOrWorkerGlobalScope {
// https://wicg.github.io/scheduling-apis/#ref-for-windoworworkerglobalscope-scheduler
partial interface mixin WindowOrWorkerGlobalScope {
[Pref="dom.enable_web_task_scheduling", SameObject]
[Replaceable, Pref="dom.enable_web_task_scheduling", SameObject]
readonly attribute Scheduler scheduler;
};

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

@ -122,7 +122,8 @@ size_t CreatePlaneDictionary(CFTypeRefPtr<CFMutableDictionaryRef>& aDict,
/* static */
already_AddRefed<MacIOSurface> MacIOSurface::CreateNV12OrP010Surface(
const IntSize& aYSize, const IntSize& aCbCrSize, YUVColorSpace aColorSpace,
ColorRange aColorRange, ColorDepth aColorDepth) {
TransferFunction aTransferFunction, ColorRange aColorRange,
ColorDepth aColorDepth) {
MOZ_ASSERT(aColorSpace == YUVColorSpace::BT601 ||
aColorSpace == YUVColorSpace::BT709 ||
aColorSpace == YUVColorSpace::BT2020);
@ -209,12 +210,10 @@ already_AddRefed<MacIOSurface> MacIOSurface::CreateNV12OrP010Surface(
// the same. Since we are creating the IOSurface directly, we use hard-coded
// keys derived from inspecting the extracted IOSurfaces in the copying case,
// but we use the API-defined values from CVImageBuffer.
// TODO: Check the actual color primaries and transfer functions of the source
// data and use the corresponding values, instead of just guessing good-enough
// values based on the color space.
#if !defined(MAC_OS_VERSION_10_13) || \
MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_VERSION_10_13
CFStringRef kCVImageBufferTransferFunction_ITU_R_2100_HLG =
CFSTR("ITU_R_2100_HLG");
CFStringRef kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ =
CFSTR("SMPTE_ST_2084_PQ");
#endif
@ -230,16 +229,14 @@ already_AddRefed<MacIOSurface> MacIOSurface::CreateNV12OrP010Surface(
IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceTransferFunction"),
kCVImageBufferTransferFunction_ITU_R_709_2);
} else {
// TODO Bug 1764618: we shouldn't guess these color primaries and transfer
// functions -- we should respect what the video specifies.
IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceYCbCrMatrix"),
kCVImageBufferYCbCrMatrix_ITU_R_2020);
IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceColorPrimaries"),
kCVImageBufferColorPrimaries_ITU_R_2020);
IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceTransferFunction"),
kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ);
NS_WARNING(
"Rec2020 transfer function is ambiguous, guessing PQ instead of HLG.");
(aTransferFunction == TransferFunction::HLG)
? kCVImageBufferTransferFunction_ITU_R_2100_HLG
: kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ);
}
// Override the color space to be the same as the main display, so that
// CoreAnimation won't try to do any color correction (from the IOSurface

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

@ -50,6 +50,7 @@ class MacIOSurface final
typedef mozilla::gfx::BackendType BackendType;
typedef mozilla::gfx::IntSize IntSize;
typedef mozilla::gfx::YUVColorSpace YUVColorSpace;
typedef mozilla::gfx::TransferFunction TransferFunction;
typedef mozilla::gfx::ColorRange ColorRange;
typedef mozilla::gfx::ColorDepth ColorDepth;
@ -61,8 +62,8 @@ class MacIOSurface final
bool aHasAlpha = true);
static already_AddRefed<MacIOSurface> CreateNV12OrP010Surface(
const IntSize& aYSize, const IntSize& aCbCrSize,
YUVColorSpace aColorSpace, ColorRange aColorRange,
ColorDepth aColorDepth);
YUVColorSpace aColorSpace, TransferFunction aTransferFunction,
ColorRange aColorRange, ColorDepth aColorDepth);
static already_AddRefed<MacIOSurface> CreateYUV422Surface(
const IntSize& aSize, YUVColorSpace aColorSpace, ColorRange aColorRange);
static void ReleaseIOSurface(MacIOSurface* aIOSurface);

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

@ -349,6 +349,15 @@ enum class ColorDepth : uint8_t {
_Last = COLOR_16,
};
enum class TransferFunction : uint8_t {
SRGB,
PQ,
HLG,
_First = SRGB,
_Last = HLG,
Default = SRGB,
};
enum class ColorRange : uint8_t {
LIMITED,
FULL,

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

@ -144,13 +144,15 @@ RefPtr<webgpu::WebGPUChild> CanvasManagerChild::GetWebGPUChild() {
}
already_AddRefed<DataSourceSurface> CanvasManagerChild::GetSnapshot(
uint32_t aManagerId, int32_t aProtocolId, bool aHasAlpha) {
uint32_t aManagerId, int32_t aProtocolId,
const layers::CompositableHandle& aHandle, SurfaceFormat aFormat,
bool aPremultiply, bool aYFlip) {
if (!CanSend()) {
return nullptr;
}
webgl::FrontBufferSnapshotIpc res;
if (!SendGetSnapshot(aManagerId, aProtocolId, &res)) {
if (!SendGetSnapshot(aManagerId, aProtocolId, aHandle, &res)) {
return nullptr;
}
@ -178,7 +180,7 @@ already_AddRefed<DataSourceSurface> CanvasManagerChild::GetSnapshot(
}
SurfaceFormat format =
aHasAlpha ? SurfaceFormat::B8G8R8A8 : SurfaceFormat::B8G8R8X8;
IsOpaque(aFormat) ? SurfaceFormat::B8G8R8X8 : SurfaceFormat::B8G8R8A8;
RefPtr<DataSourceSurface> surface =
Factory::CreateDataSourceSurfaceWithStride(size, format, stride.value(),
/* aZero */ false);
@ -192,21 +194,32 @@ already_AddRefed<DataSourceSurface> CanvasManagerChild::GetSnapshot(
return nullptr;
}
// The buffer we read back from WebGL is R8G8B8A8, not premultiplied and has
// its rows inverted. For the general case, we want surfaces represented as
// premultiplied B8G8R8A8, with its rows ordered top to bottom. Given this
// path is used for screenshots/SurfaceFromElement, that's the representation
// we need.
if (aHasAlpha) {
if (!PremultiplyYFlipData(res.shmem->get<uint8_t>(), stride.value(),
SurfaceFormat::R8G8B8A8, map.GetData(),
map.GetStride(), format, size)) {
// The buffer we may readback from the canvas could be R8G8B8A8, not
// premultiplied, and/or has its rows iverted. For the general case, we want
// surfaces represented as premultiplied B8G8R8A8, with its rows ordered top
// to bottom. Given this path is used for screenshots/SurfaceFromElement,
// that's the representation we need.
if (aYFlip) {
if (aPremultiply) {
if (!PremultiplyYFlipData(res.shmem->get<uint8_t>(), stride.value(),
aFormat, map.GetData(), map.GetStride(), format,
size)) {
return nullptr;
}
} else {
if (!SwizzleYFlipData(res.shmem->get<uint8_t>(), stride.value(), aFormat,
map.GetData(), map.GetStride(), format, size)) {
return nullptr;
}
}
} else if (aPremultiply) {
if (!PremultiplyData(res.shmem->get<uint8_t>(), stride.value(), aFormat,
map.GetData(), map.GetStride(), format, size)) {
return nullptr;
}
} else {
if (!SwizzleYFlipData(res.shmem->get<uint8_t>(), stride.value(),
SurfaceFormat::R8G8B8X8, map.GetData(),
map.GetStride(), format, size)) {
if (!SwizzleData(res.shmem->get<uint8_t>(), stride.value(), aFormat,
map.GetData(), map.GetStride(), format, size)) {
return nullptr;
}
}

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

@ -8,6 +8,7 @@
#include "mozilla/Atomics.h"
#include "mozilla/gfx/PCanvasManagerChild.h"
#include "mozilla/gfx/Types.h"
#include "mozilla/ThreadLocal.h"
namespace mozilla {
@ -29,9 +30,10 @@ class CanvasManagerChild final : public PCanvasManagerChild {
explicit CanvasManagerChild(uint32_t aId);
uint32_t Id() const { return mId; }
already_AddRefed<DataSourceSurface> GetSnapshot(uint32_t aManagerId,
int32_t aProtocolId,
bool aHasAlpha);
already_AddRefed<DataSourceSurface> GetSnapshot(
uint32_t aManagerId, int32_t aProtocolId,
const layers::CompositableHandle& aHandle, SurfaceFormat aFormat,
bool aPremultiply, bool aYFlip);
void ActorDestroy(ActorDestroyReason aReason) override;
static CanvasManagerChild* Get();

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

@ -111,7 +111,7 @@ mozilla::ipc::IPCResult CanvasManagerParent::RecvInitialize(
mozilla::ipc::IPCResult CanvasManagerParent::RecvGetSnapshot(
const uint32_t& aManagerId, const int32_t& aProtocolId,
webgl::FrontBufferSnapshotIpc* aResult) {
const CompositableHandle& aHandle, webgl::FrontBufferSnapshotIpc* aResult) {
if (!aManagerId) {
return IPC_FAIL(this, "invalid id");
}
@ -129,16 +129,33 @@ mozilla::ipc::IPCResult CanvasManagerParent::RecvGetSnapshot(
return IPC_FAIL(this, "invalid actor");
}
if (actor->GetProtocolId() != ProtocolId::PWebGLMsgStart ||
actor->GetSide() != mozilla::ipc::Side::ParentSide) {
if (actor->GetSide() != mozilla::ipc::Side::ParentSide) {
return IPC_FAIL(this, "unsupported actor");
}
RefPtr<dom::WebGLParent> webgl = static_cast<dom::WebGLParent*>(actor);
webgl::FrontBufferSnapshotIpc buffer;
mozilla::ipc::IPCResult rv = webgl->GetFrontBufferSnapshot(&buffer, this);
if (!rv) {
return rv;
switch (actor->GetProtocolId()) {
case ProtocolId::PWebGLMsgStart: {
RefPtr<dom::WebGLParent> webgl = static_cast<dom::WebGLParent*>(actor);
mozilla::ipc::IPCResult rv = webgl->GetFrontBufferSnapshot(&buffer, this);
if (!rv) {
return rv;
}
} break;
case ProtocolId::PWebGPUMsgStart: {
RefPtr<webgpu::WebGPUParent> webgpu =
static_cast<webgpu::WebGPUParent*>(actor);
IntSize size;
mozilla::ipc::IPCResult rv =
webgpu->GetFrontBufferSnapshot(this, aHandle, buffer.shmem, size);
if (!rv) {
return rv;
}
buffer.surfSize.x = static_cast<uint32_t>(size.width);
buffer.surfSize.y = static_cast<uint32_t>(size.height);
} break;
default:
return IPC_FAIL(this, "unsupported protocol");
}
*aResult = std::move(buffer);

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

@ -30,6 +30,7 @@ class CanvasManagerParent final : public PCanvasManagerParent {
mozilla::ipc::IPCResult RecvInitialize(const uint32_t& aId);
mozilla::ipc::IPCResult RecvGetSnapshot(
const uint32_t& aManagerId, const int32_t& aProtocolId,
const CompositableHandle& aHandle,
webgl::FrontBufferSnapshotIpc* aResult);
private:

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

@ -686,6 +686,13 @@ struct ParamTraits<mozilla::gfx::ColorDepth>
mozilla::gfx::ColorDepth, mozilla::gfx::ColorDepth::_First,
mozilla::gfx::ColorDepth::_Last> {};
template <>
struct ParamTraits<mozilla::gfx::TransferFunction>
: public ContiguousEnumSerializerInclusive<
mozilla::gfx::TransferFunction,
mozilla::gfx::TransferFunction::_First,
mozilla::gfx::TransferFunction::_Last> {};
template <>
struct ParamTraits<mozilla::gfx::ColorRange>
: public ContiguousEnumSerializerInclusive<

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

@ -5,9 +5,11 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
include "mozilla/layers/LayersMessageUtils.h";
include protocol PWebGL;
include protocol PWebGPU;
using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
using mozilla::webgl::FrontBufferSnapshotIpc from "mozilla/dom/WebGLIpdl.h";
namespace mozilla {
@ -39,7 +41,7 @@ parent:
// intended to be used by the main thread in the content process to block
// reading without having to block on the worker thread that owns the context
// instance.
sync GetSnapshot(uint32_t aManagerId, int32_t aProtocolId) returns (FrontBufferSnapshotIpc ret);
sync GetSnapshot(uint32_t aManagerId, int32_t aProtocolId, CompositableHandle aHandle) returns (FrontBufferSnapshotIpc ret);
};
} // gfx

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

@ -649,6 +649,7 @@ struct PlanarYCbCrData {
StereoMode mStereoMode = StereoMode::MONO;
gfx::ColorDepth mColorDepth = gfx::ColorDepth::COLOR_8;
gfx::YUVColorSpace mYUVColorSpace = gfx::YUVColorSpace::Default;
gfx::TransferFunction mTransferFunction = gfx::TransferFunction::SRGB;
gfx::ColorRange mColorRange = gfx::ColorRange::LIMITED;
gfx::ChromaSubsampling mChromaSubsampling = gfx::ChromaSubsampling::FULL;

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

@ -71,9 +71,9 @@ bool MacIOSurfaceImage::SetData(ImageContainer* aContainer,
auto ySize = aData.YDataSize();
auto cbcrSize = aData.CbCrDataSize();
RefPtr<MacIOSurface> surf =
allocator->Allocate(ySize, cbcrSize, aData.mYUVColorSpace,
aData.mColorRange, aData.mColorDepth);
RefPtr<MacIOSurface> surf = allocator->Allocate(
ySize, cbcrSize, aData.mYUVColorSpace, aData.mTransferFunction,
aData.mColorRange, aData.mColorDepth);
surf->Lock(false);
@ -200,8 +200,8 @@ bool MacIOSurfaceImage::SetData(ImageContainer* aContainer,
already_AddRefed<MacIOSurface> MacIOSurfaceRecycleAllocator::Allocate(
const gfx::IntSize aYSize, const gfx::IntSize& aCbCrSize,
gfx::YUVColorSpace aYUVColorSpace, gfx::ColorRange aColorRange,
gfx::ColorDepth aColorDepth) {
gfx::YUVColorSpace aYUVColorSpace, gfx::TransferFunction aTransferFunction,
gfx::ColorRange aColorRange, gfx::ColorDepth aColorDepth) {
nsTArray<CFTypeRefPtr<IOSurfaceRef>> surfaces = std::move(mSurfaces);
RefPtr<MacIOSurface> result;
for (auto& surf : surfaces) {
@ -224,7 +224,8 @@ already_AddRefed<MacIOSurface> MacIOSurfaceRecycleAllocator::Allocate(
if (!result) {
if (StaticPrefs::layers_iosurfaceimage_use_nv12_AtStartup()) {
result = MacIOSurface::CreateNV12OrP010Surface(
aYSize, aCbCrSize, aYUVColorSpace, aColorRange, aColorDepth);
aYSize, aCbCrSize, aYUVColorSpace, aTransferFunction, aColorRange,
aColorDepth);
} else {
result = MacIOSurface::CreateYUV422Surface(aYSize, aYUVColorSpace,
aColorRange);

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

@ -54,11 +54,11 @@ class MacIOSurfaceRecycleAllocator {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MacIOSurfaceRecycleAllocator)
already_AddRefed<MacIOSurface> Allocate(const gfx::IntSize aYSize,
const gfx::IntSize& aCbCrSize,
gfx::YUVColorSpace aYUVColorSpace,
gfx::ColorRange aColorRange,
gfx::ColorDepth aColorDepth);
already_AddRefed<MacIOSurface> Allocate(
const gfx::IntSize aYSize, const gfx::IntSize& aCbCrSize,
gfx::YUVColorSpace aYUVColorSpace,
gfx::TransferFunction aTransferFunction, gfx::ColorRange aColorRange,
gfx::ColorDepth aColorDepth);
private:
~MacIOSurfaceRecycleAllocator() = default;

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

@ -2148,8 +2148,8 @@ bool AsyncPanZoomController::CanScroll(const InputData& aEvent) const {
// to checking if it is scrollable without adjusting its delta.
// 2. For a non-auto-dir scroll, simply check if it is scrollable without
// adjusting its delta.
RecursiveMutexAutoLock lock(mRecursiveMutex);
if (scrollWheelInput.IsAutoDir(mScrollMetadata.ForceMousewheelAutodir())) {
RecursiveMutexAutoLock lock(mRecursiveMutex);
auto deltaX = scrollWheelInput.mDeltaX;
auto deltaY = scrollWheelInput.mDeltaY;
bool isRTL =
@ -2309,16 +2309,18 @@ nsEventStatus AsyncPanZoomController::OnScrollWheel(
auto deltaX = aEvent.mDeltaX;
auto deltaY = aEvent.mDeltaY;
ParentLayerPoint delta;
if (aEvent.IsAutoDir(mScrollMetadata.ForceMousewheelAutodir())) {
// It's an auto-dir scroll, so check if its delta should be adjusted, if so,
// adjust it.
{
RecursiveMutexAutoLock lock(mRecursiveMutex);
bool isRTL = IsContentOfHonouredTargetRightToLeft(
aEvent.HonoursRoot(mScrollMetadata.ForceMousewheelAutodirHonourRoot()));
APZAutoDirWheelDeltaAdjuster adjuster(deltaX, deltaY, mX, mY, isRTL);
if (adjuster.ShouldBeAdjusted()) {
adjuster.Adjust();
adjustedByAutoDir = true;
if (aEvent.IsAutoDir(mScrollMetadata.ForceMousewheelAutodir())) {
// It's an auto-dir scroll, so check if its delta should be adjusted, if
// so, adjust it.
bool isRTL = IsContentOfHonouredTargetRightToLeft(aEvent.HonoursRoot(
mScrollMetadata.ForceMousewheelAutodirHonourRoot()));
APZAutoDirWheelDeltaAdjuster adjuster(deltaX, deltaY, mX, mY, isRTL);
if (adjuster.ShouldBeAdjusted()) {
adjuster.Adjust();
adjustedByAutoDir = true;
}
}
}
// Ensure the calls to GetScrollWheelDelta are outside the mRecursiveMutex

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

@ -1381,6 +1381,26 @@ const float kIdentityNarrowYCbCrToRGB_RowMajor[16] = {
}
}
// Translate from CICP values to the transfer functions we support, or return
// Nothing() if there is no appropriate match.
//
/* static */ Maybe<gfx::TransferFunction> gfxUtils::CicpToTransferFunction(
const CICP::TransferCharacteristics aTransferCharacteristics) {
switch (aTransferCharacteristics) {
case CICP::TransferCharacteristics::TC_SRGB:
return Some(gfx::TransferFunction::SRGB);
case CICP::TransferCharacteristics::TC_SMPTE2084:
return Some(gfx::TransferFunction::PQ);
case CICP::TransferCharacteristics::TC_HLG:
return Some(gfx::TransferFunction::HLG);
default:
return {};
}
}
/* static */
void gfxUtils::WriteAsPNG(SourceSurface* aSurface, const nsAString& aFile) {
WriteAsPNG(aSurface, NS_ConvertUTF16toUTF8(aFile).get());

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

@ -232,6 +232,9 @@ class gfxUtils {
const mozilla::gfx::CICP::ColourPrimaries,
mozilla::LazyLogModule& aLogger);
static mozilla::Maybe<mozilla::gfx::TransferFunction> CicpToTransferFunction(
const mozilla::gfx::CICP::TransferCharacteristics);
/**
* Creates a copy of aSurface, but having the SurfaceFormat aFormat.
*

7
gfx/wr/Cargo.lock сгенерированный
Просмотреть файл

@ -1787,6 +1787,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "topological-sort"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa7c7f42dea4b1b99439786f5633aeb9c14c1b53f75e282803c2ec2ad545873c"
[[package]]
name = "tracy-rs"
version = "0.1.2"
@ -1982,6 +1988,7 @@ dependencies = [
"svg_fmt",
"swgl",
"time",
"topological-sort",
"tracy-rs",
"webrender_api",
"webrender_build",

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

@ -54,6 +54,7 @@ etagere = "0.2.6"
glean = "44.0.0"
fog = { version = "0.1.0", optional = true }
swgl = { path = "../swgl", optional = true }
topological-sort = "0.1"
[dev-dependencies]
mozangle = "0.3.3"

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

@ -21,6 +21,7 @@ use crate::image_source::{resolve_image, resolve_cached_render_task};
use crate::util::VecHelper;
use smallvec::SmallVec;
use std::mem;
use topological_sort::TopologicalSort;
use crate::render_target::{RenderTargetList, ColorRenderTarget};
use crate::render_target::{PictureCacheTarget, TextureCacheRenderTarget, AlphaRenderTarget};
@ -298,36 +299,71 @@ impl RenderTaskGraphBuilder {
unique_surfaces: FastHashSet::default(),
};
// Two traversals of the graph are required. The first pass determines how many passes
// are required, and assigns render tasks a pass to be drawn on. The second pass determines
// when the last time a render task is used as an input, and assigns what pass the surface
// backing that render task can be freed (the surface is then returned to the render target
// pool and may be aliased / reused during subsequent passes).
// First, use a topological sort of the dependency graph to split the task set in to
// a list of passes. This is necessary because when we have a complex graph (e.g. due
// to a large number of sibling backdrop-filter primitives) traversing it via a simple
// recursion can be too slow. The second pass determines when the last time a render task
// is used as an input, and assigns what pass the surface backing that render task can
// be freed (the surface is then returned to the render target pool and may be aliased
// or reused during subsequent passes).
let mut pass_count = 0;
let mut passes = Vec::new();
let mut task_sorter = TopologicalSort::<RenderTaskId>::new();
// Traverse each root, and assign `render_on` for each task and count number of required passes
for root_id in &self.roots {
assign_render_pass(
*root_id,
PassId(0),
&mut graph,
&mut pass_count,
);
// Iterate the task list, and add all the dependencies to the topo sort
for (parent_id, task) in graph.tasks.iter().enumerate() {
let parent_id = RenderTaskId { index: parent_id as u32 };
for child_id in &task.children {
task_sorter.add_dependency(
parent_id,
*child_id,
);
}
}
// Pop the sorted passes off the topological sort
loop {
// Get the next set of tasks that can be drawn
let tasks = task_sorter.pop_all();
// If there are no tasks left, we're done
if tasks.is_empty() {
// If the task sorter itself isn't empty but we couldn't pop off any
// tasks, that implies a circular dependency in the task graph
assert!(task_sorter.is_empty());
break;
} else {
// Assign the `render_on` field to the task
for task_id in &tasks {
graph.tasks[task_id.index as usize].render_on = PassId(pass_count);
}
// Store the task list for this pass, used later for `assign_free_pass`.
passes.push(tasks);
pass_count += 1;
}
}
// Always create at least one pass for root tasks
pass_count = pass_count.max(1);
// Determine which pass each task can be freed on, which depends on which is
// the last task that has this as an input.
for root_id in &self.roots {
assign_free_pass(
*root_id,
PassId(0),
&mut graph,
);
// the last task that has this as an input. This must be done in top-down
// pass order to ensure that RenderTaskLocation::Existing references are
// visited in the correct order
for pass in passes {
for task_id in pass {
assign_free_pass(
task_id,
&mut graph,
);
}
}
// Construct passes array for tasks to be assigned to below
for _ in 0 .. pass_count+1 {
for _ in 0 .. pass_count {
graph.passes.push(Pass {
task_ids: Vec::new(),
sub_passes: Vec::new(),
@ -690,70 +726,13 @@ impl std::ops::Index<RenderTaskId> for RenderTaskGraph {
}
}
/// Recursive helper to assign pass that a task should render on
fn assign_render_pass(
id: RenderTaskId,
pass: PassId,
graph: &mut RenderTaskGraph,
pass_count: &mut usize,
) {
let task = &mut graph.tasks[id.index as usize];
// No point in recursing into paths in the graph if this task already
// has been set to draw after this pass.
if task.render_on > pass {
return;
}
let next_pass = if task.kind.should_advance_pass() {
// Keep count of number of passes needed
*pass_count = pass.0.max(*pass_count);
PassId(pass.0 + 1)
} else {
pass
};
// A task should be rendered on the earliest pass in the dependency
// graph that it's required. Using max here ensures the correct value
// in the presence of multiple paths to this task from the root(s).
task.render_on = task.render_on.max(pass);
// TODO(gw): Work around the borrowck - maybe we could structure the dependencies
// storage better, to avoid this?
let mut child_task_ids: SmallVec<[RenderTaskId; 8]> = SmallVec::new();
child_task_ids.extend_from_slice(&task.children);
for child_id in child_task_ids {
assign_render_pass(
child_id,
next_pass,
graph,
pass_count,
);
}
}
fn assign_free_pass(
id: RenderTaskId,
pass: PassId,
graph: &mut RenderTaskGraph,
) {
let task = &mut graph.tasks[id.index as usize];
// No point in recursing into paths in the graph if this task already
// has been set to free before this pass.
if task.free_after.0 + 1 < pass.0 {
return;
}
let render_on = task.render_on;
let next_pass = if task.kind.should_advance_pass() {
PassId(pass.0 + 1)
} else {
pass
};
let mut child_task_ids: SmallVec<[RenderTaskId; 8]> = SmallVec::new();
child_task_ids.extend_from_slice(&task.children);
@ -791,12 +770,6 @@ fn assign_free_pass(
}
}
}
assign_free_pass(
child_id,
next_pass,
graph,
);
}
}

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

@ -322,6 +322,21 @@ void IProtocol::Unregister(int32_t aId) {
return mToplevel->Unregister(aId);
}
Shmem::SharedMemory* IProtocol::CreateSharedMemory(
size_t aSize, SharedMemory::SharedMemoryType aType, bool aUnsafe,
int32_t* aId) {
return mToplevel->CreateSharedMemory(aSize, aType, aUnsafe, aId);
}
Shmem::SharedMemory* IProtocol::LookupSharedMemory(int32_t aId) {
return mToplevel->LookupSharedMemory(aId);
}
bool IProtocol::IsTrackingSharedMemory(Shmem::SharedMemory* aSegment) {
return mToplevel->IsTrackingSharedMemory(aSegment);
}
bool IProtocol::DestroySharedMemory(Shmem& aShmem) {
return mToplevel->DestroySharedMemory(aShmem);
}
MessageChannel* IProtocol::GetIPCChannel() {
return mToplevel->GetIPCChannel();
}
@ -393,8 +408,14 @@ bool IProtocol::AllocShmem(size_t aSize,
return false;
}
*aOutMem = Shmem(aSize, aType, false);
return aOutMem->IsValid();
Shmem::id_t id;
Shmem::SharedMemory* rawmem(CreateSharedMemory(aSize, aType, false, &id));
if (!rawmem) {
return false;
}
*aOutMem = Shmem(Shmem::PrivateIPDLCaller(), rawmem, id);
return true;
}
bool IProtocol::AllocUnsafeShmem(size_t aSize,
@ -406,13 +427,30 @@ bool IProtocol::AllocUnsafeShmem(size_t aSize,
return false;
}
*aOutMem = Shmem(aSize, aType, true);
return aOutMem->IsValid();
Shmem::id_t id;
Shmem::SharedMemory* rawmem(CreateSharedMemory(aSize, aType, true, &id));
if (!rawmem) {
return false;
}
*aOutMem = Shmem(Shmem::PrivateIPDLCaller(), rawmem, id);
return true;
}
bool IProtocol::DeallocShmem(Shmem& aMem) {
aMem.RevokeRights();
return true;
bool ok = DestroySharedMemory(aMem);
#ifdef DEBUG
if (!ok) {
if (mSide == ChildSide) {
FatalError("bad Shmem");
} else {
NS_WARNING("bad Shmem");
}
return false;
}
#endif // DEBUG
aMem.forget(Shmem::PrivateIPDLCaller());
return ok;
}
void IProtocol::SetManager(IProtocol* aManager) {
@ -629,6 +667,104 @@ void IToplevelProtocol::Unregister(int32_t aId) {
mActorMap.Remove(aId);
}
Shmem::SharedMemory* IToplevelProtocol::CreateSharedMemory(
size_t aSize, Shmem::SharedMemory::SharedMemoryType aType, bool aUnsafe,
Shmem::id_t* aId) {
RefPtr<Shmem::SharedMemory> segment(
Shmem::Alloc(Shmem::PrivateIPDLCaller(), aSize, aType, aUnsafe));
if (!segment) {
return nullptr;
}
int32_t id = NextId();
Shmem shmem(Shmem::PrivateIPDLCaller(), segment.get(), id);
UniquePtr<Message> descriptor =
shmem.MkCreatedMessage(Shmem::PrivateIPDLCaller(), MSG_ROUTING_CONTROL);
if (!descriptor) {
return nullptr;
}
Unused << GetIPCChannel()->Send(std::move(descriptor));
*aId = shmem.Id(Shmem::PrivateIPDLCaller());
Shmem::SharedMemory* rawSegment = segment.get();
MOZ_ASSERT(!mShmemMap.Contains(*aId), "Don't insert with an existing ID");
mShmemMap.InsertOrUpdate(*aId, segment.forget().take());
return rawSegment;
}
Shmem::SharedMemory* IToplevelProtocol::LookupSharedMemory(Shmem::id_t aId) {
return mShmemMap.Get(aId);
}
bool IToplevelProtocol::IsTrackingSharedMemory(Shmem::SharedMemory* segment) {
for (const auto& shmem : mShmemMap.Values()) {
if (segment == shmem) {
return true;
}
}
return false;
}
bool IToplevelProtocol::DestroySharedMemory(Shmem& shmem) {
Shmem::id_t aId = shmem.Id(Shmem::PrivateIPDLCaller());
Shmem::SharedMemory* segment = LookupSharedMemory(aId);
if (!segment) {
return false;
}
UniquePtr<Message> descriptor =
shmem.MkDestroyedMessage(Shmem::PrivateIPDLCaller(), MSG_ROUTING_CONTROL);
MOZ_ASSERT(mShmemMap.Contains(aId),
"Attempting to remove an ID not in the shmem map");
mShmemMap.Remove(aId);
Shmem::Dealloc(Shmem::PrivateIPDLCaller(), segment);
MessageChannel* channel = GetIPCChannel();
if (!channel->CanSend()) {
return true;
}
return descriptor && channel->Send(std::move(descriptor));
}
void IToplevelProtocol::DeallocShmems() {
for (const auto& shmem : mShmemMap.Values()) {
Shmem::Dealloc(Shmem::PrivateIPDLCaller(), shmem);
}
mShmemMap.Clear();
}
bool IToplevelProtocol::ShmemCreated(const Message& aMsg) {
Shmem::id_t id;
RefPtr<Shmem::SharedMemory> rawmem(
Shmem::OpenExisting(Shmem::PrivateIPDLCaller(), aMsg, &id, true));
if (!rawmem) {
return false;
}
MOZ_ASSERT(!mShmemMap.Contains(id), "Don't insert with an existing ID");
mShmemMap.InsertOrUpdate(id, rawmem.forget().take());
return true;
}
bool IToplevelProtocol::ShmemDestroyed(const Message& aMsg) {
Shmem::id_t id;
MessageReader reader(aMsg);
if (!IPC::ReadParam(&reader, &id)) {
return false;
}
reader.EndRead();
Shmem::SharedMemory* rawmem = LookupSharedMemory(id);
if (rawmem) {
MOZ_ASSERT(mShmemMap.Contains(id),
"Attempting to remove an ID not in the shmem map");
mShmemMap.Remove(id);
Shmem::Dealloc(Shmem::PrivateIPDLCaller(), rawmem);
}
return true;
}
IPDLResolverInner::IPDLResolverInner(UniquePtr<IPC::Message> aReply,
IProtocol* aActor)
: mReply(std::move(aReply)),

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

@ -73,8 +73,8 @@ enum {
BUILD_IDS_MATCH_MESSAGE_TYPE = kuint16max - 8,
BUILD_ID_MESSAGE_TYPE = kuint16max - 7, // unused
CHANNEL_OPENED_MESSAGE_TYPE = kuint16max - 6,
SHMEM_DESTROYED_MESSAGE_TYPE = kuint16max - 5, // unused
SHMEM_CREATED_MESSAGE_TYPE = kuint16max - 4, // unused
SHMEM_DESTROYED_MESSAGE_TYPE = kuint16max - 5,
SHMEM_CREATED_MESSAGE_TYPE = kuint16max - 4,
GOODBYE_MESSAGE_TYPE = kuint16max - 3,
CANCEL_MESSAGE_TYPE = kuint16max - 2,
@ -198,6 +198,13 @@ class IProtocol : public HasResultCodes {
IProtocol* Lookup(int32_t aId);
void Unregister(int32_t aId);
Shmem::SharedMemory* CreateSharedMemory(size_t aSize,
SharedMemory::SharedMemoryType aType,
bool aUnsafe, int32_t* aId);
Shmem::SharedMemory* LookupSharedMemory(int32_t aId);
bool IsTrackingSharedMemory(Shmem::SharedMemory* aSegment);
bool DestroySharedMemory(Shmem& aShmem);
MessageChannel* GetIPCChannel();
const MessageChannel* GetIPCChannel() const;
@ -392,6 +399,13 @@ class IToplevelProtocol : public IProtocol {
IProtocol* Lookup(int32_t aId);
void Unregister(int32_t aId);
Shmem::SharedMemory* CreateSharedMemory(size_t aSize,
SharedMemory::SharedMemoryType aType,
bool aUnsafe, int32_t* aId);
Shmem::SharedMemory* LookupSharedMemory(int32_t aId);
bool IsTrackingSharedMemory(Shmem::SharedMemory* aSegment);
bool DestroySharedMemory(Shmem& aShmem);
MessageChannel* GetIPCChannel() { return &mChannel; }
const MessageChannel* GetIPCChannel() const { return &mChannel; }
@ -425,6 +439,10 @@ class IToplevelProtocol : public IProtocol {
void SetReplyTimeoutMs(int32_t aTimeoutMs);
void DeallocShmems();
bool ShmemCreated(const Message& aMsg);
bool ShmemDestroyed(const Message& aMsg);
virtual bool ShouldContinueFromReplyTimeout() { return false; }
// WARNING: This function is called with the MessageChannel monitor held.
@ -481,6 +499,7 @@ class IToplevelProtocol : public IProtocol {
// Used to be on mState
int32_t mLastLocalId;
IDMap<IProtocol*> mActorMap;
IDMap<Shmem::SharedMemory*> mShmemMap;
MessageChannel mChannel;
};

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

@ -9,16 +9,176 @@
#include "ProtocolUtils.h"
#include "SharedMemoryBasic.h"
#include "ShmemMessageUtils.h"
#include "chrome/common/ipc_message_utils.h"
#include "mozilla/Unused.h"
namespace mozilla::ipc {
namespace mozilla {
namespace ipc {
class ShmemCreated : public IPC::Message {
private:
typedef Shmem::id_t id_t;
public:
ShmemCreated(int32_t routingId, id_t aIPDLId, size_t aSize,
SharedMemory::SharedMemoryType aType)
: IPC::Message(routingId, SHMEM_CREATED_MESSAGE_TYPE, 0,
HeaderFlags(NESTED_INSIDE_CPOW)) {
MOZ_RELEASE_ASSERT(aSize < std::numeric_limits<uint32_t>::max(),
"Tried to create Shmem with size larger than 4GB");
IPC::MessageWriter writer(*this);
IPC::WriteParam(&writer, aIPDLId);
IPC::WriteParam(&writer, uint32_t(aSize));
IPC::WriteParam(&writer, int32_t(aType));
}
static bool ReadInfo(IPC::MessageReader* aReader, id_t* aIPDLId,
size_t* aSize, SharedMemory::SharedMemoryType* aType) {
uint32_t size = 0;
if (!IPC::ReadParam(aReader, aIPDLId) || !IPC::ReadParam(aReader, &size) ||
!IPC::ReadParam(aReader, reinterpret_cast<int32_t*>(aType))) {
return false;
}
*aSize = size;
return true;
}
void Log(const std::string& aPrefix, FILE* aOutf) const {
fputs("(special ShmemCreated msg)", aOutf);
}
};
class ShmemDestroyed : public IPC::Message {
private:
typedef Shmem::id_t id_t;
public:
ShmemDestroyed(int32_t routingId, id_t aIPDLId)
: IPC::Message(routingId, SHMEM_DESTROYED_MESSAGE_TYPE) {
IPC::MessageWriter writer(*this);
IPC::WriteParam(&writer, aIPDLId);
}
};
static SharedMemory* NewSegment(SharedMemory::SharedMemoryType aType) {
if (SharedMemory::TYPE_BASIC == aType) {
return new SharedMemoryBasic;
} else {
NS_ERROR("unknown Shmem type");
return nullptr;
}
}
static already_AddRefed<SharedMemory> CreateSegment(
SharedMemory::SharedMemoryType aType, size_t aNBytes, size_t aExtraSize) {
RefPtr<SharedMemory> segment = NewSegment(aType);
if (!segment) {
return nullptr;
}
size_t size = SharedMemory::PageAlignedSize(aNBytes + aExtraSize);
if (!segment->Create(size) || !segment->Map(size)) {
return nullptr;
}
return segment.forget();
}
static already_AddRefed<SharedMemory> ReadSegment(
const IPC::Message& aDescriptor, Shmem::id_t* aId, size_t* aNBytes,
size_t aExtraSize) {
if (SHMEM_CREATED_MESSAGE_TYPE != aDescriptor.type()) {
NS_ERROR("expected 'shmem created' message");
return nullptr;
}
SharedMemory::SharedMemoryType type;
IPC::MessageReader reader(aDescriptor);
if (!ShmemCreated::ReadInfo(&reader, aId, aNBytes, &type)) {
return nullptr;
}
RefPtr<SharedMemory> segment = NewSegment(type);
if (!segment) {
return nullptr;
}
if (!segment->ReadHandle(&reader)) {
NS_ERROR("trying to open invalid handle");
return nullptr;
}
reader.EndRead();
size_t size = SharedMemory::PageAlignedSize(*aNBytes + aExtraSize);
if (!segment->Map(size)) {
return nullptr;
}
// close the handle to the segment after it is mapped
segment->CloseHandle();
return segment.forget();
}
static void DestroySegment(SharedMemory* aSegment) {
// the SharedMemory dtor closes and unmaps the actual OS shmem segment
if (aSegment) {
aSegment->Release();
}
}
#if defined(DEBUG)
static const char sMagic[] =
"This little piggy went to market.\n"
"This little piggy stayed at home.\n"
"This little piggy has roast beef,\n"
"This little piggy had none.\n"
"And this little piggy cried \"Wee! Wee! Wee!\" all the way home";
struct Header {
// Don't use size_t or bool here because their size depends on the
// architecture.
uint32_t mSize;
uint32_t mUnsafe;
char mMagic[sizeof(sMagic)];
};
static void GetSections(Shmem::SharedMemory* aSegment, Header** aHeader,
char** aFrontSentinel, char** aData,
char** aBackSentinel) {
MOZ_ASSERT(aSegment && aFrontSentinel && aData && aBackSentinel,
"null param(s)");
*aFrontSentinel = reinterpret_cast<char*>(aSegment->memory());
MOZ_ASSERT(*aFrontSentinel, "null memory()");
*aHeader = reinterpret_cast<Header*>(*aFrontSentinel);
size_t pageSize = Shmem::SharedMemory::SystemPageSize();
*aData = *aFrontSentinel + pageSize;
*aBackSentinel = *aFrontSentinel + aSegment->Size() - pageSize;
}
static Header* GetHeader(Shmem::SharedMemory* aSegment) {
Header* header;
char* dontcare;
GetSections(aSegment, &header, &dontcare, &dontcare, &dontcare);
return header;
}
static void Protect(SharedMemory* aSegment) {
MOZ_ASSERT(aSegment, "null segment");
aSegment->Protect(reinterpret_cast<char*>(aSegment->memory()),
aSegment->Size(), RightsNone);
}
static void Unprotect(SharedMemory* aSegment) {
MOZ_ASSERT(aSegment, "null segment");
aSegment->Protect(reinterpret_cast<char*>(aSegment->memory()),
aSegment->Size(), RightsRead | RightsWrite);
}
//
// In debug builds, we specially allocate shmem segments. The layout
// is as follows
//
// Page 0: "front sentinel"
// [nothing]
// size of mapping
// magic bytes
// Page 1 through n-1:
// user data
// Page n: "back sentinel"
@ -47,6 +207,11 @@ namespace mozilla::ipc {
// The receiving process will then create a Shmem from the underlying
// segment, and take the segment into the "mapped" state.
//
// In the "mapping" state, we use the front sentinel to verify the
// integrity of the shmem segment. If valid, it has a size_t
// containing the number of bytes the user allocated followed by the
// magic bytes above.
//
// In the "mapped" state, the front and back sentinels have no access
// rights. They act as guards against buffer overflows and underflows
// in client code; if clients touch a sentinel, they die with SIGSEGV.
@ -56,164 +221,256 @@ namespace mozilla::ipc {
// to touch the segment, it dies with SIGSEGV.
//
static size_t MappingSize(size_t aNBytes) {
Shmem::Shmem(PrivateIPDLCaller, SharedMemory* aSegment, id_t aId)
: mSegment(aSegment), mData(nullptr), mSize(0) {
MOZ_ASSERT(mSegment, "null segment");
MOZ_ASSERT(aId != 0, "invalid ID");
Unprotect(mSegment);
Header* header;
char* frontSentinel;
char* data;
char* backSentinel;
GetSections(aSegment, &header, &frontSentinel, &data, &backSentinel);
// do a quick validity check to avoid weird-looking crashes in libc
char check = *frontSentinel;
(void)check;
MOZ_ASSERT(!strncmp(header->mMagic, sMagic, sizeof(sMagic)),
"invalid segment");
mSize = static_cast<size_t>(header->mSize);
size_t pageSize = SharedMemory::SystemPageSize();
MOZ_ASSERT(IsPowerOfTwo(pageSize));
MOZ_ASSERT(mSegment->Size() - (2 * pageSize) >= mSize,
"illegal size in shared memory segment");
// Extra padding required to align to pagesize.
size_t diff = aNBytes & (pageSize - 1);
diff = (pageSize - diff) & (pageSize - 1);
// transition into the "mapped" state by protecting the front and
// back sentinels (which guard against buffer under/overflows)
mSegment->Protect(frontSentinel, pageSize, RightsNone);
mSegment->Protect(backSentinel, pageSize, RightsNone);
CheckedInt<size_t> totalSize = aNBytes;
totalSize += diff;
#ifdef DEBUG
// Allocate 2 extra pages to act as guard pages for the shared memory region
// in debug-mode. No extra space is allocated in release mode.
totalSize += 2 * pageSize;
#endif
MOZ_RELEASE_ASSERT(totalSize.isValid());
return totalSize.value();
// don't set these until we know they're valid
mData = data;
mId = aId;
}
static Span<char> ConfigureAndGetData(SharedMemory* aSegment) {
Span<char> memory{reinterpret_cast<char*>(aSegment->memory()),
aSegment->Size()};
#ifdef DEBUG
size_t pageSize = SharedMemory::SystemPageSize();
auto [frontSentinel, suffix] = memory.SplitAt(pageSize);
auto [data, backSentinel] = memory.SplitAt(suffix.Length() - pageSize);
// The sentinel memory regions should be non-readable and non-writable,
// whereas the data region needs to be readable & writable.
aSegment->Protect(frontSentinel.data(), frontSentinel.size(), RightsNone);
aSegment->Protect(data.data(), data.size(), RightsRead | RightsWrite);
aSegment->Protect(backSentinel.data(), backSentinel.size(), RightsNone);
return data;
#else
return memory;
#endif
}
static RefPtr<SharedMemory> AllocSegment(size_t aNBytes,
SharedMemory::SharedMemoryType aType) {
size_t mappingSize = MappingSize(aNBytes);
MOZ_RELEASE_ASSERT(aType == SharedMemory::TYPE_BASIC,
"Unknown SharedMemoryType!");
RefPtr<SharedMemory> segment = new SharedMemoryBasic;
if (!segment->Create(mappingSize) || !segment->Map(mappingSize)) {
NS_WARNING("Failed to create or map segment");
return nullptr;
}
return segment;
}
Shmem::Shmem(size_t aNBytes, SharedMemoryType aType, bool aUnsafe)
: Shmem(AllocSegment(aNBytes, aType), aNBytes, aUnsafe) {}
Shmem::Shmem(RefPtr<SharedMemory> aSegment, size_t aNBytes, bool aUnsafe) {
if (!aSegment) {
return;
}
size_t mappingSize = MappingSize(aNBytes);
if (mappingSize != aSegment->Size()) {
NS_WARNING("Segment has an incorrect size");
return;
}
auto data = ConfigureAndGetData(aSegment);
MOZ_RELEASE_ASSERT(data.size() >= aNBytes);
mSegment = aSegment;
mData = data.data();
mSize = aNBytes;
#ifdef DEBUG
mUnsafe = aUnsafe;
#endif
}
#ifdef DEBUG
void Shmem::AssertInvariants() const {
MOZ_ASSERT(mSegment, "null segment");
MOZ_ASSERT(mData, "null data pointer");
MOZ_ASSERT(mSize > 0, "invalid size");
MOZ_ASSERT(MappingSize(mSize) == mSegment->Size(),
"size doesn't match segment");
// if the segment isn't owned by the current process, these will
// trigger SIGSEGV
*reinterpret_cast<volatile char*>(mData);
*(reinterpret_cast<volatile char*>(mData) + mSize - 1);
char checkMappingFront = *reinterpret_cast<char*>(mData);
char checkMappingBack = *(reinterpret_cast<char*>(mData) + mSize - 1);
// avoid "unused" warnings for these variables:
Unused << checkMappingFront;
Unused << checkMappingBack;
}
void Shmem::RevokeRights() {
void Shmem::RevokeRights(PrivateIPDLCaller) {
AssertInvariants();
// If we're not working with an "unsafe" shared memory, revoke access rights
// to the entire shared memory region.
if (!mUnsafe) {
mSegment->Protect(reinterpret_cast<char*>(mSegment->memory()),
mSegment->Size(), RightsNone);
size_t pageSize = SharedMemory::SystemPageSize();
Header* header = GetHeader(mSegment);
// Open this up for reading temporarily
mSegment->Protect(reinterpret_cast<char*>(header), pageSize, RightsRead);
if (!header->mUnsafe) {
Protect(mSegment);
} else {
mSegment->Protect(reinterpret_cast<char*>(header), pageSize, RightsNone);
}
*this = Shmem();
}
#endif
} // namespace mozilla::ipc
namespace IPC {
void ParamTraits<mozilla::ipc::Shmem>::Write(MessageWriter* aWriter,
paramType&& aParam) {
aParam.AssertInvariants();
MOZ_ASSERT(aParam.mSegment->Type() ==
mozilla::ipc::SharedMemory::SharedMemoryType::TYPE_BASIC,
"Only supported type is TYPE_BASIC");
WriteParam(aWriter, uint64_t(aParam.mSize));
aParam.mSegment->WriteHandle(aWriter);
#ifdef DEBUG
WriteParam(aWriter, aParam.mUnsafe);
#endif
aParam.RevokeRights();
}
bool ParamTraits<mozilla::ipc::Shmem>::Read(MessageReader* aReader,
paramType* aResult) {
*aResult = mozilla::ipc::Shmem();
// static
already_AddRefed<Shmem::SharedMemory> Shmem::Alloc(PrivateIPDLCaller,
size_t aNBytes,
SharedMemoryType aType,
bool aUnsafe,
bool aProtect) {
NS_ASSERTION(aNBytes <= UINT32_MAX, "Will truncate shmem segment size!");
MOZ_ASSERT(!aProtect || !aUnsafe, "protect => !unsafe");
// Size is sent as uint64_t to deal with IPC between processes with different
// `size_t` sizes.
uint64_t rawSize = 0;
if (!ReadParam(aReader, &rawSize)) {
return false;
}
mozilla::CheckedInt<size_t> size{rawSize};
if (!size.isValid() || size == 0) {
return false;
size_t pageSize = SharedMemory::SystemPageSize();
// |2*pageSize| is for the front and back sentinel
RefPtr<SharedMemory> segment = CreateSegment(aType, aNBytes, 2 * pageSize);
if (!segment) {
return nullptr;
}
RefPtr<mozilla::ipc::SharedMemory> segment =
new mozilla::ipc::SharedMemoryBasic;
if (!segment->ReadHandle(aReader) ||
!segment->Map(mozilla::ipc::MappingSize(size.value()))) {
return false;
}
Header* header;
char* frontSentinel;
char* data;
char* backSentinel;
GetSections(segment, &header, &frontSentinel, &data, &backSentinel);
bool unsafe = false;
#ifdef DEBUG
if (!ReadParam(aReader, &unsafe)) {
return false;
}
#endif
// initialize the segment with Shmem-internal information
*aResult = mozilla::ipc::Shmem(segment, size.value(), unsafe);
return aResult->IsValid();
// NB: this can't be a static assert because technically pageSize
// isn't known at compile time, event though in practice it's always
// going to be 4KiB
MOZ_ASSERT(sizeof(Header) <= pageSize, "Shmem::Header has gotten too big");
memcpy(header->mMagic, sMagic, sizeof(sMagic));
header->mSize = static_cast<uint32_t>(aNBytes);
header->mUnsafe = aUnsafe;
if (aProtect) Protect(segment);
return segment.forget();
}
} // namespace IPC
// static
already_AddRefed<Shmem::SharedMemory> Shmem::OpenExisting(
PrivateIPDLCaller, const IPC::Message& aDescriptor, id_t* aId,
bool aProtect) {
size_t size;
size_t pageSize = SharedMemory::SystemPageSize();
// |2*pageSize| is for the front and back sentinels
RefPtr<SharedMemory> segment =
ReadSegment(aDescriptor, aId, &size, 2 * pageSize);
if (!segment) {
return nullptr;
}
Header* header = GetHeader(segment);
if (size != header->mSize) {
// Deallocation should zero out the header, so check for that.
if (header->mSize || header->mUnsafe || header->mMagic[0] ||
memcmp(header->mMagic, &header->mMagic[1],
sizeof(header->mMagic) - 1)) {
NS_ERROR("Wrong size for this Shmem!");
} else {
NS_WARNING("Shmem was deallocated");
}
return nullptr;
}
// The caller of this function may not know whether the segment is
// unsafe or not
if (!header->mUnsafe && aProtect) Protect(segment);
return segment.forget();
}
// static
void Shmem::Dealloc(PrivateIPDLCaller, SharedMemory* aSegment) {
if (!aSegment) return;
size_t pageSize = SharedMemory::SystemPageSize();
Header* header;
char* frontSentinel;
char* data;
char* backSentinel;
GetSections(aSegment, &header, &frontSentinel, &data, &backSentinel);
aSegment->Protect(frontSentinel, pageSize, RightsWrite | RightsRead);
memset(header->mMagic, 0, sizeof(sMagic));
header->mSize = 0;
header->mUnsafe = false; // make it "safe" so as to catch errors
DestroySegment(aSegment);
}
#else // !defined(DEBUG)
Shmem::Shmem(PrivateIPDLCaller, SharedMemory* aSegment, id_t aId)
: mSegment(aSegment), mData(aSegment->memory()), mSize(0), mId(aId) {
mSize = static_cast<size_t>(*PtrToSize(mSegment));
MOZ_RELEASE_ASSERT(mSegment->Size() - sizeof(uint32_t) >= mSize,
"illegal size in shared memory segment");
}
// static
already_AddRefed<Shmem::SharedMemory> Shmem::Alloc(PrivateIPDLCaller,
size_t aNBytes,
SharedMemoryType aType,
bool /*unused*/,
bool /*unused*/) {
RefPtr<SharedMemory> segment =
CreateSegment(aType, aNBytes, sizeof(uint32_t));
if (!segment) {
return nullptr;
}
*PtrToSize(segment) = static_cast<uint32_t>(aNBytes);
return segment.forget();
}
// static
already_AddRefed<Shmem::SharedMemory> Shmem::OpenExisting(
PrivateIPDLCaller, const IPC::Message& aDescriptor, id_t* aId,
bool /*unused*/) {
size_t size;
RefPtr<SharedMemory> segment =
ReadSegment(aDescriptor, aId, &size, sizeof(uint32_t));
if (!segment) {
return nullptr;
}
// this is the only validity check done in non-DEBUG builds
if (size != static_cast<size_t>(*PtrToSize(segment))) {
return nullptr;
}
return segment.forget();
}
// static
void Shmem::Dealloc(PrivateIPDLCaller, SharedMemory* aSegment) {
DestroySegment(aSegment);
}
#endif // if defined(DEBUG)
UniquePtr<IPC::Message> Shmem::MkCreatedMessage(PrivateIPDLCaller,
int32_t routingId) {
AssertInvariants();
auto msg = MakeUnique<ShmemCreated>(routingId, mId, mSize, mSegment->Type());
IPC::MessageWriter writer(*msg);
if (!mSegment->WriteHandle(&writer)) {
return nullptr;
}
// close the handle to the segment after it is shared
mSegment->CloseHandle();
return msg;
}
UniquePtr<IPC::Message> Shmem::MkDestroyedMessage(PrivateIPDLCaller,
int32_t routingId) {
AssertInvariants();
return MakeUnique<ShmemDestroyed>(routingId, mId);
}
void IPDLParamTraits<Shmem>::Write(IPC::MessageWriter* aWriter,
IProtocol* aActor, Shmem&& aParam) {
WriteIPDLParam(aWriter, aActor, aParam.mId);
aParam.RevokeRights(Shmem::PrivateIPDLCaller());
aParam.forget(Shmem::PrivateIPDLCaller());
}
bool IPDLParamTraits<Shmem>::Read(IPC::MessageReader* aReader,
IProtocol* aActor, paramType* aResult) {
paramType::id_t id;
if (!ReadIPDLParam(aReader, aActor, &id)) {
return false;
}
Shmem::SharedMemory* rawmem = aActor->LookupSharedMemory(id);
if (rawmem) {
*aResult = Shmem(Shmem::PrivateIPDLCaller(), rawmem, id);
return true;
}
*aResult = Shmem();
return true;
}
} // namespace ipc
} // namespace mozilla

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

@ -19,62 +19,91 @@
#include "mozilla/Range.h"
#include "mozilla/UniquePtr.h"
namespace IPC {
template <typename T>
struct ParamTraits;
}
/**
* |Shmem| is one agent in the IPDL shared memory scheme. The way it
works is essentially
*
* (1) C++ code calls, say, |parentActor->AllocShmem(size)|
namespace mozilla::ipc {
* (2) IPDL-generated code creates a |mozilla::ipc::SharedMemory|
* wrapping the bare OS shmem primitives. The code then adds the new
* SharedMemory to the set of shmem segments being managed by IPDL.
*
* (3) IPDL-generated code "shares" the new SharedMemory to the child
* process, and then sends a special asynchronous IPC message to the
* child notifying it of the creation of the segment. (What this
* means is OS specific.)
*
* (4a) The child receives the special IPC message, and using the
* |SharedMemory{Basic}::Handle| it was passed, creates a
* |mozilla::ipc::SharedMemory| in the child
* process.
*
* (4b) After sending the "shmem-created" IPC message, IPDL-generated
* code in the parent returns a |mozilla::ipc::Shmem| back to the C++
* caller of |parentActor->AllocShmem()|. The |Shmem| is a "weak
* reference" to the underlying |SharedMemory|, which is managed by
* IPDL-generated code. C++ consumers of |Shmem| can't get at the
* underlying |SharedMemory|.
*
* If parent code wants to give access rights to the Shmem to the
* child, it does so by sending its |Shmem| to the child, in an IPDL
* message. The parent's |Shmem| then "dies", i.e. becomes
* inaccessible. This process could be compared to passing a
* "shmem-access baton" between parent and child.
*/
namespace mozilla {
namespace layers {
class ShadowLayerForwarder;
} // namespace layers
namespace ipc {
template <typename P>
struct IPDLParamTraits;
// A `Shmem` is a wrapper around OS-level shared memory. It comes in two major
// modes: safe and unsafe.
//
// Access to the memory within a "safe" shmem is conceptually transferred to the
// remote process when the shmem is transferred by marking the mapped memory in
// the sending process as inaccessable (debug-mode only). Code should not
// attempt to access the shmem after it has been transferred, though it will
// remain mapped until all other Shmem references have been dropped.
//
// Access to the memory within an "unsafe" shmem is not protected in the same
// way, and can be shared between multiple processes.
//
// For now, the primary way to create a `Shmem` is to call the
// `IProtocol::AllocShmem` or `IProtocol::AllocUnsafeShmem` methods on an IPDL
// actor, however this requirement may be relaxed in the future.
class Shmem final {
friend struct IPC::ParamTraits<mozilla::ipc::Shmem>;
friend struct IPDLParamTraits<mozilla::ipc::Shmem>;
#ifdef DEBUG
// For ShadowLayerForwarder::CheckSurfaceDescriptor
friend class mozilla::layers::ShadowLayerForwarder;
#endif
public:
typedef int32_t id_t;
// Low-level wrapper around platform shmem primitives.
typedef mozilla::ipc::SharedMemory SharedMemory;
typedef SharedMemory::SharedMemoryType SharedMemoryType;
// Shmem objects should only be constructed directly from SharedMemory
// objects by the Shmem implementation itself, or by a select few functions
// in ProtocolUtils.{h,cpp}. You should not need to add new instances of
// this token.
struct PrivateIPDLCaller {};
Shmem() = default;
Shmem() : mSegment(nullptr), mData(nullptr), mSize(0), mId(0) {}
// Allocates a brand new shared memory region with sufficient size for
// `aNBytes` bytes. This region may be transferred to other processes by being
// sent over an IPDL actor.
Shmem(size_t aNBytes, SharedMemoryType aType, bool aUnsafe);
// Create a reference to an existing shared memory region. The segment must be
// large enough for `aNBytes`.
Shmem(RefPtr<SharedMemory> aSegment, size_t aNBytes, bool aUnsafe);
// NOTE: Some callers are broken if a move constructor is provided here.
Shmem(const Shmem& aOther) = default;
Shmem(PrivateIPDLCaller, SharedMemory* aSegment, id_t aId);
~Shmem() {
// Shmem only holds a "weak ref" to the actual segment, which is
// owned by IPDL. So there's nothing interesting to be done here
forget(PrivateIPDLCaller());
}
Shmem& operator=(const Shmem& aRhs) = default;
bool operator==(const Shmem& aRhs) const { return mSegment == aRhs.mSegment; }
bool operator!=(const Shmem& aRhs) const { return mSegment != aRhs.mSegment; }
// Returns whether this Shmem is valid, and contains a reference to internal
// state.
bool IsValid() const { return mSegment != nullptr; }
// Returns whether this Shmem is writable by you, and thus whether you can
// transfer writability to another actor.
bool IsWritable() const { return mSegment != nullptr; }
// Legacy names for `IsValid()` - do _NOT_ actually confirm whether or not you
// should be able to write to or read from this type.
bool IsWritable() const { return IsValid(); }
bool IsReadable() const { return IsValid(); }
// Returns whether this Shmem is readable by you, and thus whether you can
// transfer readability to another actor.
bool IsReadable() const { return mSegment != nullptr; }
// Return a pointer to the user-visible data segment.
template <typename T>
@ -102,36 +131,80 @@ class Shmem final {
return {get<T>(), Size<T>()};
}
// For safe shmems in debug mode, immediately revoke all access rights to the
// memory when deallocating it.
// Also resets this particular `Shmem` instance to an invalid state.
// These shouldn't be used directly, use the IPDL interface instead.
id_t Id(PrivateIPDLCaller) const { return mId; }
SharedMemory* Segment(PrivateIPDLCaller) const { return mSegment; }
#ifndef DEBUG
void RevokeRights() { *this = Shmem(); }
void RevokeRights(PrivateIPDLCaller) {}
#else
void RevokeRights();
void RevokeRights(PrivateIPDLCaller);
#endif
void forget(PrivateIPDLCaller) {
mSegment = nullptr;
mData = nullptr;
mSize = 0;
mId = 0;
}
static already_AddRefed<Shmem::SharedMemory> Alloc(PrivateIPDLCaller,
size_t aNBytes,
SharedMemoryType aType,
bool aUnsafe,
bool aProtect = false);
// Prepare this to be shared with another process. Return an IPC message that
// contains enough information for the other process to map this segment in
// OpenExisting() below. Return a new message if successful (owned by the
// caller), nullptr if not.
UniquePtr<IPC::Message> MkCreatedMessage(PrivateIPDLCaller,
int32_t routingId);
// Stop sharing this with another process. Return an IPC message that
// contains enough information for the other process to unmap this
// segment. Return a new message if successful (owned by the
// caller), nullptr if not.
UniquePtr<IPC::Message> MkDestroyedMessage(PrivateIPDLCaller,
int32_t routingId);
// Return a SharedMemory instance in this process using the descriptor shared
// to us by the process that created the underlying OS shmem resource. The
// contents of the descriptor depend on the type of SharedMemory that was
// passed to us.
static already_AddRefed<SharedMemory> OpenExisting(
PrivateIPDLCaller, const IPC::Message& aDescriptor, id_t* aId,
bool aProtect = false);
static void Dealloc(PrivateIPDLCaller, SharedMemory* aSegment);
private:
template <typename T>
void AssertAligned() const {
MOZ_RELEASE_ASSERT(0 == (mSize % sizeof(T)),
"shmem size is not a multiple of sizeof(T)");
if (0 != (mSize % sizeof(T))) MOZ_CRASH("shmem is not T-aligned");
}
#ifndef DEBUG
#if !defined(DEBUG)
void AssertInvariants() const {}
static uint32_t* PtrToSize(SharedMemory* aSegment) {
char* endOfSegment =
reinterpret_cast<char*>(aSegment->memory()) + aSegment->Size();
return reinterpret_cast<uint32_t*>(endOfSegment - sizeof(uint32_t));
}
#else
void AssertInvariants() const;
#endif
RefPtr<SharedMemory> mSegment;
void* mData = nullptr;
size_t mSize = 0;
#ifdef DEBUG
bool mUnsafe = false;
#endif
void* mData;
size_t mSize;
id_t mId;
};
} // namespace mozilla::ipc
} // namespace ipc
} // namespace mozilla
#endif // ifndef mozilla_ipc_Shmem_h

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

@ -11,20 +11,24 @@
#include "mozilla/ipc/IPDLParamTraits.h"
#include "mozilla/ipc/Shmem.h"
namespace IPC {
namespace mozilla {
namespace ipc {
template <>
struct ParamTraits<mozilla::ipc::Shmem> {
typedef mozilla::ipc::Shmem paramType;
struct IPDLParamTraits<Shmem> {
typedef Shmem paramType;
static void Write(IPC::MessageWriter* aWriter, paramType&& aParam);
static bool Read(IPC::MessageReader* aReader, paramType* aResult);
static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor,
paramType&& aParam);
static bool Read(IPC::MessageReader* aReader, IProtocol* aActor,
paramType* aResult);
static void Log(const paramType& aParam, std::wstring* aLog) {
aLog->append(L"(shmem segment)");
}
};
} // namespace IPC
} // namespace ipc
} // namespace mozilla
#endif // ifndef mozilla_ipc_ShmemMessageUtils_h

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

@ -4227,6 +4227,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
"""
DestroySubtree(NormalShutdown);
ClearSubtree();
DeallocShmems();
if (GetLifecycleProxy()) {
GetLifecycleProxy()->Release();
}
@ -4242,6 +4243,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
"""
DestroySubtree(AbnormalShutdown);
ClearSubtree();
DeallocShmems();
if (GetLifecycleProxy()) {
GetLifecycleProxy()->Release();
}
@ -4371,6 +4373,32 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
methods = []
if p.decl.type.isToplevel():
# "private" message that passes shmem mappings from one process
# to the other
if p.subtreeUsesShmem():
self.asyncSwitch.addcase(
CaseLabel("SHMEM_CREATED_MESSAGE_TYPE"),
self.genShmemCreatedHandler(),
)
self.asyncSwitch.addcase(
CaseLabel("SHMEM_DESTROYED_MESSAGE_TYPE"),
self.genShmemDestroyedHandler(),
)
else:
abort = StmtBlock()
abort.addstmts(
[
_fatalError("this protocol tree does not use shmem"),
StmtReturn(_Result.NotKnown),
]
)
self.asyncSwitch.addcase(CaseLabel("SHMEM_CREATED_MESSAGE_TYPE"), abort)
self.asyncSwitch.addcase(
CaseLabel("SHMEM_DESTROYED_MESSAGE_TYPE"), abort
)
# Keep track of types created with an INOUT ctor. We need to call
# Register() or RegisterID() for them depending on the side the managee
# is created.
@ -4489,6 +4517,36 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
return methods + [removemanagee, deallocmanagee, Whitespace.NL]
def genShmemCreatedHandler(self):
assert self.protocol.decl.type.isToplevel()
return StmtCode(
"""
{
if (!ShmemCreated(${msgvar})) {
return MsgPayloadError;
}
return MsgProcessed;
}
""",
msgvar=self.msgvar,
)
def genShmemDestroyedHandler(self):
assert self.protocol.decl.type.isToplevel()
return StmtCode(
"""
{
if (!ShmemDestroyed(${msgvar})) {
return MsgPayloadError;
}
return MsgProcessed;
}
""",
msgvar=self.msgvar,
)
# -------------------------------------------------------------------------
# The next few functions are the crux of the IPDL code generator.
# They generate code for all the nasty work of message

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

@ -8107,23 +8107,28 @@ bool nsLayoutUtils::GetContentViewerSize(
bool nsLayoutUtils::UpdateCompositionBoundsForRCDRSF(
ParentLayerRect& aCompBounds, const nsPresContext* aPresContext) {
SubtractDynamicToolbar shouldSubtractDynamicToolbar =
SubtractDynamicToolbar::Yes;
aPresContext->IsRootContentDocumentCrossProcess() &&
aPresContext->HasDynamicToolbar()
? SubtractDynamicToolbar::Yes
: SubtractDynamicToolbar::No;
if (RefPtr<MobileViewportManager> MVM =
aPresContext->PresShell()->GetMobileViewportManager()) {
CSSSize intrinsicCompositionSize = MVM->GetIntrinsicCompositionSize();
if (shouldSubtractDynamicToolbar == SubtractDynamicToolbar::Yes) {
if (RefPtr<MobileViewportManager> MVM =
aPresContext->PresShell()->GetMobileViewportManager()) {
CSSSize intrinsicCompositionSize = MVM->GetIntrinsicCompositionSize();
if (nsIScrollableFrame* rootScrollableFrame =
aPresContext->PresShell()->GetRootScrollFrameAsScrollable()) {
// Expand the composition size to include the area initially covered by
// the dynamic toolbar only if the content is taller than the intrinsic
// composition size (i.e. the dynamic toolbar should be able to move only
// if the content is vertically scrollable).
if (intrinsicCompositionSize.height <
CSSPixel::FromAppUnits(
CalculateScrollableRectForFrame(rootScrollableFrame, nullptr)
.Height())) {
shouldSubtractDynamicToolbar = SubtractDynamicToolbar::No;
if (nsIScrollableFrame* rootScrollableFrame =
aPresContext->PresShell()->GetRootScrollFrameAsScrollable()) {
// Expand the composition size to include the area initially covered by
// the dynamic toolbar only if the content is taller than the intrinsic
// composition size (i.e. the dynamic toolbar should be able to move
// only if the content is vertically scrollable).
if (intrinsicCompositionSize.height <
CSSPixel::FromAppUnits(
CalculateScrollableRectForFrame(rootScrollableFrame, nullptr)
.Height())) {
shouldSubtractDynamicToolbar = SubtractDynamicToolbar::No;
}
}
}
}

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

@ -761,8 +761,10 @@ bool SVGIntegrationUtils::PaintMask(const PaintFramesParams& aParams,
ctx.SetDeviceColor(DeviceColor::MaskOpaqueWhite());
RefPtr<Path> path = CSSClipPathInstance::CreateClipPathForFrame(
ctx.GetDrawTarget(), frame, mat);
ctx.SetPath(path);
ctx.Fill();
if (path) {
ctx.SetPath(path);
ctx.Fill();
}
return true;
}

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

@ -20,10 +20,11 @@ class GeckoViewPromptChild extends GeckoViewActorChild {
debug`handleEvent: ${type}`;
switch (type) {
case "mozshowdropdown": // fall-through
case "mozshowdropdown-sourcetouch": // fall-through
case "click": // fall-through
case "contextmenu": // fall-through
case "MozOpenDateTimePicker":
case "mozshowdropdown":
case "mozshowdropdown-sourcetouch":
case "click":
case "contextmenu":
case "DOMPopupBlocked":
Services.prompt.wrappedJSObject.handleEvent(event);
}

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

@ -31,6 +31,9 @@ class PromptFactory {
case "mozshowdropdown-sourcetouch":
this._handleSelect(aEvent.composedTarget, /* aIsDropDown = */ true);
break;
case "MozOpenDateTimePicker":
this._handleDateTime(aEvent.composedTarget);
break;
case "click":
this._handleClick(aEvent);
break;
@ -80,7 +83,7 @@ class PromptFactory {
type === "time" ||
type === "datetime-local"
) {
this._handleDateTime(target, type);
this._handleDateTime(target);
aEvent.preventDefault();
}
}
@ -179,12 +182,12 @@ class PromptFactory {
);
}
_handleDateTime(aElement, aType) {
_handleDateTime(aElement) {
const prompt = new GeckoViewPrompter(aElement.ownerGlobal);
prompt.asyncShowPrompt(
{
type: "datetime",
mode: aType,
mode: aElement.type,
value: aElement.value,
min: aElement.min,
max: aElement.max,

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

@ -44,6 +44,7 @@ const JSWINDOWACTORS = {
contextmenu: { capture: false, mozSystemGroup: true },
mozshowdropdown: {},
"mozshowdropdown-sourcetouch": {},
MozOpenDateTimePicker: {},
DOMPopupBlocked: { capture: false, mozSystemGroup: true },
},
},

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

@ -1337,12 +1337,12 @@ class AccessibilityTest : BaseSessionTest() {
// TODO: Bug 1758540
assumeThat(sessionRule.env.isFission, equalTo(false))
testAccessibilityFocusIframe(REMOTE_IFRAME);
testIframeTree(REMOTE_IFRAME);
}
@Setting(key = Setting.Key.FULL_ACCESSIBILITY_TREE, value = "true")
@Test fun testLocalIframeTree() {
testAccessibilityFocusIframe(LOCAL_IFRAME);
testIframeTree(LOCAL_IFRAME);
}
@Setting(key = Setting.Key.FULL_ACCESSIBILITY_TREE, value = "true")

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

@ -489,21 +489,21 @@ class PromptDelegateTest : BaseSessionTest() {
equalTo("#123456"))
}
@Ignore // TODO: Figure out weird test env behavior here.
@WithDisplay(width = 100, height = 100)
@Test fun dateTest() {
sessionRule.setPrefsUntilTestEnd(mapOf("dom.disable_open_during_load" to false))
mainSession.loadTestPath(PROMPT_HTML_PATH)
mainSession.waitForPageStop()
mainSession.evaluateJS("document.getElementById('dateexample').click();")
mainSession.synthesizeTap(1, 1) // Provides user activation.
sessionRule.waitUntilCalled(object : PromptDelegate {
sessionRule.delegateDuringNextWait(object : PromptDelegate {
@AssertCalled(count = 1)
override fun onDateTimePrompt(session: GeckoSession, prompt: PromptDelegate.DateTimePrompt): GeckoResult<PromptDelegate.PromptResponse> {
return GeckoResult.fromValue(prompt.dismiss())
}
})
mainSession.waitForJS("document.getElementById('dateexample').showPicker();")
}
@Test fun fileTest() {

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

@ -2670,6 +2670,12 @@
value: true
mirror: always
# Is support for HTMLInputElement.showPicker enabled?
- name: dom.input.showPicker
type: bool
value: true
mirror: always
# Whether to allow or disallow web apps to cancel `beforeinput` events caused
# by MozEditableElement#setUserInput() which is used by autocomplete, autofill
# and password manager.

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

@ -740,78 +740,6 @@ prefs: [dom.security.featurePolicy.experimental.enabled:true, dom.security.featu
[HTMLMetaElement interface: document.createElement("meta") must inherit property "media" with the proper type]
expected: FAIL
[HTMLInputElement interface: operation showPicker()]
expected: FAIL
[HTMLInputElement interface: document.createElement("input") must inherit property "showPicker()" with the proper type]
expected: FAIL
[HTMLInputElement interface: createInput("text") must inherit property "showPicker()" with the proper type]
expected: FAIL
[HTMLInputElement interface: createInput("hidden") must inherit property "showPicker()" with the proper type]
expected: FAIL
[HTMLInputElement interface: createInput("search") must inherit property "showPicker()" with the proper type]
expected: FAIL
[HTMLInputElement interface: createInput("tel") must inherit property "showPicker()" with the proper type]
expected: FAIL
[HTMLInputElement interface: createInput("url") must inherit property "showPicker()" with the proper type]
expected: FAIL
[HTMLInputElement interface: createInput("email") must inherit property "showPicker()" with the proper type]
expected: FAIL
[HTMLInputElement interface: createInput("password") must inherit property "showPicker()" with the proper type]
expected: FAIL
[HTMLInputElement interface: createInput("date") must inherit property "showPicker()" with the proper type]
expected: FAIL
[HTMLInputElement interface: createInput("month") must inherit property "showPicker()" with the proper type]
expected: FAIL
[HTMLInputElement interface: createInput("week") must inherit property "showPicker()" with the proper type]
expected: FAIL
[HTMLInputElement interface: createInput("time") must inherit property "showPicker()" with the proper type]
expected: FAIL
[HTMLInputElement interface: createInput("datetime-local") must inherit property "showPicker()" with the proper type]
expected: FAIL
[HTMLInputElement interface: createInput("number") must inherit property "showPicker()" with the proper type]
expected: FAIL
[HTMLInputElement interface: createInput("range") must inherit property "showPicker()" with the proper type]
expected: FAIL
[HTMLInputElement interface: createInput("color") must inherit property "showPicker()" with the proper type]
expected: FAIL
[HTMLInputElement interface: createInput("checkbox") must inherit property "showPicker()" with the proper type]
expected: FAIL
[HTMLInputElement interface: createInput("radio") must inherit property "showPicker()" with the proper type]
expected: FAIL
[HTMLInputElement interface: createInput("file") must inherit property "showPicker()" with the proper type]
expected: FAIL
[HTMLInputElement interface: createInput("submit") must inherit property "showPicker()" with the proper type]
expected: FAIL
[HTMLInputElement interface: createInput("image") must inherit property "showPicker()" with the proper type]
expected: FAIL
[HTMLInputElement interface: createInput("reset") must inherit property "showPicker()" with the proper type]
expected: FAIL
[HTMLInputElement interface: createInput("button") must inherit property "showPicker()" with the proper type]
expected: FAIL
[HTMLLinkElement interface: attribute blocking]
expected: FAIL

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

@ -1,6 +1,4 @@
[show-picker-cross-origin-iframe.html]
[Test showPicker() called from cross-origin iframe 1]
expected: FAIL
[Test showPicker() called from cross-origin iframe 3]
expected: FAIL
disabled:
if tsan: https://bugzilla.mozilla.org/show_bug.cgi?id=1745005
if asan: https://bugzilla.mozilla.org/show_bug.cgi?id=1745005

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

@ -1,132 +0,0 @@
[show-picker-disabled-readonly.html]
[input[type=button\] showPicker() throws when disabled]
expected: FAIL
[input[type=checkbox\] showPicker() throws when disabled]
expected: FAIL
[input[type=color\] showPicker() throws when disabled]
expected: FAIL
[input[type=date\] showPicker() throws when disabled]
expected: FAIL
[input[type=datetime-local\] showPicker() throws when disabled]
expected: FAIL
[input[type=email\] showPicker() throws when disabled]
expected: FAIL
[input[type=file\] showPicker() throws when disabled]
expected: FAIL
[input[type=hidden\] showPicker() throws when disabled]
expected: FAIL
[input[type=image\] showPicker() throws when disabled]
expected: FAIL
[input[type=month\] showPicker() throws when disabled]
expected: FAIL
[input[type=number\] showPicker() throws when disabled]
expected: FAIL
[input[type=password\] showPicker() throws when disabled]
expected: FAIL
[input[type=radio\] showPicker() throws when disabled]
expected: FAIL
[input[type=range\] showPicker() throws when disabled]
expected: FAIL
[input[type=reset\] showPicker() throws when disabled]
expected: FAIL
[input[type=search\] showPicker() throws when disabled]
expected: FAIL
[input[type=submit\] showPicker() throws when disabled]
expected: FAIL
[input[type=tel\] showPicker() throws when disabled]
expected: FAIL
[input[type=text\] showPicker() throws when disabled]
expected: FAIL
[input[type=time\] showPicker() throws when disabled]
expected: FAIL
[input[type=url\] showPicker() throws when disabled]
expected: FAIL
[input[type=week\] showPicker() throws when disabled]
expected: FAIL
[input[type=button\] showPicker() throws when readonly]
expected: FAIL
[input[type=checkbox\] showPicker() throws when readonly]
expected: FAIL
[input[type=color\] showPicker() throws when readonly]
expected: FAIL
[input[type=date\] showPicker() throws when readonly]
expected: FAIL
[input[type=datetime-local\] showPicker() throws when readonly]
expected: FAIL
[input[type=email\] showPicker() throws when readonly]
expected: FAIL
[input[type=file\] showPicker() throws when readonly]
expected: FAIL
[input[type=hidden\] showPicker() throws when readonly]
expected: FAIL
[input[type=image\] showPicker() throws when readonly]
expected: FAIL
[input[type=month\] showPicker() throws when readonly]
expected: FAIL
[input[type=number\] showPicker() throws when readonly]
expected: FAIL
[input[type=password\] showPicker() throws when readonly]
expected: FAIL
[input[type=radio\] showPicker() throws when readonly]
expected: FAIL
[input[type=range\] showPicker() throws when readonly]
expected: FAIL
[input[type=reset\] showPicker() throws when readonly]
expected: FAIL
[input[type=search\] showPicker() throws when readonly]
expected: FAIL
[input[type=submit\] showPicker() throws when readonly]
expected: FAIL
[input[type=tel\] showPicker() throws when readonly]
expected: FAIL
[input[type=text\] showPicker() throws when readonly]
expected: FAIL
[input[type=time\] showPicker() throws when readonly]
expected: FAIL
[input[type=url\] showPicker() throws when readonly]
expected: FAIL
[input[type=week\] showPicker() throws when readonly]
expected: FAIL

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

@ -1,132 +1,4 @@
[show-picker-user-gesture.html]
[input[type=button\] showPicker() requires a user gesture]
expected: FAIL
[input[type=checkbox\] showPicker() requires a user gesture]
expected: FAIL
[input[type=color\] showPicker() requires a user gesture]
expected: FAIL
[input[type=date\] showPicker() requires a user gesture]
expected: FAIL
[input[type=datetime-local\] showPicker() requires a user gesture]
expected: FAIL
[input[type=email\] showPicker() requires a user gesture]
expected: FAIL
[input[type=file\] showPicker() requires a user gesture]
expected: FAIL
[input[type=hidden\] showPicker() requires a user gesture]
expected: FAIL
[input[type=image\] showPicker() requires a user gesture]
expected: FAIL
[input[type=month\] showPicker() requires a user gesture]
expected: FAIL
[input[type=number\] showPicker() requires a user gesture]
expected: FAIL
[input[type=password\] showPicker() requires a user gesture]
expected: FAIL
[input[type=radio\] showPicker() requires a user gesture]
expected: FAIL
[input[type=range\] showPicker() requires a user gesture]
expected: FAIL
[input[type=reset\] showPicker() requires a user gesture]
expected: FAIL
[input[type=search\] showPicker() requires a user gesture]
expected: FAIL
[input[type=submit\] showPicker() requires a user gesture]
expected: FAIL
[input[type=tel\] showPicker() requires a user gesture]
expected: FAIL
[input[type=text\] showPicker() requires a user gesture]
expected: FAIL
[input[type=time\] showPicker() requires a user gesture]
expected: FAIL
[input[type=url\] showPicker() requires a user gesture]
expected: FAIL
[input[type=week\] showPicker() requires a user gesture]
expected: FAIL
[input[type=button\] showPicker() does not throw when user activation is active]
expected: FAIL
[input[type=checkbox\] showPicker() does not throw when user activation is active]
expected: FAIL
[input[type=color\] showPicker() does not throw when user activation is active]
expected: FAIL
[input[type=date\] showPicker() does not throw when user activation is active]
expected: FAIL
[input[type=datetime-local\] showPicker() does not throw when user activation is active]
expected: FAIL
[input[type=email\] showPicker() does not throw when user activation is active]
expected: FAIL
[input[type=file\] showPicker() does not throw when user activation is active]
expected: FAIL
[input[type=hidden\] showPicker() does not throw when user activation is active]
expected: FAIL
[input[type=image\] showPicker() does not throw when user activation is active]
expected: FAIL
[input[type=month\] showPicker() does not throw when user activation is active]
expected: FAIL
[input[type=number\] showPicker() does not throw when user activation is active]
expected: FAIL
[input[type=password\] showPicker() does not throw when user activation is active]
expected: FAIL
[input[type=radio\] showPicker() does not throw when user activation is active]
expected: FAIL
[input[type=range\] showPicker() does not throw when user activation is active]
expected: FAIL
[input[type=reset\] showPicker() does not throw when user activation is active]
expected: FAIL
[input[type=search\] showPicker() does not throw when user activation is active]
expected: FAIL
[input[type=submit\] showPicker() does not throw when user activation is active]
expected: FAIL
[input[type=tel\] showPicker() does not throw when user activation is active]
expected: FAIL
[input[type=text\] showPicker() does not throw when user activation is active]
expected: FAIL
[input[type=time\] showPicker() does not throw when user activation is active]
expected: FAIL
[input[type=url\] showPicker() does not throw when user activation is active]
expected: FAIL
[input[type=week\] showPicker() does not throw when user activation is active]
expected: FAIL
disabled:
if tsan: https://bugzilla.mozilla.org/show_bug.cgi?id=1745005
if asan: https://bugzilla.mozilla.org/show_bug.cgi?id=1745005

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

@ -18,13 +18,26 @@ for (const inputType of inputTypes) {
}, `input[type=${inputType}] showPicker() throws when disabled`);
}
const noReadonlySupport = ['button', 'checkbox', 'color', 'file',
'hidden', 'image', 'radio', 'range', 'reset', 'submit'];
for (const inputType of inputTypes) {
test(() => {
const input = document.createElement("input");
input.setAttribute("type", inputType);
input.setAttribute("readonly", "");
if (!noReadonlySupport.includes(inputType)) {
test(() => {
const input = document.createElement("input");
input.setAttribute("type", inputType);
input.setAttribute("readonly", "");
assert_throws_dom('InvalidStateError', () => { input.showPicker(); });
}, `input[type=${inputType}] showPicker() throws when readonly`);
assert_throws_dom('InvalidStateError', () => { input.showPicker(); });
}, `input[type=${inputType}] showPicker() throws when readonly`);
} else {
test(() => {
const input = document.createElement("input");
input.setAttribute("type", inputType);
input.setAttribute("readonly", "");
// Missing user gesture activation throws.
assert_throws_dom('NotAllowedError', () => { input.showPicker(); });
}, `input[type=${inputType}] showPicker() doesn't throw when readonly`);
}
}
</script>

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

@ -0,0 +1,12 @@
// META: title=Scheduler: scheduler should be replaceable
// META: global=window,worker
'use strict';
test(() => {
class Scheduler {
constructor() {
scheduler = this;
}
}
new Scheduler();
}, 'Tests replacing window.scheduler with a different object');

1
third_party/rust/topological-sort/.cargo-checksum.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
{"files":{"Cargo.toml":"b4bfbe31191f3e86445d277cf036bc7b2318baf853e8b2b0c7fcca5d61f7a089","LICENSE-APACHE":"c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08","LICENSE-MIT":"bf04ecfb8f9aec247301556319593dd528886f67bb9ad81654025d12b20d9e01","README.md":"fb45c72e0317c713589e90b028324c0b7320b6eb7aebbbefd31ade38aaf7e1e2","rustfmt.toml":"785022765c76126d4a9954f880c30cc651b3895857ffcfed55ce8a4f8cf3f61a","src/lib.rs":"aaa8b5ff915f1c6e3132bef12afacede4fc91216fe0711efbd1ead7ab106e82e"},"package":"aa7c7f42dea4b1b99439786f5633aeb9c14c1b53f75e282803c2ec2ad545873c"}

21
third_party/rust/topological-sort/Cargo.toml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,21 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g. crates.io) dependencies
#
# If you believe there's an error in this file please file an
# issue against the rust-lang/cargo repository. If you're
# editing this file be aware that the upstream Cargo.toml
# will likely look very different (and much more reasonable)
[package]
name = "topological-sort"
version = "0.1.0"
authors = ["gifnksm <makoto.nksm+github@gmail.com>"]
description = "Performs topological sorting."
documentation = "https://docs.rs/topological-sort/~0.0"
readme = "README.md"
license = "MIT OR Apache-2.0"
repository = "https://github.com/gifnksm/topological-sort-rs"

202
third_party/rust/topological-sort/LICENSE-APACHE поставляемый Normal file
Просмотреть файл

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

19
third_party/rust/topological-sort/LICENSE-MIT поставляемый Normal file
Просмотреть файл

@ -0,0 +1,19 @@
Copyright (c) 2015 The topological-sort-rs Developers
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

40
third_party/rust/topological-sort/README.md поставляемый Normal file
Просмотреть файл

@ -0,0 +1,40 @@
# topological-sort-rs
[![Build Status](https://travis-ci.org/gifnksm/topological-sort-rs.svg)](https://travis-ci.org/gifnksm/topological-sort-rs)
[![Coverage Status](https://coveralls.io/repos/gifnksm/topological-sort-rs/badge.svg?branch=master&service=github)](https://coveralls.io/github/gifnksm/topological-sort-rs?branch=master)
[![crates.io](http://meritbadge.herokuapp.com/topological-sort)](https://crates.io/crates/topological-sort)
Performs topological sorting.
[Documentation](https://docs.rs/topological-sort/~0.0)
## How to use?
Add this to your `Cargo.toml`:
```toml
[dependencies]
topological-sort = "0.0"
```
and this to your crate root:
```rust
extern crate topological_sort;
```
## License
Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally
submitted for inclusion in the work by you, as defined in the Apache-2.0
license, shall be dual licensed as above, without any additional terms or
conditions.

2
third_party/rust/topological-sort/rustfmt.toml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,2 @@
reorder_imports = true
reorder_imported_names = true

384
third_party/rust/topological-sort/src/lib.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,384 @@
// Copyright 2016 oauth-client-rs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
//! Performs topological sorting.
#![warn(bad_style)]
#![warn(missing_copy_implementations)]
#![warn(missing_debug_implementations)]
#![warn(missing_docs)]
#![warn(trivial_casts)]
#![warn(trivial_numeric_casts)]
#![warn(unused)]
#![warn(unused_extern_crates)]
#![warn(unused_import_braces)]
#![warn(unused_qualifications)]
#![warn(unused_results)]
#![cfg_attr(feature = "cargo-clippy", warn(if_not_else))]
#![cfg_attr(feature = "cargo-clippy", warn(invalid_upcast_comparisons))]
#![cfg_attr(feature = "cargo-clippy", warn(items_after_statements))]
#![cfg_attr(feature = "cargo-clippy", warn(mut_mut))]
#![cfg_attr(feature = "cargo-clippy", warn(never_loop))]
#![cfg_attr(feature = "cargo-clippy", warn(nonminimal_bool))]
#![cfg_attr(feature = "cargo-clippy", warn(option_map_unwrap_or))]
#![cfg_attr(feature = "cargo-clippy", warn(option_map_unwrap_or_else))]
#![cfg_attr(feature = "cargo-clippy", warn(option_unwrap_used))]
#![cfg_attr(feature = "cargo-clippy", warn(result_unwrap_used))]
#![cfg_attr(feature = "cargo-clippy", warn(used_underscore_binding))]
use std::cmp::Ordering;
use std::collections::{HashMap, HashSet};
use std::collections::hash_map::Entry;
use std::fmt;
use std::hash::Hash;
use std::iter::FromIterator;
#[derive(Clone)]
struct Dependency<T> {
num_prec: usize,
succ: HashSet<T>,
}
impl<T: Hash + Eq> Dependency<T> {
fn new() -> Dependency<T> {
Dependency {
num_prec: 0,
succ: HashSet::new(),
}
}
}
/// Performs topological sorting.
#[derive(Clone)]
pub struct TopologicalSort<T> {
top: HashMap<T, Dependency<T>>,
}
impl<T: Hash + Eq + Clone> TopologicalSort<T> {
/// Creates new empty `TopologicalSort`.
///
/// ```rust
/// # extern crate topological_sort;
/// # fn main() {
/// use topological_sort::TopologicalSort;
/// let mut ts = TopologicalSort::<&str>::new();
/// ts.add_dependency("hello_world.o", "hello_world");
/// ts.add_dependency("hello_world.c", "hello_world");
/// ts.add_dependency("stdio.h", "hello_world.o");
/// ts.add_dependency("glibc.so", "hello_world");
/// assert_eq!(vec!["glibc.so", "hello_world.c", "stdio.h"],
/// { let mut v = ts.pop_all(); v.sort(); v });
/// assert_eq!(vec!["hello_world.o"],
/// { let mut v = ts.pop_all(); v.sort(); v });
/// assert_eq!(vec!["hello_world"],
/// { let mut v = ts.pop_all(); v.sort(); v });
/// assert!(ts.pop_all().is_empty());
/// # }
/// ```
#[inline]
pub fn new() -> TopologicalSort<T> {
TopologicalSort {
top: HashMap::new(),
}
}
/// Returns the number of elements in the `TopologicalSort`.
#[inline]
pub fn len(&self) -> usize {
self.top.len()
}
/// Returns true if the `TopologicalSort` contains no elements.
#[inline]
pub fn is_empty(&self) -> bool {
self.top.is_empty()
}
/// Registers the two elements' dependency.
///
/// # Arguments
///
/// * `prec` - The element appears before `succ`. `prec` is depended on by `succ`.
/// * `succ` - The element appears after `prec`. `succ` depends on `prec`.
pub fn add_dependency<P, S>(&mut self, prec: P, succ: S)
where
P: Into<T>,
S: Into<T>,
{
self.add_dependency_impl(prec.into(), succ.into())
}
fn add_dependency_impl(&mut self, prec: T, succ: T) {
match self.top.entry(prec) {
Entry::Vacant(e) => {
let mut dep = Dependency::new();
let _ = dep.succ.insert(succ.clone());
let _ = e.insert(dep);
}
Entry::Occupied(e) => {
if !e.into_mut().succ.insert(succ.clone()) {
// Already registered
return;
}
}
}
match self.top.entry(succ) {
Entry::Vacant(e) => {
let mut dep = Dependency::new();
dep.num_prec += 1;
let _ = e.insert(dep);
}
Entry::Occupied(e) => {
e.into_mut().num_prec += 1;
}
}
}
/// Registers a dependency link.
pub fn add_link(&mut self, link: DependencyLink<T>) {
self.add_dependency(link.prec, link.succ)
}
/// Inserts an element, without adding any dependencies from or to it.
///
/// If the `TopologicalSort` did not have this element present, `true` is returned.
///
/// If the `TopologicalSort` already had this element present, `false` is returned.
pub fn insert<U>(&mut self, elt: U) -> bool
where
U: Into<T>,
{
match self.top.entry(elt.into()) {
Entry::Vacant(e) => {
let dep = Dependency::new();
let _ = e.insert(dep);
true
}
Entry::Occupied(_) => false,
}
}
/// Removes the item that is not depended on by any other items and returns it, or `None` if
/// there is no such item.
///
/// If `pop` returns `None` and `len` is not 0, there is cyclic dependencies.
pub fn pop(&mut self) -> Option<T> {
self.peek().map(T::clone).map(|key| {
let _ = self.remove(&key);
key
})
}
/// Removes all items that are not depended on by any other items and returns it, or empty
/// vector if there are no such items.
///
/// If `pop_all` returns an empty vector and `len` is not 0, there is cyclic dependencies.
pub fn pop_all(&mut self) -> Vec<T> {
let keys = self.top
.iter()
.filter(|&(_, v)| v.num_prec == 0)
.map(|(k, _)| k.clone())
.collect::<Vec<_>>();
for k in &keys {
let _ = self.remove(k);
}
keys
}
/// Return a reference to the first item that does not depend on any other items, or `None` if
/// there is no such item.
pub fn peek(&self) -> Option<&T> {
self.top
.iter()
.filter(|&(_, v)| v.num_prec == 0)
.map(|(k, _)| k)
.next()
}
/// Return a vector of references to all items that do not depend on any other items, or an
/// empty vector if there are no such items.
pub fn peek_all(&self) -> Vec<&T> {
self.top
.iter()
.filter(|&(_, v)| v.num_prec == 0)
.map(|(k, _)| k)
.collect::<Vec<_>>()
}
fn remove(&mut self, prec: &T) -> Option<Dependency<T>> {
let result = self.top.remove(prec);
if let Some(ref p) = result {
for s in &p.succ {
if let Some(y) = self.top.get_mut(s) {
y.num_prec -= 1;
}
}
}
result
}
}
impl<T: PartialOrd + Eq + Hash + Clone> FromIterator<T> for TopologicalSort<T> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> TopologicalSort<T> {
let mut top = TopologicalSort::new();
let mut seen = Vec::<T>::default();
for item in iter {
let _ = top.insert(item.clone());
for seen_item in seen.iter().cloned() {
match seen_item.partial_cmp(&item) {
Some(Ordering::Less) => {
top.add_dependency(seen_item, item.clone());
}
Some(Ordering::Greater) => {
top.add_dependency(item.clone(), seen_item);
}
_ => (),
}
}
seen.push(item);
}
top
}
}
/// A link between two items in a sort.
#[derive(Copy, Clone, Debug)]
pub struct DependencyLink<T> {
/// The element which is depened upon by `succ`.
pub prec: T,
/// The element which depends on `prec`.
pub succ: T,
}
impl<T> From<(T, T)> for DependencyLink<T> {
fn from(tuple: (T, T)) -> Self {
DependencyLink {
succ: tuple.0,
prec: tuple.1,
}
}
}
impl<T: Eq + Hash + Clone> FromIterator<DependencyLink<T>> for TopologicalSort<T> {
fn from_iter<I: IntoIterator<Item = DependencyLink<T>>>(iter: I) -> TopologicalSort<T> {
let mut top = TopologicalSort::new();
for link in iter {
top.add_link(link);
}
top
}
}
impl<T: Hash + Eq + Clone> Iterator for TopologicalSort<T> {
type Item = T;
fn next(&mut self) -> Option<T> {
self.pop()
}
}
impl<T: fmt::Debug + Hash + Eq> fmt::Debug for Dependency<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "prec={}, succ={:?}", self.num_prec, self.succ)
}
}
impl<T: fmt::Debug + Hash + Eq + Clone> fmt::Debug for TopologicalSort<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self.top)
}
}
#[cfg(test)]
mod test {
use super::TopologicalSort;
use std::iter::FromIterator;
#[test]
fn from_iter() {
let t = vec![4, 3, 3, 5, 7, 6, 8];
let mut ts = TopologicalSort::<i32>::from_iter(t);
assert_eq!(Some(3), ts.next());
assert_eq!(Some(4), ts.next());
assert_eq!(Some(5), ts.next());
assert_eq!(Some(6), ts.next());
assert_eq!(Some(7), ts.next());
assert_eq!(Some(8), ts.next());
assert_eq!(None, ts.next());
}
#[test]
fn iter() {
let mut ts = TopologicalSort::<i32>::new();
ts.add_dependency(1, 2);
ts.add_dependency(2, 3);
ts.add_dependency(3, 4);
assert_eq!(Some(1), ts.next());
assert_eq!(Some(2), ts.next());
assert_eq!(Some(3), ts.next());
assert_eq!(Some(4), ts.next());
assert_eq!(None, ts.next());
}
#[test]
fn pop_all() {
fn check(result: &[i32], ts: &mut TopologicalSort<i32>) {
let l = ts.len();
let mut v = ts.pop_all();
v.sort();
assert_eq!(result, &v[..]);
assert_eq!(l - result.len(), ts.len());
}
let mut ts = TopologicalSort::new();
ts.add_dependency(7, 11);
assert_eq!(2, ts.len());
ts.add_dependency(7, 8);
assert_eq!(3, ts.len());
ts.add_dependency(5, 11);
assert_eq!(4, ts.len());
ts.add_dependency(3, 8);
assert_eq!(5, ts.len());
ts.add_dependency(3, 10);
assert_eq!(6, ts.len());
ts.add_dependency(11, 2);
assert_eq!(7, ts.len());
ts.add_dependency(11, 9);
assert_eq!(8, ts.len());
ts.add_dependency(11, 10);
assert_eq!(8, ts.len());
ts.add_dependency(8, 9);
assert_eq!(8, ts.len());
check(&[3, 5, 7], &mut ts);
check(&[8, 11], &mut ts);
check(&[2, 9, 10], &mut ts);
check(&[], &mut ts);
}
#[test]
fn cyclic_deadlock() {
let mut ts = TopologicalSort::new();
ts.add_dependency("stone", "sharp");
ts.add_dependency("bucket", "hole");
ts.add_dependency("hole", "straw");
ts.add_dependency("straw", "axe");
ts.add_dependency("axe", "sharp");
ts.add_dependency("sharp", "water");
ts.add_dependency("water", "bucket");
assert_eq!(ts.pop(), Some("stone"));
assert!(ts.pop().is_none());
println!("{:?}", ts);
}
}

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

@ -2235,8 +2235,7 @@ class PictureInPictureChild extends JSWindowActorChild {
*
* - The "site wrapper" script must export a class called "PictureInPictureVideoWrapper"
* - Method names on a site wrapper class should match its caller's name
* (i.e: PictureInPictureChildVideoWrapper.play will only call `play` on a site-wrapper,
* if available)
* (i.e: PictureInPictureChildVideoWrapper.play will only call `play` on a site-wrapper, if available)
*/
class PictureInPictureChildVideoWrapper {
#sandbox;
@ -2252,12 +2251,14 @@ class PictureInPictureChildVideoWrapper {
* commanding the original <video>.
* @param {HTMLVideoElement} video
* The original <video> we want to create a wrapper class for.
* @param {Object} pipChild
* Reference to PictureInPictureChild class calling this function.
*/
constructor(videoWrapperScriptPath, video, piPChild) {
constructor(videoWrapperScriptPath, video, pipChild) {
this.#sandbox = videoWrapperScriptPath
? this.#createSandbox(videoWrapperScriptPath, video)
: null;
this.#PictureInPictureChild = piPChild;
this.#PictureInPictureChild = pipChild;
}
/**
@ -2278,7 +2279,6 @@ class PictureInPictureChildVideoWrapper {
* return null.
*
* @returns The expected output of the wrapper function.
*
*/
#callWrapperMethod({ name, args = [], fallback = () => {}, validateRetVal }) {
try {
@ -2362,6 +2362,9 @@ class PictureInPictureChildVideoWrapper {
return typeof val === "number";
}
/**
* Destroys the sandbox for the site wrapper class
*/
destroy() {
if (this.#sandbox) {
Cu.nukeSandbox(this.#sandbox);
@ -2381,6 +2384,13 @@ class PictureInPictureChildVideoWrapper {
/* Video methods to be used for video controls from the PiP window. */
/**
* OVERRIDABLE - calls the play() method defined in the site wrapper script. Runs a fallback implementation
* if the method does not exist or if an error is thrown while calling it. This method is meant to handle video
* behaviour when a video is played.
* @param {HTMLVideoElement} video
* The originating video source element
*/
play(video) {
return this.#callWrapperMethod({
name: "play",
@ -2390,6 +2400,13 @@ class PictureInPictureChildVideoWrapper {
});
}
/**
* OVERRIDABLE - calls the pause() method defined in the site wrapper script. Runs a fallback implementation
* if the method does not exist or if an error is thrown while calling it. This method is meant to handle video
* behaviour when a video is paused.
* @param {HTMLVideoElement} video
* The originating video source element
*/
pause(video) {
return this.#callWrapperMethod({
name: "pause",
@ -2399,6 +2416,14 @@ class PictureInPictureChildVideoWrapper {
});
}
/**
* OVERRIDABLE - calls the getPaused() method defined in the site wrapper script. Runs a fallback implementation
* if the method does not exist or if an error is thrown while calling it. This method is meant to determine if
* a video is paused or not.
* @param {HTMLVideoElement} video
* The originating video source element
* @returns {Boolean} Boolean value true if paused, or false if video is still playing
*/
getPaused(video) {
return this.#callWrapperMethod({
name: "getPaused",
@ -2408,6 +2433,14 @@ class PictureInPictureChildVideoWrapper {
});
}
/**
* OVERRIDABLE - calls the getEnded() method defined in the site wrapper script. Runs a fallback implementation
* if the method does not exist or if an error is thrown while calling it. This method is meant to determine if
* video playback or streaming has stopped.
* @param {HTMLVideoElement} video
* The originating video source element
* @returns {Boolean} Boolean value true if the video has ended, or false if still playing
*/
getEnded(video) {
return this.#callWrapperMethod({
name: "getEnded",
@ -2417,6 +2450,14 @@ class PictureInPictureChildVideoWrapper {
});
}
/**
* OVERRIDABLE - calls the getDuration() method defined in the site wrapper script. Runs a fallback implementation
* if the method does not exist or if an error is thrown while calling it. This method is meant to get the current
* duration of a video in seconds.
* @param {HTMLVideoElement} video
* The originating video source element
* @returns {Number} Duration of the video in seconds
*/
getDuration(video) {
return this.#callWrapperMethod({
name: "getDuration",
@ -2426,6 +2467,14 @@ class PictureInPictureChildVideoWrapper {
});
}
/**
* OVERRIDABLE - calls the getCurrentTime() method defined in the site wrapper script. Runs a fallback implementation
* if the method does not exist or if an error is thrown while calling it. This method is meant to get the current
* time of a video in seconds.
* @param {HTMLVideoElement} video
* The originating video source element
* @returns {Number} Current time of the video in seconds
*/
getCurrentTime(video) {
return this.#callWrapperMethod({
name: "getCurrentTime",
@ -2435,6 +2484,15 @@ class PictureInPictureChildVideoWrapper {
});
}
/**
* OVERRIDABLE - calls the setCurrentTime() method defined in the site wrapper script. Runs a fallback implementation
* if the method does not exist or if an error is thrown while calling it. This method is meant to set the current
* time of a video.
* @param {HTMLVideoElement} video
* The originating video source element
* @param {Number} position
* The current playback time of the video
*/
setCurrentTime(video, position) {
return this.#callWrapperMethod({
name: "setCurrentTime",
@ -2446,6 +2504,14 @@ class PictureInPictureChildVideoWrapper {
});
}
/**
* OVERRIDABLE - calls the getVolume() method defined in the site wrapper script. Runs a fallback implementation
* if the method does not exist or if an error is thrown while calling it. This method is meant to get the volume
* value of a video.
* @param {HTMLVideoElement} video
* The originating video source element
* @returns {Number} Volume of the video between 0 (muted) and 1 (loudest)
*/
getVolume(video) {
return this.#callWrapperMethod({
name: "getVolume",
@ -2455,6 +2521,15 @@ class PictureInPictureChildVideoWrapper {
});
}
/**
* OVERRIDABLE - calls the setVolume() method defined in the site wrapper script. Runs a fallback implementation
* if the method does not exist or if an error is thrown while calling it. This method is meant to set the volume
* value of a video.
* @param {HTMLVideoElement} video
* The originating video source element
* @param {Number} volume
* Value between 0 (muted) and 1 (loudest)
*/
setVolume(video, volume) {
return this.#callWrapperMethod({
name: "setVolume",
@ -2466,6 +2541,15 @@ class PictureInPictureChildVideoWrapper {
});
}
/**
* OVERRIDABLE - calls the setMuted() method defined in the site wrapper script. Runs a fallback implementation
* if the method does not exist or if an error is thrown while calling it. This method is meant to mute or unmute
* a video.
* @param {HTMLVideoElement} video
* The originating video source element
* @param {Boolean} shouldMute
* Boolean value true to mute the video, or false to unmute the video
*/
setMuted(video, shouldMute) {
return this.#callWrapperMethod({
name: "setMuted",
@ -2477,7 +2561,17 @@ class PictureInPictureChildVideoWrapper {
});
}
setCaptionContainerObserver(video) {
/**
* OVERRIDABLE - calls the setCaptionContainerObserver() method defined in the site wrapper script. Runs a fallback implementation
* if the method does not exist or if an error is thrown while calling it. This method is meant to listen for any cue changes in a
* video's caption container and execute a callback function responsible for updating the pip window's text tracks container whenever
* a cue change is triggered {@see updatePiPTextTracks()}.
* @param {HTMLVideoElement} video
* The originating video source element
* @param {Function} callback
* The callback function to be executed when cue changes are detected
*/
setCaptionContainerObserver(video, callback) {
return this.#callWrapperMethod({
name: "setCaptionContainerObserver",
args: [
@ -2491,6 +2585,14 @@ class PictureInPictureChildVideoWrapper {
});
}
/**
* OVERRIDABLE - calls the shouldHideToggle() method defined in the site wrapper script. Runs a fallback implementation
* if the method does not exist or if an error is thrown while calling it. This method is meant to determine if the pip toggle
* for a video should be hidden by the site wrapper.
* @param {HTMLVideoElement} video
* The originating video source element
* @returns {Boolean} Boolean value true if the pip toggle should be hidden by the site wrapper, or false if it should not
*/
shouldHideToggle(video) {
return this.#callWrapperMethod({
name: "shouldHideToggle",

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше