зеркало из https://github.com/mozilla/gecko-dev.git
Merge autoland to mozilla-central a=merge
This commit is contained in:
Коммит
f655bdf6b4
|
@ -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. */
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче