Merge autoland to mozilla-central a=merge

This commit is contained in:
Andreea Pavel 2022-08-11 00:28:37 +03:00
Родитель 4667627646 dbc0dcf10f
Коммит f655bdf6b4
379 изменённых файлов: 12501 добавлений и 9221 удалений

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

@ -30,7 +30,26 @@ var gPermissionPanel = {
}
},
/**
* _popupAnchorNode will be set by setAnchor if an outside consumer
* of this object wants to override the default anchor for the panel.
* If there is no override, this remains null, and the _identityPermissionBox
* will be used as the anchor.
*/
_popupAnchorNode: null,
_popupPosition: "bottomcenter topleft",
setAnchor(anchorNode, popupPosition) {
this._popupAnchorNode = anchorNode;
this._popupPosition = popupPosition;
},
// smart getters
get _popupAnchor() {
if (this._popupAnchorNode) {
return this._popupAnchorNode;
}
return this._identityPermissionBox;
},
get _identityPermissionBox() {
delete this._identityPermissionBox;
return (this._identityPermissionBox = document.getElementById(
@ -187,7 +206,28 @@ var gPermissionPanel = {
* Shows the permission popup.
* @param {Event} event - Event which caused the popup to show.
*/
_openPopup(event) {
openPopup(event) {
// If we are in DOM fullscreen, exit it before showing the permission popup
// (see bug 1557041)
if (document.fullscreen) {
// Open the identity popup after DOM fullscreen exit
// We need to wait for the exit event and after that wait for the fullscreen exit transition to complete
// If we call openPopup before the fullscreen transition ends it can get cancelled
// Only waiting for painted is not sufficient because we could still be in the fullscreen enter transition.
this._exitedEventReceived = false;
this._event = event;
Services.obs.addObserver(this, "fullscreen-painted");
window.addEventListener(
"MozDOMFullscreen:Exited",
() => {
this._exitedEventReceived = true;
},
{ once: true }
);
document.exitFullscreen();
return;
}
// Make the popup available.
this._initializePopup();
@ -204,14 +244,10 @@ var gPermissionPanel = {
}
// Now open the popup, anchored off the primary chrome element
PanelMultiView.openPopup(
this._permissionPopup,
this._identityPermissionBox,
{
position: "bottomcenter topleft",
triggerEvent: event,
}
).catch(Cu.reportError);
PanelMultiView.openPopup(this._permissionPopup, this._popupAnchor, {
position: this._popupPosition,
triggerEvent: event,
}).catch(Cu.reportError);
},
/**
@ -308,28 +344,7 @@ var gPermissionPanel = {
return;
}
// If we are in DOM fullscreen, exit it before showing the permission popup
// (see bug 1557041)
if (document.fullscreen) {
// Open the identity popup after DOM fullscreen exit
// We need to wait for the exit event and after that wait for the fullscreen exit transition to complete
// If we call _openPopup before the fullscreen transition ends it can get cancelled
// Only waiting for painted is not sufficient because we could still be in the fullscreen enter transition.
this._exitedEventReceived = false;
this._event = event;
Services.obs.addObserver(this, "fullscreen-painted");
window.addEventListener(
"MozDOMFullscreen:Exited",
() => {
this._exitedEventReceived = true;
},
{ once: true }
);
document.exitFullscreen();
return;
}
this._openPopup(event);
this.openPopup(event);
},
onPopupShown(event) {
@ -369,7 +384,7 @@ var gPermissionPanel = {
return;
}
Services.obs.removeObserver(this, "fullscreen-painted");
this._openPopup(this._event);
this.openPopup(this._event);
delete this._event;
break;
}

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

@ -14,7 +14,7 @@ function openPermissionPopup() {
gPermissionPanel._initializePopup();
let mainView = document.getElementById("permission-popup-mainView");
let viewShown = BrowserTestUtils.waitForEvent(mainView, "ViewShown");
gPermissionPanel._openPopup();
gPermissionPanel.openPopup();
return viewShown;
}

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

@ -50,7 +50,7 @@ var gTests = [
// the second window.
ok(permissionPopupHidden(win), "control center should be hidden");
let activeStreams = webrtcUI.getActiveStreams(true, false, false);
webrtcUI.showSharingDoorhanger(activeStreams[0], "Devices");
webrtcUI.showSharingDoorhanger(activeStreams[0]);
// If the popup gets hidden before being shown, by stray focus/activate
// events, don't bother failing the test. It's enough to know that we
// started showing the popup.

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

@ -553,7 +553,6 @@ const WebRTCIndicator = {
bundle.GetStringFromName("webrtcIndicator.controlSharing.menuitem")
);
menuitem.stream = stream;
menuitem.addEventListener("command", this);
menupopup.appendChild(menuitem);
return;
@ -577,7 +576,6 @@ const WebRTCIndicator = {
label = stream.browser.contentTitle || stream.uri;
item.setAttribute("label", bundle.formatStringFromName(labelId, [label]));
item.stream = stream;
item.addEventListener("command", this);
menupopup.appendChild(item);
}
},
@ -594,7 +592,7 @@ const WebRTCIndicator = {
},
onCommand(event) {
webrtcUI.showSharingDoorhanger(event.target.stream);
webrtcUI.showSharingDoorhanger(event.target.stream, event);
},
/**

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

@ -111,7 +111,7 @@ function onPopupMenuShowing(event) {
}
if (activeStreams.length) {
let index = activeStreams.length - 1;
webrtcUI.showSharingDoorhanger(activeStreams[index]);
webrtcUI.showSharingDoorhanger(activeStreams[index], event);
event.preventDefault();
return;
}
@ -133,7 +133,7 @@ function onPopupMenuHiding(event) {
}
function onPopupMenuCommand(event) {
webrtcUI.showSharingDoorhanger(event.target.stream);
webrtcUI.showSharingDoorhanger(event.target.stream, event);
}
function onFirefoxButtonClick(event) {

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

@ -12,13 +12,25 @@ const { BrowserWindowTracker } = ChromeUtils.import(
let ColorwayClosetOpener = {
openModal: () => {
let win = BrowserWindowTracker.getTopWindow();
let dialogBox = win.gBrowser.getTabDialogBox(win.gBrowser.selectedBrowser);
return dialogBox.open(
let { gBrowser } = BrowserWindowTracker.getTopWindow();
let dialogBox = gBrowser.getTabDialogBox(gBrowser.selectedBrowser);
let rv = dialogBox.open(
"chrome://browser/content/colorways/colorwaycloset.html",
{
features: "resizable=no",
}
);
let { dialog } = rv;
dialog._dialogReady.then(() => {
// The modal document had a width set so `SubDialog` could use it to
// determine the initial frame size. We'll now remove that width because
// the modal document's layout is responsive and we don't want to scroll
// horizontally when resizing the browser window such that the frame
// becomes narrower than its initial width.
dialog._frame.contentDocument.documentElement.style.removeProperty(
"width"
);
});
return rv;
},
};

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

@ -47,20 +47,6 @@ const ColorwayCloset = {
init() {
window.addEventListener("unload", this);
// We had set a width so `SubDialog` code can use it to determine the
// initial frame size. We're now going to remove that width because our
// layout is responsive and we don't want to scroll horizontally when
// resizing the browser window such that the frame becomes narrower than
// its initial width.
// We'll wait for the load event and l10n to be ready because `SubDialog`
// does the same before determining the frame size.
window.addEventListener("load", async () => {
await document.l10n.ready;
requestAnimationFrame(() => {
document.documentElement.style.removeProperty("width");
});
});
this._displayCollectionData();
AddonManager.addAddonListener(this);

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

@ -9,6 +9,13 @@ add_setup(async function setup_tests() {
add_task(async function colorwaycloset_show_colorway() {
await testInColorwayClosetModal(document => {
is(
document.documentElement.style.width,
"",
"In order for the modal layout to be responsive, the modal document " +
"should not have a width set after the dialog frame has been set up"
);
const el = getColorwayClosetTestElements(document);
const expiryL10nAttributes = document.l10n.getAttributes(el.expiryDateSpan);
is(

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

@ -2537,7 +2537,11 @@ var CustomizableUIInternal = {
// to be restored. This can occur when add-ons register widgets for a
// lazily-restored area before it's been restored.
if (gFuturePlacements.has(aArea)) {
let areaPlacements = gPlacements.get(aArea);
for (let id of gFuturePlacements.get(aArea)) {
if (areaPlacements.includes(id)) {
continue;
}
this.addWidgetToArea(id, aArea);
}
gFuturePlacements.delete(aArea);

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

@ -103,7 +103,17 @@ const MESSAGES = [
},
dismiss_button: {
action: {
navigate: true,
type: "SET_PREF",
data: {
pref: {
name: "browser.firefox-view.feature-tour",
value: JSON.stringify({
message: "FIREFOX_VIEW_FEATURE_TOUR",
screen: "FEATURE_CALLOUT_1",
complete: true,
}),
},
},
},
},
},
@ -135,7 +145,17 @@ const MESSAGES = [
},
dismiss_button: {
action: {
navigate: true,
type: "SET_PREF",
data: {
pref: {
name: "browser.firefox-view.feature-tour",
value: JSON.stringify({
message: "FIREFOX_VIEW_FEATURE_TOUR",
screen: "FEATURE_CALLOUT_2",
complete: true,
}),
},
},
},
},
},
@ -171,7 +191,17 @@ const MESSAGES = [
},
dismiss_button: {
action: {
navigate: true,
type: "SET_PREF",
data: {
pref: {
name: "browser.firefox-view.feature-tour",
value: JSON.stringify({
message: "FIREFOX_VIEW_FEATURE_TOUR",
screen: "FEATURE_CALLOUT_3",
complete: true,
}),
},
},
},
},
},

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

@ -34,7 +34,7 @@
<main>
<template id="sync-setup-template">
<named-deck class="sync-setup-container" data-prefix="id:">
<div name="sync-setup-view0" data-prefix="id:-view0" class="card error-state" data-prefix="aria-labelledby:-view0-header">
<div name="sync-setup-view0" data-prefix="id:-view0" class="card card-no-hover error-state" data-prefix="aria-labelledby:-view0-header">
<icon class="icon info primary"></icon><h3 data-prefix="id:-view0-header" class="card-header"></h3>
<section>
<p>
@ -43,7 +43,7 @@
<button id="error-state-button" class="primary"></button>
</section>
</div>
<div name="sync-setup-view1" data-prefix="id:-view1" class="card zap-card setup-step" data-prefix="aria-labelledby:-view1-header">
<div name="sync-setup-view1" data-prefix="id:-view1" class="card card-no-hover zap-card setup-step" data-prefix="aria-labelledby:-view1-header">
<h2 data-prefix="id:-view1-header" data-l10n-id="firefoxview-tabpickup-step-signin-header" class="card-header"></h2>
<section class="step-body">
<p class="step-content" data-l10n-id="firefoxview-tabpickup-step-signin-description"></p>
@ -57,7 +57,7 @@
data-l10n-args='{"percentValue":"11"}'></label>
</footer>
</div>
<div name="sync-setup-view2" data-prefix="id:-view2" class="card zap-card setup-step" data-prefix="aria-labelledby:-view2-header">
<div name="sync-setup-view2" data-prefix="id:-view2" class="card card-no-hover zap-card setup-step" data-prefix="aria-labelledby:-view2-header">
<h2 data-prefix="id:-view2-header" data-l10n-id="firefoxview-tabpickup-adddevice-header" class="card-header"></h2>
<section class="step-body">
<p class="step-content">
@ -75,7 +75,7 @@
data-l10n-args='{"percentValue":"33"}'></label>
</footer>
</div>
<div name="sync-setup-view3" data-prefix="id:-view3" class="card zap-card setup-step" data-prefix="aria-labelledby:-view3-header">
<div name="sync-setup-view3" data-prefix="id:-view3" class="card card-no-hover zap-card setup-step" data-prefix="aria-labelledby:-view3-header">
<h2 data-prefix="id:-view3-header" data-l10n-id="firefoxview-tabpickup-synctabs-header" class="card-header"></h2>
<section class="step-body">
<p class="step-content">

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

@ -5,6 +5,8 @@ const { calloutMessages } = ChromeUtils.importESModule(
);
const calloutSelector = "#root.featureCallout";
const featureTourPref = "browser.firefox-view.feature-tour";
const waitForCalloutRender = async doc => {
// Wait for callout to be rendered
await BrowserTestUtils.waitForMutationCondition(
@ -34,13 +36,21 @@ const waitForCalloutScreen = async (doc, screenId) => {
);
};
const waitForCalloutRemoved = async doc => {
await BrowserTestUtils.waitForMutationCondition(
doc.body,
{ childList: true },
() => !doc.body.querySelector(calloutSelector)
);
};
const clickPrimaryButton = doc => {
doc.querySelector(".action-buttons .primary").click();
};
add_task(async function feature_callout_renders_in_firefox_view() {
await SpecialPowers.pushPrefEnv({
set: [["browser.firefox-view.feature-tour", "{}"]],
set: [[featureTourPref, "{}"]],
});
await BrowserTestUtils.withNewTab(
@ -60,7 +70,7 @@ add_task(async function feature_callout_renders_in_firefox_view() {
add_task(async function feature_callout_moves_on_screen_change() {
await SpecialPowers.pushPrefEnv({
set: [["browser.firefox-view.feature-tour", "{}"]],
set: [[featureTourPref, "{}"]],
});
await BrowserTestUtils.withNewTab(
@ -104,12 +114,7 @@ add_task(async function feature_callout_moves_on_screen_change() {
add_task(async function feature_callout_is_not_shown_twice() {
// Third comma-separated value of the pref is set to a string value once a user completes the tour
await SpecialPowers.pushPrefEnv({
set: [
[
"browser.firefox-view.feature-tour",
'{"message":"","screen":"","complete":true}',
],
],
set: [[featureTourPref, '{"message":"","screen":"","complete":true}']],
});
await BrowserTestUtils.withNewTab(
@ -130,7 +135,7 @@ add_task(async function feature_callout_is_not_shown_twice() {
add_task(
async function feature_callout_first_screen_positioned_below_element() {
await SpecialPowers.pushPrefEnv({
set: [["browser.firefox-view.feature-tour", "{}"]],
set: [[featureTourPref, "{}"]],
});
await BrowserTestUtils.withNewTab(
@ -161,7 +166,7 @@ add_task(
add_task(
async function feature_callout_second_screen_positioned_above_element() {
await SpecialPowers.pushPrefEnv({
set: [["browser.firefox-view.feature-tour", "{}"]],
set: [[featureTourPref, "{}"]],
});
await BrowserTestUtils.withNewTab(
@ -196,7 +201,7 @@ add_task(
add_task(
async function feature_callout_third_screen_positioned_left_of_element() {
await SpecialPowers.pushPrefEnv({
set: [["browser.firefox-view.feature-tour", "{}"]],
set: [[featureTourPref, "{}"]],
});
await BrowserTestUtils.withNewTab(
@ -234,7 +239,7 @@ add_task(
async function feature_callout_third_screen_position_respects_RTL_layouts() {
await SpecialPowers.pushPrefEnv({
set: [
["browser.firefox-view.feature-tour", "{}"],
[featureTourPref, "{}"],
// Set layout direction to right to left
["intl.l10n.pseudo", "bidi"],
],
@ -277,7 +282,7 @@ add_task(async function feature_callout_syncs_across_visits_and_tabs() {
await SpecialPowers.pushPrefEnv({
set: [
[
"browser.firefox-view.feature-tour",
featureTourPref,
'{"message":"FIREFOX_VIEW_FEATURE_TOUR","screen":"FEATURE_CALLOUT_2","complete":false}',
],
],
@ -319,17 +324,8 @@ add_task(async function feature_callout_syncs_across_visits_and_tabs() {
tab1Doc.querySelector(".action-buttons .primary").click();
await BrowserTestUtils.waitForMutationCondition(
tab1Doc.body,
{ childList: true },
() => !tab1Doc.body.querySelector(calloutSelector)
);
await BrowserTestUtils.waitForMutationCondition(
tab2Doc.body,
{ childList: true },
() => !tab2Doc.body.querySelector(calloutSelector)
);
await waitForCalloutRemoved(tab1Doc);
await waitForCalloutRemoved(tab2Doc);
ok(
!tab1Doc.body.querySelector(calloutSelector),
@ -343,3 +339,37 @@ add_task(async function feature_callout_syncs_across_visits_and_tabs() {
BrowserTestUtils.removeTab(tab1);
BrowserTestUtils.removeTab(tab2);
});
add_task(async function feature_callout_closes_on_dismiss() {
await SpecialPowers.pushPrefEnv({
set: [[featureTourPref, "{}"]],
});
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
const { document } = browser.contentWindow;
await waitForCalloutRender(document);
await waitForCalloutPositioned(document);
document.querySelector(".dismiss-button").click();
await waitForCalloutRemoved(document);
ok(
!document.querySelector(calloutSelector),
"Callout is removed from screen on dismiss"
);
let tourComplete = JSON.parse(
Services.prefs.getStringPref(featureTourPref)
).complete;
ok(
tourComplete,
`Tour is recorded as complete in ${featureTourPref} preference value`
);
}
);
});

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

@ -173,7 +173,7 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
/* harmony import */ var _MultiStageProtonScreen__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6);
/* harmony import */ var _LanguageSwitcher__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(10);
/* harmony import */ var _asrouter_templates_FirstRun_addUtmParams__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(11);
/* harmony import */ var _asrouter_templates_FirstRun_addUtmParams__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(13);
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -598,6 +598,8 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var _Themes__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(9);
/* harmony import */ var _MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(4);
/* harmony import */ var _LanguageSwitcher__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(10);
/* harmony import */ var _LinkText__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(11);
/* harmony import */ var _HeroImage__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(12);
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -608,6 +610,8 @@ __webpack_require__.r(__webpack_exports__);
const MultiStageProtonScreen = props => {
const {
autoAdvance,
@ -727,7 +731,9 @@ class ProtonScreen extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCom
style: content.background ? {
background: content.background
} : {}
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
}, content.hero_image ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_HeroImage__WEBPACK_IMPORTED_MODULE_8__.HeroImage, {
url: content.hero_image.url
}) : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "message-text"
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "spacer-top"
@ -739,7 +745,7 @@ class ProtonScreen extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCom
text: content.help_text
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {
className: "attrib-text"
})));
}))));
}
render() {
@ -807,7 +813,10 @@ class ProtonScreen extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCom
"addon-name": this.props.addonName,
...((_this$props$appAndSys = this.props.appAndSystemLocaleInfo) === null || _this$props$appAndSys === void 0 ? void 0 : _this$props$appAndSys.displayNames)
})
}))), this.renderContentTiles(), this.renderLanguageSwitcher(), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
})), content.cta_paragraph ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_LinkText__WEBPACK_IMPORTED_MODULE_7__.LinkText, {
content: content.cta_paragraph,
handleAction: this.props.handleAction
}) : null), this.renderContentTiles(), this.renderLanguageSwitcher(), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "action-buttons"
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
text: (_content$primary_butt = content.primary_button) === null || _content$primary_butt === void 0 ? void 0 : _content$primary_butt.label
@ -1316,6 +1325,82 @@ function LanguageSwitcher(props) {
/* 11 */
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "LinkText": () => (/* binding */ LinkText)
/* harmony export */ });
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const LinkText = props => {
const {
content,
handleAction
} = props;
if (!(content !== null && content !== void 0 && content.text && content !== null && content !== void 0 && content.button_label && typeof handleAction === "function")) {
return null;
}
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h2", {
className: "cta-paragraph"
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
text: content.text
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null)), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, {
text: content.button_label
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
onClick: handleAction,
value: "cta_paragraph",
className: "text-link"
})));
};
/***/ }),
/* 12 */
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "HeroImage": () => (/* binding */ HeroImage)
/* harmony export */ });
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const HeroImage = props => {
const {
height,
url,
alt
} = props;
if (!url) {
return null;
}
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "hero-image"
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", {
style: height ? {
height
} : null,
src: url,
alt: alt || "",
role: alt ? null : "presentation"
}));
};
/***/ }),
/* 13 */
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "BASE_PARAMS": () => (/* binding */ BASE_PARAMS),
@ -1355,7 +1440,7 @@ function addUtmParams(url, utmTerm) {
}
/***/ }),
/* 12 */
/* 14 */
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
@ -1366,7 +1451,7 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
/* harmony import */ var _MultiStageProtonScreen__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6);
/* harmony import */ var _asrouter_templates_FirstRun_addUtmParams__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(11);
/* harmony import */ var _asrouter_templates_FirstRun_addUtmParams__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(13);
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -1561,7 +1646,7 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_dom__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
/* harmony import */ var _components_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4);
/* harmony import */ var _components_ReturnToAMO__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(12);
/* harmony import */ var _components_ReturnToAMO__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(14);
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
/* This Source Code Form is subject to the terms of the Mozilla Public

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

@ -409,6 +409,17 @@ body[lwt-newtab-brighttext] {
text-shadow: none;
margin-inline: 40px 0;
}
.onboardingContainer .screen[pos=split] .section-secondary .hero-image {
flex: 1;
display: flex;
justify-content: center;
max-height: 100%;
}
.onboardingContainer .screen[pos=split] .section-secondary .hero-image img {
width: 100%;
max-width: 150px;
margin: 25px 0;
}
.onboardingContainer .screen[pos=split] .tiles-theme-container {
margin-block: -20px auto;
align-items: initial;
@ -593,6 +604,11 @@ body[lwt-newtab-brighttext] {
background-position: 400%;
}
}
.onboardingContainer .welcome-text .cta-paragraph button.text-link {
margin: 0;
text-decoration: underline;
cursor: pointer;
}
.onboardingContainer .screen.light-text .welcome-text.fancy h1 {
background-image: linear-gradient(90deg, #C688FF, #FF84C0, #FFBD4F, #FF84C0, #C688FF);
}

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

@ -19,6 +19,19 @@ XPCOMUtils.defineLazyModuleGetters(lazy, {
AttributionCode: "resource:///modules/AttributionCode.jsm",
});
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"usesFirefoxSync",
"services.sync.username"
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"mobileDevices",
"services.sync.clients.devices.mobile",
0
);
const DEFAULT_WELCOME_CONTENT = {
id: "DEFAULT_ABOUTWELCOME_PROTON",
template: "multistage",
@ -466,6 +479,41 @@ const MR_ABOUT_WELCOME_DEFAULT = {
},
},
},
{
id: "AW_MOBILE_DOWNLOAD",
content: {
position: "split",
background:
"radial-gradient(83.12% 83.12% at 80.59% 16.88%, #9059FF 0%, #3A8EE6 54.51%, #A0C4EA 100%)",
progress_bar: true,
logo: {},
title: "Hop from laptop to phone and back again",
subtitle:
"Grab tabs from one device and pick up where you left off on another. Plus sync your bookmarks and passwords anywhere you use Firefox.",
hero_image: {
url:
"chrome://activity-stream/content/data/content/assets/mobile-download-qr-new-user.svg",
},
cta_paragraph: {
text: "Scan the QR code to get Firefox for mobile or",
button_label: "send yourself a download link.",
action: {
type: "OPEN_URL",
data: {
args:
"https://www.mozilla.org/en-US/firefox/mobile/get-app/?utm_medium=firefox-desktop&utm_source=onboarding-modal&utm_campaign=mr2022&utm_content=new-global",
where: "tabshifted",
},
},
},
secondary_button: {
label: "Skip this step",
action: {
navigate: true,
},
},
},
},
{
id: "AW_GRATITUDE",
content: {
@ -598,13 +646,10 @@ function prepareMRContent(content) {
// Expand with logic for finalized MR designs
const { screens } = content;
//If Fx is set as default, skip Import settings screen and show colorways
let removeDefault = !content.needDefault;
if (removeDefault) {
removeScreens(
screen => screen.id?.startsWith("AW_IMPORT_SETTINGS"),
screens
);
// Do not show the screen to users who are already using firefox sync
// and syncing to a mobile device
if (lazy.usesFirefoxSync && lazy.mobileDevices > 0) {
removeScreens(screen => screen.id === "AW_MOBILE_DOWNLOAD", screens);
}
return content;

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

@ -388,6 +388,19 @@ body {
text-shadow: none;
margin-inline: 40px 0;
}
.hero-image {
flex: 1;
display: flex;
justify-content: center;
max-height: 100%;
img {
width: 100%;
max-width: 150px;
margin: 25px 0;
}
}
}
.tiles-theme-container {
@ -615,6 +628,14 @@ body {
}
}
}
.cta-paragraph button {
&.text-link {
margin: 0;
text-decoration: underline;
cursor: pointer;
}
}
}
// Override light and dark mode fancy title colors for use over light and dark backgrounds

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

@ -0,0 +1,24 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
import React from "react";
export const HeroImage = props => {
const { height, url, alt } = props;
if (!url) {
return null;
}
return (
<div className="hero-image">
<img
style={height ? { height } : null}
src={url}
alt={alt || ""}
role={alt ? null : "presentation"}
/>
</div>
);
};

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

@ -0,0 +1,35 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
import React from "react";
import { Localized } from "./MSLocalized";
export const LinkText = props => {
const { content, handleAction } = props;
if (
!(
content?.text &&
content?.button_label &&
typeof handleAction === "function"
)
) {
return null;
}
return (
<h2 className="cta-paragraph">
<Localized text={content.text}>
<span />
</Localized>
<Localized text={content.button_label}>
<button
onClick={handleAction}
value="cta_paragraph"
className="text-link"
/>
</Localized>
</h2>
);
};

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

@ -9,6 +9,8 @@ import { MobileDownloads } from "./MobileDownloads";
import { Themes } from "./Themes";
import { SecondaryCTA, StepsIndicator } from "./MultiStageAboutWelcome";
import { LanguageSwitcher } from "./LanguageSwitcher";
import { LinkText } from "./LinkText";
import { HeroImage } from "./HeroImage";
export const MultiStageProtonScreen = props => {
const { autoAdvance, handleAction, order } = props;
@ -147,16 +149,22 @@ export class ProtonScreen extends React.PureComponent {
className="section-secondary"
style={content.background ? { background: content.background } : {}}
>
<div className="message-text">
<div className="spacer-top" />
<Localized text={content.hero_text}>
<h1 />
</Localized>
<div className="spacer-bottom" />
</div>
<Localized text={content.help_text}>
<span className="attrib-text" />
</Localized>
{content.hero_image ? (
<HeroImage url={content.hero_image.url} />
) : (
<React.Fragment>
<div className="message-text">
<div className="spacer-top" />
<Localized text={content.hero_text}>
<h1 />
</Localized>
<div className="spacer-bottom" />
</div>
<Localized text={content.help_text}>
<span className="attrib-text" />
</Localized>
</React.Fragment>
)}
</div>
);
}
@ -247,6 +255,12 @@ export class ProtonScreen extends React.PureComponent {
})}
/>
</Localized>
{content.cta_paragraph ? (
<LinkText
content={content.cta_paragraph}
handleAction={this.props.handleAction}
/>
) : null}
</div>
{this.renderContentTiles()}
{this.renderLanguageSwitcher()}

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

После

Ширина:  |  Высота:  |  Размер: 21 KiB

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

После

Ширина:  |  Высота:  |  Размер: 21 KiB

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

@ -14,6 +14,19 @@ XPCOMUtils.defineLazyModuleGetters(lazy, {
ShellService: "resource:///modules/ShellService.jsm",
});
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"usesFirefoxSync",
"services.sync.username"
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"mobileDevices",
"services.sync.clients.devices.mobile",
0
);
const L10N = new Localization([
"branding/brand.ftl",
"browser/branding/brandings.ftl",
@ -230,6 +243,41 @@ const ONBOARDING_MESSAGES = () => [
},
},
},
{
id: "UPGRADE_MOBILE_DOWNLOAD",
content: {
position: "split",
background:
"radial-gradient(83.12% 83.12% at 80.59% 16.88%, #9059FF 0%, #3A8EE6 54.51%, #A0C4EA 100%)",
progress_bar: true,
logo: {},
title: "Hop from laptop to phone and back again",
subtitle:
"Grab tabs from one device and pick up where you left off on another. Plus sync your bookmarks and passwords anywhere you use Firefox.",
hero_image: {
url:
"chrome://activity-stream/content/data/content/assets/mobile-download-qr-existing-user.svg",
},
cta_paragraph: {
text: "Scan the QR code to get Firefox for mobile or",
button_label: "send yourself a download link.",
action: {
type: "OPEN_URL",
data: {
args:
"https://www.mozilla.org/en-US/firefox/mobile/get-app/?utm_medium=firefox-desktop&utm_source=onboarding-modal&utm_campaign=mr2022&utm_content=existing-global",
where: "tabshifted",
},
},
},
secondary_button: {
label: "Skip this step",
action: {
navigate: true,
},
},
},
},
{
id: "UPGRADE_GRATITUDE",
content: {
@ -723,6 +771,11 @@ const OnboardingMessageProvider = {
removeScreens(screen => screen.id?.startsWith("UPGRADE_SET_DEFAULT"));
}
// Remove mobile download screen if user has sync enabled
if (lazy.usesFirefoxSync && lazy.mobileDevices > 0) {
removeScreens(screen => screen.id === "UPGRADE_MOBILE_DOWNLOAD");
}
return message;
},
};

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

@ -0,0 +1,40 @@
import React from "react";
import { shallow } from "enzyme";
import { HeroImage } from "content-src/aboutwelcome/components/HeroImage";
describe("HeroImage component", () => {
const imageUrl = "https://example.com";
const imageHeight = "100px";
const imageAlt = "Alt text";
let wrapper;
beforeEach(() => {
wrapper = shallow(
<HeroImage url={imageUrl} alt={imageAlt} height={imageHeight} />
);
});
it("should render HeroImage component", () => {
assert.ok(wrapper.exists());
});
it("should render an image element with src prop", () => {
let imgEl = wrapper.find("img");
assert.strictEqual(imgEl.prop("src"), imageUrl);
});
it("should render image element with alt text prop", () => {
let imgEl = wrapper.find("img");
assert.equal(imgEl.prop("alt"), imageAlt);
});
it("should render an image with a set height prop", () => {
let imgEl = wrapper.find("img");
assert.propertyVal(imgEl.prop("style"), "height", imageHeight);
});
it("should not render HeroImage component", () => {
wrapper.setProps({ url: null });
assert.ok(wrapper.isEmptyRender());
});
});

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

@ -0,0 +1,42 @@
import React from "react";
import { shallow } from "enzyme";
import { LinkText } from "content-src/aboutwelcome/components/LinkText";
describe("LinkText component", () => {
let sandbox;
let wrapper;
beforeEach(() => {
sandbox = sinon.createSandbox();
const handleAction = sandbox.stub();
wrapper = shallow(
<LinkText
content={{ text: "Link Text", button_label: "Button Label" }}
handleAction={handleAction}
/>
);
});
afterEach(() => {
sandbox.restore();
});
it("should render LinkText component", () => {
assert.ok(wrapper.exists);
});
it("should not render LinkText component if button text is not passed", () => {
wrapper.setProps({ content: { text: null, button_label: "Button Label" } });
assert.ok(wrapper.isEmptyRender());
});
it("should not render LinkText component if button label is not passed", () => {
wrapper.setProps({ content: { text: "Link Text", button_label: null } });
assert.ok(wrapper.isEmptyRender());
});
it("should not render LinkText component if button action is not passed", () => {
wrapper.setProps({ handleAction: null });
assert.ok(wrapper.isEmptyRender());
});
});

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

@ -108,11 +108,11 @@
static get markup() {
return `
<hbox class="search-panel-header search-panel-current-engine">
<image class="searchbar-engine-image"></image>
<label class="searchbar-engine-name" flex="1" crop="end" role="presentation"></label>
<image class="searchbar-engine-image"/>
<label class="searchbar-engine-name" flex="1" crop="end" role="presentation"/>
</hbox>
<menuseparator class="searchbar-separator"/>
<richlistbox class="autocomplete-richlistbox search-panel-tree" flex="1"></richlistbox>
<richlistbox class="autocomplete-richlistbox search-panel-tree"/>
<menuseparator class="searchbar-separator"/>
<hbox class="search-one-offs" is_searchbar="true"/>
`;

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

@ -334,6 +334,7 @@ const AVAILABLE_SHIMS = [
"*://*.imgur.com/js/vendor.*.bundle.js",
"*://*.imgur.io/js/vendor.*.bundle.js",
"*://www.rva311.com/static/js/main.*.chunk.js",
"*://web-assets.toggl.com/app/assets/scripts/*.js", // bug 1783919
],
onlyIfPrivateBrowsing: true,
},
@ -600,6 +601,7 @@ const AVAILABLE_SHIMS = [
"*://orangerie.eu/*", // bug 1758442
"*://web.whatsapp.com/*", // bug 1767407
"*://www.tripadvisor.com/*", // bug 1779536
"*://www.office.com/*", // bug 1783921
],
},
],

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

@ -2,7 +2,7 @@
"manifest_version": 2,
"name": "Web Compatibility Interventions",
"description": "Urgent post-release fixes for web compatibility.",
"version": "104.3.0",
"version": "104.5.0",
"applications": {
"gecko": {
"id": "webcompat@mozilla.org",

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

@ -694,7 +694,14 @@ var webrtcUI = {
browserWindowIds.forEach(id => this.activePerms.delete(id));
},
showSharingDoorhanger(aActiveStream) {
/**
* Shows the Permission Panel for the tab associated with the provided
* active stream.
* @param aActiveStream - The stream that the user wants to see permissions for.
* @param aEvent - The user input event that is invoking the panel. This can be
* undefined / null if no such event exists.
*/
showSharingDoorhanger(aActiveStream, aEvent) {
let browserWindow = aActiveStream.browser.ownerGlobal;
if (aActiveStream.tab) {
browserWindow.gBrowser.selectedTab = aActiveStream.tab;
@ -702,15 +709,13 @@ var webrtcUI = {
aActiveStream.browser.focus();
}
browserWindow.focus();
let permissionBox = browserWindow.document.getElementById(
"identity-permission-box"
);
if (AppConstants.platform == "macosx" && !Services.focus.activeWindow) {
browserWindow.addEventListener(
"activate",
function() {
Services.tm.dispatchToMainThread(function() {
permissionBox.click();
browserWindow.gPermissionPanel.openPopup(aEvent);
});
},
{ once: true }
@ -720,7 +725,7 @@ var webrtcUI = {
.activateApplication(true);
return;
}
permissionBox.click();
browserWindow.gPermissionPanel.openPopup(aEvent);
},
updateWarningLabel(aMenuList) {
@ -1033,7 +1038,7 @@ class MacOSWebRTCStatusbarIndicator {
* @param {Event} aEvent - The command event for the <menuitem>.
*/
_command(aEvent) {
webrtcUI.showSharingDoorhanger(aEvent.target.stream);
webrtcUI.showSharingDoorhanger(aEvent.target.stream, aEvent);
}
/**
@ -1195,7 +1200,7 @@ function onTabSharingMenuPopupHiding(e) {
}
function onTabSharingMenuPopupCommand(e) {
webrtcUI.showSharingDoorhanger(e.target.stream);
webrtcUI.showSharingDoorhanger(e.target.stream, e);
}
function showOrCreateMenuForWindow(aWindow) {

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

@ -113,6 +113,7 @@
.search-panel-tree {
background: transparent;
color: inherit;
-moz-box-flex: 0;
}
.search-panel-tree > .autocomplete-richlistitem {

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

@ -14,6 +14,7 @@ support-files =
[browser_changes_copy_all_changes.js]
[browser_changes_copy_declaration.js]
[browser_changes_copy_rule.js]
[browser_changes_declaration_add_special_character.js]
[browser_changes_declaration_disable.js]
[browser_changes_declaration_duplicate.js]
[browser_changes_declaration_edit_value.js]

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

@ -0,0 +1,76 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that adding new CSS properties with special characters in the property
// name does note create duplicate entries.
const PROPERTY_NAME = '"abc"';
const INITIAL_VALUE = "foo";
// For assertions the quotes in the property will be escaped.
const EXPECTED_PROPERTY_NAME = '\\"abc\\"';
const TEST_URI = `
<style type='text/css'>
div {
color: red;
}
</style>
<div>test</div>
`;
add_task(async function addWithSpecialCharacter() {
await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
const { inspector, view: ruleView } = await openRuleView();
const { document: doc, store } = selectChangesView(inspector);
await selectNode("div", inspector);
const ruleEditor = getRuleViewRuleEditor(ruleView, 1);
const editor = await focusEditableField(ruleView, ruleEditor.closeBrace);
const input = editor.input;
input.value = `${PROPERTY_NAME}: ${INITIAL_VALUE};`;
let onTrackChange = waitForDispatch(store, "TRACK_CHANGE");
info("Pressing return to commit and focus the new value field");
const onModifications = ruleView.once("ruleview-changed");
EventUtils.synthesizeKey("VK_RETURN", {}, ruleView.styleWindow);
await onModifications;
await onTrackChange;
await assertAddedDeclaration(doc, EXPECTED_PROPERTY_NAME, INITIAL_VALUE);
let newValue = "def";
info(`Change the CSS declaration value to ${newValue}`);
const prop = getTextProperty(ruleView, 1, { [PROPERTY_NAME]: INITIAL_VALUE });
onTrackChange = waitForDispatch(store, "TRACK_CHANGE");
// flushCount needs to be set to 2 once when quotes are involved.
await setProperty(ruleView, prop, newValue, { flushCount: 2 });
await onTrackChange;
await assertAddedDeclaration(doc, EXPECTED_PROPERTY_NAME, newValue);
newValue = "123";
info(`Change the CSS declaration value to ${newValue}`);
onTrackChange = waitForDispatch(store, "TRACK_CHANGE");
await setProperty(ruleView, prop, newValue);
await onTrackChange;
await assertAddedDeclaration(doc, EXPECTED_PROPERTY_NAME, newValue);
});
/**
* Check that we only received a single added declaration with the expected
* value.
*/
async function assertAddedDeclaration(doc, expectedName, expectedValue) {
await waitFor(() => {
const addDecl = getAddedDeclarations(doc);
return (
addDecl.length == 1 &&
addDecl[0].value == expectedValue &&
addDecl[0].property == expectedName
);
}, "Got the expected declaration");
is(getAddedDeclarations(doc).length, 1, "Only one added declaration");
is(getRemovedDeclarations(doc).length, 0, "No removed declaration");
}

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

@ -946,7 +946,8 @@ const StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
commentOffsets,
} = oldDeclarations[index] || {};
const { value: currentValue } = newDeclarations[index] || {};
const { value: currentValue, name: currentName } =
newDeclarations[index] || {};
// A declaration is disabled if it has a `commentOffsets` array.
// Here we type coerce the value to a boolean with double-bang (!!)
const prevDisabled = !!commentOffsets;
@ -961,8 +962,10 @@ const StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
data.type = prevValue ? "declaration-add" : "declaration-update";
// If `change.newName` is defined, use it because the property is being renamed.
// Otherwise, a new declaration is being created or the value of an existing
// declaration is being updated. In that case, use the provided `change.name`.
const name = change.newName ? change.newName : change.name;
// declaration is being updated. In that case, use the currentName computed
// by the engine.
const changeName = currentName || change.name;
const name = change.newName ? change.newName : changeName;
// Append the "!important" string if defined in the incoming priority flag.
const changeValue = currentValue || change.value;

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

@ -46,20 +46,34 @@ add_task(async function() {
);
info(
"Check whether ResourceCommand catches CSS change after the property changed"
"Check whether ResourceCommand catches CSS changes after the property was renamed and updated"
);
await setProperty(style.rule, 0, "background-color", "pink");
// RuleRewriter:apply will not support a simultaneous rename + setProperty.
// Doing so would send inconsistent arguments to StyleRuleActor:setRuleText,
// the CSS text for the rule will not match the list of modifications, which
// would desynchronize the Changes view. Thankfully this scenario should not
// happen when using the UI to update the rules.
await renameProperty(style.rule, 0, "color", "background-color");
await waitUntil(() => availableResources.length === 2);
assertResource(
availableResources[1],
{ index: 0, property: "background-color", value: "pink" },
{ index: 0, property: "background-color", value: "black" },
{ index: 0, property: "color", value: "black" }
);
await setProperty(style.rule, 0, "background-color", "pink");
await waitUntil(() => availableResources.length === 3);
assertResource(
availableResources[2],
{ index: 0, property: "background-color", value: "pink" },
{ index: 0, property: "background-color", value: "black" }
);
info("Check whether ResourceCommand catches CSS change of disabling");
await setPropertyEnabled(style.rule, 0, "background-color", false);
await waitUntil(() => availableResources.length === 3);
assertResource(availableResources[2], null, {
await waitUntil(() => availableResources.length === 4);
assertResource(availableResources[3], null, {
index: 0,
property: "background-color",
value: "pink",
@ -67,9 +81,9 @@ add_task(async function() {
info("Check whether ResourceCommand catches CSS change of new property");
await createProperty(style.rule, 1, "font-size", "100px");
await waitUntil(() => availableResources.length === 4);
await waitUntil(() => availableResources.length === 5);
assertResource(
availableResources[3],
availableResources[4],
{ index: 1, property: "font-size", value: "100px" },
null
);
@ -79,11 +93,12 @@ add_task(async function() {
await resourceCommand.watchResources([resourceCommand.TYPES.CSS_CHANGE], {
onAvailable: resources => existingResources.push(...resources),
});
await waitUntil(() => existingResources.length === 4);
await waitUntil(() => existingResources.length === 5);
is(availableResources[0], existingResources[0], "1st resource is correct");
is(availableResources[1], existingResources[1], "2nd resource is correct");
is(availableResources[2], existingResources[2], "3rd resource is correct");
is(availableResources[3], existingResources[3], "4th resource is correct");
is(availableResources[4], existingResources[4], "4th resource is correct");
targetCommand.destroy();
await client.close();
@ -117,6 +132,12 @@ async function setProperty(rule, index, property, value) {
await modifications.apply();
}
async function renameProperty(rule, index, oldName, newName, value) {
const modifications = rule.startModifyingProperties({ isKnown: true });
modifications.renameProperty(index, oldName, newName);
await modifications.apply();
}
async function createProperty(rule, index, property, value) {
const modifications = rule.startModifyingProperties({ isKnown: true });
modifications.createProperty(index, property, value, "", true);

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

@ -17338,9 +17338,7 @@ Document::AutomaticStorageAccessPermissionCanBeGranted(bool hasUserActivation) {
ContentChild* cc = ContentChild::GetSingleton();
MOZ_ASSERT(cc);
return cc
->SendAutomaticStorageAccessPermissionCanBeGranted(
IPC::Principal(NodePrincipal()))
return cc->SendAutomaticStorageAccessPermissionCanBeGranted(NodePrincipal())
->Then(GetCurrentSerialEventTarget(), __func__,
[](const ContentChild::
AutomaticStorageAccessPermissionCanBeGrantedPromise::

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

@ -293,8 +293,7 @@ nsresult nsContentPermissionUtils::AskPermission(
req->IPDLAddRef();
ContentChild::GetSingleton()->SendPContentPermissionRequestConstructor(
req, permArray, IPC::Principal(principal),
IPC::Principal(topLevelPrincipal),
req, permArray, principal, topLevelPrincipal,
hasValidTransientUserGestureActivation,
isRequestDelegatedToUnsafeThirdParty, child->GetTabId());
ContentPermissionRequestChildMap()[req.get()] = child->GetTabId();

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

@ -22,16 +22,6 @@
class nsPIDOMWindowInner;
class nsContentPermissionRequestProxy;
// Forward declare IPC::Principal here which is defined in
// PermissionMessageUtils.h. Include this file will transitively includes
// "windows.h" and it defines
// #define CreateEvent CreateEventW
// #define LoadImage LoadImageW
// That will mess up windows build.
namespace IPC {
class Principal;
} // namespace IPC
namespace mozilla::dom {
class Element;

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

@ -1437,7 +1437,7 @@ DOMInterfaces = {
},
'GPUBuffer': {
'nativeType': 'mozilla::webgpu::Buffer',
'implicitJSContext': [ 'unmap' ],
'implicitJSContext': [ 'unmap', 'destroy' ],
},
'GPUCanvasContext': {
'nativeType': 'mozilla::webgpu::CanvasContext',

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

@ -156,7 +156,7 @@ void BroadcastBlobURLRegistration(const nsACString& aURI,
dom::ContentChild* cc = dom::ContentChild::GetSingleton();
Unused << NS_WARN_IF(!cc->SendStoreAndBroadcastBlobURLRegistration(
nsCString(aURI), ipcBlob, IPC::Principal(aPrincipal), aAgentClusterId));
nsCString(aURI), ipcBlob, aPrincipal, aAgentClusterId));
}
void BroadcastBlobURLUnregistration(const nsCString& aURI,
@ -170,8 +170,8 @@ void BroadcastBlobURLUnregistration(const nsCString& aURI,
dom::ContentChild* cc = dom::ContentChild::GetSingleton();
if (cc) {
Unused << NS_WARN_IF(!cc->SendUnstoreAndBroadcastBlobURLUnregistration(
aURI, IPC::Principal(aPrincipal)));
Unused << NS_WARN_IF(
!cc->SendUnstoreAndBroadcastBlobURLUnregistration(aURI, aPrincipal));
}
}

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

@ -698,8 +698,8 @@ class nsGtkNativeInitRunnable : public Runnable {
}
};
void ContentChild::Init(base::ProcessId aParentPid, const char* aParentBuildID,
mozilla::ipc::ScopedPort aPort, uint64_t aChildID,
void ContentChild::Init(mozilla::ipc::UntypedEndpoint&& aEndpoint,
const char* aParentBuildID, uint64_t aChildID,
bool aIsForBrowser) {
#ifdef MOZ_WIDGET_GTK
// When running X11 only build we need to pass a display down
@ -753,8 +753,8 @@ void ContentChild::Init(base::ProcessId aParentPid, const char* aParentBuildID,
MOZ_CRASH("Failed to initialize the thread manager in ContentChild::Init");
}
if (!Open(std::move(aPort), aParentPid)) {
MOZ_CRASH("Open failed in ContentChild::Init");
if (!aEndpoint.Bind(this)) {
MOZ_CRASH("Bind failed in ContentChild::Init");
}
sSingleton = this;

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

@ -45,6 +45,10 @@ class RemoteSpellcheckEngineChild;
class ChildProfilerController;
class BenchmarkStorageChild;
namespace ipc {
class UntypedEndpoint;
}
namespace loader {
class PScriptCacheChild;
}
@ -102,9 +106,8 @@ class ContentChild final : public PContentChild,
nsDocShellLoadState* aLoadState, bool* aWindowIsNew,
BrowsingContext** aReturn);
void Init(base::ProcessId aParentPid, const char* aParentBuildID,
mozilla::ipc::ScopedPort aPort, uint64_t aChildID,
bool aIsForBrowser);
void Init(mozilla::ipc::UntypedEndpoint&& aEndpoint,
const char* aParentBuildID, uint64_t aChildID, bool aIsForBrowser);
void InitXPCOM(XPCOMInitData&& aXPCOMInit,
const mozilla::dom::ipc::StructuredCloneData& aInitialData,

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

@ -2681,9 +2681,7 @@ bool ContentParent::LaunchSubprocessResolve(bool aIsSync,
sCreatedFirstContentProcess = true;
}
base::ProcessId procId =
base::GetProcId(mSubprocess->GetChildProcessHandle());
Open(mSubprocess->TakeInitialPort(), procId);
mSubprocess->TakeInitialEndpoint().Bind(this);
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
if (!cpm) {
@ -5191,11 +5189,6 @@ bool ContentParent::DeallocPWebrtcGlobalParent(PWebrtcGlobalParent* aActor) {
}
#endif
mozilla::ipc::IPCResult ContentParent::RecvSetOfflinePermission(
nsIPrincipal* aPrincipal) {
return IPC_OK();
}
void ContentParent::MaybeInvokeDragSession(BrowserParent* aParent) {
// dnd uses IPCBlob to transfer data to the content process and the IPC
// message is sent as normal priority. When sending input events with input
@ -5930,7 +5923,6 @@ void ContentParent::BroadcastBlobURLRegistration(
BlobURLProtocolHandler::IsBlobURLBroadcastPrincipal(aPrincipal);
nsCString uri(aURI);
IPC::Principal principal(aPrincipal);
for (auto* cp : AllProcesses(eLive)) {
if (cp != aIgnoreThisCP) {
@ -5938,7 +5930,7 @@ void ContentParent::BroadcastBlobURLRegistration(
continue;
}
nsresult rv = cp->TransmitPermissionsForPrincipal(principal);
nsresult rv = cp->TransmitPermissionsForPrincipal(aPrincipal);
if (NS_WARN_IF(NS_FAILED(rv))) {
break;
}
@ -5949,7 +5941,7 @@ void ContentParent::BroadcastBlobURLRegistration(
break;
}
Unused << cp->SendBlobURLRegistration(uri, ipcBlob, principal,
Unused << cp->SendBlobURLRegistration(uri, ipcBlob, aPrincipal,
aAgentClusterId);
}
}

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

@ -466,8 +466,6 @@ class ContentParent final : public PContentParent,
mozilla::ipc::IPCResult RecvNotifyTabDestroying(const TabId& aTabId,
const ContentParentId& aCpId);
mozilla::ipc::IPCResult RecvSetOfflinePermission(nsIPrincipal* principal);
mozilla::ipc::IPCResult RecvFinishShutdown();
void MaybeInvokeDragSession(BrowserParent* aParent);

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

@ -151,8 +151,7 @@ bool ContentProcess::Init(int aArgc, char* aArgv[]) {
return false;
}
mContent.Init(ParentPid(), *parentBuildID, IOThreadChild::TakeInitialPort(),
*childID, *isForBrowser);
mContent.Init(TakeInitialEndpoint(), *parentBuildID, *childID, *isForBrowser);
mXREEmbed.Start();
#if (defined(XP_MACOSX)) && defined(MOZ_SANDBOX)

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

@ -25,7 +25,7 @@ class ContentProcess : public mozilla::ipc::ProcessChild {
using ProcessChild = mozilla::ipc::ProcessChild;
public:
explicit ContentProcess(ProcessId aParentPid) : ProcessChild(aParentPid) {}
using ProcessChild::ProcessChild;
~ContentProcess() = default;
@ -40,10 +40,6 @@ class ContentProcess : public mozilla::ipc::ProcessChild {
mozilla::mscom::ProcessRuntime mCOMRuntime;
#endif
mozilla::ipc::ScopedXREEmbed mXREEmbed;
ContentProcess(const ContentProcess&) = delete;
const ContentProcess& operator=(const ContentProcess&) = delete;
};
} // namespace mozilla::dom

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

@ -1276,12 +1276,6 @@ parent:
async RequestAnonymousTemporaryFile(uint64_t aID);
/**
* Sets "offline-app" permission for the principal. Called when we hit
* a web app with the manifest attribute in <html>
*/
async SetOfflinePermission(nsIPrincipal principal);
/**
* Notifies the parent to continue shutting down after the child performs
* its shutdown tasks.

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

@ -12,31 +12,6 @@
#include "nsCOMPtr.h"
#include "nsIPrincipal.h"
namespace IPC {
/**
* Legacy IPC::Principal type. Use nsIPrincipal directly in new IPDL code.
*/
class Principal {
friend struct mozilla::ipc::IPDLParamTraits<Principal>;
public:
Principal() = default;
explicit Principal(nsIPrincipal* aPrincipal) : mPrincipal(aPrincipal) {}
Principal(Principal&&) = default;
Principal& operator=(Principal&& aOther) = default;
Principal& operator=(const Principal& aOther) = delete;
operator nsIPrincipal*() const { return mPrincipal.get(); }
private:
RefPtr<nsIPrincipal> mPrincipal;
};
} // namespace IPC
namespace mozilla::ipc {
template <>
@ -58,19 +33,6 @@ struct IPDLParamTraits<nsIPrincipal*> {
}
};
template <>
struct IPDLParamTraits<IPC::Principal> {
typedef IPC::Principal paramType;
static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor,
const paramType& aParam) {
WriteIPDLParam(aWriter, aActor, aParam.mPrincipal);
}
static bool Read(IPC::MessageReader* aReader, IProtocol* aActor,
paramType* aResult) {
return ReadIPDLParam(aReader, aActor, &aResult->mPrincipal);
}
};
} // namespace mozilla::ipc
#endif // mozilla_dom_permission_message_utils_h__

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

@ -159,8 +159,8 @@ static bool GetPluginPaths(const nsAString& aPluginPath,
# endif // MOZ_SANDBOX
#endif // XP_MACOSX
bool GMPChild::Init(const nsAString& aPluginPath, base::ProcessId aParentPid,
mozilla::ipc::ScopedPort aPort) {
bool GMPChild::Init(const nsAString& aPluginPath,
mozilla::ipc::UntypedEndpoint&& aEndpoint) {
GMP_CHILD_LOG_DEBUG("%s pluginPath=%s", __FUNCTION__,
NS_ConvertUTF16toUTF8(aPluginPath).get());
@ -170,7 +170,7 @@ bool GMPChild::Init(const nsAString& aPluginPath, base::ProcessId aParentPid,
return false;
}
if (NS_WARN_IF(!Open(std::move(aPort), aParentPid))) {
if (NS_WARN_IF(!aEndpoint.Bind(this))) {
return false;
}

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

@ -28,8 +28,8 @@ class GMPChild : public PGMPChild {
GMPChild();
virtual ~GMPChild();
bool Init(const nsAString& aPluginPath, base::ProcessId aParentPid,
mozilla::ipc::ScopedPort aPort);
bool Init(const nsAString& aPluginPath,
mozilla::ipc::UntypedEndpoint&& aEndpoint);
MessageLoop* GMPMessageLoop();
// Main thread only.

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

@ -326,8 +326,7 @@ nsresult GMPParent::LoadProcess() {
mChildPid = base::GetProcId(mProcess->GetChildProcessHandle());
GMP_PARENT_LOG_DEBUG("%s: Launched new child process", __FUNCTION__);
bool opened = Open(mProcess->TakeInitialPort(),
base::GetProcId(mProcess->GetChildProcessHandle()));
bool opened = mProcess->TakeInitialEndpoint().Bind(this);
if (!opened) {
GMP_PARENT_LOG_DEBUG("%s: Failed to open channel to new child process",
__FUNCTION__);

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

@ -14,9 +14,6 @@ using mozilla::ipc::IOThreadChild;
namespace mozilla::gmp {
GMPProcessChild::GMPProcessChild(ProcessId aParentPid)
: ProcessChild(aParentPid) {}
GMPProcessChild::~GMPProcessChild() = default;
bool GMPProcessChild::Init(int aArgc, char* aArgv[]) {
@ -40,8 +37,7 @@ bool GMPProcessChild::Init(int aArgc, char* aArgv[]) {
BackgroundHangMonitor::Startup();
return mPlugin.Init(pluginFilename, ParentPid(),
IOThreadChild::TakeInitialPort());
return mPlugin.Init(pluginFilename, TakeInitialEndpoint());
}
void GMPProcessChild::CleanUp() { BackgroundHangMonitor::Shutdown(); }

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

@ -18,7 +18,7 @@ class GMPProcessChild final : public mozilla::ipc::ProcessChild {
typedef mozilla::ipc::ProcessChild ProcessChild;
public:
explicit GMPProcessChild(ProcessId aParentPid);
using ProcessChild::ProcessChild;
~GMPProcessChild();
bool Init(int aArgc, char* aArgv[]) override;
@ -26,7 +26,6 @@ class GMPProcessChild final : public mozilla::ipc::ProcessChild {
private:
GMPChild mPlugin;
DISALLOW_COPY_AND_ASSIGN(GMPProcessChild);
};
} // namespace mozilla::gmp

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

@ -68,8 +68,8 @@ RDDParent* RDDParent::GetSingleton() {
return sRDDParent;
}
bool RDDParent::Init(base::ProcessId aParentPid, const char* aParentBuildID,
mozilla::ipc::ScopedPort aPort) {
bool RDDParent::Init(mozilla::ipc::UntypedEndpoint&& aEndpoint,
const char* aParentBuildID) {
// Initialize the thread manager before starting IPC. Otherwise, messages
// may be posted to the main thread and we won't be able to process them.
if (NS_WARN_IF(NS_FAILED(nsThreadManager::get().Init()))) {
@ -77,7 +77,7 @@ bool RDDParent::Init(base::ProcessId aParentPid, const char* aParentBuildID,
}
// Now it's safe to start IPC.
if (NS_WARN_IF(!Open(std::move(aPort), aParentPid))) {
if (NS_WARN_IF(!aEndpoint.Bind(this))) {
return false;
}

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

@ -24,8 +24,8 @@ class RDDParent final : public PRDDParent {
ipc::AsyncBlockers& AsyncShutdownService() { return mShutdownBlockers; }
bool Init(base::ProcessId aParentPid, const char* aParentBuildID,
mozilla::ipc::ScopedPort aPort);
bool Init(mozilla::ipc::UntypedEndpoint&& aEndpoint,
const char* aParentBuildID);
mozilla::ipc::IPCResult RecvInit(nsTArray<GfxVarUpdate>&& vars,
const Maybe<ipc::FileDescriptor>& aBrokerFd,

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

@ -169,8 +169,7 @@ void RDDProcessHost::InitAfterConnect(bool aSucceeded) {
}
mProcessToken = ++sRDDProcessTokenCounter;
mRDDChild = MakeUnique<RDDChild>(this);
DebugOnly<bool> rv = mRDDChild->Open(
TakeInitialPort(), base::GetProcId(GetChildProcessHandle()));
DebugOnly<bool> rv = TakeInitialEndpoint().Bind(mRDDChild.get());
MOZ_ASSERT(rv);
// Only clear mPrefSerializer in the success case to avoid a

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

@ -19,9 +19,6 @@ namespace mozilla {
using namespace ipc;
RDDProcessImpl::RDDProcessImpl(ProcessId aParentPid)
: ProcessChild(aParentPid) {}
RDDProcessImpl::~RDDProcessImpl() = default;
bool RDDProcessImpl::Init(int aArgc, char* aArgv[]) {
@ -46,8 +43,7 @@ bool RDDProcessImpl::Init(int aArgc, char* aArgv[]) {
return false;
}
return mRDD.Init(ParentPid(), *parentBuildID,
IOThreadChild::TakeInitialPort());
return mRDD.Init(TakeInitialEndpoint(), *parentBuildID);
}
void RDDProcessImpl::CleanUp() { NS_ShutdownXPCOM(nullptr); }

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

@ -19,15 +19,13 @@ namespace mozilla {
// is a RDDParent. It is instantiated as a singleton in XRE_InitChildProcess.
class RDDProcessImpl final : public ipc::ProcessChild {
public:
explicit RDDProcessImpl(ProcessId aParentPid);
using ipc::ProcessChild::ProcessChild;
~RDDProcessImpl();
bool Init(int aArgc, char* aArgv[]) override;
void CleanUp() override;
private:
DISALLOW_COPY_AND_ASSIGN(RDDProcessImpl);
RDDParent mRDD;
#if defined(XP_WIN)

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

@ -1091,8 +1091,7 @@ NotificationObserver::Observe(nsISupports* aSubject, const char* aTopic,
// Permissions can't be removed from the content process. Send a message
// to the parent; `ContentParent::RecvDisableNotifications` will call
// `RemovePermission`.
ContentChild::GetSingleton()->SendDisableNotifications(
IPC::Principal(mPrincipal));
ContentChild::GetSingleton()->SendDisableNotifications(mPrincipal);
return NS_OK;
} else if (!strcmp("alertsettingscallback", aTopic)) {
if (XRE_IsParentProcess()) {
@ -1100,8 +1099,7 @@ NotificationObserver::Observe(nsISupports* aSubject, const char* aTopic,
}
// `ContentParent::RecvOpenNotificationSettings` notifies observers in the
// parent process.
ContentChild::GetSingleton()->SendOpenNotificationSettings(
IPC::Principal(mPrincipal));
ContentChild::GetSingleton()->SendOpenNotificationSettings(mPrincipal);
return NS_OK;
} else if (!strcmp("alertshow", aTopic) || !strcmp("alertfinished", aTopic)) {
Unused << NS_WARN_IF(NS_FAILED(AdjustPushQuota(aTopic)));

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

@ -148,7 +148,7 @@ already_AddRefed<Promise> Permissions::Revoke(JSContext* aCx,
// to the parent; `ContentParent::RecvRemovePermission` will call
// `RemovePermission`.
ContentChild::GetSingleton()->SendRemovePermission(
IPC::Principal(document->NodePrincipal()), permissionType, &rv);
document->NodePrincipal(), permissionType, &rv);
}
if (NS_WARN_IF(NS_FAILED(rv))) {

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

@ -297,19 +297,17 @@ nsresult PushMessageDispatcher::NotifyWorkers() {
bool PushMessageDispatcher::SendToParent(ContentChild* aParentActor) {
if (mData) {
return aParentActor->SendNotifyPushObserversWithData(
mScope, IPC::Principal(mPrincipal), mMessageId, mData.ref());
mScope, mPrincipal, mMessageId, mData.ref());
}
return aParentActor->SendNotifyPushObservers(
mScope, IPC::Principal(mPrincipal), mMessageId);
return aParentActor->SendNotifyPushObservers(mScope, mPrincipal, mMessageId);
}
bool PushMessageDispatcher::SendToChild(ContentParent* aContentActor) {
if (mData) {
return aContentActor->SendPushWithData(mScope, IPC::Principal(mPrincipal),
mMessageId, mData.ref());
return aContentActor->SendPushWithData(mScope, mPrincipal, mMessageId,
mData.ref());
}
return aContentActor->SendPush(mScope, IPC::Principal(mPrincipal),
mMessageId);
return aContentActor->SendPush(mScope, mPrincipal, mMessageId);
}
PushSubscriptionChangeDispatcher::PushSubscriptionChangeDispatcher(
@ -341,14 +339,13 @@ nsresult PushSubscriptionChangeDispatcher::NotifyWorkers() {
bool PushSubscriptionChangeDispatcher::SendToParent(
ContentChild* aParentActor) {
return aParentActor->SendNotifyPushSubscriptionChangeObservers(
mScope, IPC::Principal(mPrincipal));
return aParentActor->SendNotifyPushSubscriptionChangeObservers(mScope,
mPrincipal);
}
bool PushSubscriptionChangeDispatcher::SendToChild(
ContentParent* aContentActor) {
return aContentActor->SendPushSubscriptionChange(mScope,
IPC::Principal(mPrincipal));
return aContentActor->SendPushSubscriptionChange(mScope, mPrincipal);
}
PushSubscriptionModifiedDispatcher::PushSubscriptionModifiedDispatcher(
@ -367,14 +364,14 @@ nsresult PushSubscriptionModifiedDispatcher::NotifyWorkers() { return NS_OK; }
bool PushSubscriptionModifiedDispatcher::SendToParent(
ContentChild* aParentActor) {
return aParentActor->SendNotifyPushSubscriptionModifiedObservers(
mScope, IPC::Principal(mPrincipal));
return aParentActor->SendNotifyPushSubscriptionModifiedObservers(mScope,
mPrincipal);
}
bool PushSubscriptionModifiedDispatcher::SendToChild(
ContentParent* aContentActor) {
return aContentActor->SendNotifyPushSubscriptionModifiedObservers(
mScope, IPC::Principal(mPrincipal));
return aContentActor->SendNotifyPushSubscriptionModifiedObservers(mScope,
mPrincipal);
}
PushErrorDispatcher::PushErrorDispatcher(const nsACString& aScope,
@ -414,13 +411,11 @@ nsresult PushErrorDispatcher::NotifyWorkers() {
}
bool PushErrorDispatcher::SendToParent(ContentChild* aContentActor) {
return aContentActor->SendPushError(mScope, IPC::Principal(mPrincipal),
mMessage, mFlags);
return aContentActor->SendPushError(mScope, mPrincipal, mMessage, mFlags);
}
bool PushErrorDispatcher::SendToChild(ContentParent* aContentActor) {
return aContentActor->SendPushError(mScope, IPC::Principal(mPrincipal),
mMessage, mFlags);
return aContentActor->SendPushError(mScope, mPrincipal, mMessage, mFlags);
}
nsresult PushErrorDispatcher::HandleNoChildProcesses() {

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

@ -25,7 +25,7 @@ NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(Buffer, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(Buffer, Release)
NS_IMPL_CYCLE_COLLECTION_CLASS(Buffer)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Buffer)
tmp->Cleanup();
tmp->Drop();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
@ -43,55 +43,101 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Buffer)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
Buffer::Buffer(Device* const aParent, RawId aId, BufferAddress aSize,
uint32_t aUsage)
: ChildOf(aParent), mId(aId), mSize(aSize), mUsage(aUsage) {
uint32_t aUsage, ipc::Shmem&& aShmem)
: ChildOf(aParent), mId(aId), mSize(aSize), mUsage(aUsage), mShmem(aShmem) {
mozilla::HoldJSObjects(this);
MOZ_ASSERT(mParent);
}
Buffer::~Buffer() {
Cleanup();
Drop();
mozilla::DropJSObjects(this);
}
bool Buffer::Mappable() const {
return (mUsage & (dom::GPUBufferUsage_Binding::MAP_WRITE |
dom::GPUBufferUsage_Binding::MAP_READ)) != 0;
}
already_AddRefed<Buffer> Buffer::Create(Device* aDevice, RawId aDeviceId,
const dom::GPUBufferDescriptor& aDesc,
ErrorResult& aRv) {
if (aDevice->IsLost()) {
RefPtr<Buffer> buffer =
new Buffer(aDevice, 0, aDesc.mSize, 0, ipc::Shmem());
return buffer.forget();
}
void Buffer::Cleanup() {
if (mValid && mParent) {
mValid = false;
RefPtr<WebGPUChild> actor = aDevice->GetBridge();
if (mMapped && !mMapped->mArrayBuffers.IsEmpty()) {
// The array buffers could live longer than us and our shmem, so make sure
// we clear the external buffer bindings.
dom::AutoJSAPI jsapi;
if (jsapi.Init(mParent->GetOwnerGlobal())) {
IgnoredErrorResult rv;
UnmapArrayBuffers(jsapi.cx(), rv);
}
ipc::Shmem shmem;
bool hasMapFlags = aDesc.mUsage & (dom::GPUBufferUsage_Binding::MAP_WRITE |
dom::GPUBufferUsage_Binding::MAP_READ);
if (hasMapFlags || aDesc.mMappedAtCreation) {
const auto checked = CheckedInt<size_t>(aDesc.mSize);
if (!checked.isValid()) {
aRv.ThrowRangeError("Mappable size is too large");
return nullptr;
}
size_t size = checked.value();
if (size == 0) {
// Can't send zero-sized shmems.
size = 1;
}
auto bridge = mParent->GetBridge();
if (bridge && bridge->IsOpen()) {
// Note: even if the buffer is considered mapped,
// the shmem may be empty before the mapAsync callback
// is resolved.
if (mMapped && mMapped->mShmem.IsReadable()) {
// Note: if the bridge is closed, all associated shmems are already
// deleted.
bridge->DeallocShmem(mMapped->mShmem);
}
bridge->SendBufferDestroy(mId);
if (!actor->AllocUnsafeShmem(size, &shmem)) {
aRv.ThrowAbortError(
nsPrintfCString("Unable to allocate shmem of size %" PRIuPTR, size));
return nullptr;
}
// zero out memory
memset(shmem.get<uint8_t>(), 0, size);
}
MaybeShmem maybeShmem = mozilla::null_t();
if (shmem.IsReadable()) {
maybeShmem = shmem;
}
RawId id = actor->DeviceCreateBuffer(aDeviceId, aDesc, std::move(maybeShmem));
RefPtr<Buffer> buffer =
new Buffer(aDevice, id, aDesc.mSize, aDesc.mUsage, std::move(shmem));
if (aDesc.mMappedAtCreation) {
// Mapped at creation's raison d'être is write access, since the buffer is
// being created and there isn't anything interesting to read in it yet.
bool writable = true;
buffer->SetMapped(0, aDesc.mSize, writable);
}
return buffer.forget();
}
void Buffer::Drop() {
AbortMapRequest();
if (mMapped && !mMapped->mArrayBuffers.IsEmpty()) {
// The array buffers could live longer than us and our shmem, so make sure
// we clear the external buffer bindings.
dom::AutoJSAPI jsapi;
if (jsapi.Init(GetDevice().GetOwnerGlobal())) {
IgnoredErrorResult rv;
UnmapArrayBuffers(jsapi.cx(), rv);
}
}
mMapped.reset();
if (mValid && !GetDevice().IsLost()) {
GetDevice().GetBridge()->SendBufferDrop(mId);
}
mValid = false;
}
void Buffer::SetMapped(ipc::Shmem&& aShmem, bool aWritable) {
void Buffer::SetMapped(BufferAddress aOffset, BufferAddress aSize,
bool aWritable) {
MOZ_ASSERT(!mMapped);
MOZ_RELEASE_ASSERT(aOffset <= mSize);
MOZ_RELEASE_ASSERT(aSize <= mSize - aOffset);
mMapped.emplace();
mMapped->mShmem = std::move(aShmem);
mMapped->mWritable = aWritable;
mMapped->mOffset = aOffset;
mMapped->mSize = aSize;
}
already_AddRefed<dom::Promise> Buffer::MapAsync(
@ -101,59 +147,63 @@ already_AddRefed<dom::Promise> Buffer::MapAsync(
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
if (mMapped) {
aRv.ThrowInvalidStateError("Unable to map a buffer that is already mapped");
return nullptr;
if (GetDevice().IsLost()) {
promise->MaybeRejectWithOperationError("Device Lost");
return promise.forget();
}
switch (aMode) {
case dom::GPUMapMode_Binding::READ:
if ((mUsage & dom::GPUBufferUsage_Binding::MAP_READ) == 0) {
promise->MaybeRejectWithOperationError(
"mapAsync: 'mode' is GPUMapMode.READ, \
but GPUBuffer was not created with GPUBufferUsage.MAP_READ");
return promise.forget();
}
break;
case dom::GPUMapMode_Binding::WRITE:
if ((mUsage & dom::GPUBufferUsage_Binding::MAP_WRITE) == 0) {
promise->MaybeRejectWithOperationError(
"mapAsync: 'mode' is GPUMapMode.WRITE, \
but GPUBuffer was not created with GPUBufferUsage.MAP_WRITE");
return promise.forget();
}
break;
default:
promise->MaybeRejectWithOperationError(
"GPUBuffer.mapAsync 'mode' argument \
must be either GPUMapMode.READ or GPUMapMode.WRITE");
return promise.forget();
if (mMapRequest) {
promise->MaybeRejectWithOperationError("Buffer mapping is already pending");
return promise.forget();
}
// Initialize with a dummy shmem, it will become real after the promise is
// resolved.
SetMapped(ipc::Shmem(), aMode == dom::GPUMapMode_Binding::WRITE);
const auto checked = aSize.WasPassed() ? CheckedInt<size_t>(aSize.Value())
: CheckedInt<size_t>(mSize) - aOffset;
if (!checked.isValid()) {
aRv.ThrowRangeError("Mapped size is too large");
return nullptr;
BufferAddress size = 0;
if (aSize.WasPassed()) {
size = aSize.Value();
} else if (aOffset <= mSize) {
// Default to passing the reminder of the buffer after the provided offset.
size = mSize - aOffset;
} else {
// The provided offset is larger than the buffer size.
// The parent side will handle the error, we can let the requested size be
// zero.
}
const auto& size = checked.value();
RefPtr<Buffer> self(this);
auto mappingPromise = mParent->MapBufferAsync(mId, aMode, aOffset, size, aRv);
if (!mappingPromise) {
return nullptr;
}
auto mappingPromise =
GetDevice().GetBridge()->SendBufferMap(mId, aMode, aOffset, size);
MOZ_ASSERT(mappingPromise);
mMapRequest = promise;
mappingPromise->Then(
GetCurrentSerialEventTarget(), __func__,
[promise, self](ipc::Shmem&& aShmem) {
self->mMapped->mShmem = std::move(aShmem);
promise->MaybeResolve(0);
[promise, self](BufferMapResult&& aResult) {
// Unmap might have been called while the result was on the way back.
if (promise->State() != dom::Promise::PromiseState::Pending) {
return;
}
switch (aResult.type()) {
case BufferMapResult::TBufferMapSuccess: {
auto& success = aResult.get_BufferMapSuccess();
self->mMapRequest = nullptr;
self->SetMapped(success.offset(), success.size(),
success.writable());
promise->MaybeResolve(0);
break;
}
case BufferMapResult::TBufferMapError: {
auto& error = aResult.get_BufferMapError();
self->RejectMapRequest(promise, error.message());
break;
}
default: {
MOZ_CRASH("unreachable");
}
}
},
[promise](const ipc::ResponseRejectReason&) {
promise->MaybeRejectWithAbortError("Internal communication error!");
@ -165,27 +215,26 @@ must be either GPUMapMode.READ or GPUMapMode.WRITE");
void Buffer::GetMappedRange(JSContext* aCx, uint64_t aOffset,
const dom::Optional<uint64_t>& aSize,
JS::Rooted<JSObject*>* aObject, ErrorResult& aRv) {
if (!mMapped || !mShmem.IsReadable()) {
aRv.ThrowInvalidStateError("Buffer is not mapped");
return;
}
const auto checkedOffset = CheckedInt<size_t>(aOffset);
const auto checkedSize = aSize.WasPassed()
? CheckedInt<size_t>(aSize.Value())
: CheckedInt<size_t>(mSize) - aOffset;
const auto checkedMinBufferSize = checkedOffset + checkedSize;
if (!checkedOffset.isValid() || !checkedSize.isValid() ||
!checkedMinBufferSize.isValid()) {
aRv.ThrowRangeError("Invalid mapped range");
return;
}
if (!mMapped || !mMapped->IsReady()) {
aRv.ThrowInvalidStateError("Buffer is not mapped");
return;
}
if (checkedMinBufferSize.value() > mMapped->mShmem.Size<uint8_t>()) {
aRv.ThrowOperationError("Mapped range exceeds buffer size");
!checkedMinBufferSize.isValid() || aOffset < mMapped->mOffset ||
checkedMinBufferSize.value() > mMapped->mOffset + mMapped->mSize) {
aRv.ThrowRangeError("Invalid range");
return;
}
auto* const arrayBuffer = mParent->CreateExternalArrayBuffer(
aCx, checkedOffset.value(), checkedSize.value(), mMapped->mShmem);
auto* const arrayBuffer = GetDevice().CreateExternalArrayBuffer(
aCx, checkedOffset.value(), checkedSize.value(), mShmem);
if (!arrayBuffer) {
aRv.NoteJSContextException(aCx);
return;
@ -208,24 +257,62 @@ void Buffer::UnmapArrayBuffers(JSContext* aCx, ErrorResult& aRv) {
mMapped->mArrayBuffers.Clear();
AbortMapRequest();
if (NS_WARN_IF(!detachedArrayBuffers)) {
aRv.NoteJSContextException(aCx);
return;
}
}
void Buffer::RejectMapRequest(dom::Promise* aPromise, nsACString& message) {
if (mMapRequest == aPromise) {
mMapRequest = nullptr;
}
aPromise->MaybeRejectWithOperationError(message);
}
void Buffer::AbortMapRequest() {
if (mMapRequest) {
mMapRequest->MaybeRejectWithAbortError("Buffer unmapped");
}
mMapRequest = nullptr;
}
void Buffer::Unmap(JSContext* aCx, ErrorResult& aRv) {
if (!mMapped) {
return;
}
UnmapArrayBuffers(aCx, aRv);
mParent->UnmapBuffer(mId, std::move(mMapped->mShmem), mMapped->mWritable,
Mappable());
bool hasMapFlags = mUsage & (dom::GPUBufferUsage_Binding::MAP_WRITE |
dom::GPUBufferUsage_Binding::MAP_READ);
if (!hasMapFlags) {
// We get here if the buffer was mapped at creation without map flags.
// It won't be possible to map the buffer again so we can get rid of
// our shmem handle on this side. The parent side will deallocate it.
mShmem = ipc::Shmem();
}
if (!GetDevice().IsLost()) {
GetDevice().GetBridge()->SendBufferUnmap(GetDevice().mId, mId,
mMapped->mWritable);
}
mMapped.reset();
}
void Buffer::Destroy() {
void Buffer::Destroy(JSContext* aCx, ErrorResult& aRv) {
if (mMapped) {
Unmap(aCx, aRv);
}
if (!GetDevice().IsLost()) {
GetDevice().GetBridge()->SendBufferDestroy(mId);
}
// TODO: we don't have to implement it right now, but it's used by the
// examples
}

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

@ -17,9 +17,10 @@ namespace mozilla {
class ErrorResult;
namespace dom {
struct GPUBufferDescriptor;
template <typename T>
class Optional;
}
} // namespace dom
namespace ipc {
class Shmem;
@ -29,15 +30,14 @@ namespace webgpu {
class Device;
struct MappedInfo {
ipc::Shmem mShmem;
// True if mapping is requested for writing.
bool mWritable = false;
// Populated by `GetMappedRange`.
nsTArray<JS::Heap<JSObject*>> mArrayBuffers;
BufferAddress mOffset;
BufferAddress mSize;
MappedInfo() = default;
MappedInfo(const MappedInfo&) = delete;
bool IsReady() const { return mShmem.IsReadable(); }
};
class Buffer final : public ObjectBase, public ChildOf<Device> {
@ -45,17 +45,31 @@ class Buffer final : public ObjectBase, public ChildOf<Device> {
GPU_DECL_CYCLE_COLLECTION(Buffer)
GPU_DECL_JS_WRAP(Buffer)
Buffer(Device* const aParent, RawId aId, BufferAddress aSize,
uint32_t aUsage);
void SetMapped(ipc::Shmem&& aShmem, bool aWritable);
static already_AddRefed<Buffer> Create(Device* aDevice, RawId aDeviceId,
const dom::GPUBufferDescriptor& aDesc,
ErrorResult& aRv);
already_AddRefed<dom::Promise> MapAsync(uint32_t aMode, uint64_t aOffset,
const dom::Optional<uint64_t>& aSize,
ErrorResult& aRv);
void GetMappedRange(JSContext* aCx, uint64_t aOffset,
const dom::Optional<uint64_t>& aSize,
JS::Rooted<JSObject*>* aObject, ErrorResult& aRv);
void Unmap(JSContext* aCx, ErrorResult& aRv);
void Destroy(JSContext* aCx, ErrorResult& aRv);
const RawId mId;
private:
Buffer(Device* const aParent, RawId aId, BufferAddress aSize, uint32_t aUsage,
ipc::Shmem&& aShmem);
virtual ~Buffer();
void Cleanup();
Device& GetDevice() { return *mParent; }
void Drop();
void UnmapArrayBuffers(JSContext* aCx, ErrorResult& aRv);
bool Mappable() const;
void RejectMapRequest(dom::Promise* aPromise, nsACString& message);
void AbortMapRequest();
void SetMapped(BufferAddress aOffset, BufferAddress aSize, bool aWritable);
// Note: we can't map a buffer with the size that don't fit into `size_t`
// (which may be smaller than `BufferAddress`), but general not all buffers
@ -65,16 +79,10 @@ class Buffer final : public ObjectBase, public ChildOf<Device> {
nsString mLabel;
// Information about the currently active mapping.
Maybe<MappedInfo> mMapped;
public:
already_AddRefed<dom::Promise> MapAsync(uint32_t aMode, uint64_t aOffset,
const dom::Optional<uint64_t>& aSize,
ErrorResult& aRv);
void GetMappedRange(JSContext* aCx, uint64_t aOffset,
const dom::Optional<uint64_t>& aSize,
JS::Rooted<JSObject*>* aObject, ErrorResult& aRv);
void Unmap(JSContext* aCx, ErrorResult& aRv);
void Destroy();
RefPtr<dom::Promise> mMapRequest;
// mShmem does not point to a shared memory segment if the buffer is not
// mappable.
ipc::Shmem mShmem;
};
} // namespace webgpu

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

@ -95,6 +95,8 @@ void Device::CleanupUnregisteredInParent() {
mValid = false;
}
bool Device::IsLost() const { return !mBridge || !mBridge->CanSend(); }
// Generate an error on the Device timeline for this device.
//
// aMessage is interpreted as UTF-8.
@ -121,81 +123,7 @@ dom::Promise* Device::GetLost(ErrorResult& aRv) {
already_AddRefed<Buffer> Device::CreateBuffer(
const dom::GPUBufferDescriptor& aDesc, ErrorResult& aRv) {
if (!mBridge->CanSend()) {
RefPtr<Buffer> buffer = new Buffer(this, 0, aDesc.mSize, 0);
return buffer.forget();
}
ipc::Shmem shmem;
bool hasMapFlags = aDesc.mUsage & (dom::GPUBufferUsage_Binding::MAP_WRITE |
dom::GPUBufferUsage_Binding::MAP_READ);
if (hasMapFlags || aDesc.mMappedAtCreation) {
const auto checked = CheckedInt<size_t>(aDesc.mSize);
if (!checked.isValid()) {
aRv.ThrowRangeError("Mappable size is too large");
return nullptr;
}
const auto& size = checked.value();
// TODO: use `ShmemPool`?
if (!mBridge->AllocShmem(size, &shmem)) {
aRv.ThrowAbortError(
nsPrintfCString("Unable to allocate shmem of size %" PRIuPTR, size));
return nullptr;
}
// zero out memory
memset(shmem.get<uint8_t>(), 0, size);
}
// If the buffer is not mapped at creation, and it has Shmem, we send it
// to the GPU process. Otherwise, we keep it.
RawId id = mBridge->DeviceCreateBuffer(mId, aDesc);
RefPtr<Buffer> buffer = new Buffer(this, id, aDesc.mSize, aDesc.mUsage);
if (aDesc.mMappedAtCreation) {
buffer->SetMapped(std::move(shmem),
!(aDesc.mUsage & dom::GPUBufferUsage_Binding::MAP_READ));
} else if (hasMapFlags) {
mBridge->SendBufferReturnShmem(id, std::move(shmem));
}
return buffer.forget();
}
RefPtr<MappingPromise> Device::MapBufferAsync(RawId aId, uint32_t aMode,
size_t aOffset, size_t aSize,
ErrorResult& aRv) {
ffi::WGPUHostMap mode;
switch (aMode) {
case dom::GPUMapMode_Binding::READ:
mode = ffi::WGPUHostMap_Read;
break;
case dom::GPUMapMode_Binding::WRITE:
mode = ffi::WGPUHostMap_Write;
break;
default:
MOZ_CRASH("should have checked aMode in Buffer::MapAsync");
}
const CheckedInt<uint64_t> offset(aOffset);
if (!offset.isValid()) {
aRv.ThrowRangeError("Mapped offset is too large");
return nullptr;
}
const CheckedInt<uint64_t> size(aSize);
if (!size.isValid()) {
aRv.ThrowRangeError("Mapped size is too large");
return nullptr;
}
return mBridge->SendBufferMap(aId, mode, offset.value(), size.value());
}
void Device::UnmapBuffer(RawId aId, ipc::Shmem&& aShmem, bool aFlush,
bool aKeepShmem) {
if (mBridge->CanSend()) {
mBridge->SendBufferUnmap(aId, std::move(aShmem), aFlush, aKeepShmem);
}
return Buffer::Create(this, mId, aDesc, aRv);
}
already_AddRefed<Texture> Device::CreateTexture(

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

@ -12,6 +12,7 @@
#include "mozilla/RefPtr.h"
#include "mozilla/WeakPtr.h"
#include "mozilla/webgpu/WebGPUTypes.h"
#include "mozilla/webgpu/PWebGPUTypes.h"
#include "mozilla/webrender/WebRenderAPI.h"
#include "mozilla/DOMEventTargetHelper.h"
@ -79,7 +80,8 @@ class SupportedLimits;
class Texture;
class WebGPUChild;
using MappingPromise = MozPromise<ipc::Shmem, ipc::ResponseRejectReason, true>;
using MappingPromise =
MozPromise<BufferMapResult, ipc::ResponseRejectReason, true>;
class Device final : public DOMEventTargetHelper, public SupportsWeakPtr {
public:
@ -98,11 +100,6 @@ class Device final : public DOMEventTargetHelper, public SupportsWeakPtr {
static JSObject* CreateExternalArrayBuffer(JSContext* aCx, size_t aOffset,
size_t aSize,
const ipc::Shmem& aShmem);
RefPtr<MappingPromise> MapBufferAsync(RawId aId, uint32_t aMode,
size_t aOffset, size_t aSize,
ErrorResult& aRv);
void UnmapBuffer(RawId aId, ipc::Shmem&& aShmem, bool aFlush,
bool aKeepShmem);
already_AddRefed<Texture> InitSwapChain(
const dom::GPUCanvasConfiguration& aDesc,
const layers::CompositableHandle& aHandle, gfx::SurfaceFormat aFormat,
@ -113,6 +110,8 @@ class Device final : public DOMEventTargetHelper, public SupportsWeakPtr {
void GenerateError(const nsCString& aMessage);
bool IsLost() const;
private:
~Device();
void Cleanup();

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

@ -11,7 +11,6 @@ using RawId from "mozilla/webgpu/WebGPUTypes.h";
using dom::GPURequestAdapterOptions from "mozilla/dom/WebGPUBinding.h";
using dom::GPUCommandBufferDescriptor from "mozilla/dom/WebGPUBinding.h";
using dom::GPUBufferDescriptor from "mozilla/dom/WebGPUBinding.h";
using webgpu::ffi::WGPUHostMap from "mozilla/webgpu/ffi/wgpu.h";
using MaybeScopedError from "mozilla/webgpu/WebGPUTypes.h";
using WebGPUCompilationMessage from "mozilla/webgpu/WebGPUTypes.h";
@ -20,6 +19,7 @@ include "mozilla/layers/LayersMessageUtils.h";
include "mozilla/webgpu/WebGPUSerialize.h";
include "mozilla/layers/WebRenderMessageUtils.h";
include protocol PCanvasManager;
include PWebGPUTypes;
namespace mozilla {
namespace webgpu {
@ -40,17 +40,17 @@ parent:
async CommandEncoderAction(RawId selfId, RawId aDeviceId, ByteBuf buf);
async BumpImplicitBindGroupLayout(RawId pipelineId, bool isCompute, uint32_t index, RawId assignId);
async CreateBuffer(RawId deviceId, RawId bufferId, GPUBufferDescriptor desc);
async CreateBuffer(RawId deviceId, RawId bufferId, GPUBufferDescriptor desc, MaybeShmem shmem);
async InstanceRequestAdapter(GPURequestAdapterOptions options, RawId[] ids) returns (ByteBuf byteBuf);
async AdapterRequestDevice(RawId selfId, ByteBuf buf, RawId newId) returns (bool success);
async AdapterDestroy(RawId selfId);
// TODO: We want to return an array of compilation messages.
async DeviceCreateShaderModule(RawId selfId, RawId bufferId, nsString label, nsCString code) returns (WebGPUCompilationMessage[] messages);
async BufferReturnShmem(RawId selfId, Shmem shmem);
async BufferMap(RawId selfId, WGPUHostMap hostMap, uint64_t offset, uint64_t size) returns (Shmem sm);
async BufferUnmap(RawId selfId, Shmem shmem, bool flush, bool keepShmem);
async BufferMap(RawId selfId, uint32_t aMode, uint64_t offset, uint64_t size) returns (BufferMapResult result);
async BufferUnmap(RawId deviceId, RawId bufferId, bool flush);
async BufferDestroy(RawId selfId);
async BufferDrop(RawId selfId);
async TextureDestroy(RawId selfId);
async TextureViewDestroy(RawId selfId);
async SamplerDestroy(RawId selfId);

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

@ -0,0 +1,31 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
using struct mozilla::null_t from "mozilla/ipc/IPCCore.h";
namespace mozilla {
namespace webgpu {
[Comparable] union MaybeShmem {
Shmem;
null_t;
};
struct BufferMapSuccess {
uint64_t offset;
uint64_t size;
bool writable;
};
struct BufferMapError {
nsCString message;
};
union BufferMapResult {
BufferMapSuccess;
BufferMapError;
};
} // namespace layers
} // namespace mozilla

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

@ -355,9 +355,10 @@ Maybe<DeviceRequest> WebGPUChild::AdapterRequestDevice(
}
RawId WebGPUChild::DeviceCreateBuffer(RawId aSelfId,
const dom::GPUBufferDescriptor& aDesc) {
const dom::GPUBufferDescriptor& aDesc,
MaybeShmem&& aShmem) {
RawId bufferId = ffi::wgpu_client_make_buffer_id(mClient.get(), aSelfId);
if (!SendCreateBuffer(aSelfId, bufferId, aDesc)) {
if (!SendCreateBuffer(aSelfId, bufferId, aDesc, aShmem)) {
MOZ_CRASH("IPC failure");
}
return bufferId;

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

@ -9,6 +9,7 @@
#include "mozilla/webgpu/PWebGPUChild.h"
#include "mozilla/MozPromise.h"
#include "mozilla/WeakPtr.h"
#include "mozilla/webgpu/ffi/wgpu.h"
namespace mozilla {
namespace dom {
@ -62,8 +63,8 @@ class WebGPUChild final : public PWebGPUChild, public SupportsWeakPtr {
Maybe<DeviceRequest> AdapterRequestDevice(
RawId aSelfId, const dom::GPUDeviceDescriptor& aDesc,
ffi::WGPULimits* aLimits);
RawId DeviceCreateBuffer(RawId aSelfId,
const dom::GPUBufferDescriptor& aDesc);
RawId DeviceCreateBuffer(RawId aSelfId, const dom::GPUBufferDescriptor& aDesc,
MaybeShmem&& aShmem);
RawId DeviceCreateTexture(RawId aSelfId,
const dom::GPUTextureDescriptor& aDesc);
RawId TextureCreateView(RawId aSelfId, RawId aDeviceId,

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

@ -312,163 +312,249 @@ ipc::IPCResult WebGPUParent::RecvInstanceRequestAdapter(
}
ipc::IPCResult WebGPUParent::RecvAdapterRequestDevice(
RawId aSelfId, const ipc::ByteBuf& aByteBuf, RawId aNewId,
RawId aAdapterId, const ipc::ByteBuf& aByteBuf, RawId aDeviceId,
AdapterRequestDeviceResolver&& resolver) {
ErrorBuffer error;
ffi::wgpu_server_adapter_request_device(
mContext.get(), aSelfId, ToFFI(&aByteBuf), aNewId, error.ToFFI());
mContext.get(), aAdapterId, ToFFI(&aByteBuf), aDeviceId, error.ToFFI());
if (ForwardError(0, error)) {
resolver(false);
} else {
mErrorScopeMap.insert({aSelfId, ErrorScopeStack()});
mErrorScopeMap.insert({aAdapterId, ErrorScopeStack()});
resolver(true);
}
return IPC_OK();
}
ipc::IPCResult WebGPUParent::RecvAdapterDestroy(RawId aSelfId) {
ffi::wgpu_server_adapter_drop(mContext.get(), aSelfId);
ipc::IPCResult WebGPUParent::RecvAdapterDestroy(RawId aAdapterId) {
ffi::wgpu_server_adapter_drop(mContext.get(), aAdapterId);
return IPC_OK();
}
ipc::IPCResult WebGPUParent::RecvDeviceDestroy(RawId aSelfId) {
ffi::wgpu_server_device_drop(mContext.get(), aSelfId);
mErrorScopeMap.erase(aSelfId);
ipc::IPCResult WebGPUParent::RecvDeviceDestroy(RawId aDeviceId) {
ffi::wgpu_server_device_drop(mContext.get(), aDeviceId);
mErrorScopeMap.erase(aDeviceId);
return IPC_OK();
}
ipc::IPCResult WebGPUParent::RecvCreateBuffer(
RawId aSelfId, RawId aBufferId, dom::GPUBufferDescriptor&& aDesc) {
WebGPUParent::BufferMapData* WebGPUParent::GetBufferMapData(RawId aBufferId) {
const auto iter = mSharedMemoryMap.find(aBufferId);
if (iter == mSharedMemoryMap.end()) {
return nullptr;
}
return &iter->second;
}
ipc::IPCResult WebGPUParent::RecvCreateBuffer(RawId aDeviceId, RawId aBufferId,
dom::GPUBufferDescriptor&& aDesc,
MaybeShmem&& aShmem) {
webgpu::StringHelper label(aDesc.mLabel);
if (aShmem.type() == MaybeShmem::TShmem) {
bool hasMapFlags = aDesc.mUsage & (dom::GPUBufferUsage_Binding::MAP_WRITE |
dom::GPUBufferUsage_Binding::MAP_READ);
uint64_t offset = 0;
uint64_t size = 0;
if (aDesc.mMappedAtCreation) {
size = aDesc.mSize;
MOZ_RELEASE_ASSERT(aShmem.get_Shmem().Size<uint8_t>() >= size);
}
mSharedMemoryMap[aBufferId] = {aShmem.get_Shmem(), hasMapFlags, offset,
size};
}
ErrorBuffer error;
ffi::wgpu_server_device_create_buffer(mContext.get(), aSelfId, aBufferId,
ffi::wgpu_server_device_create_buffer(mContext.get(), aDeviceId, aBufferId,
label.Get(), aDesc.mSize, aDesc.mUsage,
aDesc.mMappedAtCreation, error.ToFFI());
ForwardError(aSelfId, error);
return IPC_OK();
}
ipc::IPCResult WebGPUParent::RecvBufferReturnShmem(RawId aSelfId,
Shmem&& aShmem) {
MOZ_LOG(sLogger, LogLevel::Info,
("RecvBufferReturnShmem %" PRIu64 "\n", aSelfId));
mSharedMemoryMap[aSelfId] = aShmem;
ForwardError(aDeviceId, error);
return IPC_OK();
}
struct MapRequest {
const ffi::WGPUGlobal* const mContext;
RefPtr<WebGPUParent> mParent;
ffi::WGPUGlobal* mContext;
ffi::WGPUBufferId mBufferId;
ffi::WGPUHostMap mHostMap;
uint64_t mOffset;
ipc::Shmem mShmem;
uint64_t mSize;
WebGPUParent::BufferMapResolver mResolver;
MapRequest(const ffi::WGPUGlobal* context, ffi::WGPUBufferId bufferId,
ffi::WGPUHostMap hostMap, uint64_t offset, ipc::Shmem&& shmem,
WebGPUParent::BufferMapResolver&& resolver)
: mContext(context),
mBufferId(bufferId),
mHostMap(hostMap),
mOffset(offset),
mShmem(shmem),
mResolver(resolver) {}
};
static void MapCallback(ffi::WGPUBufferMapAsyncStatus status,
uint8_t* userdata) {
auto* req = reinterpret_cast<MapRequest*>(userdata);
// TODO: better handle errors
MOZ_ASSERT(status == ffi::WGPUBufferMapAsyncStatus_Success);
if (req->mHostMap == ffi::WGPUHostMap_Read) {
const uint8_t* ptr = ffi::wgpu_server_buffer_get_mapped_range(
req->mContext, req->mBufferId, req->mOffset,
req->mShmem.Size<uint8_t>());
memcpy(req->mShmem.get<uint8_t>(), ptr, req->mShmem.Size<uint8_t>());
if (!req->mParent->CanSend()) {
delete req;
return;
}
req->mResolver(std::move(req->mShmem));
BufferMapResult result;
auto bufferId = req->mBufferId;
auto* mapData = req->mParent->GetBufferMapData(bufferId);
MOZ_RELEASE_ASSERT(mapData);
if (status != ffi::WGPUBufferMapAsyncStatus_Success) {
// TODO: construct a proper error message from the status code.
nsCString errorString("mapAsync: Failed to map the buffer");
result = BufferMapError(errorString);
} else {
auto size = req->mSize;
auto offset = req->mOffset;
if (req->mHostMap == ffi::WGPUHostMap_Read && size > 0) {
const auto src = ffi::wgpu_server_buffer_get_mapped_range(
req->mContext, req->mBufferId, offset, size);
MOZ_RELEASE_ASSERT(mapData->mShmem.Size<uint8_t>() >= offset + size);
if (src.ptr != nullptr && src.length >= size) {
auto dstPtr = mapData->mShmem.get<uint8_t>() + offset;
memcpy(dstPtr, src.ptr, size);
}
}
result =
BufferMapSuccess(offset, size, req->mHostMap == ffi::WGPUHostMap_Write);
mapData->mMappedOffset = offset;
mapData->mMappedSize = size;
}
req->mResolver(std::move(result));
delete req;
}
ipc::IPCResult WebGPUParent::RecvBufferMap(RawId aSelfId,
ffi::WGPUHostMap aHostMap,
ipc::IPCResult WebGPUParent::RecvBufferMap(RawId aBufferId, uint32_t aMode,
uint64_t aOffset, uint64_t aSize,
BufferMapResolver&& aResolver) {
MOZ_LOG(sLogger, LogLevel::Info,
("RecvBufferMap %" PRIu64 " offset=%" PRIu64 " size=%" PRIu64 "\n",
aSelfId, aOffset, aSize));
auto& shmem = mSharedMemoryMap[aSelfId];
if (!shmem.IsReadable()) {
MOZ_LOG(sLogger, LogLevel::Error, ("\tshmem is empty\n"));
aBufferId, aOffset, aSize));
ffi::WGPUHostMap mode;
switch (aMode) {
case dom::GPUMapMode_Binding::READ:
mode = ffi::WGPUHostMap_Read;
break;
case dom::GPUMapMode_Binding::WRITE:
mode = ffi::WGPUHostMap_Write;
break;
default: {
nsCString errorString(
"GPUBuffer.mapAsync 'mode' argument must be either GPUMapMode.READ "
"or GPUMapMode.WRITE");
aResolver(BufferMapError(errorString));
return IPC_OK();
}
}
auto* mapData = GetBufferMapData(aBufferId);
if (!mapData) {
nsCString errorString("Buffer is not mappable");
aResolver(BufferMapError(errorString));
return IPC_OK();
}
auto* request = new MapRequest(mContext.get(), aSelfId, aHostMap, aOffset,
std::move(shmem), std::move(aResolver));
auto* request =
new MapRequest{this, mContext.get(), aBufferId, mode,
aOffset, aSize, std::move(aResolver)};
ffi::WGPUBufferMapCallbackC callback = {&MapCallback,
reinterpret_cast<uint8_t*>(request)};
ffi::wgpu_server_buffer_map(mContext.get(), aSelfId, aOffset, aSize, aHostMap,
ffi::wgpu_server_buffer_map(mContext.get(), aBufferId, aOffset, aSize, mode,
callback);
return IPC_OK();
}
ipc::IPCResult WebGPUParent::RecvBufferUnmap(RawId aSelfId, Shmem&& aShmem,
bool aFlush, bool aKeepShmem) {
if (aFlush) {
// TODO: flush exact modified sub-range
uint8_t* ptr = ffi::wgpu_server_buffer_get_mapped_range(
mContext.get(), aSelfId, 0, aShmem.Size<uint8_t>());
MOZ_ASSERT(ptr != nullptr);
memcpy(ptr, aShmem.get<uint8_t>(), aShmem.Size<uint8_t>());
}
ffi::wgpu_server_buffer_unmap(mContext.get(), aSelfId);
ipc::IPCResult WebGPUParent::RecvBufferUnmap(RawId aDeviceId, RawId aBufferId,
bool aFlush) {
MOZ_LOG(sLogger, LogLevel::Info,
("RecvBufferUnmap %" PRIu64 " flush=%d\n", aSelfId, aFlush));
const auto iter = mSharedMemoryMap.find(aSelfId);
if (iter != mSharedMemoryMap.end()) {
iter->second = aShmem;
} else if (aKeepShmem) {
mSharedMemoryMap[aSelfId] = aShmem;
} else {
// we are here if the buffer was mapped at creation, but doesn't have any
// mapping flags
DeallocShmem(aShmem);
("RecvBufferUnmap %" PRIu64 " flush=%d\n", aBufferId, aFlush));
auto* mapData = GetBufferMapData(aBufferId);
if (mapData && aFlush) {
uint64_t offset = mapData->mMappedOffset;
uint64_t size = mapData->mMappedSize;
uint8_t* srcPtr = mapData->mShmem.get<uint8_t>() + offset;
const auto mapped = ffi::wgpu_server_buffer_get_mapped_range(
mContext.get(), aBufferId, offset, size);
if (mapped.ptr != nullptr && mapped.length >= size) {
auto shmSize = mapData->mShmem.Size<uint8_t>();
MOZ_RELEASE_ASSERT(offset <= shmSize);
MOZ_RELEASE_ASSERT(size <= shmSize - offset);
memcpy(mapped.ptr, srcPtr, size);
}
mapData->mMappedOffset = 0;
mapData->mMappedSize = 0;
}
ErrorBuffer error;
ffi::wgpu_server_buffer_unmap(mContext.get(), aBufferId, error.ToFFI());
ForwardError(aDeviceId, error);
if (mapData && !mapData->mHasMapFlags) {
// We get here if the buffer was mapped at creation without map flags.
// We don't need the shared memory anymore.
DeallocBufferShmem(aBufferId);
}
return IPC_OK();
}
ipc::IPCResult WebGPUParent::RecvBufferDestroy(RawId aSelfId) {
ffi::wgpu_server_buffer_drop(mContext.get(), aSelfId);
MOZ_LOG(sLogger, LogLevel::Info,
("RecvBufferDestroy %" PRIu64 "\n", aSelfId));
const auto iter = mSharedMemoryMap.find(aSelfId);
void WebGPUParent::DeallocBufferShmem(RawId aBufferId) {
const auto iter = mSharedMemoryMap.find(aBufferId);
if (iter != mSharedMemoryMap.end()) {
DeallocShmem(iter->second);
DeallocShmem(iter->second.mShmem);
mSharedMemoryMap.erase(iter);
}
}
ipc::IPCResult WebGPUParent::RecvBufferDrop(RawId aBufferId) {
ffi::wgpu_server_buffer_drop(mContext.get(), aBufferId);
MOZ_LOG(sLogger, LogLevel::Info, ("RecvBufferDrop %" PRIu64 "\n", aBufferId));
DeallocBufferShmem(aBufferId);
return IPC_OK();
}
ipc::IPCResult WebGPUParent::RecvTextureDestroy(RawId aSelfId) {
ffi::wgpu_server_texture_drop(mContext.get(), aSelfId);
ipc::IPCResult WebGPUParent::RecvBufferDestroy(RawId aBufferId) {
ffi::wgpu_server_buffer_destroy(mContext.get(), aBufferId);
MOZ_LOG(sLogger, LogLevel::Info,
("RecvBufferDestroy %" PRIu64 "\n", aBufferId));
DeallocBufferShmem(aBufferId);
return IPC_OK();
}
ipc::IPCResult WebGPUParent::RecvTextureViewDestroy(RawId aSelfId) {
ffi::wgpu_server_texture_view_drop(mContext.get(), aSelfId);
ipc::IPCResult WebGPUParent::RecvTextureDestroy(RawId aTextureId) {
ffi::wgpu_server_texture_drop(mContext.get(), aTextureId);
return IPC_OK();
}
ipc::IPCResult WebGPUParent::RecvSamplerDestroy(RawId aSelfId) {
ffi::wgpu_server_sampler_drop(mContext.get(), aSelfId);
ipc::IPCResult WebGPUParent::RecvTextureViewDestroy(RawId aTextureViewId) {
ffi::wgpu_server_texture_view_drop(mContext.get(), aTextureViewId);
return IPC_OK();
}
ipc::IPCResult WebGPUParent::RecvSamplerDestroy(RawId aSamplerId) {
ffi::wgpu_server_sampler_drop(mContext.get(), aSamplerId);
return IPC_OK();
}
ipc::IPCResult WebGPUParent::RecvCommandEncoderFinish(
RawId aSelfId, RawId aDeviceId,
RawId aEncoderId, RawId aDeviceId,
const dom::GPUCommandBufferDescriptor& aDesc) {
Unused << aDesc;
ffi::WGPUCommandBufferDescriptor desc = {};
@ -477,78 +563,78 @@ ipc::IPCResult WebGPUParent::RecvCommandEncoderFinish(
desc.label = label.Get();
ErrorBuffer error;
ffi::wgpu_server_encoder_finish(mContext.get(), aSelfId, &desc,
ffi::wgpu_server_encoder_finish(mContext.get(), aEncoderId, &desc,
error.ToFFI());
ForwardError(aDeviceId, error);
return IPC_OK();
}
ipc::IPCResult WebGPUParent::RecvCommandEncoderDestroy(RawId aSelfId) {
ffi::wgpu_server_encoder_drop(mContext.get(), aSelfId);
ipc::IPCResult WebGPUParent::RecvCommandEncoderDestroy(RawId aEncoderId) {
ffi::wgpu_server_encoder_drop(mContext.get(), aEncoderId);
return IPC_OK();
}
ipc::IPCResult WebGPUParent::RecvCommandBufferDestroy(RawId aSelfId) {
ffi::wgpu_server_command_buffer_drop(mContext.get(), aSelfId);
ipc::IPCResult WebGPUParent::RecvCommandBufferDestroy(RawId aCommandBufferId) {
ffi::wgpu_server_command_buffer_drop(mContext.get(), aCommandBufferId);
return IPC_OK();
}
ipc::IPCResult WebGPUParent::RecvRenderBundleDestroy(RawId aSelfId) {
ffi::wgpu_server_render_bundle_drop(mContext.get(), aSelfId);
ipc::IPCResult WebGPUParent::RecvRenderBundleDestroy(RawId aBundleId) {
ffi::wgpu_server_render_bundle_drop(mContext.get(), aBundleId);
return IPC_OK();
}
ipc::IPCResult WebGPUParent::RecvQueueSubmit(
RawId aSelfId, RawId aDeviceId, const nsTArray<RawId>& aCommandBuffers) {
RawId aQueueId, RawId aDeviceId, const nsTArray<RawId>& aCommandBuffers) {
ErrorBuffer error;
ffi::wgpu_server_queue_submit(mContext.get(), aSelfId,
ffi::wgpu_server_queue_submit(mContext.get(), aQueueId,
aCommandBuffers.Elements(),
aCommandBuffers.Length(), error.ToFFI());
ForwardError(aDeviceId, error);
return IPC_OK();
}
ipc::IPCResult WebGPUParent::RecvQueueWriteAction(RawId aSelfId,
ipc::IPCResult WebGPUParent::RecvQueueWriteAction(RawId aQueueId,
RawId aDeviceId,
const ipc::ByteBuf& aByteBuf,
Shmem&& aShmem) {
ErrorBuffer error;
ffi::wgpu_server_queue_write_action(mContext.get(), aSelfId, ToFFI(&aByteBuf),
aShmem.get<uint8_t>(),
ffi::wgpu_server_queue_write_action(mContext.get(), aQueueId,
ToFFI(&aByteBuf), aShmem.get<uint8_t>(),
aShmem.Size<uint8_t>(), error.ToFFI());
ForwardError(aDeviceId, error);
DeallocShmem(aShmem);
return IPC_OK();
}
ipc::IPCResult WebGPUParent::RecvBindGroupLayoutDestroy(RawId aSelfId) {
ffi::wgpu_server_bind_group_layout_drop(mContext.get(), aSelfId);
ipc::IPCResult WebGPUParent::RecvBindGroupLayoutDestroy(RawId aBindGroupId) {
ffi::wgpu_server_bind_group_layout_drop(mContext.get(), aBindGroupId);
return IPC_OK();
}
ipc::IPCResult WebGPUParent::RecvPipelineLayoutDestroy(RawId aSelfId) {
ffi::wgpu_server_pipeline_layout_drop(mContext.get(), aSelfId);
ipc::IPCResult WebGPUParent::RecvPipelineLayoutDestroy(RawId aLayoutId) {
ffi::wgpu_server_pipeline_layout_drop(mContext.get(), aLayoutId);
return IPC_OK();
}
ipc::IPCResult WebGPUParent::RecvBindGroupDestroy(RawId aSelfId) {
ffi::wgpu_server_bind_group_drop(mContext.get(), aSelfId);
ipc::IPCResult WebGPUParent::RecvBindGroupDestroy(RawId aBindGroupId) {
ffi::wgpu_server_bind_group_drop(mContext.get(), aBindGroupId);
return IPC_OK();
}
ipc::IPCResult WebGPUParent::RecvShaderModuleDestroy(RawId aSelfId) {
ffi::wgpu_server_shader_module_drop(mContext.get(), aSelfId);
ipc::IPCResult WebGPUParent::RecvShaderModuleDestroy(RawId aModuleId) {
ffi::wgpu_server_shader_module_drop(mContext.get(), aModuleId);
return IPC_OK();
}
ipc::IPCResult WebGPUParent::RecvComputePipelineDestroy(RawId aSelfId) {
ffi::wgpu_server_compute_pipeline_drop(mContext.get(), aSelfId);
ipc::IPCResult WebGPUParent::RecvComputePipelineDestroy(RawId aPipelineId) {
ffi::wgpu_server_compute_pipeline_drop(mContext.get(), aPipelineId);
return IPC_OK();
}
ipc::IPCResult WebGPUParent::RecvRenderPipelineDestroy(RawId aSelfId) {
ffi::wgpu_server_render_pipeline_drop(mContext.get(), aSelfId);
ipc::IPCResult WebGPUParent::RecvRenderPipelineDestroy(RawId aPipelineId) {
ffi::wgpu_server_render_pipeline_drop(mContext.get(), aPipelineId);
return IPC_OK();
}
@ -564,7 +650,7 @@ ipc::IPCResult WebGPUParent::RecvImplicitLayoutDestroy(
// TODO: proper destruction
ipc::IPCResult WebGPUParent::RecvDeviceCreateSwapChain(
RawId aSelfId, RawId aQueueId, const RGBDescriptor& aDesc,
RawId aDeviceId, RawId aQueueId, const RGBDescriptor& aDesc,
const nsTArray<RawId>& aBufferIds, const CompositableHandle& aHandle) {
switch (aDesc.format()) {
case gfx::SurfaceFormat::R8G8B8A8:
@ -609,7 +695,7 @@ ipc::IPCResult WebGPUParent::RecvDeviceCreateSwapChain(
auto* textureHostData = new (fallible) uint8_t[wholeTextureSize.value()];
if (NS_WARN_IF(!textureHostData)) {
ReportError(
aSelfId,
aDeviceId,
"Error in Device::create_swapchain: failed to allocate texture buffer"_ns);
return IPC_OK();
}
@ -628,8 +714,8 @@ ipc::IPCResult WebGPUParent::RecvDeviceCreateSwapChain(
textureHost->EnsureRenderTexture(Some(externalId));
auto data = MakeRefPtr<PresentationData>(
aSelfId, aQueueId, imageHost.forget(), textureHost.forget(), bufferStride,
textureStride, rows.value(), aBufferIds);
aDeviceId, aQueueId, imageHost.forget(), textureHost.forget(),
bufferStride, textureStride, rows.value(), aBufferIds);
if (!mCanvasMap.insert({aHandle.Value(), data}).second) {
NS_ERROR("External image is already registered as WebGPU canvas!");
}
@ -637,7 +723,7 @@ ipc::IPCResult WebGPUParent::RecvDeviceCreateSwapChain(
}
ipc::IPCResult WebGPUParent::RecvDeviceCreateShaderModule(
RawId aSelfId, RawId aBufferId, const nsString& aLabel,
RawId aDeviceId, RawId aModuleId, const nsString& aLabel,
const nsCString& aCode, DeviceCreateShaderModuleResolver&& aOutMessage) {
// TODO: this should probably be an optional label in the IPC message.
const nsACString* label = nullptr;
@ -649,7 +735,7 @@ ipc::IPCResult WebGPUParent::RecvDeviceCreateShaderModule(
ffi::WGPUShaderModuleCompilationMessage message;
bool ok = ffi::wgpu_server_device_create_shader_module(
mContext.get(), aSelfId, aBufferId, label, &aCode, &message);
mContext.get(), aDeviceId, aModuleId, label, &aCode, &message);
nsTArray<WebGPUCompilationMessage> messages;
@ -694,14 +780,16 @@ static void PresentCallback(ffi::WGPUBufferMapAsyncStatus status,
// copy the data
if (status == ffi::WGPUBufferMapAsyncStatus_Success) {
const auto bufferSize = data->mRowCount * data->mSourcePitch;
const uint8_t* ptr = ffi::wgpu_server_buffer_get_mapped_range(
const auto mapped = ffi::wgpu_server_buffer_get_mapped_range(
req->mContext, bufferId, 0, bufferSize);
MOZ_ASSERT(mapped.length >= bufferSize);
if (data->mTextureHost) {
uint8_t* src = mapped.ptr;
uint8_t* dst = data->mTextureHost->GetBuffer();
for (uint32_t row = 0; row < data->mRowCount; ++row) {
memcpy(dst, ptr, data->mTargetPitch);
memcpy(dst, src, data->mTargetPitch);
dst += data->mTargetPitch;
ptr += data->mSourcePitch;
src += data->mSourcePitch;
}
layers::CompositorThread()->Dispatch(NS_NewRunnableFunction(
"webgpu::WebGPUParent::PresentCallback",
@ -732,7 +820,13 @@ static void PresentCallback(ffi::WGPUBufferMapAsyncStatus status,
} else {
NS_WARNING("WebGPU present skipped: the swapchain is resized!");
}
wgpu_server_buffer_unmap(req->mContext, bufferId);
ErrorBuffer error;
wgpu_server_buffer_unmap(req->mContext, bufferId, error.ToFFI());
if (auto errorString = error.GetError()) {
MOZ_LOG(
sLogger, LogLevel::Info,
("WebGPU present: buffer unmap failed: %s\n", errorString->get()));
}
} else {
// TODO: better handle errors
NS_WARNING("WebGPU frame mapping failed!");
@ -931,44 +1025,45 @@ void WebGPUParent::ActorDestroy(ActorDestroyReason aWhy) {
mContext = nullptr;
}
ipc::IPCResult WebGPUParent::RecvDeviceAction(RawId aSelf,
ipc::IPCResult WebGPUParent::RecvDeviceAction(RawId aDeviceId,
const ipc::ByteBuf& aByteBuf) {
ErrorBuffer error;
ffi::wgpu_server_device_action(mContext.get(), aSelf, ToFFI(&aByteBuf),
ffi::wgpu_server_device_action(mContext.get(), aDeviceId, ToFFI(&aByteBuf),
error.ToFFI());
ForwardError(aSelf, error);
ForwardError(aDeviceId, error);
return IPC_OK();
}
ipc::IPCResult WebGPUParent::RecvDeviceActionWithAck(
RawId aSelf, const ipc::ByteBuf& aByteBuf,
RawId aDeviceId, const ipc::ByteBuf& aByteBuf,
DeviceActionWithAckResolver&& aResolver) {
ErrorBuffer error;
ffi::wgpu_server_device_action(mContext.get(), aSelf, ToFFI(&aByteBuf),
ffi::wgpu_server_device_action(mContext.get(), aDeviceId, ToFFI(&aByteBuf),
error.ToFFI());
ForwardError(aSelf, error);
ForwardError(aDeviceId, error);
aResolver(true);
return IPC_OK();
}
ipc::IPCResult WebGPUParent::RecvTextureAction(RawId aSelf, RawId aDevice,
ipc::IPCResult WebGPUParent::RecvTextureAction(RawId aTextureId,
RawId aDeviceId,
const ipc::ByteBuf& aByteBuf) {
ErrorBuffer error;
ffi::wgpu_server_texture_action(mContext.get(), aSelf, ToFFI(&aByteBuf),
ffi::wgpu_server_texture_action(mContext.get(), aTextureId, ToFFI(&aByteBuf),
error.ToFFI());
ForwardError(aDevice, error);
ForwardError(aDeviceId, error);
return IPC_OK();
}
ipc::IPCResult WebGPUParent::RecvCommandEncoderAction(
RawId aSelf, RawId aDevice, const ipc::ByteBuf& aByteBuf) {
RawId aEncoderId, RawId aDeviceId, const ipc::ByteBuf& aByteBuf) {
ErrorBuffer error;
ffi::wgpu_server_command_encoder_action(mContext.get(), aSelf,
ffi::wgpu_server_command_encoder_action(mContext.get(), aEncoderId,
ToFFI(&aByteBuf), error.ToFFI());
ForwardError(aDevice, error);
ForwardError(aDeviceId, error);
return IPC_OK();
}
@ -989,8 +1084,8 @@ ipc::IPCResult WebGPUParent::RecvBumpImplicitBindGroupLayout(RawId aPipelineId,
return IPC_OK();
}
ipc::IPCResult WebGPUParent::RecvDevicePushErrorScope(RawId aSelfId) {
const auto& lookup = mErrorScopeMap.find(aSelfId);
ipc::IPCResult WebGPUParent::RecvDevicePushErrorScope(RawId aDeviceId) {
const auto& lookup = mErrorScopeMap.find(aDeviceId);
if (lookup == mErrorScopeMap.end()) {
NS_WARNING("WebGPU error scopes on a destroyed device!");
return IPC_OK();
@ -1001,8 +1096,8 @@ ipc::IPCResult WebGPUParent::RecvDevicePushErrorScope(RawId aSelfId) {
}
ipc::IPCResult WebGPUParent::RecvDevicePopErrorScope(
RawId aSelfId, DevicePopErrorScopeResolver&& aResolver) {
const auto& lookup = mErrorScopeMap.find(aSelfId);
RawId aDeviceId, DevicePopErrorScopeResolver&& aResolver) {
const auto& lookup = mErrorScopeMap.find(aDeviceId);
if (lookup == mErrorScopeMap.end()) {
NS_WARNING("WebGPU error scopes on a destroyed device!");
ScopedError error = {true};

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

@ -6,6 +6,7 @@
#ifndef WEBGPU_PARENT_H_
#define WEBGPU_PARENT_H_
#include "mozilla/webgpu/ffi/wgpu.h"
#include "mozilla/webgpu/PWebGPUParent.h"
#include "mozilla/webrender/WebRenderAPI.h"
#include "WebGPUTypes.h"
@ -30,47 +31,48 @@ class WebGPUParent final : public PWebGPUParent {
const nsTArray<RawId>& aTargetIds,
InstanceRequestAdapterResolver&& resolver);
ipc::IPCResult RecvAdapterRequestDevice(
RawId aSelfId, const ipc::ByteBuf& aByteBuf, RawId aNewId,
RawId aAdapterId, const ipc::ByteBuf& aByteBuf, RawId aDeviceId,
AdapterRequestDeviceResolver&& resolver);
ipc::IPCResult RecvAdapterDestroy(RawId aSelfId);
ipc::IPCResult RecvDeviceDestroy(RawId aSelfId);
ipc::IPCResult RecvCreateBuffer(RawId aSelfId, RawId aBufferId,
dom::GPUBufferDescriptor&& aDesc);
ipc::IPCResult RecvBufferReturnShmem(RawId aSelfId, Shmem&& aShmem);
ipc::IPCResult RecvBufferMap(RawId aSelfId, ffi::WGPUHostMap aHostMap,
ipc::IPCResult RecvAdapterDestroy(RawId aAdapterId);
ipc::IPCResult RecvDeviceDestroy(RawId aDeviceId);
ipc::IPCResult RecvCreateBuffer(RawId aDeviceId, RawId aBufferId,
dom::GPUBufferDescriptor&& aDesc,
MaybeShmem&& aShmem);
ipc::IPCResult RecvBufferReturnShmem(RawId aBufferId, Shmem&& aShmem);
ipc::IPCResult RecvBufferMap(RawId aBufferId, uint32_t aMode,
uint64_t aOffset, uint64_t size,
BufferMapResolver&& aResolver);
ipc::IPCResult RecvBufferUnmap(RawId aSelfId, Shmem&& aShmem, bool aFlush,
bool aKeepShmem);
ipc::IPCResult RecvBufferDestroy(RawId aSelfId);
ipc::IPCResult RecvTextureDestroy(RawId aSelfId);
ipc::IPCResult RecvTextureViewDestroy(RawId aSelfId);
ipc::IPCResult RecvSamplerDestroy(RawId aSelfId);
ipc::IPCResult RecvBufferUnmap(RawId aDeviceId, RawId aBufferId, bool aFlush);
ipc::IPCResult RecvBufferDestroy(RawId aBufferId);
ipc::IPCResult RecvBufferDrop(RawId aBufferId);
ipc::IPCResult RecvTextureDestroy(RawId aTextureId);
ipc::IPCResult RecvTextureViewDestroy(RawId aTextureViewId);
ipc::IPCResult RecvSamplerDestroy(RawId aSamplerId);
ipc::IPCResult RecvCommandEncoderFinish(
RawId aSelfId, RawId aDeviceId,
RawId aEncoderId, RawId aDeviceId,
const dom::GPUCommandBufferDescriptor& aDesc);
ipc::IPCResult RecvCommandEncoderDestroy(RawId aSelfId);
ipc::IPCResult RecvCommandBufferDestroy(RawId aSelfId);
ipc::IPCResult RecvRenderBundleDestroy(RawId aSelfId);
ipc::IPCResult RecvQueueSubmit(RawId aSelfId, RawId aDeviceId,
ipc::IPCResult RecvCommandEncoderDestroy(RawId aEncoderId);
ipc::IPCResult RecvCommandBufferDestroy(RawId aCommandBufferId);
ipc::IPCResult RecvRenderBundleDestroy(RawId aBundleId);
ipc::IPCResult RecvQueueSubmit(RawId aQueueId, RawId aDeviceId,
const nsTArray<RawId>& aCommandBuffers);
ipc::IPCResult RecvQueueWriteAction(RawId aSelfId, RawId aDeviceId,
ipc::IPCResult RecvQueueWriteAction(RawId aQueueId, RawId aDeviceId,
const ipc::ByteBuf& aByteBuf,
Shmem&& aShmem);
ipc::IPCResult RecvBindGroupLayoutDestroy(RawId aSelfId);
ipc::IPCResult RecvPipelineLayoutDestroy(RawId aSelfId);
ipc::IPCResult RecvBindGroupDestroy(RawId aSelfId);
ipc::IPCResult RecvShaderModuleDestroy(RawId aSelfId);
ipc::IPCResult RecvComputePipelineDestroy(RawId aSelfId);
ipc::IPCResult RecvRenderPipelineDestroy(RawId aSelfId);
ipc::IPCResult RecvBindGroupLayoutDestroy(RawId aBindGroupLayoutId);
ipc::IPCResult RecvPipelineLayoutDestroy(RawId aPipelineLayoutId);
ipc::IPCResult RecvBindGroupDestroy(RawId aBindGroupId);
ipc::IPCResult RecvShaderModuleDestroy(RawId aModuleId);
ipc::IPCResult RecvComputePipelineDestroy(RawId aPipelineId);
ipc::IPCResult RecvRenderPipelineDestroy(RawId aPipelineId);
ipc::IPCResult RecvImplicitLayoutDestroy(
RawId aImplicitPlId, const nsTArray<RawId>& aImplicitBglIds);
ipc::IPCResult RecvDeviceCreateSwapChain(RawId aSelfId, RawId aQueueId,
ipc::IPCResult RecvDeviceCreateSwapChain(RawId aDeviceId, RawId aQueueId,
const layers::RGBDescriptor& aDesc,
const nsTArray<RawId>& aBufferIds,
const CompositableHandle& aHandle);
ipc::IPCResult RecvDeviceCreateShaderModule(
RawId aSelfId, RawId aModuleId, const nsString& aLabel,
RawId aDeviceId, RawId aModuleId, const nsString& aLabel,
const nsCString& aCode, DeviceCreateShaderModuleResolver&& aOutMessage);
ipc::IPCResult RecvSwapChainPresent(const CompositableHandle& aHandle,
@ -78,22 +80,23 @@ class WebGPUParent final : public PWebGPUParent {
RawId aCommandEncoderId);
ipc::IPCResult RecvSwapChainDestroy(const CompositableHandle& aHandle);
ipc::IPCResult RecvDeviceAction(RawId aSelf, const ipc::ByteBuf& aByteBuf);
ipc::IPCResult RecvDeviceAction(RawId aDeviceId,
const ipc::ByteBuf& aByteBuf);
ipc::IPCResult RecvDeviceActionWithAck(
RawId aSelf, const ipc::ByteBuf& aByteBuf,
RawId aDeviceId, const ipc::ByteBuf& aByteBuf,
DeviceActionWithAckResolver&& aResolver);
ipc::IPCResult RecvTextureAction(RawId aSelf, RawId aDevice,
ipc::IPCResult RecvTextureAction(RawId aTextureId, RawId aDevice,
const ipc::ByteBuf& aByteBuf);
ipc::IPCResult RecvCommandEncoderAction(RawId aSelf, RawId aDevice,
ipc::IPCResult RecvCommandEncoderAction(RawId aEncoderId, RawId aDeviceId,
const ipc::ByteBuf& aByteBuf);
ipc::IPCResult RecvBumpImplicitBindGroupLayout(RawId aPipelineId,
bool aIsCompute,
uint32_t aIndex,
RawId aAssignId);
ipc::IPCResult RecvDevicePushErrorScope(RawId aSelfId);
ipc::IPCResult RecvDevicePushErrorScope(RawId aDeviceId);
ipc::IPCResult RecvDevicePopErrorScope(
RawId aSelfId, DevicePopErrorScopeResolver&& aResolver);
RawId aDeviceId, DevicePopErrorScopeResolver&& aResolver);
ipc::IPCResult RecvGenerateError(RawId aDeviceId, const nsCString& message);
ipc::IPCResult GetFrontBufferSnapshot(IProtocol* aProtocol,
@ -103,18 +106,31 @@ class WebGPUParent final : public PWebGPUParent {
void ActorDestroy(ActorDestroyReason aWhy) override;
struct BufferMapData {
Shmem mShmem;
// True if buffer's usage has MAP_READ or MAP_WRITE set.
bool mHasMapFlags;
uint64_t mMappedOffset;
uint64_t mMappedSize;
};
BufferMapData* GetBufferMapData(RawId aBufferId);
private:
void DeallocBufferShmem(RawId aBufferId);
virtual ~WebGPUParent();
void MaintainDevices();
bool ForwardError(RawId aDeviceID, ErrorBuffer& aError);
bool ForwardError(RawId aDeviceId, ErrorBuffer& aError);
void ReportError(RawId aDeviceId, const nsCString& message);
UniquePtr<ffi::WGPUGlobal> mContext;
base::RepeatingTimer<WebGPUParent> mTimer;
/// Shmem associated with a mappable buffer has to be owned by one of the
/// processes. We keep it here for every mappable buffer while the buffer is
/// used by GPU.
std::unordered_map<uint64_t, Shmem> mSharedMemoryMap;
/// A map from wgpu buffer ids to data about their shared memory segments.
/// Includes entries about mappedAtCreation, MAP_READ and MAP_WRITE buffers,
/// regardless of their state.
std::unordered_map<uint64_t, BufferMapData> mSharedMemoryMap;
/// Associated presentation data for each swapchain.
std::unordered_map<uint64_t, RefPtr<PresentationData>> mCanvasMap;
/// Associated stack of error scopes for each device.

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

@ -51,6 +51,7 @@ UNIFIED_SOURCES += [x + ".cpp" for x in h_and_cpp]
IPDL_SOURCES += [
"ipc/PWebGPU.ipdl",
"ipc/PWebGPUTypes.ipdlh",
]
EXPORTS.mozilla.webgpu += [

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

@ -285,7 +285,7 @@ interface GPUBuffer {
ArrayBuffer getMappedRange(optional GPUSize64 offset = 0, optional GPUSize64 size);
[Throws]
void unmap();
[Throws]
void destroy();
};
GPUBuffer includes GPUObjectBase;

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

@ -1,3 +1,66 @@
Overview of changes leading to 5.1.0
Sunday, July 31, 2022
====================================
- More extensive buffer tracing messages. (Behdad Esfahbod)
- Fix hb-ft regression in bitmap fonts rendering. (Behdad Esfahbod)
- Support extension promotion of lookups in hb-subset-repacker. (Garret Rieger)
- A new HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL for scripts that use elongation
(e.g. Arabic) to signify where it is safe to insert tatweel glyph without
interrupting shaping. (Behdad Esfahbod)
- Add “--safe-to-insert-tatweel” to “hb-shape” tool. (Behdad Esfahbod)
- New API
+HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL
+HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL
Overview of changes leading to 5.0.1
Saturday, July 23, 2022
====================================
- Fix version 2 “avar” table with hb-ft. (Behdad Esfahbod)
Overview of changes leading to 5.0.0
Saturday, July 23, 2022
====================================
- Support fonts with more than 65535 glyphs in “GDEF”, “GSUB”, and “GPOS”
tables. This is part of https://github.com/be-fonts/boring-expansion-spec to
extend OpenType in a backward-compatible way.
(Behdad Esfahbod, Garret Rieger)
- Complete support for more than 65535 glyphs in “glyf” table that started in
4.0.0 release. Part of boring-expansion-spec. (Behdad Esfahbod)
- Support version 2 of “avar” table. Part of boring-expansion-spec.
(Behdad Esfahbod)
- Fix mark attachment on multiple substitutions in some cases.
(Behdad Esfahbod)
- Fix application of “calt”, “rclt”, and “ccmp” features to better match
Uniscribe behaviour with some Arabic fonts. (Behdad Esfahbod)
- Improvement to interaction between multiple cursive attachments.
(Behdad Esfahbod)
- Improve multiple mark interactions in Hebrew. (Behdad Esfahbod)
- Implement language-specific forms in AAT shaping. (Behdad Esfahbod)
- Fix variation of “VORG” table. (Behdad Esfahbod)
- Support for specific script tags to be retained in the subsetter, and add
“--layout-scripts” option to “hb-subset” tool. (Garret Rieger)
- Accept space as delimiter for --features/--variations in command line tools.
- Improve subsetting of “COLR” table. (Qunxin Liu)
- Improved fuzzing coverage for ot-math API. (Frédéric Wang)
- Fix “kern” table version 2 (AAT) sanitization on 32-bit systems.
(Behdad Esfahbod)
- Allow negative glyph advances from “graphite2” shaper. (Stephan Bergmann)
- Implement loading (color) bitmap fonts with hb-ft. (Behdad Esfahbod)
- Fix regression in hb-ft when changing font size. (Behdad Esfahbod)
- Fix build on GCC < 7. (Kleis Auke Wolthuizen)
- Dynamically load dwrite.dll on windows if “directwrite” shaper is enabled.
(Luca Bacci)
- Provide a single-file harfbuzz-subset.cc file for easier alternate building
of hb-subset library, similar to harfbuzz.cc. (Khaled Hosny)
- New API
+HB_SUBSET_SETS_LAYOUT_SCRIPT_TAG
+hb_language_matches()
Overview of changes leading to 4.4.1
Wednesday, June 29, 2022
====================================

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

@ -1,7 +1,7 @@
This directory contains the HarfBuzz source from the upstream repo:
https://github.com/harfbuzz/harfbuzz
Current version: 4.4.1 [commit 096aaa62a6e0d07c02a4894fc036efc927e5aaf9]
Current version: 5.1.0 [commit f1f2be776bcd994fa9262622e1a7098a066e5cf7]
UPDATING:

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

@ -1,6 +1,6 @@
AC_PREREQ([2.64])
AC_INIT([HarfBuzz],
[4.4.1],
[5.1.0],
[https://github.com/harfbuzz/harfbuzz/issues/new],
[harfbuzz],
[http://harfbuzz.org/])
@ -308,7 +308,7 @@ if $have_freetype; then
AC_DEFINE(HAVE_FREETYPE, 1, [Have FreeType 2 library])
save_libs=$LIBS
LIBS="$LIBS $FREETYPE_LIBS"
AC_CHECK_FUNCS(FT_Get_Var_Blend_Coordinates FT_Set_Var_Blend_Coordinates FT_Done_MM_Var)
AC_CHECK_FUNCS(FT_Get_Var_Blend_Coordinates FT_Set_Var_Blend_Coordinates FT_Done_MM_Var FT_Get_Transform)
LIBS=$save_libs
fi
AM_CONDITIONAL(HAVE_FREETYPE, $have_freetype)
@ -366,17 +366,13 @@ AC_ARG_WITH(directwrite,
have_directwrite=false
AC_LANG_PUSH([C++])
if test "x$with_directwrite" = "xyes" -o "x$with_directwrite" = "xauto"; then
AC_CHECK_HEADERS(dwrite.h, have_directwrite=true)
AC_CHECK_HEADERS(dwrite_1.h, have_directwrite=true)
fi
AC_LANG_POP([C++])
if test "x$with_directwrite" = "xyes" -a "x$have_directwrite" != "xtrue"; then
AC_MSG_ERROR([directwrite support requested but not found])
fi
if $have_directwrite; then
DIRECTWRITE_CXXFLAGS=
DIRECTWRITE_LIBS=-ldwrite
AC_SUBST(DIRECTWRITE_CXXFLAGS)
AC_SUBST(DIRECTWRITE_LIBS)
AC_DEFINE(HAVE_DIRECTWRITE, 1, [Have DirectWrite library])
fi
AM_CONDITIONAL(HAVE_DIRECTWRITE, $have_directwrite)

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

@ -12,7 +12,7 @@ DISTCHECK_CONFIGURE_FLAGS = --enable-introspection
TESTS =
check_PROGRAMS =
EXTRA_DIST += harfbuzz.cc
EXTRA_DIST += harfbuzz.cc harfbuzz-subset.cc
EXTRA_DIST += meson.build
EXTRA_DIST += fix_get_types.py
@ -160,6 +160,18 @@ pkginclude_HEADERS += $(HB_SUBSET_headers)
pkgconfig_DATA += harfbuzz-subset.pc
EXTRA_DIST += harfbuzz-subset.pc.in
harfbuzz-subset.cc: Makefile.sources
$(AM_V_GEN) \
LANG=C; \
for f in \
$(HB_BASE_sources) \
$(HB_SUBSET_sources) \
; do echo '#include "'$$f'"'; done | \
sort -u | \
grep '[.]cc"' > $(srcdir)/harfbuzz-subset.cc \
|| ($(RM) $(srcdir)/harfbuzz-subset.cc; false)
BUILT_SOURCES += harfbuzz-subset.cc
if HAVE_ICU
if HAVE_ICU_BUILTIN
HBCFLAGS += $(ICU_CFLAGS)
@ -284,6 +296,7 @@ $(srcdir)/%.hh: $(srcdir)/%.rl
harfbuzz.cc: Makefile.sources
$(AM_V_GEN) \
LANG=C; \
for f in \
$(HB_BASE_sources) \
$(HB_GLIB_sources) \
@ -294,6 +307,7 @@ harfbuzz.cc: Makefile.sources
$(HB_DIRECTWRITE_sources) \
$(HB_CORETEXT_sources) \
; do echo '#include "'$$f'"'; done | \
sort -u | \
grep '[.]cc"' > $(srcdir)/harfbuzz.cc \
|| ($(RM) $(srcdir)/harfbuzz.cc; false)
BUILT_SOURCES += harfbuzz.cc
@ -399,7 +413,7 @@ test_priority_queue_SOURCES = test-priority-queue.cc hb-static.cc
test_priority_queue_CPPFLAGS = $(HBCFLAGS)
test_priority_queue_LDADD = libharfbuzz.la $(HBLIBS)
test_repacker_SOURCES = test-repacker.cc hb-static.cc
test_repacker_SOURCES = test-repacker.cc hb-static.cc graph/gsubgpos-context.cc
test_repacker_CPPFLAGS = $(HBCFLAGS)
test_repacker_LDADD = libharfbuzz.la libharfbuzz-subset.la $(HBLIBS)

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

@ -97,57 +97,65 @@ HB_BASE_sources = \
OT/glyf/SimpleGlyph.hh \
OT/glyf/CompositeGlyph.hh \
OT/glyf/SubsetGlyph.hh \
OT/Layout/types.hh \
OT/Layout/Common/Coverage.hh \
OT/Layout/Common/CoverageFormat1.hh \
OT/Layout/Common/CoverageFormat2.hh \
OT/Layout/Common/RangeRecord.hh \
OT/Layout/GPOS/AnchorFormat1.hh \
OT/Layout/GPOS/AnchorFormat2.hh \
OT/Layout/GPOS/AnchorFormat3.hh \
OT/Layout/GPOS/Anchor.hh \
OT/Layout/GPOS/AnchorMatrix.hh \
OT/Layout/GPOS/ChainContextPos.hh \
OT/Layout/GPOS/Common.hh \
OT/Layout/GPOS/ContextPos.hh \
OT/Layout/GPOS/CursivePosFormat1.hh \
OT/Layout/GPOS/CursivePos.hh \
OT/Layout/GPOS/ExtensionPos.hh \
OT/Layout/GPOS/GPOS.hh \
OT/Layout/GPOS/LigatureArray.hh \
OT/Layout/GPOS/MarkArray.hh \
OT/Layout/GPOS/MarkBasePosFormat1.hh \
OT/Layout/GPOS/MarkBasePos.hh \
OT/Layout/GPOS/MarkLigPosFormat1.hh \
OT/Layout/GPOS/MarkLigPos.hh \
OT/Layout/GPOS/MarkMarkPosFormat1.hh \
OT/Layout/GPOS/MarkMarkPos.hh \
OT/Layout/GPOS/MarkRecord.hh \
OT/Layout/GPOS/PairPosFormat1.hh \
OT/Layout/GPOS/PairPosFormat2.hh \
OT/Layout/GPOS/PairPos.hh \
OT/Layout/GPOS/PairSet.hh \
OT/Layout/GPOS/PairValueRecord.hh \
OT/Layout/GPOS/PosLookup.hh \
OT/Layout/GPOS/PosLookupSubTable.hh \
OT/Layout/GPOS/SinglePosFormat1.hh \
OT/Layout/GPOS/SinglePosFormat2.hh \
OT/Layout/GPOS/SinglePos.hh \
OT/Layout/GPOS/ValueFormat.hh \
OT/Layout/GSUB/AlternateSet.hh \
OT/Layout/GSUB/AlternateSubstFormat1.hh \
OT/Layout/GSUB/AlternateSubst.hh \
OT/Layout/GSUB/ChainContextSubst.hh \
OT/Layout/GSUB/Common.hh \
OT/Layout/GSUB/ContextSubst.hh \
OT/Layout/GSUB/ExtensionSubst.hh \
OT/Layout/GSUB/GSUB.hh \
OT/Layout/GSUB/Ligature.hh \
OT/Layout/GSUB/LigatureSet.hh \
OT/Layout/GSUB/LigatureSubstFormat1.hh \
OT/Layout/GSUB/LigatureSubst.hh \
OT/Layout/GSUB/MultipleSubstFormat1.hh \
OT/Layout/GSUB/MultipleSubst.hh \
OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh \
OT/Layout/GSUB/ReverseChainSingleSubst.hh \
OT/Layout/GSUB/Sequence.hh \
OT/Layout/GSUB/SingleSubstFormat1.hh \
OT/Layout/GSUB/SingleSubstFormat2.hh \
OT/Layout/GSUB/SingleSubst.hh \
OT/Layout/GSUB/MultipleSubstFormat1.hh \
OT/Layout/GSUB/MultipleSubst.hh \
OT/Layout/GSUB/AlternateSubstFormat1.hh \
OT/Layout/GSUB/AlternateSubst.hh \
OT/Layout/GSUB/AlternateSet.hh \
OT/Layout/GSUB/LigatureSubstFormat1.hh \
OT/Layout/GSUB/LigatureSubst.hh \
OT/Layout/GSUB/LigatureSet.hh \
OT/Layout/GSUB/Ligature.hh \
OT/Layout/GSUB/ReverseChainSingleSubst.hh \
OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh \
OT/Layout/GSUB/ContextSubst.hh \
OT/Layout/GSUB/ChainContextSubst.hh \
OT/Layout/GSUB/ExtensionSubst.hh \
OT/Layout/GSUB/SubstLookupSubTable.hh \
OT/Layout/GSUB/SubstLookup.hh \
OT/Layout/GSUB/GSUB.hh \
OT/Layout/GPOS.hh \
OT/Layout/GPOS/CursivePosFormat1.hh \
OT/Layout/GPOS/MarkLigPos.hh \
OT/Layout/GPOS/PairPos.hh \
OT/Layout/GPOS/Anchor.hh \
OT/Layout/GPOS/AnchorFormat1.hh \
OT/Layout/GPOS/MarkLigPosFormat1.hh \
OT/Layout/GPOS/PairPosFormat1.hh \
OT/Layout/GPOS/ExtensionPos.hh \
OT/Layout/GPOS/ChainContextPos.hh \
OT/Layout/GPOS/Common.hh \
OT/Layout/GPOS/ValueFormat.hh \
OT/Layout/GPOS/AnchorMatrix.hh \
OT/Layout/GPOS/MarkBasePosFormat1.hh \
OT/Layout/GPOS/AnchorFormat3.hh \
OT/Layout/GPOS/PosLookup.hh \
OT/Layout/GPOS/MarkMarkPos.hh \
OT/Layout/GPOS/PairPosFormat2.hh \
OT/Layout/GPOS/MarkBasePos.hh \
OT/Layout/GPOS/MarkMarkPosFormat1.hh \
OT/Layout/GPOS/SinglePos.hh \
OT/Layout/GPOS/MarkArray.hh \
OT/Layout/GPOS/CursivePos.hh \
OT/Layout/GPOS/PosLookupSubTable.hh \
OT/Layout/GPOS/MarkRecord.hh \
OT/Layout/GPOS/AnchorFormat2.hh \
OT/Layout/GPOS/ContextPos.hh \
OT/Layout/GPOS/SinglePosFormat2.hh \
OT/Layout/GPOS/SinglePosFormat1.hh \
OT/Layout/GSUB/SubstLookupSubTable.hh \
hb-ot-layout-gsubgpos.hh \
hb-ot-layout-jstf-table.hh \
hb-ot-layout.cc \
@ -340,6 +348,12 @@ HB_SUBSET_sources = \
hb-subset.hh \
hb-repacker.hh \
graph/graph.hh \
graph/gsubgpos-graph.hh \
graph/gsubgpos-context.hh \
graph/gsubgpos-context.cc \
graph/pairpos-graph.hh \
graph/coverage-graph.hh \
graph/pairpos-graph.hh \
graph/serialize.hh \
$(NULL)

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

@ -0,0 +1,338 @@
/*
* Copyright © 2007,2008,2009 Red Hat, Inc.
* Copyright © 2010,2012 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Red Hat Author(s): Behdad Esfahbod
* Google Author(s): Behdad Esfahbod, Garret Rieger
*/
#ifndef OT_LAYOUT_COMMON_COVERAGE_HH
#define OT_LAYOUT_COMMON_COVERAGE_HH
#include "../types.hh"
#include "CoverageFormat1.hh"
#include "CoverageFormat2.hh"
namespace OT {
namespace Layout {
namespace Common {
template<typename Iterator>
static inline void Coverage_serialize (hb_serialize_context_t *c,
Iterator it);
struct Coverage
{
protected:
union {
HBUINT16 format; /* Format identifier */
CoverageFormat1_3<SmallTypes> format1;
CoverageFormat2_4<SmallTypes> format2;
#ifndef HB_NO_BORING_EXPANSION
CoverageFormat1_3<MediumTypes>format3;
CoverageFormat2_4<MediumTypes>format4;
#endif
} u;
public:
DEFINE_SIZE_UNION (2, format);
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
if (!u.format.sanitize (c)) return_trace (false);
switch (u.format)
{
case 1: return_trace (u.format1.sanitize (c));
case 2: return_trace (u.format2.sanitize (c));
#ifndef HB_NO_BORING_EXPANSION
case 3: return_trace (u.format3.sanitize (c));
case 4: return_trace (u.format4.sanitize (c));
#endif
default:return_trace (true);
}
}
/* Has interface. */
static constexpr unsigned SENTINEL = NOT_COVERED;
typedef unsigned int value_t;
value_t operator [] (hb_codepoint_t k) const { return get (k); }
bool has (hb_codepoint_t k) const { return (*this)[k] != SENTINEL; }
/* Predicate. */
bool operator () (hb_codepoint_t k) const { return has (k); }
unsigned int get (hb_codepoint_t k) const { return get_coverage (k); }
unsigned int get_coverage (hb_codepoint_t glyph_id) const
{
switch (u.format) {
case 1: return u.format1.get_coverage (glyph_id);
case 2: return u.format2.get_coverage (glyph_id);
#ifndef HB_NO_BORING_EXPANSION
case 3: return u.format3.get_coverage (glyph_id);
case 4: return u.format4.get_coverage (glyph_id);
#endif
default:return NOT_COVERED;
}
}
unsigned get_population () const
{
switch (u.format) {
case 1: return u.format1.get_population ();
case 2: return u.format2.get_population ();
#ifndef HB_NO_BORING_EXPANSION
case 3: return u.format3.get_population ();
case 4: return u.format4.get_population ();
#endif
default:return NOT_COVERED;
}
}
template <typename Iterator,
hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_t))>
bool serialize (hb_serialize_context_t *c, Iterator glyphs)
{
TRACE_SERIALIZE (this);
if (unlikely (!c->extend_min (this))) return_trace (false);
unsigned count = 0;
unsigned num_ranges = 0;
hb_codepoint_t last = (hb_codepoint_t) -2;
for (auto g: glyphs)
{
if (last + 1 != g)
num_ranges++;
last = g;
count++;
}
u.format = count <= num_ranges * 3 ? 1 : 2;
#ifndef HB_NO_BORING_EXPANSION
if (count && last > 0xFFFFu)
u.format += 2;
#endif
switch (u.format)
{
case 1: return_trace (u.format1.serialize (c, glyphs));
case 2: return_trace (u.format2.serialize (c, glyphs));
#ifndef HB_NO_BORING_EXPANSION
case 3: return_trace (u.format3.serialize (c, glyphs));
case 4: return_trace (u.format4.serialize (c, glyphs));
#endif
default:return_trace (false);
}
}
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
auto it =
+ iter ()
| hb_filter (c->plan->glyph_map_gsub)
| hb_map_retains_sorting (c->plan->glyph_map_gsub)
;
// Cache the iterator result as it will be iterated multiple times
// by the serialize code below.
hb_sorted_vector_t<hb_codepoint_t> glyphs (it);
Coverage_serialize (c->serializer, glyphs.iter ());
return_trace (bool (glyphs));
}
bool intersects (const hb_set_t *glyphs) const
{
switch (u.format)
{
case 1: return u.format1.intersects (glyphs);
case 2: return u.format2.intersects (glyphs);
#ifndef HB_NO_BORING_EXPANSION
case 3: return u.format3.intersects (glyphs);
case 4: return u.format4.intersects (glyphs);
#endif
default:return false;
}
}
bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const
{
switch (u.format)
{
case 1: return u.format1.intersects_coverage (glyphs, index);
case 2: return u.format2.intersects_coverage (glyphs, index);
#ifndef HB_NO_BORING_EXPANSION
case 3: return u.format3.intersects_coverage (glyphs, index);
case 4: return u.format4.intersects_coverage (glyphs, index);
#endif
default:return false;
}
}
/* Might return false if array looks unsorted.
* Used for faster rejection of corrupt data. */
template <typename set_t>
bool collect_coverage (set_t *glyphs) const
{
switch (u.format)
{
case 1: return u.format1.collect_coverage (glyphs);
case 2: return u.format2.collect_coverage (glyphs);
#ifndef HB_NO_BORING_EXPANSION
case 3: return u.format3.collect_coverage (glyphs);
case 4: return u.format4.collect_coverage (glyphs);
#endif
default:return false;
}
}
template <typename IterableOut,
hb_requires (hb_is_sink_of (IterableOut, hb_codepoint_t))>
void intersect_set (const hb_set_t &glyphs, IterableOut &intersect_glyphs) const
{
switch (u.format)
{
case 1: return u.format1.intersect_set (glyphs, intersect_glyphs);
case 2: return u.format2.intersect_set (glyphs, intersect_glyphs);
#ifndef HB_NO_BORING_EXPANSION
case 3: return u.format3.intersect_set (glyphs, intersect_glyphs);
case 4: return u.format4.intersect_set (glyphs, intersect_glyphs);
#endif
default:return ;
}
}
struct iter_t : hb_iter_with_fallback_t<iter_t, hb_codepoint_t>
{
static constexpr bool is_sorted_iterator = true;
iter_t (const Coverage &c_ = Null (Coverage))
{
memset (this, 0, sizeof (*this));
format = c_.u.format;
switch (format)
{
case 1: u.format1.init (c_.u.format1); return;
case 2: u.format2.init (c_.u.format2); return;
#ifndef HB_NO_BORING_EXPANSION
case 3: u.format3.init (c_.u.format3); return;
case 4: u.format4.init (c_.u.format4); return;
#endif
default: return;
}
}
bool __more__ () const
{
switch (format)
{
case 1: return u.format1.__more__ ();
case 2: return u.format2.__more__ ();
#ifndef HB_NO_BORING_EXPANSION
case 3: return u.format3.__more__ ();
case 4: return u.format4.__more__ ();
#endif
default:return false;
}
}
void __next__ ()
{
switch (format)
{
case 1: u.format1.__next__ (); break;
case 2: u.format2.__next__ (); break;
#ifndef HB_NO_BORING_EXPANSION
case 3: u.format3.__next__ (); break;
case 4: u.format4.__next__ (); break;
#endif
default: break;
}
}
typedef hb_codepoint_t __item_t__;
__item_t__ __item__ () const { return get_glyph (); }
hb_codepoint_t get_glyph () const
{
switch (format)
{
case 1: return u.format1.get_glyph ();
case 2: return u.format2.get_glyph ();
#ifndef HB_NO_BORING_EXPANSION
case 3: return u.format3.get_glyph ();
case 4: return u.format4.get_glyph ();
#endif
default:return 0;
}
}
bool operator != (const iter_t& o) const
{
if (unlikely (format != o.format)) return true;
switch (format)
{
case 1: return u.format1 != o.u.format1;
case 2: return u.format2 != o.u.format2;
#ifndef HB_NO_BORING_EXPANSION
case 3: return u.format3 != o.u.format3;
case 4: return u.format4 != o.u.format4;
#endif
default:return false;
}
}
iter_t __end__ () const
{
iter_t it = {};
it.format = format;
switch (format)
{
case 1: it.u.format1 = u.format1.__end__ (); break;
case 2: it.u.format2 = u.format2.__end__ (); break;
#ifndef HB_NO_BORING_EXPANSION
case 3: it.u.format3 = u.format3.__end__ (); break;
case 4: it.u.format4 = u.format4.__end__ (); break;
#endif
default: break;
}
return it;
}
private:
unsigned int format;
union {
#ifndef HB_NO_BORING_EXPANSION
CoverageFormat2_4<MediumTypes>::iter_t format4; /* Put this one first since it's larger; helps shut up compiler. */
CoverageFormat1_3<MediumTypes>::iter_t format3;
#endif
CoverageFormat2_4<SmallTypes>::iter_t format2; /* Put this one first since it's larger; helps shut up compiler. */
CoverageFormat1_3<SmallTypes>::iter_t format1;
} u;
};
iter_t iter () const { return iter_t (*this); }
};
template<typename Iterator>
static inline void
Coverage_serialize (hb_serialize_context_t *c,
Iterator it)
{ c->start_embed<Coverage> ()->serialize (c, it); }
}
}
}
#endif // #ifndef OT_LAYOUT_COMMON_COVERAGE_HH

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

@ -0,0 +1,126 @@
/*
* Copyright © 2007,2008,2009 Red Hat, Inc.
* Copyright © 2010,2012 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Red Hat Author(s): Behdad Esfahbod
* Google Author(s): Behdad Esfahbod, Garret Rieger
*/
#ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT1_HH
#define OT_LAYOUT_COMMON_COVERAGEFORMAT1_HH
namespace OT {
namespace Layout {
namespace Common {
#define NOT_COVERED ((unsigned int) -1)
template <typename Types>
struct CoverageFormat1_3
{
friend struct Coverage;
protected:
HBUINT16 coverageFormat; /* Format identifier--format = 1 */
SortedArray16Of<typename Types::HBGlyphID>
glyphArray; /* Array of GlyphIDs--in numerical order */
public:
DEFINE_SIZE_ARRAY (4, glyphArray);
private:
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (glyphArray.sanitize (c));
}
unsigned int get_coverage (hb_codepoint_t glyph_id) const
{
unsigned int i;
glyphArray.bfind (glyph_id, &i, HB_NOT_FOUND_STORE, NOT_COVERED);
return i;
}
unsigned get_population () const
{
return glyphArray.len;
}
template <typename Iterator,
hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_t))>
bool serialize (hb_serialize_context_t *c, Iterator glyphs)
{
TRACE_SERIALIZE (this);
return_trace (glyphArray.serialize (c, glyphs));
}
bool intersects (const hb_set_t *glyphs) const
{
/* TODO Speed up, using hb_set_next() and bsearch()? */
for (const auto& g : glyphArray.as_array ())
if (glyphs->has (g))
return true;
return false;
}
bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const
{ return glyphs->has (glyphArray[index]); }
template <typename IterableOut,
hb_requires (hb_is_sink_of (IterableOut, hb_codepoint_t))>
void intersect_set (const hb_set_t &glyphs, IterableOut &intersect_glyphs) const
{
unsigned count = glyphArray.len;
for (unsigned i = 0; i < count; i++)
if (glyphs.has (glyphArray[i]))
intersect_glyphs << glyphArray[i];
}
template <typename set_t>
bool collect_coverage (set_t *glyphs) const
{ return glyphs->add_sorted_array (glyphArray.as_array ()); }
public:
/* Older compilers need this to be public. */
struct iter_t
{
void init (const struct CoverageFormat1_3 &c_) { c = &c_; i = 0; }
bool __more__ () const { return i < c->glyphArray.len; }
void __next__ () { i++; }
hb_codepoint_t get_glyph () const { return c->glyphArray[i]; }
bool operator != (const iter_t& o) const
{ return i != o.i; }
iter_t __end__ () const { iter_t it; it.init (*c); it.i = c->glyphArray.len; return it; }
private:
const struct CoverageFormat1_3 *c;
unsigned int i;
};
private:
};
}
}
}
#endif // #ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT1_HH

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

@ -0,0 +1,233 @@
/*
* Copyright © 2007,2008,2009 Red Hat, Inc.
* Copyright © 2010,2012 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Red Hat Author(s): Behdad Esfahbod
* Google Author(s): Behdad Esfahbod, Garret Rieger
*/
#ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT2_HH
#define OT_LAYOUT_COMMON_COVERAGEFORMAT2_HH
#include "RangeRecord.hh"
namespace OT {
namespace Layout {
namespace Common {
template <typename Types>
struct CoverageFormat2_4
{
friend struct Coverage;
protected:
HBUINT16 coverageFormat; /* Format identifier--format = 2 */
SortedArray16Of<RangeRecord<Types>>
rangeRecord; /* Array of glyph ranges--ordered by
* Start GlyphID. rangeCount entries
* long */
public:
DEFINE_SIZE_ARRAY (4, rangeRecord);
private:
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (rangeRecord.sanitize (c));
}
unsigned int get_coverage (hb_codepoint_t glyph_id) const
{
const RangeRecord<Types> &range = rangeRecord.bsearch (glyph_id);
return likely (range.first <= range.last)
? (unsigned int) range.value + (glyph_id - range.first)
: NOT_COVERED;
}
unsigned get_population () const
{
typename Types::large_int ret = 0;
for (const auto &r : rangeRecord)
ret += r.get_population ();
return ret > UINT_MAX ? UINT_MAX : (unsigned) ret;
}
template <typename Iterator,
hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_t))>
bool serialize (hb_serialize_context_t *c, Iterator glyphs)
{
TRACE_SERIALIZE (this);
if (unlikely (!c->extend_min (this))) return_trace (false);
/* TODO(iter) Write more efficiently? */
unsigned num_ranges = 0;
hb_codepoint_t last = (hb_codepoint_t) -2;
for (auto g: glyphs)
{
if (last + 1 != g)
num_ranges++;
last = g;
}
if (unlikely (!rangeRecord.serialize (c, num_ranges))) return_trace (false);
if (!num_ranges) return_trace (true);
unsigned count = 0;
unsigned range = (unsigned) -1;
last = (hb_codepoint_t) -2;
for (auto g: glyphs)
{
if (last + 1 != g)
{
range++;
rangeRecord[range].first = g;
rangeRecord[range].value = count;
}
rangeRecord[range].last = g;
last = g;
count++;
}
return_trace (true);
}
bool intersects (const hb_set_t *glyphs) const
{
return hb_any (+ hb_iter (rangeRecord)
| hb_map ([glyphs] (const RangeRecord<Types> &range) { return range.intersects (*glyphs); }));
}
bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const
{
auto cmp = [] (const void *pk, const void *pr) -> int
{
unsigned index = * (const unsigned *) pk;
const RangeRecord<Types> &range = * (const RangeRecord<Types> *) pr;
if (index < range.value) return -1;
if (index > (unsigned int) range.value + (range.last - range.first)) return +1;
return 0;
};
auto arr = rangeRecord.as_array ();
unsigned idx;
if (hb_bsearch_impl (&idx, index,
arr.arrayZ, arr.length, sizeof (arr[0]),
(int (*)(const void *_key, const void *_item)) cmp))
return arr.arrayZ[idx].intersects (*glyphs);
return false;
}
template <typename IterableOut,
hb_requires (hb_is_sink_of (IterableOut, hb_codepoint_t))>
void intersect_set (const hb_set_t &glyphs, IterableOut &intersect_glyphs) const
{
for (const auto& range : rangeRecord)
{
hb_codepoint_t last = range.last;
for (hb_codepoint_t g = range.first - 1;
glyphs.next (&g) && g <= last;)
intersect_glyphs << g;
}
}
template <typename set_t>
bool collect_coverage (set_t *glyphs) const
{
for (const auto& range: rangeRecord)
if (unlikely (!range.collect_coverage (glyphs)))
return false;
return true;
}
public:
/* Older compilers need this to be public. */
struct iter_t
{
void init (const CoverageFormat2_4 &c_)
{
c = &c_;
coverage = 0;
i = 0;
j = c->rangeRecord.len ? c->rangeRecord[0].first : 0;
if (unlikely (c->rangeRecord[0].first > c->rangeRecord[0].last))
{
/* Broken table. Skip. */
i = c->rangeRecord.len;
j = 0;
}
}
bool __more__ () const { return i < c->rangeRecord.len; }
void __next__ ()
{
if (j >= c->rangeRecord[i].last)
{
i++;
if (__more__ ())
{
unsigned int old = coverage;
j = c->rangeRecord[i].first;
coverage = c->rangeRecord[i].value;
if (unlikely (coverage != old + 1))
{
/* Broken table. Skip. Important to avoid DoS.
* Also, our callers depend on coverage being
* consecutive and monotonically increasing,
* ie. iota(). */
i = c->rangeRecord.len;
j = 0;
return;
}
}
else
j = 0;
return;
}
coverage++;
j++;
}
hb_codepoint_t get_glyph () const { return j; }
bool operator != (const iter_t& o) const
{ return i != o.i || j != o.j; }
iter_t __end__ () const
{
iter_t it;
it.init (*c);
it.i = c->rangeRecord.len;
it.j = 0;
return it;
}
private:
const struct CoverageFormat2_4 *c;
unsigned int i, coverage;
hb_codepoint_t j;
};
private:
};
}
}
}
#endif // #ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT2_HH

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

@ -0,0 +1,85 @@
/*
* Copyright © 2007,2008,2009 Red Hat, Inc.
* Copyright © 2010,2012 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Red Hat Author(s): Behdad Esfahbod
* Google Author(s): Behdad Esfahbod, Garret Rieger
*/
#ifndef OT_LAYOUT_COMMON_RANGERECORD_HH
#define OT_LAYOUT_COMMON_RANGERECORD_HH
namespace OT {
namespace Layout {
namespace Common {
template <typename Types>
struct RangeRecord
{
typename Types::HBGlyphID first; /* First GlyphID in the range */
typename Types::HBGlyphID last; /* Last GlyphID in the range */
HBUINT16 value; /* Value */
DEFINE_SIZE_STATIC (2 + 2 * Types::size);
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this));
}
int cmp (hb_codepoint_t g) const
{ return g < first ? -1 : g <= last ? 0 : +1; }
unsigned get_population () const
{
if (unlikely (last < first)) return 0;
return (last - first + 1);
}
bool intersects (const hb_set_t &glyphs) const
{ return glyphs.intersects (first, last); }
template <typename set_t>
bool collect_coverage (set_t *glyphs) const
{ return glyphs->add_range (first, last); }
};
}
}
}
// TODO(garretrieger): This was previously implemented using
// DECLARE_NULL_NAMESPACE_BYTES_TEMPLATE1 (OT, RangeRecord, 9);
// but that only works when there is only a single namespace level.
// The macro should probably be fixed so it can work in this situation.
extern HB_INTERNAL const unsigned char _hb_Null_OT_RangeRecord[9];
template <typename Spec>
struct Null<OT::Layout::Common::RangeRecord<Spec>> {
static OT::Layout::Common::RangeRecord<Spec> const & get_null () {
return *reinterpret_cast<const OT::Layout::Common::RangeRecord<Spec> *> (_hb_Null_OT_RangeRecord);
}
};
#endif // #ifndef OT_LAYOUT_COMMON_RANGERECORD_HH

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

@ -140,7 +140,14 @@ struct CursivePosFormat1
unsigned int i = skippy_iter.idx;
unsigned int j = buffer->idx;
buffer->unsafe_to_break (i, j);
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"cursive attaching glyph at %d to glyph at %d",
i, j);
}
buffer->unsafe_to_break (i, j + 1);
float entry_x, entry_y, exit_x, exit_y;
(this+prev_record.exitAnchor).get_anchor (c, buffer->info[i].codepoint, &exit_x, &exit_y);
(this+this_record.entryAnchor).get_anchor (c, buffer->info[j].codepoint, &entry_x, &entry_y);
@ -223,7 +230,20 @@ struct CursivePosFormat1
* https://github.com/harfbuzz/harfbuzz/issues/2469
*/
if (unlikely (pos[parent].attach_chain() == -pos[child].attach_chain()))
{
pos[parent].attach_chain() = 0;
if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction)))
pos[parent].y_offset = 0;
else
pos[parent].x_offset = 0;
}
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"cursive attached glyph at %d to glyph at %d",
i, j);
}
buffer->idx++;
return_trace (true);

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

@ -1,12 +1,15 @@
#ifndef OT_LAYOUT_GPOS_HH
#define OT_LAYOUT_GPOS_HH
#ifndef OT_LAYOUT_GPOS_GPOS_HH
#define OT_LAYOUT_GPOS_GPOS_HH
#include "../../hb-ot-layout-common.hh"
#include "../../hb-ot-layout-gsubgpos.hh"
#include "GPOS/Common.hh"
#include "GPOS/PosLookup.hh"
#include "../../../hb-ot-layout-common.hh"
#include "../../../hb-ot-layout-gsubgpos.hh"
#include "Common.hh"
#include "PosLookup.hh"
namespace OT {
using Layout::GPOS_impl::PosLookup;
namespace Layout {
static void
@ -25,10 +28,10 @@ struct GPOS : GSUBGPOS
{
static constexpr hb_tag_t tableTag = HB_OT_TAG_GPOS;
using Lookup = GPOS_impl::PosLookup;
using Lookup = PosLookup;
const GPOS_impl::PosLookup& get_lookup (unsigned int i) const
{ return static_cast<const GPOS_impl::PosLookup &> (GSUBGPOS::get_lookup (i)); }
const PosLookup& get_lookup (unsigned int i) const
{ return static_cast<const PosLookup &> (GSUBGPOS::get_lookup (i)); }
static inline void position_start (hb_font_t *font, hb_buffer_t *buffer);
static inline void position_finish_advances (hb_font_t *font, hb_buffer_t *buffer);
@ -37,11 +40,14 @@ struct GPOS : GSUBGPOS
bool subset (hb_subset_context_t *c) const
{
hb_subset_layout_context_t l (c, tableTag, c->plan->gpos_lookups, c->plan->gpos_langsys, c->plan->gpos_features);
return GSUBGPOS::subset<GPOS_impl::PosLookup> (&l);
return GSUBGPOS::subset<PosLookup> (&l);
}
bool sanitize (hb_sanitize_context_t *c) const
{ return GSUBGPOS::sanitize<GPOS_impl::PosLookup> (c); }
{
TRACE_SANITIZE (this);
return_trace (GSUBGPOS::sanitize<PosLookup> (c));
}
HB_INTERNAL bool is_blocklisted (hb_blob_t *blob,
hb_face_t *face) const;
@ -51,7 +57,7 @@ struct GPOS : GSUBGPOS
for (unsigned i = 0; i < GSUBGPOS::get_lookup_count (); i++)
{
if (!c->gpos_lookups->has (i)) continue;
const GPOS_impl::PosLookup &l = get_lookup (i);
const PosLookup &l = get_lookup (i);
l.dispatch (c);
}
}
@ -59,7 +65,7 @@ struct GPOS : GSUBGPOS
void closure_lookups (hb_face_t *face,
const hb_set_t *glyphs,
hb_set_t *lookup_indexes /* IN/OUT */) const
{ GSUBGPOS::closure_lookups<GPOS_impl::PosLookup> (face, glyphs, lookup_indexes); }
{ GSUBGPOS::closure_lookups<PosLookup> (face, glyphs, lookup_indexes); }
typedef GSUBGPOS::accelerator_t<GPOS> accelerator_t;
};
@ -162,4 +168,4 @@ struct GPOS_accelerator_t : Layout::GPOS::accelerator_t {
}
#endif /* OT_LAYOUT_GPOS_HH */
#endif /* OT_LAYOUT_GPOS_GPOS_HH */

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

@ -0,0 +1,56 @@
#ifndef OT_LAYOUT_GPOS_LIGATUREARRAY_HH
#define OT_LAYOUT_GPOS_LIGATUREARRAY_HH
namespace OT {
namespace Layout {
namespace GPOS_impl {
typedef AnchorMatrix LigatureAttach; /* component-major--
* in order of writing direction--,
* mark-minor--
* ordered by class--zero-based. */
/* Array of LigatureAttach tables ordered by LigatureCoverage Index */
struct LigatureArray : List16OfOffset16To<LigatureAttach>
{
template <typename Iterator,
hb_requires (hb_is_iterator (Iterator))>
bool subset (hb_subset_context_t *c,
Iterator coverage,
unsigned class_count,
const hb_map_t *klass_mapping) const
{
TRACE_SUBSET (this);
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
auto *out = c->serializer->start_embed (this);
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
for (const auto _ : + hb_zip (coverage, *this)
| hb_filter (glyphset, hb_first))
{
auto *matrix = out->serialize_append (c->serializer);
if (unlikely (!matrix)) return_trace (false);
const LigatureAttach& src = (this + _.second);
auto indexes =
+ hb_range (src.rows * class_count)
| hb_filter ([=] (unsigned index) { return klass_mapping->has (index % class_count); })
;
matrix->serialize_subset (c,
_.second,
this,
src.rows,
indexes);
}
return_trace (this->len);
}
};
}
}
}
#endif /* OT_LAYOUT_GPOS_LIGATUREARRAY_HH */

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

@ -39,6 +39,13 @@ struct MarkArray : Array16Of<MarkRecord> /* Array of MarkRecords--in Cove
mark_anchor.get_anchor (c, buffer->cur().codepoint, &mark_x, &mark_y);
glyph_anchor.get_anchor (c, buffer->info[glyph_pos].codepoint, &base_x, &base_y);
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"attaching mark glyph at %d to glyph at %d",
c->buffer->idx, glyph_pos);
}
hb_glyph_position_t &o = buffer->cur_pos();
o.x_offset = roundf (base_x - mark_x);
o.y_offset = roundf (base_y - mark_y);
@ -46,6 +53,13 @@ struct MarkArray : Array16Of<MarkRecord> /* Array of MarkRecords--in Cove
o.attach_chain() = (int) glyph_pos - (int) buffer->idx;
buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"attached mark glyph at %d to glyph at %d",
c->buffer->idx, glyph_pos);
}
buffer->idx++;
return_trace (true);
}
@ -83,10 +97,11 @@ struct MarkArray : Array16Of<MarkRecord> /* Array of MarkRecords--in Cove
}
};
static void Markclass_closure_and_remap_indexes (const Coverage &mark_coverage,
const MarkArray &mark_array,
const hb_set_t &glyphset,
hb_map_t* klass_mapping /* INOUT */)
HB_INTERNAL inline
void Markclass_closure_and_remap_indexes (const Coverage &mark_coverage,
const MarkArray &mark_array,
const hb_set_t &glyphset,
hb_map_t* klass_mapping /* INOUT */)
{
hb_set_t orig_classes;

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

@ -11,8 +11,11 @@ struct MarkBasePos
{
protected:
union {
HBUINT16 format; /* Format identifier */
MarkBasePosFormat1 format1;
HBUINT16 format; /* Format identifier */
MarkBasePosFormat1_2<SmallTypes> format1;
#ifndef HB_NO_BORING_EXPANSION
MarkBasePosFormat1_2<MediumTypes> format2;
#endif
} u;
public:
@ -23,6 +26,9 @@ struct MarkBasePos
if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
switch (u.format) {
case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
#ifndef HB_NO_BORING_EXPANSION
case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
#endif
default:return_trace (c->default_return_value ());
}
}

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

@ -12,26 +12,27 @@ typedef AnchorMatrix BaseArray; /* base-major--
* mark-minor--
* ordered by class--zero-based. */
struct MarkBasePosFormat1
template <typename Types>
struct MarkBasePosFormat1_2
{
protected:
HBUINT16 format; /* Format identifier--format = 1 */
Offset16To<Coverage>
typename Types::template OffsetTo<Coverage>
markCoverage; /* Offset to MarkCoverage table--from
* beginning of MarkBasePos subtable */
Offset16To<Coverage>
typename Types::template OffsetTo<Coverage>
baseCoverage; /* Offset to BaseCoverage table--from
* beginning of MarkBasePos subtable */
HBUINT16 classCount; /* Number of classes defined for marks */
Offset16To<MarkArray>
typename Types::template OffsetTo<MarkArray>
markArray; /* Offset to MarkArray table--from
* beginning of MarkBasePos subtable */
Offset16To<BaseArray>
typename Types::template OffsetTo<BaseArray>
baseArray; /* Offset to BaseArray table--from
* beginning of MarkBasePos subtable */
public:
DEFINE_SIZE_STATIC (12);
DEFINE_SIZE_STATIC (4 + 4 * Types::size);
bool sanitize (hb_sanitize_context_t *c) const
{
@ -117,6 +118,7 @@ struct MarkBasePosFormat1
0 == _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) ||
(skippy_iter.idx == 0 ||
_hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx - 1]) ||
!_hb_glyph_info_multiplied (&buffer->info[skippy_iter.idx - 1]) ||
_hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx]) !=
_hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx - 1]) ||
_hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) !=

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

@ -11,8 +11,11 @@ struct MarkLigPos
{
protected:
union {
HBUINT16 format; /* Format identifier */
MarkLigPosFormat1 format1;
HBUINT16 format; /* Format identifier */
MarkLigPosFormat1_2<SmallTypes> format1;
#ifndef HB_NO_BORING_EXPANSION
MarkLigPosFormat1_2<MediumTypes> format2;
#endif
} u;
public:
@ -23,6 +26,9 @@ struct MarkLigPos
if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
switch (u.format) {
case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
#ifndef HB_NO_BORING_EXPANSION
case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
#endif
default:return_trace (c->default_return_value ());
}
}

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

@ -1,72 +1,34 @@
#ifndef OT_LAYOUT_GPOS_MARKLIGPOSFORMAT1_HH
#define OT_LAYOUT_GPOS_MARKLIGPOSFORMAT1_HH
#include "LigatureArray.hh"
namespace OT {
namespace Layout {
namespace GPOS_impl {
typedef AnchorMatrix LigatureAttach; /* component-major--
* in order of writing direction--,
* mark-minor--
* ordered by class--zero-based. */
/* Array of LigatureAttach tables ordered by LigatureCoverage Index */
struct LigatureArray : List16OfOffset16To<LigatureAttach>
{
template <typename Iterator,
hb_requires (hb_is_iterator (Iterator))>
bool subset (hb_subset_context_t *c,
Iterator coverage,
unsigned class_count,
const hb_map_t *klass_mapping) const
{
TRACE_SUBSET (this);
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
auto *out = c->serializer->start_embed (this);
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
for (const auto _ : + hb_zip (coverage, *this)
| hb_filter (glyphset, hb_first))
{
auto *matrix = out->serialize_append (c->serializer);
if (unlikely (!matrix)) return_trace (false);
const LigatureAttach& src = (this + _.second);
auto indexes =
+ hb_range (src.rows * class_count)
| hb_filter ([=] (unsigned index) { return klass_mapping->has (index % class_count); })
;
matrix->serialize_subset (c,
_.second,
this,
src.rows,
indexes);
}
return_trace (this->len);
}
};
struct MarkLigPosFormat1
template <typename Types>
struct MarkLigPosFormat1_2
{
protected:
HBUINT16 format; /* Format identifier--format = 1 */
Offset16To<Coverage>
typename Types::template OffsetTo<Coverage>
markCoverage; /* Offset to Mark Coverage table--from
* beginning of MarkLigPos subtable */
Offset16To<Coverage>
typename Types::template OffsetTo<Coverage>
ligatureCoverage; /* Offset to Ligature Coverage
* table--from beginning of MarkLigPos
* subtable */
HBUINT16 classCount; /* Number of defined mark classes */
Offset16To<MarkArray>
typename Types::template OffsetTo<MarkArray>
markArray; /* Offset to MarkArray table--from
* beginning of MarkLigPos subtable */
Offset16To<LigatureArray>
typename Types::template OffsetTo<LigatureArray>
ligatureArray; /* Offset to LigatureArray table--from
* beginning of MarkLigPos subtable */
public:
DEFINE_SIZE_STATIC (12);
DEFINE_SIZE_STATIC (4 + 4 * Types::size);
bool sanitize (hb_sanitize_context_t *c) const
{

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

@ -11,8 +11,11 @@ struct MarkMarkPos
{
protected:
union {
HBUINT16 format; /* Format identifier */
MarkMarkPosFormat1 format1;
HBUINT16 format; /* Format identifier */
MarkMarkPosFormat1_2<SmallTypes> format1;
#ifndef HB_NO_BORING_EXPANSION
MarkMarkPosFormat1_2<MediumTypes> format2;
#endif
} u;
public:
@ -23,6 +26,9 @@ struct MarkMarkPos
if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
switch (u.format) {
case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
#ifndef HB_NO_BORING_EXPANSION
case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
#endif
default:return_trace (c->default_return_value ());
}
}

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

@ -12,27 +12,28 @@ typedef AnchorMatrix Mark2Array; /* mark2-major--
* mark1-minor--
* ordered by class--zero-based. */
struct MarkMarkPosFormat1
template <typename Types>
struct MarkMarkPosFormat1_2
{
protected:
HBUINT16 format; /* Format identifier--format = 1 */
Offset16To<Coverage>
typename Types::template OffsetTo<Coverage>
mark1Coverage; /* Offset to Combining Mark1 Coverage
* table--from beginning of MarkMarkPos
* subtable */
Offset16To<Coverage>
typename Types::template OffsetTo<Coverage>
mark2Coverage; /* Offset to Combining Mark2 Coverage
* table--from beginning of MarkMarkPos
* subtable */
HBUINT16 classCount; /* Number of defined mark classes */
Offset16To<MarkArray>
typename Types::template OffsetTo<MarkArray>
mark1Array; /* Offset to Mark1Array table--from
* beginning of MarkMarkPos subtable */
Offset16To<Mark2Array>
typename Types::template OffsetTo<Mark2Array>
mark2Array; /* Offset to Mark2Array table--from
* beginning of MarkMarkPos subtable */
public:
DEFINE_SIZE_STATIC (12);
DEFINE_SIZE_STATIC (4 + 4 * Types::size);
bool sanitize (hb_sanitize_context_t *c) const
{
@ -100,7 +101,7 @@ struct MarkMarkPosFormat1
/* now we search backwards for a suitable mark glyph until a non-mark glyph */
hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
skippy_iter.reset (buffer->idx, 1);
skippy_iter.set_lookup_props (c->lookup_props & ~LookupFlag::IgnoreFlags);
skippy_iter.set_lookup_props (c->lookup_props & ~(uint32_t)LookupFlag::IgnoreFlags);
unsigned unsafe_from;
if (!skippy_iter.prev (&unsafe_from))
{

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

@ -12,9 +12,13 @@ struct PairPos
{
protected:
union {
HBUINT16 format; /* Format identifier */
PairPosFormat1 format1;
PairPosFormat2 format2;
HBUINT16 format; /* Format identifier */
PairPosFormat1_3<SmallTypes> format1;
PairPosFormat2_4<SmallTypes> format2;
#ifndef HB_NO_BORING_EXPANSION
PairPosFormat1_3<MediumTypes> format3;
PairPosFormat2_4<MediumTypes> format4;
#endif
} u;
public:
@ -26,6 +30,10 @@ struct PairPos
switch (u.format) {
case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
#ifndef HB_NO_BORING_EXPANSION
case 3: return_trace (c->dispatch (u.format3, std::forward<Ts> (ds)...));
case 4: return_trace (c->dispatch (u.format4, std::forward<Ts> (ds)...));
#endif
default:return_trace (c->default_return_value ());
}
}

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

@ -1,248 +1,22 @@
#ifndef OT_LAYOUT_GPOS_PAIRPOSFORMAT1_HH
#define OT_LAYOUT_GPOS_PAIRPOSFORMAT1_HH
#include "PairSet.hh"
namespace OT {
namespace Layout {
namespace GPOS_impl {
struct PairValueRecord
template <typename Types>
struct PairPosFormat1_3
{
friend struct PairSet;
using PairSet = GPOS_impl::PairSet<Types>;
using PairValueRecord = GPOS_impl::PairValueRecord<Types>;
int cmp (hb_codepoint_t k) const
{ return secondGlyph.cmp (k); }
struct context_t
{
const void *base;
const ValueFormat *valueFormats;
const ValueFormat *newFormats;
unsigned len1; /* valueFormats[0].get_len() */
const hb_map_t *glyph_map;
const hb_map_t *layout_variation_idx_map;
};
bool subset (hb_subset_context_t *c,
context_t *closure) const
{
TRACE_SERIALIZE (this);
auto *s = c->serializer;
auto *out = s->start_embed (*this);
if (unlikely (!s->extend_min (out))) return_trace (false);
out->secondGlyph = (*closure->glyph_map)[secondGlyph];
closure->valueFormats[0].copy_values (s,
closure->newFormats[0],
closure->base, &values[0],
closure->layout_variation_idx_map);
closure->valueFormats[1].copy_values (s,
closure->newFormats[1],
closure->base,
&values[closure->len1],
closure->layout_variation_idx_map);
return_trace (true);
}
void collect_variation_indices (hb_collect_variation_indices_context_t *c,
const ValueFormat *valueFormats,
const void *base) const
{
unsigned record1_len = valueFormats[0].get_len ();
unsigned record2_len = valueFormats[1].get_len ();
const hb_array_t<const Value> values_array = values.as_array (record1_len + record2_len);
if (valueFormats[0].has_device ())
valueFormats[0].collect_variation_indices (c, base, values_array.sub_array (0, record1_len));
if (valueFormats[1].has_device ())
valueFormats[1].collect_variation_indices (c, base, values_array.sub_array (record1_len, record2_len));
}
bool intersects (const hb_set_t& glyphset) const
{
return glyphset.has(secondGlyph);
}
const Value* get_values_1 () const
{
return &values[0];
}
const Value* get_values_2 (ValueFormat format1) const
{
return &values[format1.get_len ()];
}
protected:
HBGlyphID16 secondGlyph; /* GlyphID of second glyph in the
* pair--first glyph is listed in the
* Coverage table */
ValueRecord values; /* Positioning data for the first glyph
* followed by for second glyph */
public:
DEFINE_SIZE_ARRAY (2, values);
};
struct PairSet
{
friend struct PairPosFormat1;
bool intersects (const hb_set_t *glyphs,
const ValueFormat *valueFormats) const
{
unsigned int len1 = valueFormats[0].get_len ();
unsigned int len2 = valueFormats[1].get_len ();
unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
const PairValueRecord *record = &firstPairValueRecord;
unsigned int count = len;
for (unsigned int i = 0; i < count; i++)
{
if (glyphs->has (record->secondGlyph))
return true;
record = &StructAtOffset<const PairValueRecord> (record, record_size);
}
return false;
}
void collect_glyphs (hb_collect_glyphs_context_t *c,
const ValueFormat *valueFormats) const
{
unsigned int len1 = valueFormats[0].get_len ();
unsigned int len2 = valueFormats[1].get_len ();
unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
const PairValueRecord *record = &firstPairValueRecord;
c->input->add_array (&record->secondGlyph, len, record_size);
}
void collect_variation_indices (hb_collect_variation_indices_context_t *c,
const ValueFormat *valueFormats) const
{
unsigned len1 = valueFormats[0].get_len ();
unsigned len2 = valueFormats[1].get_len ();
unsigned record_size = HBUINT16::static_size * (1 + len1 + len2);
const PairValueRecord *record = &firstPairValueRecord;
unsigned count = len;
for (unsigned i = 0; i < count; i++)
{
if (c->glyph_set->has (record->secondGlyph))
{ record->collect_variation_indices (c, valueFormats, this); }
record = &StructAtOffset<const PairValueRecord> (record, record_size);
}
}
bool apply (hb_ot_apply_context_t *c,
const ValueFormat *valueFormats,
unsigned int pos) const
{
TRACE_APPLY (this);
hb_buffer_t *buffer = c->buffer;
unsigned int len1 = valueFormats[0].get_len ();
unsigned int len2 = valueFormats[1].get_len ();
unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
const PairValueRecord *record = hb_bsearch (buffer->info[pos].codepoint,
&firstPairValueRecord,
len,
record_size);
if (record)
{
bool applied_first = valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos());
bool applied_second = valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos]);
if (applied_first || applied_second)
buffer->unsafe_to_break (buffer->idx, pos + 1);
if (len2)
pos++;
buffer->idx = pos;
return_trace (true);
}
buffer->unsafe_to_concat (buffer->idx, pos + 1);
return_trace (false);
}
bool subset (hb_subset_context_t *c,
const ValueFormat valueFormats[2],
const ValueFormat newFormats[2]) const
{
TRACE_SUBSET (this);
auto snap = c->serializer->snapshot ();
auto *out = c->serializer->start_embed (*this);
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
out->len = 0;
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
const hb_map_t &glyph_map = *c->plan->glyph_map;
unsigned len1 = valueFormats[0].get_len ();
unsigned len2 = valueFormats[1].get_len ();
unsigned record_size = HBUINT16::static_size + Value::static_size * (len1 + len2);
PairValueRecord::context_t context =
{
this,
valueFormats,
newFormats,
len1,
&glyph_map,
c->plan->layout_variation_idx_map
};
const PairValueRecord *record = &firstPairValueRecord;
unsigned count = len, num = 0;
for (unsigned i = 0; i < count; i++)
{
if (glyphset.has (record->secondGlyph)
&& record->subset (c, &context)) num++;
record = &StructAtOffset<const PairValueRecord> (record, record_size);
}
out->len = num;
if (!num) c->serializer->revert (snap);
return_trace (num);
}
struct sanitize_closure_t
{
const ValueFormat *valueFormats;
unsigned int len1; /* valueFormats[0].get_len() */
unsigned int stride; /* 1 + len1 + len2 */
};
bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) const
{
TRACE_SANITIZE (this);
if (!(c->check_struct (this)
&& c->check_range (&firstPairValueRecord,
len,
HBUINT16::static_size,
closure->stride))) return_trace (false);
unsigned int count = len;
const PairValueRecord *record = &firstPairValueRecord;
return_trace (closure->valueFormats[0].sanitize_values_stride_unsafe (c, this, &record->values[0], count, closure->stride) &&
closure->valueFormats[1].sanitize_values_stride_unsafe (c, this, &record->values[closure->len1], count, closure->stride));
}
protected:
HBUINT16 len; /* Number of PairValueRecords */
PairValueRecord firstPairValueRecord;
/* Array of PairValueRecords--ordered
* by GlyphID of the second glyph */
public:
DEFINE_SIZE_MIN (2);
};
struct PairPosFormat1
{
protected:
HBUINT16 format; /* Format identifier--format = 1 */
Offset16To<Coverage>
typename Types::template OffsetTo<Coverage>
coverage; /* Offset to Coverage table--from
* beginning of subtable */
ValueFormat valueFormat[2]; /* [0] Defines the types of data in
@ -251,11 +25,11 @@ struct PairPosFormat1
/* [1] Defines the types of data in
* ValueRecord2--for the second glyph
* in the pair--may be zero (0) */
Array16OfOffset16To<PairSet>
Array16Of<typename Types::template OffsetTo<PairSet>>
pairSet; /* Array of PairSet tables
* ordered by Coverage Index */
public:
DEFINE_SIZE_ARRAY (10, pairSet);
DEFINE_SIZE_ARRAY (8 + Types::size, pairSet);
bool sanitize (hb_sanitize_context_t *c) const
{
@ -265,7 +39,7 @@ struct PairPosFormat1
unsigned int len1 = valueFormat[0].get_len ();
unsigned int len2 = valueFormat[1].get_len ();
PairSet::sanitize_closure_t closure =
typename PairSet::sanitize_closure_t closure =
{
valueFormat,
len1,
@ -275,14 +49,13 @@ struct PairPosFormat1
return_trace (coverage.sanitize (c, this) && pairSet.sanitize (c, this, &closure));
}
bool intersects (const hb_set_t *glyphs) const
{
return
+ hb_zip (this+coverage, pairSet)
| hb_filter (*glyphs, hb_first)
| hb_map (hb_second)
| hb_map ([glyphs, this] (const Offset16To<PairSet> &_)
| hb_map ([glyphs, this] (const typename Types::template OffsetTo<PairSet> &_)
{ return (this+_).intersects (glyphs, valueFormat); })
| hb_any
;
@ -358,7 +131,7 @@ struct PairPosFormat1
+ hb_zip (this+coverage, pairSet)
| hb_filter (glyphset, hb_first)
| hb_filter ([this, c, out] (const Offset16To<PairSet>& _)
| hb_filter ([this, c, out] (const typename Types::template OffsetTo<PairSet>& _)
{
auto snap = c->serializer->snapshot ();
auto *o = out->pairSet.serialize_append (c->serializer);
@ -391,7 +164,7 @@ struct PairPosFormat1
unsigned format1 = 0;
unsigned format2 = 0;
for (const Offset16To<PairSet>& _ :
for (const auto & _ :
+ hb_zip (this+coverage, pairSet) | hb_filter (glyphset, hb_first) | hb_map (hb_second))
{
const PairSet& set = (this + _);

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

@ -7,11 +7,12 @@ namespace OT {
namespace Layout {
namespace GPOS_impl {
struct PairPosFormat2
template <typename Types>
struct PairPosFormat2_4
{
protected:
HBUINT16 format; /* Format identifier--format = 2 */
Offset16To<Coverage>
typename Types::template OffsetTo<Coverage>
coverage; /* Offset to Coverage table--from
* beginning of subtable */
ValueFormat valueFormat1; /* ValueRecord definition--for the
@ -20,11 +21,11 @@ struct PairPosFormat2
ValueFormat valueFormat2; /* ValueRecord definition--for the
* second glyph of the pair--may be
* zero (0) */
Offset16To<ClassDef>
typename Types::template OffsetTo<ClassDef>
classDef1; /* Offset to ClassDef table--from
* beginning of PairPos subtable--for
* the first glyph of the pair */
Offset16To<ClassDef>
typename Types::template OffsetTo<ClassDef>
classDef2; /* Offset to ClassDef table--from
* beginning of PairPos subtable--for
* the second glyph of the pair */
@ -36,7 +37,7 @@ struct PairPosFormat2
* class1-major, class2-minor,
* Each entry has value1 and value2 */
public:
DEFINE_SIZE_ARRAY (16, values);
DEFINE_SIZE_ARRAY (10 + 3 * Types::size, values);
bool sanitize (hb_sanitize_context_t *c) const
{
@ -216,10 +217,23 @@ struct PairPosFormat2
}
bail:
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"kerning glyphs at %d,%d",
c->buffer->idx, skippy_iter.idx);
}
applied_first = valueFormat1.apply_value (c, this, v, buffer->cur_pos());
applied_second = valueFormat2.apply_value (c, this, v + len1, buffer->pos[skippy_iter.idx]);
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"kerned glyphs at %d,%d",
c->buffer->idx, skippy_iter.idx);
}
success:
if (applied_first || applied_second)
buffer->unsafe_to_break (buffer->idx, skippy_iter.idx + 1);

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

@ -0,0 +1,189 @@
#ifndef OT_LAYOUT_GPOS_PAIRSET_HH
#define OT_LAYOUT_GPOS_PAIRSET_HH
#include "PairValueRecord.hh"
namespace OT {
namespace Layout {
namespace GPOS_impl {
template <typename Types>
struct PairSet
{
template <typename Types2>
friend struct PairPosFormat1_3;
using PairValueRecord = GPOS_impl::PairValueRecord<Types>;
protected:
HBUINT16 len; /* Number of PairValueRecords */
PairValueRecord firstPairValueRecord;
/* Array of PairValueRecords--ordered
* by GlyphID of the second glyph */
public:
DEFINE_SIZE_MIN (2);
struct sanitize_closure_t
{
const ValueFormat *valueFormats;
unsigned int len1; /* valueFormats[0].get_len() */
unsigned int stride; /* 1 + len1 + len2 */
};
bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) const
{
TRACE_SANITIZE (this);
if (!(c->check_struct (this)
&& c->check_range (&firstPairValueRecord,
len,
HBUINT16::static_size,
closure->stride))) return_trace (false);
unsigned int count = len;
const PairValueRecord *record = &firstPairValueRecord;
return_trace (closure->valueFormats[0].sanitize_values_stride_unsafe (c, this, &record->values[0], count, closure->stride) &&
closure->valueFormats[1].sanitize_values_stride_unsafe (c, this, &record->values[closure->len1], count, closure->stride));
}
bool intersects (const hb_set_t *glyphs,
const ValueFormat *valueFormats) const
{
unsigned int len1 = valueFormats[0].get_len ();
unsigned int len2 = valueFormats[1].get_len ();
unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
const PairValueRecord *record = &firstPairValueRecord;
unsigned int count = len;
for (unsigned int i = 0; i < count; i++)
{
if (glyphs->has (record->secondGlyph))
return true;
record = &StructAtOffset<const PairValueRecord> (record, record_size);
}
return false;
}
void collect_glyphs (hb_collect_glyphs_context_t *c,
const ValueFormat *valueFormats) const
{
unsigned int len1 = valueFormats[0].get_len ();
unsigned int len2 = valueFormats[1].get_len ();
unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
const PairValueRecord *record = &firstPairValueRecord;
c->input->add_array (&record->secondGlyph, len, record_size);
}
void collect_variation_indices (hb_collect_variation_indices_context_t *c,
const ValueFormat *valueFormats) const
{
unsigned len1 = valueFormats[0].get_len ();
unsigned len2 = valueFormats[1].get_len ();
unsigned record_size = HBUINT16::static_size * (1 + len1 + len2);
const PairValueRecord *record = &firstPairValueRecord;
unsigned count = len;
for (unsigned i = 0; i < count; i++)
{
if (c->glyph_set->has (record->secondGlyph))
{ record->collect_variation_indices (c, valueFormats, this); }
record = &StructAtOffset<const PairValueRecord> (record, record_size);
}
}
bool apply (hb_ot_apply_context_t *c,
const ValueFormat *valueFormats,
unsigned int pos) const
{
TRACE_APPLY (this);
hb_buffer_t *buffer = c->buffer;
unsigned int len1 = valueFormats[0].get_len ();
unsigned int len2 = valueFormats[1].get_len ();
unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
const PairValueRecord *record = hb_bsearch (buffer->info[pos].codepoint,
&firstPairValueRecord,
len,
record_size);
if (record)
{
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"kerning glyphs at %d,%d",
c->buffer->idx, pos);
}
bool applied_first = valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos());
bool applied_second = valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos]);
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"kerned glyphs at %d,%d",
c->buffer->idx, pos);
}
if (applied_first || applied_second)
buffer->unsafe_to_break (buffer->idx, pos + 1);
if (len2)
pos++;
buffer->idx = pos;
return_trace (true);
}
buffer->unsafe_to_concat (buffer->idx, pos + 1);
return_trace (false);
}
bool subset (hb_subset_context_t *c,
const ValueFormat valueFormats[2],
const ValueFormat newFormats[2]) const
{
TRACE_SUBSET (this);
auto snap = c->serializer->snapshot ();
auto *out = c->serializer->start_embed (*this);
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
out->len = 0;
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
const hb_map_t &glyph_map = *c->plan->glyph_map;
unsigned len1 = valueFormats[0].get_len ();
unsigned len2 = valueFormats[1].get_len ();
unsigned record_size = HBUINT16::static_size + Value::static_size * (len1 + len2);
typename PairValueRecord::context_t context =
{
this,
valueFormats,
newFormats,
len1,
&glyph_map,
c->plan->layout_variation_idx_map
};
const PairValueRecord *record = &firstPairValueRecord;
unsigned count = len, num = 0;
for (unsigned i = 0; i < count; i++)
{
if (glyphset.has (record->secondGlyph)
&& record->subset (c, &context)) num++;
record = &StructAtOffset<const PairValueRecord> (record, record_size);
}
out->len = num;
if (!num) c->serializer->revert (snap);
return_trace (num);
}
};
}
}
}
#endif // OT_LAYOUT_GPOS_PAIRSET_HH

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

@ -0,0 +1,99 @@
#ifndef OT_LAYOUT_GPOS_PAIRVALUERECORD_HH
#define OT_LAYOUT_GPOS_PAIRVALUERECORD_HH
#include "ValueFormat.hh"
namespace OT {
namespace Layout {
namespace GPOS_impl {
template <typename Types>
struct PairValueRecord
{
template <typename Types2>
friend struct PairSet;
protected:
typename Types::HBGlyphID
secondGlyph; /* GlyphID of second glyph in the
* pair--first glyph is listed in the
* Coverage table */
ValueRecord values; /* Positioning data for the first glyph
* followed by for second glyph */
public:
DEFINE_SIZE_ARRAY (Types::size, values);
int cmp (hb_codepoint_t k) const
{ return secondGlyph.cmp (k); }
struct context_t
{
const void *base;
const ValueFormat *valueFormats;
const ValueFormat *newFormats;
unsigned len1; /* valueFormats[0].get_len() */
const hb_map_t *glyph_map;
const hb_map_t *layout_variation_idx_map;
};
bool subset (hb_subset_context_t *c,
context_t *closure) const
{
TRACE_SERIALIZE (this);
auto *s = c->serializer;
auto *out = s->start_embed (*this);
if (unlikely (!s->extend_min (out))) return_trace (false);
out->secondGlyph = (*closure->glyph_map)[secondGlyph];
closure->valueFormats[0].copy_values (s,
closure->newFormats[0],
closure->base, &values[0],
closure->layout_variation_idx_map);
closure->valueFormats[1].copy_values (s,
closure->newFormats[1],
closure->base,
&values[closure->len1],
closure->layout_variation_idx_map);
return_trace (true);
}
void collect_variation_indices (hb_collect_variation_indices_context_t *c,
const ValueFormat *valueFormats,
const void *base) const
{
unsigned record1_len = valueFormats[0].get_len ();
unsigned record2_len = valueFormats[1].get_len ();
const hb_array_t<const Value> values_array = values.as_array (record1_len + record2_len);
if (valueFormats[0].has_device ())
valueFormats[0].collect_variation_indices (c, base, values_array.sub_array (0, record1_len));
if (valueFormats[1].has_device ())
valueFormats[1].collect_variation_indices (c, base, values_array.sub_array (record1_len, record2_len));
}
bool intersects (const hb_set_t& glyphset) const
{
return glyphset.has(secondGlyph);
}
const Value* get_values_1 () const
{
return &values[0];
}
const Value* get_values_2 (ValueFormat format1) const
{
return &values[format1.get_len ()];
}
};
}
}
}
#endif // OT_LAYOUT_GPOS_PAIRVALUERECORD_HH

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

@ -45,10 +45,7 @@ struct SinglePos
ValueFormat new_format = src->get_value_format ();
if (glyph_val_iter_pairs)
{
format = get_format (glyph_val_iter_pairs);
new_format = src->get_value_format ().get_effective_format (+ glyph_val_iter_pairs | hb_map (hb_second));
}
u.format = format;
switch (u.format) {

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

@ -39,12 +39,10 @@ struct SinglePosFormat1
{
if (!valueFormat.has_device ()) return;
auto it =
+ hb_iter (this+coverage)
| hb_filter (c->glyph_set)
;
hb_set_t intersection;
(this+coverage).intersect_set (*c->glyph_set, intersection);
if (!intersection) return;
if (!it) return;
valueFormat.collect_variation_indices (c, this, values.as_array (valueFormat.get_len ()));
}
@ -62,8 +60,22 @@ struct SinglePosFormat1
unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false);
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"positioning glyph at %d",
c->buffer->idx);
}
valueFormat.apply_value (c, this, values, buffer->cur_pos());
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"positioned glyph at %d",
c->buffer->idx);
}
buffer->idx++;
return_trace (true);
}
@ -104,9 +116,11 @@ struct SinglePosFormat1
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
const hb_map_t &glyph_map = *c->plan->glyph_map;
hb_set_t intersection;
(this+coverage).intersect_set (glyphset, intersection);
auto it =
+ hb_iter (this+coverage)
| hb_filter (glyphset)
+ hb_iter (intersection)
| hb_map_retains_sorting (glyph_map)
| hb_zip (hb_repeat (values.as_array (valueFormat.get_len ())))
;

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

@ -70,10 +70,24 @@ struct SinglePosFormat2
if (likely (index >= valueCount)) return_trace (false);
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"positioning glyph at %d",
c->buffer->idx);
}
valueFormat.apply_value (c, this,
&values[index * valueFormat.get_len ()],
buffer->cur_pos());
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"positioned glyph at %d",
c->buffer->idx);
}
buffer->idx++;
return_trace (true);
}

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

@ -5,12 +5,13 @@
namespace OT {
namespace Layout {
namespace GSUB {
namespace GSUB_impl {
template <typename Types>
struct AlternateSet
{
protected:
Array16Of<HBGlyphID16>
Array16Of<typename Types::HBGlyphID>
alternates; /* Array of alternate GlyphIDs--in
* arbitrary order */
public:
@ -56,8 +57,23 @@ struct AlternateSet
if (unlikely (alt_index > count || alt_index == 0)) return_trace (false);
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->sync_so_far ();
c->buffer->message (c->font,
"replacing glyph at %d (alternate substitution)",
c->buffer->idx);
}
c->replace_glyph (alternates[alt_index - 1]);
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"replaced glyph at %d (alternate substitution)",
c->buffer->idx - 1);
}
return_trace (true);
}

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

@ -6,14 +6,17 @@
namespace OT {
namespace Layout {
namespace GSUB {
namespace GSUB_impl {
struct AlternateSubst
{
protected:
union {
HBUINT16 format; /* Format identifier */
AlternateSubstFormat1 format1;
HBUINT16 format; /* Format identifier */
AlternateSubstFormat1_2<SmallTypes> format1;
#ifndef HB_NO_BORING_EXPANSION
AlternateSubstFormat1_2<MediumTypes> format2;
#endif
} u;
public:
@ -24,10 +27,15 @@ struct AlternateSubst
if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
switch (u.format) {
case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
#ifndef HB_NO_BORING_EXPANSION
case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
#endif
default:return_trace (c->default_return_value ());
}
}
/* TODO This function is unused and not updated to 24bit GIDs. Should be done by using
* iterators. While at it perhaps using iterator of arrays of hb_codepoint_t instead. */
bool serialize (hb_serialize_context_t *c,
hb_sorted_array_t<const HBGlyphID16> glyphs,
hb_array_t<const unsigned int> alternate_len_list,
@ -42,6 +50,9 @@ struct AlternateSubst
default:return_trace (false);
}
}
/* TODO subset() should choose format. */
};
}

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