зеркало из https://github.com/mozilla/gecko-dev.git
Merge autoland to mozilla-central a=merge
This commit is contained in:
Коммит
86c98c486f
|
@ -6318,7 +6318,7 @@ nsBrowserAccess.prototype = {
|
|||
forceNotRemote,
|
||||
userContextId,
|
||||
aOpenWindowInfo,
|
||||
null,
|
||||
aOpenWindowInfo?.parent?.top.embedderElement,
|
||||
aTriggeringPrincipal,
|
||||
"",
|
||||
aCsp,
|
||||
|
|
|
@ -914,7 +914,9 @@ DownloadsDataCtor.prototype = {
|
|||
|
||||
if (!download.newDownloadNotified) {
|
||||
download.newDownloadNotified = true;
|
||||
this._notifyDownloadEvent("start");
|
||||
this._notifyDownloadEvent("start", {
|
||||
openDownloadsListOnStart: download.openDownloadsListOnStart,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -968,12 +970,15 @@ DownloadsDataCtor.prototype = {
|
|||
/**
|
||||
* Displays a new or finished download notification in the most recent browser
|
||||
* window, if one is currently available with the required privacy type.
|
||||
*
|
||||
* @param aType
|
||||
* @param {string} aType
|
||||
* Set to "start" for new downloads, "finish" for completed downloads,
|
||||
* "error" for downloads that failed and need attention
|
||||
* @param {boolean} [openDownloadsListOnStart]
|
||||
* (Only relevant when aType = "start")
|
||||
* true (default) - open the downloads panel.
|
||||
* false - only show an indicator notification.
|
||||
*/
|
||||
_notifyDownloadEvent(aType) {
|
||||
_notifyDownloadEvent(aType, { openDownloadsListOnStart = true } = {}) {
|
||||
DownloadsCommon.log(
|
||||
"Attempting to notify that a new download has started or finished."
|
||||
);
|
||||
|
@ -987,6 +992,7 @@ DownloadsDataCtor.prototype = {
|
|||
}
|
||||
|
||||
let shouldOpenDownloadsPanel =
|
||||
openDownloadsListOnStart &&
|
||||
aType == "start" &&
|
||||
Services.prefs.getBoolPref(
|
||||
"browser.download.improvements_to_download_panel"
|
||||
|
|
|
@ -77,23 +77,20 @@ add_task(async function test_customizemode_doesnt_wreck_things() {
|
|||
});
|
||||
|
||||
/**
|
||||
* Make sure the downloads panel _does not_ open automatically if we set the
|
||||
* pref telling it not to do that.
|
||||
* Start a download and check that the downloads panel opens correctly according
|
||||
* to the download parameter, openDownloadsListOnStart
|
||||
* @param {boolean} [openDownloadsListOnStart]
|
||||
* true (default) - open downloads panel when download starts
|
||||
* false - no downloads panel; update indicator attention state
|
||||
*/
|
||||
add_task(async function test_downloads_panel_opening_pref() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.download.improvements_to_download_panel", true],
|
||||
["browser.download.always_ask_before_handling_new_types", false],
|
||||
["browser.download.alwaysOpenPanel", false],
|
||||
],
|
||||
});
|
||||
registerCleanupFunction(async () => {
|
||||
await SpecialPowers.popPrefEnv();
|
||||
});
|
||||
|
||||
async function downloadAndCheckPanel({ openDownloadsListOnStart = true } = {}) {
|
||||
info("creating a download and setting it to in progress");
|
||||
await task_addDownloads([{ state: DownloadsCommon.DOWNLOAD_DOWNLOADING }]);
|
||||
await task_addDownloads([
|
||||
{
|
||||
state: DownloadsCommon.DOWNLOAD_DOWNLOADING,
|
||||
openDownloadsListOnStart,
|
||||
},
|
||||
]);
|
||||
let publicList = await Downloads.getList(Downloads.PUBLIC);
|
||||
let downloads = await publicList.getAll();
|
||||
downloads[0].stopped = false;
|
||||
|
@ -116,7 +113,9 @@ add_task(async function test_downloads_panel_opening_pref() {
|
|||
};
|
||||
});
|
||||
|
||||
DownloadsCommon.getData(window)._notifyDownloadEvent("start");
|
||||
DownloadsCommon.getData(window)._notifyDownloadEvent("start", {
|
||||
openDownloadsListOnStart,
|
||||
});
|
||||
is(
|
||||
DownloadsPanel.isPanelShowing,
|
||||
false,
|
||||
|
@ -126,12 +125,160 @@ add_task(async function test_downloads_panel_opening_pref() {
|
|||
info("waiting for download to start");
|
||||
await promiseDownloadStartedNotification;
|
||||
|
||||
DownloadsIndicatorView.showEventNotification = oldShowEventNotification;
|
||||
is(DownloadsPanel.panel.state, "closed", "Panel should be closed");
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure the downloads panel _does not_ open automatically if we set the
|
||||
* pref telling it not to do that.
|
||||
*/
|
||||
add_task(async function test_downloads_panel_opening_pref() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.download.improvements_to_download_panel", true],
|
||||
["browser.download.always_ask_before_handling_new_types", false],
|
||||
["browser.download.alwaysOpenPanel", false],
|
||||
],
|
||||
});
|
||||
registerCleanupFunction(async () => {
|
||||
await SpecialPowers.popPrefEnv();
|
||||
});
|
||||
await downloadAndCheckPanel();
|
||||
await SpecialPowers.popPrefEnv();
|
||||
});
|
||||
|
||||
/**
|
||||
* Make sure the downloads panel opens automatically with new download, only if no other downloads are in progress.
|
||||
* Make sure the downloads panel _does not_ open automatically if we pass the
|
||||
* parameter telling it not to do that to the download constructor.
|
||||
*/
|
||||
add_task(async function test_downloads_openDownloadsListOnStart_param() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.download.improvements_to_download_panel", true],
|
||||
["browser.download.always_ask_before_handling_new_types", false],
|
||||
["browser.download.alwaysOpenPanel", true],
|
||||
],
|
||||
});
|
||||
registerCleanupFunction(async () => {
|
||||
await SpecialPowers.popPrefEnv();
|
||||
});
|
||||
await downloadAndCheckPanel({ openDownloadsListOnStart: false });
|
||||
await SpecialPowers.popPrefEnv();
|
||||
});
|
||||
|
||||
/**
|
||||
* Make sure the downloads panel _does not_ open automatically when an
|
||||
* extension calls the browser.downloads.download API method while it is
|
||||
* not handling user input, but that we do open it automatically when
|
||||
* the same WebExtensions API is called while handling user input
|
||||
* (See Bug 1759231)
|
||||
*/
|
||||
add_task(async function test_downloads_panel_on_webext_download_api() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.download.improvements_to_download_panel", true],
|
||||
["browser.download.always_ask_before_handling_new_types", false],
|
||||
["browser.download.alwaysOpenPanel", true],
|
||||
],
|
||||
});
|
||||
registerCleanupFunction(async () => {
|
||||
await SpecialPowers.popPrefEnv();
|
||||
});
|
||||
|
||||
const extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
permissions: ["downloads"],
|
||||
},
|
||||
background() {
|
||||
async function startDownload(downloadOptions) {
|
||||
/* globals browser */
|
||||
const downloadId = await browser.downloads.download(downloadOptions);
|
||||
const downloadDone = new Promise(resolve => {
|
||||
browser.downloads.onChanged.addListener(function listener(delta) {
|
||||
browser.test.log(`downloads.onChanged = ${JSON.stringify(delta)}`);
|
||||
if (
|
||||
delta.id == downloadId &&
|
||||
delta.state?.current !== "in_progress"
|
||||
) {
|
||||
browser.downloads.onChanged.removeListener(listener);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
browser.test.sendMessage("start-download:done");
|
||||
await downloadDone;
|
||||
await browser.downloads.removeFile(downloadId);
|
||||
browser.test.sendMessage("removed-download-file");
|
||||
}
|
||||
|
||||
browser.test.onMessage.addListener(
|
||||
(msg, { withHandlingUserInput, downloadOptions }) => {
|
||||
if (msg !== "start-download") {
|
||||
browser.test.fail(`Got unexpected test message: ${msg}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (withHandlingUserInput) {
|
||||
browser.test.withHandlingUserInput(() =>
|
||||
startDownload(downloadOptions)
|
||||
);
|
||||
} else {
|
||||
startDownload(downloadOptions);
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
await extension.startup();
|
||||
|
||||
startServer();
|
||||
|
||||
async function testExtensionDownloadCall({ withHandlingUserInput }) {
|
||||
mustInterruptResponses();
|
||||
let rnd = Math.random();
|
||||
let url = httpUrl(`interruptible.txt?q=${rnd}`);
|
||||
|
||||
extension.sendMessage("start-download", {
|
||||
withHandlingUserInput,
|
||||
downloadOptions: { url },
|
||||
});
|
||||
await extension.awaitMessage("start-download:done");
|
||||
|
||||
let publicList = await Downloads.getList(Downloads.PUBLIC);
|
||||
let downloads = await publicList.getAll();
|
||||
|
||||
let download = downloads.find(d => d.source.url === url);
|
||||
is(download.source.url, url, "download has the expected url");
|
||||
is(
|
||||
download.openDownloadsListOnStart,
|
||||
withHandlingUserInput,
|
||||
`download panel should ${withHandlingUserInput ? "open" : "stay closed"}`
|
||||
);
|
||||
|
||||
continueResponses();
|
||||
await extension.awaitMessage("removed-download-file");
|
||||
}
|
||||
|
||||
info(
|
||||
"Test extension downloads.download API method call without handling user input"
|
||||
);
|
||||
await testExtensionDownloadCall({ withHandlingUserInput: true });
|
||||
|
||||
info(
|
||||
"Test extension downloads.download API method call while handling user input"
|
||||
);
|
||||
await testExtensionDownloadCall({ withHandlingUserInput: false });
|
||||
|
||||
await extension.unload();
|
||||
await SpecialPowers.popPrefEnv();
|
||||
});
|
||||
|
||||
/**
|
||||
* Make sure the downloads panel opens automatically with new download, only if
|
||||
* no other downloads are in progress.
|
||||
*/
|
||||
add_task(async function test_downloads_panel_remains_closed() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
|
|
|
@ -242,6 +242,7 @@ async function task_addDownloads(aItems) {
|
|||
: null,
|
||||
hasPartialData: item.state == DownloadsCommon.DOWNLOAD_PAUSED,
|
||||
hasBlockedData: item.hasBlockedData || false,
|
||||
openDownloadsListOnStart: item.openDownloadsListOnStart ?? true,
|
||||
contentType: item.contentType,
|
||||
startTime: new Date(startTimeMs++),
|
||||
};
|
||||
|
|
|
@ -425,6 +425,12 @@ class BasePopup {
|
|||
if (this.destroyed) {
|
||||
return;
|
||||
}
|
||||
// Only block the parser for the preloaded browser, initBrowser will be
|
||||
// called again when the browserAction popup is navigated and we should
|
||||
// not block the parser in that case, otherwise the navigating the popup
|
||||
// to another extension page will never complete and the popup will
|
||||
// stay stuck on the previous extension page. See Bug 1747813.
|
||||
this.blockParser = false;
|
||||
this.browser.messageManager.sendAsyncMessage("Extension:UnblockParser");
|
||||
});
|
||||
}
|
||||
|
@ -580,6 +586,11 @@ class ViewPopup extends BasePopup {
|
|||
this.tempPanel = panel;
|
||||
this.tempBrowser = this.browser;
|
||||
|
||||
// NOTE: this class is added to the preload browser and never removed because
|
||||
// the preload browser is then switched with a new browser once we are about to
|
||||
// make the popup visible (this class is not actually used anywhere but it may
|
||||
// be useful to keep it around to be able to identify the preload buffer while
|
||||
// investigating issues).
|
||||
this.browser.classList.add("webextension-preload-browser");
|
||||
}
|
||||
|
||||
|
|
|
@ -76,3 +76,72 @@ add_task(async function process_switch_in_sidebars_popups() {
|
|||
await closeBrowserAction(extension);
|
||||
await extension.unload();
|
||||
});
|
||||
|
||||
// Test that navigating the browserAction popup between extension pages doesn't keep the
|
||||
// parser blocked (See Bug 1747813).
|
||||
add_task(
|
||||
async function test_navigate_browserActionPopups_shouldnot_block_parser() {
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
browser_action: {
|
||||
default_popup: "popup-1.html",
|
||||
},
|
||||
},
|
||||
files: {
|
||||
"popup-1.html": `<!DOCTYPE html><meta charset=utf-8><script src=popup-1.js></script><h1>Popup 1</h1>`,
|
||||
"popup-2.html": `<!DOCTYPE html><meta charset=utf-8><script src=popup-2.js></script><h1>Popup 2</h1>`,
|
||||
|
||||
"popup-1.js": function() {
|
||||
browser.test.onMessage.addListener(msg => {
|
||||
if (msg !== "navigate-popup") {
|
||||
browser.test.fail(`Unexpected test message "${msg}"`);
|
||||
return;
|
||||
}
|
||||
location.href = "/popup-2.html";
|
||||
});
|
||||
window.onload = () => browser.test.sendMessage("popup-page-1");
|
||||
},
|
||||
|
||||
"popup-2.js": function() {
|
||||
window.onload = () => browser.test.sendMessage("popup-page-2");
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Make sure the mouse isn't hovering over the browserAction widget.
|
||||
EventUtils.synthesizeMouseAtCenter(
|
||||
gURLBar.textbox,
|
||||
{ type: "mouseover" },
|
||||
window
|
||||
);
|
||||
|
||||
await extension.startup();
|
||||
|
||||
// Triggers popup preload (otherwise we wouldn't be blocking the parser for the browserAction popup
|
||||
// and the issue wouldn't be triggered, a real user on the contrary has a pretty high chance to trigger a
|
||||
// preload while hovering the browserAction popup before opening the popup with a click).
|
||||
let widget = getBrowserActionWidget(extension).forWindow(window);
|
||||
EventUtils.synthesizeMouseAtCenter(
|
||||
widget.node,
|
||||
{ type: "mouseover" },
|
||||
window
|
||||
);
|
||||
await clickBrowserAction(extension);
|
||||
|
||||
await extension.awaitMessage("popup-page-1");
|
||||
|
||||
extension.sendMessage("navigate-popup");
|
||||
|
||||
await extension.awaitMessage("popup-page-2");
|
||||
// If the bug is triggered (e.g. it did regress), the test will get stuck waiting for
|
||||
// the test message "popup-page-2" (which will never be sent because the extension page
|
||||
// script isn't executed while the parser is blocked).
|
||||
ok(
|
||||
true,
|
||||
"Extension browserAction popup successfully navigated to popup-page-2.html"
|
||||
);
|
||||
|
||||
await closeBrowserAction(extension);
|
||||
await extension.unload();
|
||||
}
|
||||
);
|
||||
|
|
|
@ -290,7 +290,6 @@ class AboutWelcomeChild extends JSWindowActorChild {
|
|||
...eventData,
|
||||
event_context: {
|
||||
...eventData.event_context,
|
||||
page: "about:welcome",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -23,6 +23,141 @@ module.exports = ReactDOM;
|
|||
/* 3 */
|
||||
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
__webpack_require__.r(__webpack_exports__);
|
||||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||
/* harmony export */ "AboutWelcomeUtils": () => (/* binding */ AboutWelcomeUtils),
|
||||
/* harmony export */ "DEFAULT_RTAMO_CONTENT": () => (/* binding */ DEFAULT_RTAMO_CONTENT)
|
||||
/* harmony export */ });
|
||||
/* 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/. */
|
||||
// If we're in a subdialog, then this is a spotlight modal
|
||||
const page = document.querySelector(":root[dialogroot=true]") ? "spotlight" : "about:welcome";
|
||||
const AboutWelcomeUtils = {
|
||||
handleUserAction(action) {
|
||||
window.AWSendToParent("SPECIAL_ACTION", action);
|
||||
},
|
||||
|
||||
sendImpressionTelemetry(messageId, context) {
|
||||
window.AWSendEventTelemetry({
|
||||
event: "IMPRESSION",
|
||||
event_context: { ...context,
|
||||
page
|
||||
},
|
||||
message_id: messageId
|
||||
});
|
||||
},
|
||||
|
||||
sendActionTelemetry(messageId, elementId, eventName = "CLICK_BUTTON") {
|
||||
const ping = {
|
||||
event: eventName,
|
||||
event_context: {
|
||||
source: elementId,
|
||||
page
|
||||
},
|
||||
message_id: messageId
|
||||
};
|
||||
window.AWSendEventTelemetry(ping);
|
||||
},
|
||||
|
||||
async fetchFlowParams(metricsFlowUri) {
|
||||
let flowParams;
|
||||
|
||||
try {
|
||||
const response = await fetch(metricsFlowUri, {
|
||||
credentials: "omit"
|
||||
});
|
||||
|
||||
if (response.status === 200) {
|
||||
const {
|
||||
deviceId,
|
||||
flowId,
|
||||
flowBeginTime
|
||||
} = await response.json();
|
||||
flowParams = {
|
||||
deviceId,
|
||||
flowId,
|
||||
flowBeginTime
|
||||
};
|
||||
} else {
|
||||
console.error("Non-200 response", response); // eslint-disable-line no-console
|
||||
}
|
||||
} catch (e) {
|
||||
flowParams = null;
|
||||
}
|
||||
|
||||
return flowParams;
|
||||
},
|
||||
|
||||
sendEvent(type, detail) {
|
||||
document.dispatchEvent(new CustomEvent(`AWPage:${type}`, {
|
||||
bubbles: true,
|
||||
detail
|
||||
}));
|
||||
}
|
||||
|
||||
};
|
||||
const DEFAULT_RTAMO_CONTENT = {
|
||||
template: "return_to_amo",
|
||||
utm_term: "rtamo",
|
||||
content: {
|
||||
position: "corner",
|
||||
hero_text: {
|
||||
string_id: "mr1-welcome-screen-hero-text"
|
||||
},
|
||||
title: {
|
||||
string_id: "return-to-amo-subtitle"
|
||||
},
|
||||
has_noodles: true,
|
||||
subtitle: {
|
||||
string_id: "return-to-amo-addon-title"
|
||||
},
|
||||
help_text: {
|
||||
string_id: "mr1-onboarding-welcome-image-caption"
|
||||
},
|
||||
backdrop: "#212121 url(chrome://activity-stream/content/data/content/assets/proton-bkg.avif) center/cover no-repeat fixed",
|
||||
primary_button: {
|
||||
label: {
|
||||
string_id: "return-to-amo-add-extension-label"
|
||||
},
|
||||
source_id: "ADD_EXTENSION_BUTTON",
|
||||
action: {
|
||||
type: "INSTALL_ADDON_FROM_URL",
|
||||
data: {
|
||||
url: null,
|
||||
telemetrySource: "rtamo"
|
||||
}
|
||||
}
|
||||
},
|
||||
secondary_button: {
|
||||
label: {
|
||||
string_id: "onboarding-not-now-button-label"
|
||||
},
|
||||
source_id: "RTAMO_START_BROWSING_BUTTON",
|
||||
action: {
|
||||
type: "OPEN_AWESOME_BAR"
|
||||
}
|
||||
},
|
||||
secondary_button_top: {
|
||||
label: {
|
||||
string_id: "mr1-onboarding-sign-in-button-label"
|
||||
},
|
||||
source_id: "RTAMO_FXA_SIGNIN_BUTTON",
|
||||
action: {
|
||||
data: {
|
||||
entrypoint: "activity-stream-firstrun"
|
||||
},
|
||||
type: "SHOW_FIREFOX_ACCOUNTS",
|
||||
addFlowParams: true
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/***/ }),
|
||||
/* 4 */
|
||||
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
__webpack_require__.r(__webpack_exports__);
|
||||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||
/* harmony export */ "MultiStageAboutWelcome": () => (/* binding */ MultiStageAboutWelcome),
|
||||
|
@ -32,8 +167,8 @@ __webpack_require__.r(__webpack_exports__);
|
|||
/* 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__(4);
|
||||
/* harmony import */ var _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(5);
|
||||
/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
|
||||
/* 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);
|
||||
|
@ -342,7 +477,7 @@ class WelcomeScreen extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCo
|
|||
}
|
||||
|
||||
/***/ }),
|
||||
/* 4 */
|
||||
/* 5 */
|
||||
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
__webpack_require__.r(__webpack_exports__);
|
||||
|
@ -435,137 +570,6 @@ const Localized = ({
|
|||
textNodes.length ? textNodes : null);
|
||||
};
|
||||
|
||||
/***/ }),
|
||||
/* 5 */
|
||||
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
__webpack_require__.r(__webpack_exports__);
|
||||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||
/* harmony export */ "AboutWelcomeUtils": () => (/* binding */ AboutWelcomeUtils),
|
||||
/* harmony export */ "DEFAULT_RTAMO_CONTENT": () => (/* binding */ DEFAULT_RTAMO_CONTENT)
|
||||
/* harmony export */ });
|
||||
/* 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 AboutWelcomeUtils = {
|
||||
handleUserAction(action) {
|
||||
window.AWSendToParent("SPECIAL_ACTION", action);
|
||||
},
|
||||
|
||||
sendImpressionTelemetry(messageId, context) {
|
||||
window.AWSendEventTelemetry({
|
||||
event: "IMPRESSION",
|
||||
event_context: context,
|
||||
message_id: messageId
|
||||
});
|
||||
},
|
||||
|
||||
sendActionTelemetry(messageId, elementId, eventName = "CLICK_BUTTON") {
|
||||
const ping = {
|
||||
event: eventName,
|
||||
event_context: {
|
||||
source: elementId,
|
||||
page: "about:welcome"
|
||||
},
|
||||
message_id: messageId
|
||||
};
|
||||
window.AWSendEventTelemetry(ping);
|
||||
},
|
||||
|
||||
async fetchFlowParams(metricsFlowUri) {
|
||||
let flowParams;
|
||||
|
||||
try {
|
||||
const response = await fetch(metricsFlowUri, {
|
||||
credentials: "omit"
|
||||
});
|
||||
|
||||
if (response.status === 200) {
|
||||
const {
|
||||
deviceId,
|
||||
flowId,
|
||||
flowBeginTime
|
||||
} = await response.json();
|
||||
flowParams = {
|
||||
deviceId,
|
||||
flowId,
|
||||
flowBeginTime
|
||||
};
|
||||
} else {
|
||||
console.error("Non-200 response", response); // eslint-disable-line no-console
|
||||
}
|
||||
} catch (e) {
|
||||
flowParams = null;
|
||||
}
|
||||
|
||||
return flowParams;
|
||||
},
|
||||
|
||||
sendEvent(type, detail) {
|
||||
document.dispatchEvent(new CustomEvent(`AWPage:${type}`, {
|
||||
bubbles: true,
|
||||
detail
|
||||
}));
|
||||
}
|
||||
|
||||
};
|
||||
const DEFAULT_RTAMO_CONTENT = {
|
||||
template: "return_to_amo",
|
||||
utm_term: "rtamo",
|
||||
content: {
|
||||
position: "corner",
|
||||
hero_text: {
|
||||
string_id: "mr1-welcome-screen-hero-text"
|
||||
},
|
||||
title: {
|
||||
string_id: "return-to-amo-subtitle"
|
||||
},
|
||||
has_noodles: true,
|
||||
subtitle: {
|
||||
string_id: "return-to-amo-addon-title"
|
||||
},
|
||||
help_text: {
|
||||
string_id: "mr1-onboarding-welcome-image-caption"
|
||||
},
|
||||
backdrop: "#212121 url(chrome://activity-stream/content/data/content/assets/proton-bkg.avif) center/cover no-repeat fixed",
|
||||
primary_button: {
|
||||
label: {
|
||||
string_id: "return-to-amo-add-extension-label"
|
||||
},
|
||||
source_id: "ADD_EXTENSION_BUTTON",
|
||||
action: {
|
||||
type: "INSTALL_ADDON_FROM_URL",
|
||||
data: {
|
||||
url: null,
|
||||
telemetrySource: "rtamo"
|
||||
}
|
||||
}
|
||||
},
|
||||
secondary_button: {
|
||||
label: {
|
||||
string_id: "onboarding-not-now-button-label"
|
||||
},
|
||||
source_id: "RTAMO_START_BROWSING_BUTTON",
|
||||
action: {
|
||||
type: "OPEN_AWESOME_BAR"
|
||||
}
|
||||
},
|
||||
secondary_button_top: {
|
||||
label: {
|
||||
string_id: "mr1-onboarding-sign-in-button-label"
|
||||
},
|
||||
source_id: "RTAMO_FXA_SIGNIN_BUTTON",
|
||||
action: {
|
||||
data: {
|
||||
entrypoint: "activity-stream-firstrun"
|
||||
},
|
||||
type: "SHOW_FIREFOX_ACCOUNTS",
|
||||
addFlowParams: true
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/***/ }),
|
||||
/* 6 */
|
||||
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
||||
|
@ -577,11 +581,11 @@ __webpack_require__.r(__webpack_exports__);
|
|||
/* 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__(4);
|
||||
/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
|
||||
/* harmony import */ var _Colorways__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(7);
|
||||
/* harmony import */ var _MobileDownloads__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(8);
|
||||
/* harmony import */ var _Themes__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(9);
|
||||
/* harmony import */ var _MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(3);
|
||||
/* harmony import */ var _MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(4);
|
||||
/* harmony import */ var _LanguageSwitcher__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(10);
|
||||
/* 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,
|
||||
|
@ -820,7 +824,7 @@ __webpack_require__.r(__webpack_exports__);
|
|||
/* 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__(4);
|
||||
/* 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/. */
|
||||
|
@ -1008,7 +1012,7 @@ __webpack_require__.r(__webpack_exports__);
|
|||
/* 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__(4);
|
||||
/* 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/. */
|
||||
|
@ -1067,7 +1071,7 @@ __webpack_require__.r(__webpack_exports__);
|
|||
/* 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__(4);
|
||||
/* 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/. */
|
||||
|
@ -1122,8 +1126,8 @@ __webpack_require__.r(__webpack_exports__);
|
|||
/* 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__(4);
|
||||
/* harmony import */ var _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(5);
|
||||
/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
|
||||
/* harmony import */ var _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
|
||||
/* 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/. */
|
||||
|
@ -1412,7 +1416,7 @@ __webpack_require__.r(__webpack_exports__);
|
|||
/* 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 _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
|
||||
/* 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);
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
|
@ -1607,8 +1611,9 @@ __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 react_dom__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2);
|
||||
/* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_dom__WEBPACK_IMPORTED_MODULE_1__);
|
||||
/* harmony import */ var _components_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
|
||||
/* harmony import */ var _components_ReturnToAMO__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(12);
|
||||
/* 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);
|
||||
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
|
||||
|
@ -1619,6 +1624,7 @@ function _extends() { _extends = Object.assign || function (target) { for (var i
|
|||
|
||||
|
||||
|
||||
|
||||
class AboutWelcome extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
@ -1645,17 +1651,12 @@ class AboutWelcome extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCom
|
|||
domComplete,
|
||||
domInteractive
|
||||
} = performance.getEntriesByType("navigation").pop();
|
||||
window.AWSendEventTelemetry({
|
||||
event: "IMPRESSION",
|
||||
event_context: {
|
||||
domComplete,
|
||||
domInteractive,
|
||||
mountStart: performance.getEntriesByName("mount").pop().startTime,
|
||||
domState,
|
||||
source: this.props.UTMTerm,
|
||||
page: "about:welcome"
|
||||
},
|
||||
message_id: this.props.messageId
|
||||
_lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendImpressionTelemetry(this.props.messageId, {
|
||||
domComplete,
|
||||
domInteractive,
|
||||
mountStart: performance.getEntriesByName("mount").pop().startTime,
|
||||
domState,
|
||||
source: this.props.UTMTerm
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -1681,7 +1682,7 @@ class AboutWelcome extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCom
|
|||
} = this;
|
||||
|
||||
if (props.template === "return_to_amo") {
|
||||
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_components_ReturnToAMO__WEBPACK_IMPORTED_MODULE_3__.ReturnToAMO, {
|
||||
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_components_ReturnToAMO__WEBPACK_IMPORTED_MODULE_4__.ReturnToAMO, {
|
||||
message_id: props.messageId,
|
||||
type: props.type,
|
||||
name: props.name,
|
||||
|
@ -1692,7 +1693,7 @@ class AboutWelcome extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCom
|
|||
});
|
||||
}
|
||||
|
||||
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_components_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_2__.MultiStageAboutWelcome, {
|
||||
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_components_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_3__.MultiStageAboutWelcome, {
|
||||
message_id: props.messageId,
|
||||
screens: props.screens,
|
||||
metricsFlowUri: this.state.metricsFlowUri,
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import { AboutWelcomeUtils } from "../lib/aboutwelcome-utils";
|
||||
import { MultiStageAboutWelcome } from "./components/MultiStageAboutWelcome";
|
||||
import { ReturnToAMO } from "./components/ReturnToAMO";
|
||||
|
||||
|
@ -27,17 +28,12 @@ class AboutWelcome extends React.PureComponent {
|
|||
const { domComplete, domInteractive } = performance
|
||||
.getEntriesByType("navigation")
|
||||
.pop();
|
||||
window.AWSendEventTelemetry({
|
||||
event: "IMPRESSION",
|
||||
event_context: {
|
||||
domComplete,
|
||||
domInteractive,
|
||||
mountStart: performance.getEntriesByName("mount").pop().startTime,
|
||||
domState,
|
||||
source: this.props.UTMTerm,
|
||||
page: "about:welcome",
|
||||
},
|
||||
message_id: this.props.messageId,
|
||||
AboutWelcomeUtils.sendImpressionTelemetry(this.props.messageId, {
|
||||
domComplete,
|
||||
domInteractive,
|
||||
mountStart: performance.getEntriesByName("mount").pop().startTime,
|
||||
domState,
|
||||
source: this.props.UTMTerm,
|
||||
});
|
||||
};
|
||||
if (document.readyState === "complete") {
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// If we're in a subdialog, then this is a spotlight modal
|
||||
const page = document.querySelector(":root[dialogroot=true]")
|
||||
? "spotlight"
|
||||
: "about:welcome";
|
||||
|
||||
export const AboutWelcomeUtils = {
|
||||
handleUserAction(action) {
|
||||
window.AWSendToParent("SPECIAL_ACTION", action);
|
||||
|
@ -9,7 +14,10 @@ export const AboutWelcomeUtils = {
|
|||
sendImpressionTelemetry(messageId, context) {
|
||||
window.AWSendEventTelemetry({
|
||||
event: "IMPRESSION",
|
||||
event_context: context,
|
||||
event_context: {
|
||||
...context,
|
||||
page,
|
||||
},
|
||||
message_id: messageId,
|
||||
});
|
||||
},
|
||||
|
@ -18,7 +26,7 @@ export const AboutWelcomeUtils = {
|
|||
event: eventName,
|
||||
event_context: {
|
||||
source: elementId,
|
||||
page: "about:welcome",
|
||||
page,
|
||||
},
|
||||
message_id: messageId,
|
||||
};
|
||||
|
|
|
@ -50,6 +50,7 @@ support-files=
|
|||
[browser_discovery_card.js]
|
||||
[browser_getScreenshots.js]
|
||||
[browser_multistage_spotlight.js]
|
||||
[browser_multistage_spotlight_telemetry.js]
|
||||
[browser_newtab_overrides.js]
|
||||
[browser_newtab_header.js]
|
||||
[browser_newtab_last_LinkMenu.js]
|
||||
|
|
|
@ -342,6 +342,11 @@ add_task(async function test_AWMultistage_Primary_Action() {
|
|||
"DEFAULT_ABOUTWELCOME_PROTON_SITES",
|
||||
"SITES MessageId sent in impression event telemetry"
|
||||
);
|
||||
Assert.equal(
|
||||
impressionCall.args[1].event_context.page,
|
||||
"about:welcome",
|
||||
"event context page set to 'about:welcome'"
|
||||
);
|
||||
}
|
||||
|
||||
// For some builds, we can stub fast enough to catch the performance
|
||||
|
@ -677,3 +682,28 @@ test_newtab(
|
|||
},
|
||||
"about:welcome"
|
||||
);
|
||||
|
||||
add_task(async function test_send_aboutwelcome_as_page_in_event_telemetry() {
|
||||
const sandbox = sinon.createSandbox();
|
||||
let browser = await openAboutWelcome();
|
||||
let aboutWelcomeActor = await getAboutWelcomeParent(browser);
|
||||
// Stub AboutWelcomeParent Content Message Handler
|
||||
let telemetryStub = sandbox.stub(aboutWelcomeActor, "onContentMessage");
|
||||
|
||||
await onButtonClick(browser, "button.primary");
|
||||
|
||||
Assert.equal(
|
||||
telemetryStub.lastCall.args[1].event,
|
||||
"CLICK_BUTTON",
|
||||
"Event telemetry sent on primary button press"
|
||||
);
|
||||
Assert.equal(
|
||||
telemetryStub.lastCall.args[1].event_context.page,
|
||||
"about:welcome",
|
||||
"Event context page set to 'about:welcome' in event telemetry"
|
||||
);
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
"use strict";
|
||||
|
||||
const { Spotlight } = ChromeUtils.import(
|
||||
"resource://activity-stream/lib/Spotlight.jsm"
|
||||
);
|
||||
const { PanelTestProvider } = ChromeUtils.import(
|
||||
"resource://activity-stream/lib/PanelTestProvider.jsm"
|
||||
);
|
||||
const { BrowserWindowTracker } = ChromeUtils.import(
|
||||
"resource:///modules/BrowserWindowTracker.jsm"
|
||||
);
|
||||
|
||||
async function waitForClick(selector, win) {
|
||||
await TestUtils.waitForCondition(() => win.document.querySelector(selector));
|
||||
win.document.querySelector(selector).click();
|
||||
}
|
||||
|
||||
async function showDialog(dialogOptions) {
|
||||
Spotlight.showSpotlightDialog(
|
||||
dialogOptions.browser,
|
||||
dialogOptions.message,
|
||||
dialogOptions.dispatchStub
|
||||
);
|
||||
const [win] = await TestUtils.topicObserved("subdialog-loaded");
|
||||
return win;
|
||||
}
|
||||
|
||||
add_task(async function send_spotlight_as_page_in_telemetry() {
|
||||
let message = (await PanelTestProvider.getMessages()).find(
|
||||
m => m.id === "MULTISTAGE_SPOTLIGHT_MESSAGE"
|
||||
);
|
||||
let dispatchStub = sinon.stub();
|
||||
let browser = BrowserWindowTracker.getTopWindow().gBrowser.selectedBrowser;
|
||||
let win = await showDialog({ message, browser, dispatchStub });
|
||||
let sandbox = sinon.createSandbox();
|
||||
|
||||
sandbox.stub(win, "AWSendEventTelemetry");
|
||||
await waitForClick("button.secondary", win);
|
||||
|
||||
Assert.equal(
|
||||
win.AWSendEventTelemetry.lastCall.args[0].event_context.page,
|
||||
"spotlight",
|
||||
"The value of event context page should be set to 'spotlight' in event telemetry"
|
||||
);
|
||||
|
||||
win.close();
|
||||
registerCleanupFunction(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
});
|
|
@ -70,8 +70,10 @@ add_task(async function test_click_on_footer() {
|
|||
PRIVACY_PREF_URL,
|
||||
true
|
||||
);
|
||||
// Wait for dropdown animation finished to continue mouse synthesizing.
|
||||
await sleep(3000);
|
||||
// Make sure dropdown is visible before continuing mouse synthesizing.
|
||||
await BrowserTestUtils.waitForCondition(() =>
|
||||
BrowserTestUtils.is_visible(optionButton)
|
||||
);
|
||||
await EventUtils.synthesizeMouseAtCenter(optionButton, {});
|
||||
info(`expecting tab: about:preferences#privacy opened`);
|
||||
const prefTab = await prefTabPromise;
|
||||
|
|
|
@ -475,6 +475,7 @@ class HTTPCustomRequestPanel extends Component {
|
|||
onClick: () => {
|
||||
const customRequestDetails = {
|
||||
...this.state,
|
||||
cause: this.props.request?.cause,
|
||||
urlQueryParams: urlQueryParams.map(
|
||||
({ checked, ...params }) => params
|
||||
),
|
||||
|
|
|
@ -20,6 +20,7 @@ add_task(async function() {
|
|||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const { tab, monitor } = await initNetMonitor(POST_RAW_URL, {
|
||||
requestCount: 1,
|
||||
});
|
||||
|
@ -79,3 +80,83 @@ add_task(async function() {
|
|||
|
||||
await teardown(monitor);
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests if resending an XHR request while XHR filtering is on, displays
|
||||
* the correct requests
|
||||
*/
|
||||
add_task(async function() {
|
||||
if (
|
||||
Services.prefs.getBoolPref(
|
||||
"devtools.netmonitor.features.newEditAndResend",
|
||||
true
|
||||
)
|
||||
) {
|
||||
const { tab, monitor } = await initNetMonitor(POST_RAW_URL, {
|
||||
requestCount: 1,
|
||||
});
|
||||
const { document, store, windowRequire } = monitor.panelWin;
|
||||
const { getSelectedRequest } = windowRequire(
|
||||
"devtools/client/netmonitor/src/selectors/index"
|
||||
);
|
||||
const Actions = windowRequire(
|
||||
"devtools/client/netmonitor/src/actions/index"
|
||||
);
|
||||
store.dispatch(Actions.batchEnable(false));
|
||||
|
||||
// Execute XHR request and filter by XHR
|
||||
await performRequests(monitor, tab, 1);
|
||||
document.querySelector(".requests-list-filter-xhr-button").click();
|
||||
|
||||
// Confirm XHR request and click it
|
||||
const xhrRequestItem = document.querySelectorAll(".request-list-item")[0];
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" }, xhrRequestItem);
|
||||
const waitForHeaders = waitUntil(() =>
|
||||
document.querySelector(".headers-overview")
|
||||
);
|
||||
await waitForHeaders;
|
||||
const firstRequest = getSelectedRequest(store.getState());
|
||||
|
||||
// Open context menu and execute "Edit & Resend".
|
||||
EventUtils.sendMouseEvent({ type: "contextmenu" }, xhrRequestItem);
|
||||
|
||||
info("Opening the new request panel");
|
||||
const waitForPanels = waitForDOM(
|
||||
document,
|
||||
".monitor-panel .network-action-bar"
|
||||
);
|
||||
const menuItem = getContextMenuItem(monitor, "request-list-context-resend");
|
||||
getContextMenuItem(monitor, "request-list-context-resend").click();
|
||||
|
||||
const menuPopup = menuItem.parentNode;
|
||||
|
||||
const onHidden = new Promise(resolve => {
|
||||
menuPopup.addEventListener("popuphidden", resolve, { once: true });
|
||||
});
|
||||
|
||||
menuItem.click();
|
||||
menuPopup.hidePopup();
|
||||
|
||||
await onHidden;
|
||||
await waitForPanels;
|
||||
|
||||
// Click the "Send" button and wait till the new request appears in the list
|
||||
document.querySelector("#http-custom-request-send-button").click();
|
||||
await waitForNetworkEvents(monitor, 1);
|
||||
|
||||
// Filtering by "other" so the resent request is visible after completion
|
||||
document.querySelector(".requests-list-filter-other-button").click();
|
||||
|
||||
// Select new request
|
||||
const newRequest = document.querySelectorAll(".request-list-item")[1];
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" }, newRequest);
|
||||
const resendRequest = getSelectedRequest(store.getState());
|
||||
|
||||
ok(
|
||||
resendRequest.id !== firstRequest.id,
|
||||
"The second XHR request was made and is unique"
|
||||
);
|
||||
|
||||
await teardown(monitor);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -67,3 +67,63 @@ add_task(async function() {
|
|||
|
||||
await teardown(monitor);
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests if editing and resending a XHR request works and the
|
||||
* new request retains the same cause type.
|
||||
*/
|
||||
|
||||
add_task(async function() {
|
||||
if (
|
||||
Services.prefs.getBoolPref(
|
||||
"devtools.netmonitor.features.newEditAndResend",
|
||||
true
|
||||
)
|
||||
) {
|
||||
const { tab, monitor } = await initNetMonitor(POST_RAW_URL, {
|
||||
requestCount: 1,
|
||||
});
|
||||
|
||||
const { document, store, windowRequire } = monitor.panelWin;
|
||||
const Actions = windowRequire(
|
||||
"devtools/client/netmonitor/src/actions/index"
|
||||
);
|
||||
store.dispatch(Actions.batchEnable(false));
|
||||
|
||||
// Executes 1 XHR request
|
||||
await performRequests(monitor, tab, 1);
|
||||
|
||||
// Selects 1st XHR request
|
||||
const xhrRequest = document.querySelectorAll(".request-list-item")[0];
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" }, xhrRequest);
|
||||
|
||||
// Stores original request for comparison of values later
|
||||
const { getSelectedRequest } = windowRequire(
|
||||
"devtools/client/netmonitor/src/selectors/index"
|
||||
);
|
||||
const original = getSelectedRequest(store.getState());
|
||||
|
||||
// Context Menu > "Edit & Resend"
|
||||
EventUtils.sendMouseEvent({ type: "contextmenu" }, xhrRequest);
|
||||
getContextMenuItem(monitor, "request-list-context-resend").click();
|
||||
|
||||
// 1) Wait for "Edit & Resend" panel to appear
|
||||
// 2) Click the "Send" button
|
||||
// 3) Wait till the new request appears in the list
|
||||
await waitUntil(() => document.querySelector(".http-custom-request-panel"));
|
||||
document.querySelector("#http-custom-request-send-button").click();
|
||||
await waitForNetworkEvents(monitor, 1);
|
||||
|
||||
// Selects new request
|
||||
const newRequest = document.querySelectorAll(".request-list-item")[1];
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" }, newRequest);
|
||||
const request = getSelectedRequest(store.getState());
|
||||
|
||||
ok(
|
||||
original.cause.type === request.cause.type,
|
||||
"Both requests retain the same cause type"
|
||||
);
|
||||
|
||||
await teardown(monitor);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -69,3 +69,94 @@ add_task(async function() {
|
|||
|
||||
await teardown(monitor);
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests if resending an image request uses the same content type
|
||||
* and hence is not blocked by the CSP of the page.
|
||||
*/
|
||||
|
||||
add_task(async function() {
|
||||
if (
|
||||
Services.prefs.getBoolPref(
|
||||
"devtools.netmonitor.features.newEditAndResend",
|
||||
true
|
||||
)
|
||||
) {
|
||||
const { tab, monitor } = await initNetMonitor(CSP_RESEND_URL, {
|
||||
requestCount: 1,
|
||||
});
|
||||
const { document, store, windowRequire } = monitor.panelWin;
|
||||
const Actions = windowRequire(
|
||||
"devtools/client/netmonitor/src/actions/index"
|
||||
);
|
||||
store.dispatch(Actions.batchEnable(false));
|
||||
|
||||
// Executes 1 request
|
||||
await performRequests(monitor, tab, 1);
|
||||
|
||||
// Select the image request
|
||||
const imgRequest = document.querySelectorAll(".request-list-item")[0];
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" }, imgRequest);
|
||||
|
||||
// Stores original request for comparison of values later
|
||||
const { getSelectedRequest } = windowRequire(
|
||||
"devtools/client/netmonitor/src/selectors/index"
|
||||
);
|
||||
const origReq = getSelectedRequest(store.getState());
|
||||
|
||||
// Context Menu > "Resend"
|
||||
EventUtils.sendMouseEvent({ type: "contextmenu" }, imgRequest);
|
||||
|
||||
info("Opening the new request panel");
|
||||
const waitForPanels = waitForDOM(
|
||||
document,
|
||||
".monitor-panel .network-action-bar"
|
||||
);
|
||||
const menuItem = getContextMenuItem(monitor, "request-list-context-resend");
|
||||
getContextMenuItem(monitor, "request-list-context-resend").click();
|
||||
|
||||
const menuPopup = menuItem.parentNode;
|
||||
|
||||
const onHidden = new Promise(resolve => {
|
||||
menuPopup.addEventListener("popuphidden", resolve, { once: true });
|
||||
});
|
||||
|
||||
menuItem.click();
|
||||
menuPopup.hidePopup();
|
||||
|
||||
await onHidden;
|
||||
await waitForPanels;
|
||||
|
||||
const waitForResentRequest = waitForNetworkEvents(monitor, 1);
|
||||
const buttonSend = document.querySelector(
|
||||
"#http-custom-request-send-button"
|
||||
);
|
||||
buttonSend.click();
|
||||
await waitForResentRequest;
|
||||
|
||||
// Selects request that was resent
|
||||
const selReq = getSelectedRequest(store.getState());
|
||||
|
||||
// Finally, some sanity checks
|
||||
ok(selReq.url.endsWith("test-image.png"), "Correct request selected");
|
||||
ok(origReq.url === selReq.url, "Orig and Sel url match");
|
||||
|
||||
ok(selReq.cause.type === "img", "Correct type of selected");
|
||||
ok(origReq.cause.type === selReq.cause.type, "Orig and Sel type match");
|
||||
|
||||
const cspOBJ = await SpecialPowers.spawn(
|
||||
tab.linkedBrowser,
|
||||
[],
|
||||
async () => {
|
||||
return JSON.parse(content.document.cspJSON);
|
||||
}
|
||||
);
|
||||
|
||||
const policies = cspOBJ["csp-policies"];
|
||||
is(policies.length, 1, "CSP: should be one policy");
|
||||
const policy = policies[0];
|
||||
is(`${policy["img-src"]}`, "*", "CSP: img-src should be *");
|
||||
|
||||
await teardown(monitor);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -417,6 +417,19 @@ CanonicalBrowsingContext::GetParentProcessWidgetContaining() {
|
|||
return widget.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<nsIBrowserDOMWindow>
|
||||
CanonicalBrowsingContext::GetBrowserDOMWindow() {
|
||||
RefPtr<CanonicalBrowsingContext> chromeTop = TopCrossChromeBoundary();
|
||||
if (nsCOMPtr<nsIDOMChromeWindow> chromeWin =
|
||||
do_QueryInterface(chromeTop->GetDOMWindow())) {
|
||||
nsCOMPtr<nsIBrowserDOMWindow> bdw;
|
||||
if (NS_SUCCEEDED(chromeWin->GetBrowserDOMWindow(getter_AddRefs(bdw)))) {
|
||||
return bdw.forget();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
already_AddRefed<WindowGlobalParent>
|
||||
CanonicalBrowsingContext::GetEmbedderWindowGlobal() const {
|
||||
uint64_t windowId = GetEmbedderInnerWindowId();
|
||||
|
@ -2162,10 +2175,10 @@ bool CanonicalBrowsingContext::StartDocumentLoad(
|
|||
return true;
|
||||
}
|
||||
|
||||
void CanonicalBrowsingContext::EndDocumentLoad(bool aForProcessSwitch) {
|
||||
void CanonicalBrowsingContext::EndDocumentLoad(bool aContinueNavigating) {
|
||||
mCurrentLoad = nullptr;
|
||||
|
||||
if (!aForProcessSwitch) {
|
||||
if (!aContinueNavigating) {
|
||||
// Resetting the current load identifier on a discarded context
|
||||
// has no effect when a document load has finished.
|
||||
Unused << SetCurrentLoadIdentifier(Nothing());
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "nsHashKeys.h"
|
||||
#include "nsISecureBrowserUI.h"
|
||||
|
||||
class nsIBrowserDOMWindow;
|
||||
class nsISHistory;
|
||||
class nsIWidget;
|
||||
class nsIPrintSettings;
|
||||
|
@ -111,6 +112,7 @@ class CanonicalBrowsingContext final : public BrowsingContext {
|
|||
WindowGlobalParent* GetTopWindowContext();
|
||||
|
||||
already_AddRefed<nsIWidget> GetParentProcessWidgetContaining();
|
||||
already_AddRefed<nsIBrowserDOMWindow> GetBrowserDOMWindow();
|
||||
|
||||
// Same as `GetParentWindowContext`, but will also cross <browser> and
|
||||
// content/chrome boundaries.
|
||||
|
@ -439,7 +441,10 @@ class CanonicalBrowsingContext final : public BrowsingContext {
|
|||
// Called once DocumentLoadListener completes handling a load, and it
|
||||
// is either complete, or handed off to the final channel to deliver
|
||||
// data to the destination docshell.
|
||||
void EndDocumentLoad(bool aForProcessSwitch);
|
||||
// If aContinueNavigating it set, the reference to the DocumentLoadListener
|
||||
// will be cleared to prevent it being cancelled, however the current load ID
|
||||
// will be preserved until `EndDocumentLoad` is called again.
|
||||
void EndDocumentLoad(bool aContinueNavigating);
|
||||
|
||||
bool SupportsLoadingInParent(nsDocShellLoadState* aLoadState,
|
||||
uint64_t* aOuterWindowId);
|
||||
|
|
|
@ -15,7 +15,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||
TestUtils: "resource://testing-common/TestUtils.jsm",
|
||||
});
|
||||
|
||||
var dirSvc = Services.dirSvc;
|
||||
var profileDir = do_get_profile();
|
||||
|
||||
const kSearchEngineID = "test_urifixup_search_engine";
|
||||
|
|
|
@ -8,7 +8,7 @@ function run_test() {
|
|||
notifications++;
|
||||
},
|
||||
};
|
||||
Services.os.addObserver(obs, "last-pb-context-exited");
|
||||
Services.obs.addObserver(obs, "last-pb-context-exited");
|
||||
|
||||
run_test_in_child("../unit/test_pb_notification.js", function() {
|
||||
Assert.equal(notifications, 1);
|
||||
|
|
|
@ -342,6 +342,11 @@ enum class FlushLayout {
|
|||
Yes,
|
||||
};
|
||||
|
||||
enum class PerformRetargeting {
|
||||
No,
|
||||
Yes,
|
||||
};
|
||||
|
||||
template <typename NodeOrElement>
|
||||
NodeOrElement* CastTo(nsIContent* aContent);
|
||||
|
||||
|
@ -360,6 +365,7 @@ static void QueryNodesFromRect(DocumentOrShadowRoot& aRoot, const nsRect& aRect,
|
|||
FrameForPointOptions aOptions,
|
||||
FlushLayout aShouldFlushLayout,
|
||||
Multiple aMultiple, ViewportType aViewportType,
|
||||
PerformRetargeting aPerformRetargeting,
|
||||
nsTArray<RefPtr<NodeOrElement>>& aNodes) {
|
||||
static_assert(std::is_same<nsINode, NodeOrElement>::value ||
|
||||
std::is_same<Element, NodeOrElement>::value,
|
||||
|
@ -367,6 +373,7 @@ static void QueryNodesFromRect(DocumentOrShadowRoot& aRoot, const nsRect& aRect,
|
|||
|
||||
constexpr bool returningElements =
|
||||
std::is_same<Element, NodeOrElement>::value;
|
||||
const bool retargeting = aPerformRetargeting == PerformRetargeting::Yes;
|
||||
|
||||
nsCOMPtr<Document> doc = aRoot.AsNode().OwnerDoc();
|
||||
|
||||
|
@ -420,7 +427,9 @@ static void QueryNodesFromRect(DocumentOrShadowRoot& aRoot, const nsRect& aRect,
|
|||
// XXXsmaug There is plenty of unspec'ed behavior here
|
||||
// https://github.com/w3c/webcomponents/issues/735
|
||||
// https://github.com/w3c/webcomponents/issues/736
|
||||
content = aRoot.Retarget(content);
|
||||
if (retargeting) {
|
||||
content = aRoot.Retarget(content);
|
||||
}
|
||||
|
||||
if (content && content != aNodes.SafeLastElement(nullptr)) {
|
||||
aNodes.AppendElement(CastTo<NodeOrElement>(content));
|
||||
|
@ -436,6 +445,7 @@ static void QueryNodesFromPoint(DocumentOrShadowRoot& aRoot, float aX, float aY,
|
|||
FrameForPointOptions aOptions,
|
||||
FlushLayout aShouldFlushLayout,
|
||||
Multiple aMultiple, ViewportType aViewportType,
|
||||
PerformRetargeting aPerformRetargeting,
|
||||
nsTArray<RefPtr<NodeOrElement>>& aNodes) {
|
||||
// As per the spec, we return null if either coord is negative.
|
||||
if (!aOptions.mBits.contains(FrameForPointOption::IgnoreRootScrollFrame) &&
|
||||
|
@ -447,7 +457,8 @@ static void QueryNodesFromPoint(DocumentOrShadowRoot& aRoot, float aX, float aY,
|
|||
nscoord y = nsPresContext::CSSPixelsToAppUnits(aY);
|
||||
nsPoint pt(x, y);
|
||||
QueryNodesFromRect(aRoot, nsRect(pt, nsSize(1, 1)), aOptions,
|
||||
aShouldFlushLayout, aMultiple, aViewportType, aNodes);
|
||||
aShouldFlushLayout, aMultiple, aViewportType,
|
||||
aPerformRetargeting, aNodes);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -459,19 +470,20 @@ Element* DocumentOrShadowRoot::ElementFromPoint(float aX, float aY) {
|
|||
void DocumentOrShadowRoot::ElementsFromPoint(
|
||||
float aX, float aY, nsTArray<RefPtr<Element>>& aElements) {
|
||||
QueryNodesFromPoint(*this, aX, aY, {}, FlushLayout::Yes, Multiple::Yes,
|
||||
ViewportType::Layout, aElements);
|
||||
ViewportType::Layout, PerformRetargeting::Yes,
|
||||
aElements);
|
||||
}
|
||||
|
||||
void DocumentOrShadowRoot::NodesFromPoint(float aX, float aY,
|
||||
nsTArray<RefPtr<nsINode>>& aNodes) {
|
||||
QueryNodesFromPoint(*this, aX, aY, {}, FlushLayout::Yes, Multiple::Yes,
|
||||
ViewportType::Layout, aNodes);
|
||||
ViewportType::Layout, PerformRetargeting::Yes, aNodes);
|
||||
}
|
||||
|
||||
nsINode* DocumentOrShadowRoot::NodeFromPoint(float aX, float aY) {
|
||||
AutoTArray<RefPtr<nsINode>, 1> nodes;
|
||||
QueryNodesFromPoint(*this, aX, aY, {}, FlushLayout::Yes, Multiple::No,
|
||||
ViewportType::Layout, nodes);
|
||||
ViewportType::Layout, PerformRetargeting::Yes, nodes);
|
||||
return nodes.SafeElementAt(0);
|
||||
}
|
||||
|
||||
|
@ -487,7 +499,7 @@ Element* DocumentOrShadowRoot::ElementFromPointHelper(
|
|||
|
||||
AutoTArray<RefPtr<Element>, 1> elements;
|
||||
QueryNodesFromPoint(*this, aX, aY, options, flush, Multiple::No,
|
||||
aViewportType, elements);
|
||||
aViewportType, PerformRetargeting::Yes, elements);
|
||||
return elements.SafeElementAt(0);
|
||||
}
|
||||
|
||||
|
@ -522,7 +534,7 @@ void DocumentOrShadowRoot::NodesFromRect(float aX, float aY, float aTopSize,
|
|||
|
||||
auto flush = aFlushLayout ? FlushLayout::Yes : FlushLayout::No;
|
||||
QueryNodesFromRect(*this, rect, options, flush, Multiple::Yes,
|
||||
ViewportType::Layout, aReturn);
|
||||
ViewportType::Layout, PerformRetargeting::No, aReturn);
|
||||
}
|
||||
|
||||
Element* DocumentOrShadowRoot::AddIDTargetObserver(nsAtom* aID,
|
||||
|
|
|
@ -1807,7 +1807,7 @@ already_AddRefed<PathCacheEntry> PathCache::FindOrInsertEntry(
|
|||
}
|
||||
Pattern* pattern = nullptr;
|
||||
if (aPattern) {
|
||||
pattern = aPattern->Clone();
|
||||
pattern = aPattern->CloneWeak();
|
||||
if (!pattern) {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -72,6 +72,8 @@ class CacheEntry : public RefCounted<CacheEntry> {
|
|||
const IntRect& GetBounds() const { return mBounds; }
|
||||
HashNumber GetHash() const { return mHash; }
|
||||
|
||||
virtual bool IsValid() const { return true; }
|
||||
|
||||
protected:
|
||||
virtual void RemoveFromList() = 0;
|
||||
|
||||
|
@ -173,7 +175,9 @@ class TextureHandle : public RefCounted<TextureHandle>,
|
|||
void SetCacheEntry(const RefPtr<CacheEntry>& aEntry) { mCacheEntry = aEntry; }
|
||||
|
||||
// Note as used if there is corresponding surface or cache entry.
|
||||
bool IsUsed() const { return mSurface || mCacheEntry; }
|
||||
bool IsUsed() const {
|
||||
return mSurface || (mCacheEntry && mCacheEntry->IsValid());
|
||||
}
|
||||
|
||||
private:
|
||||
bool mValid = true;
|
||||
|
@ -360,6 +364,9 @@ class PathCacheEntry : public CacheEntryImpl<PathCacheEntry> {
|
|||
|
||||
const Point& GetOrigin() const { return mOrigin; }
|
||||
|
||||
// Valid if either a mask (no pattern) or there is valid pattern.
|
||||
bool IsValid() const override { return !mPattern || mPattern->IsValid(); }
|
||||
|
||||
private:
|
||||
// The actual path geometry supplied
|
||||
SkPath mPath;
|
||||
|
|
|
@ -252,12 +252,14 @@ OffscreenCanvasDisplayHelper::GetSurfaceSnapshot() {
|
|||
Maybe<int32_t> childId;
|
||||
HTMLCanvasElement* canvasElement;
|
||||
RefPtr<gfx::SourceSurface> surface;
|
||||
layers::CompositableHandle handle;
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
hasAlpha = !mData.mIsOpaque;
|
||||
isAlphaPremult = mData.mIsAlphaPremult;
|
||||
originPos = mData.mOriginPos;
|
||||
handle = mData.mHandle;
|
||||
managerId = mContextManagerId;
|
||||
childId = mContextChildId;
|
||||
canvasElement = mCanvasElement;
|
||||
|
@ -311,7 +313,9 @@ OffscreenCanvasDisplayHelper::GetSurfaceSnapshot() {
|
|||
// We don't have a usable surface, and the context lives in the compositor
|
||||
// process.
|
||||
return gfx::CanvasManagerChild::Get()->GetSnapshot(
|
||||
managerId.value(), childId.value(), hasAlpha);
|
||||
managerId.value(), childId.value(), handle,
|
||||
hasAlpha ? gfx::SurfaceFormat::R8G8B8A8 : gfx::SurfaceFormat::R8G8B8X8,
|
||||
hasAlpha && !isAlphaPremult, originPos == gl::OriginPos::BottomLeft);
|
||||
}
|
||||
|
||||
// If we don't have any protocol IDs, or an existing surface, it is possible
|
||||
|
|
|
@ -155,7 +155,6 @@ tags = imagebitmap
|
|||
tags = imagebitmap
|
||||
[test_ImageData_ctor.html]
|
||||
[test_isPointInStroke.html]
|
||||
[test_mozGetAsFile.html]
|
||||
[test_strokeText_throw.html]
|
||||
[test_toBlob.html]
|
||||
[test_toBlob_zero_dimension.html]
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<title>Canvas test: mozGetAsFile</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
|
||||
<body>
|
||||
<canvas id="c" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
|
||||
<script>
|
||||
|
||||
function compareAsync(file, canvas, type, callback)
|
||||
{
|
||||
var reader = new FileReader();
|
||||
reader.onload =
|
||||
function(e) {
|
||||
is(e.target.result, canvas.toDataURL(type),
|
||||
"<canvas>.mozGetAsFile().getAsDataURL() should equal <canvas>.toDataURL()");
|
||||
callback(canvas);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
function test1(canvas)
|
||||
{
|
||||
var pngfile = canvas.mozGetAsFile("foo.png");
|
||||
is(pngfile.type, "image/png", "Default type for mozGetAsFile should be PNG");
|
||||
compareAsync(pngfile, canvas, "image/png", test2);
|
||||
is(pngfile.name, "foo.png", "File name should be what we passed in");
|
||||
}
|
||||
|
||||
function test2(canvas)
|
||||
{
|
||||
var jpegfile = canvas.mozGetAsFile("bar.jpg", "image/jpeg");
|
||||
is(jpegfile.type, "image/jpeg",
|
||||
"When a valid type is specified that should be returned");
|
||||
compareAsync(jpegfile, canvas, "image/jpeg", parent.SimpleTest.finish);
|
||||
is(jpegfile.name, "bar.jpg", "File name should be what we passed in");
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(async function () {
|
||||
if (location.search == "?framed") {
|
||||
is = parent.is;
|
||||
|
||||
var canvas = document.getElementById('c');
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
|
||||
|
||||
test1(canvas);
|
||||
} else {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["canvas.mozgetasfile.enabled", true],
|
||||
],
|
||||
});
|
||||
let iframe = document.querySelector("iframe");
|
||||
iframe.src = "test_mozGetAsFile.html?framed";
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<img src="image_yellow75.png" id="yellow75.png" class="resource">
|
||||
<iframe></iframe>
|
||||
|
|
@ -1018,53 +1018,6 @@ OffscreenCanvas* HTMLCanvasElement::TransferControlToOffscreen(
|
|||
return mOffscreenCanvas;
|
||||
}
|
||||
|
||||
already_AddRefed<File> HTMLCanvasElement::MozGetAsFile(
|
||||
const nsAString& aName, const nsAString& aType,
|
||||
nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
|
||||
// do a trust check if this is a write-only canvas
|
||||
if (mWriteOnly && !aSubjectPrincipal.IsSystemPrincipal()) {
|
||||
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<File> file;
|
||||
aRv = MozGetAsFileImpl(aName, aType, aSubjectPrincipal, getter_AddRefs(file));
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
return file.forget();
|
||||
}
|
||||
|
||||
nsresult HTMLCanvasElement::MozGetAsFileImpl(const nsAString& aName,
|
||||
const nsAString& aType,
|
||||
nsIPrincipal& aSubjectPrincipal,
|
||||
File** aResult) {
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
nsAutoString type(aType);
|
||||
nsresult rv =
|
||||
ExtractData(nsContentUtils::GetCurrentJSContext(), aSubjectPrincipal,
|
||||
type, u""_ns, getter_AddRefs(stream));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
uint64_t imgSize;
|
||||
void* imgData = nullptr;
|
||||
rv = NS_ReadInputStreamToBuffer(stream, &imgData, -1, &imgSize);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> win =
|
||||
do_QueryInterface(OwnerDoc()->GetScopeObject());
|
||||
|
||||
// The File takes ownership of the buffer
|
||||
RefPtr<File> file = File::CreateMemoryFileWithLastModifiedNow(
|
||||
win->AsGlobal(), imgData, imgSize, aName, type);
|
||||
if (NS_WARN_IF(!file)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
file.forget(aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult HTMLCanvasElement::GetContext(const nsAString& aContextId,
|
||||
nsISupports** aContext) {
|
||||
ErrorResult rv;
|
||||
|
|
|
@ -163,10 +163,6 @@ class HTMLCanvasElement final : public nsGenericHTMLElement,
|
|||
|
||||
SetHTMLBoolAttr(nsGkAtoms::moz_opaque, aValue, aRv);
|
||||
}
|
||||
already_AddRefed<File> MozGetAsFile(const nsAString& aName,
|
||||
const nsAString& aType,
|
||||
nsIPrincipal& aSubjectPrincipal,
|
||||
ErrorResult& aRv);
|
||||
already_AddRefed<nsISupports> MozGetIPCContext(const nsAString& aContextId,
|
||||
ErrorResult& aRv);
|
||||
PrintCallback* GetMozPrintCallback() const;
|
||||
|
@ -325,8 +321,6 @@ class HTMLCanvasElement final : public nsGenericHTMLElement,
|
|||
nsresult ToDataURLImpl(JSContext* aCx, nsIPrincipal& aSubjectPrincipal,
|
||||
const nsAString& aMimeType,
|
||||
const JS::Value& aEncoderOptions, nsAString& aDataURL);
|
||||
nsresult MozGetAsFileImpl(const nsAString& aName, const nsAString& aType,
|
||||
nsIPrincipal& aSubjectPrincipal, File** aResult);
|
||||
MOZ_CAN_RUN_SCRIPT void CallPrintCallback();
|
||||
|
||||
virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
|
||||
|
|
|
@ -5089,9 +5089,8 @@ bool HTMLInputElement::IsDateTimeTypeSupported(
|
|||
switch (aDateTimeInputType) {
|
||||
case FormControlType::InputDate:
|
||||
case FormControlType::InputTime:
|
||||
return true;
|
||||
case FormControlType::InputDatetimeLocal:
|
||||
return StaticPrefs::dom_forms_datetime_local();
|
||||
return true;
|
||||
case FormControlType::InputMonth:
|
||||
case FormControlType::InputWeek:
|
||||
return StaticPrefs::dom_forms_datetime_others();
|
||||
|
|
|
@ -1567,8 +1567,7 @@ class HTMLInputElement final : public TextControlElement,
|
|||
static bool CreatesDateTimeWidget(FormControlType aType) {
|
||||
return aType == FormControlType::InputDate ||
|
||||
aType == FormControlType::InputTime ||
|
||||
(aType == FormControlType::InputDatetimeLocal &&
|
||||
StaticPrefs::dom_forms_datetime_local_widget());
|
||||
aType == FormControlType::InputDatetimeLocal;
|
||||
}
|
||||
|
||||
bool CreatesDateTimeWidget() const { return CreatesDateTimeWidget(mType); }
|
||||
|
|
|
@ -244,8 +244,6 @@ bool nsIFormControl::IsSingleLineTextControl(bool aExcludePassword,
|
|||
case FormControlType::InputMonth:
|
||||
case FormControlType::InputWeek:
|
||||
return true;
|
||||
case FormControlType::InputDatetimeLocal:
|
||||
return !mozilla::StaticPrefs::dom_forms_datetime_local_widget();
|
||||
case FormControlType::InputPassword:
|
||||
return !aExcludePassword;
|
||||
default:
|
||||
|
|
|
@ -43,18 +43,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=764481
|
|||
prefs: [["dom.forms.datetime.others", true]],
|
||||
inputType: "week",
|
||||
expectedType: "week"
|
||||
}, {
|
||||
prefs: [["dom.forms.datetime-local", false]],
|
||||
inputType: "datetime-local",
|
||||
expectedType: "text"
|
||||
}, {
|
||||
prefs: [["dom.forms.datetime-local", false]],
|
||||
inputType: "datetime-local",
|
||||
expectedType: "text"
|
||||
}, {
|
||||
prefs: [["dom.forms.datetime-local", true]],
|
||||
inputType: "datetime-local",
|
||||
expectedType: "datetime-local"
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -80,6 +80,7 @@
|
|||
#include "mozilla/Components.h"
|
||||
#include "mozilla/Sprintf.h"
|
||||
#include "mozilla/StaticPrefs_dom.h"
|
||||
#include "mozilla/StaticPrefs_fission.h"
|
||||
#include "mozilla/StaticPrefs_media.h"
|
||||
#include "mozilla/StaticPrefs_widget.h"
|
||||
#include "mozilla/StyleSheet.h"
|
||||
|
@ -643,6 +644,23 @@ static const char* sObserverTopics[] = {
|
|||
"network:socket-process-crashed",
|
||||
};
|
||||
|
||||
static const char kFissionEnforceBlockList[] =
|
||||
"fission.enforceBlocklistedPrefsInSubprocesses";
|
||||
static const char kFissionOmitBlockListValues[] =
|
||||
"fission.omitBlocklistedPrefsInSubprocesses";
|
||||
|
||||
static void OnFissionBlocklistPrefChange(const char* aPref, void* aData) {
|
||||
if (strcmp(aPref, kFissionEnforceBlockList) == 0) {
|
||||
sCrashOnBlocklistedPref =
|
||||
StaticPrefs::fission_enforceBlocklistedPrefsInSubprocesses();
|
||||
} else if (strcmp(aPref, kFissionOmitBlockListValues) == 0) {
|
||||
sOmitBlocklistedPrefValues =
|
||||
StaticPrefs::fission_omitBlocklistedPrefsInSubprocesses();
|
||||
} else {
|
||||
MOZ_CRASH("Unknown pref passed to callback");
|
||||
}
|
||||
}
|
||||
|
||||
// PreallocateProcess is called by the PreallocatedProcessManager.
|
||||
// ContentParent then takes this process back within GetNewOrUsedBrowserProcess.
|
||||
/*static*/ already_AddRefed<ContentParent>
|
||||
|
@ -669,6 +687,11 @@ void ContentParent::StartUp() {
|
|||
BackgroundChild::Startup();
|
||||
ClientManager::Startup();
|
||||
|
||||
Preferences::RegisterCallbackAndCall(&OnFissionBlocklistPrefChange,
|
||||
kFissionEnforceBlockList);
|
||||
Preferences::RegisterCallbackAndCall(&OnFissionBlocklistPrefChange,
|
||||
kFissionOmitBlockListValues);
|
||||
|
||||
#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
|
||||
sSandboxBrokerPolicyFactory = MakeUnique<SandboxBrokerPolicyFactory>();
|
||||
#endif
|
||||
|
@ -2511,9 +2534,9 @@ bool ContentParent::BeginSubprocessLaunch(ProcessPriority aPriority) {
|
|||
|
||||
// Instantiate the pref serializer. It will be cleaned up in
|
||||
// `LaunchSubprocessReject`/`LaunchSubprocessResolve`.
|
||||
mPrefSerializer = MakeUnique<mozilla::ipc::SharedPreferenceSerializer>(
|
||||
ShouldSyncPreference);
|
||||
if (!mPrefSerializer->SerializeToSharedMemory()) {
|
||||
mPrefSerializer = MakeUnique<mozilla::ipc::SharedPreferenceSerializer>();
|
||||
if (!mPrefSerializer->SerializeToSharedMemory(GeckoProcessType_Content,
|
||||
GetRemoteType())) {
|
||||
NS_WARNING("SharedPreferenceSerializer::SerializeToSharedMemory failed");
|
||||
MarkAsDead();
|
||||
return false;
|
||||
|
@ -3639,13 +3662,11 @@ ContentParent::Observe(nsISupports* aSubject, const char* aTopic,
|
|||
// We know prefs are ASCII here.
|
||||
NS_LossyConvertUTF16toASCII strData(aData);
|
||||
|
||||
// A pref changed. If it is useful to do so, inform child processes.
|
||||
if (!ShouldSyncPreference(strData.Data())) {
|
||||
return NS_OK;
|
||||
}
|
||||
Pref pref(strData, /* isLocked */ false,
|
||||
/* isSanitized */ false, Nothing(), Nothing());
|
||||
|
||||
Pref pref(strData, /* isLocked */ false, Nothing(), Nothing());
|
||||
Preferences::GetPreference(&pref);
|
||||
Preferences::GetPreference(&pref, GeckoProcessType_Content,
|
||||
GetRemoteType());
|
||||
if (IsInitialized()) {
|
||||
MOZ_ASSERT(mQueuedPrefs.IsEmpty());
|
||||
if (!SendPreferenceUpdate(pref)) {
|
||||
|
@ -3792,37 +3813,6 @@ ContentParent::Observe(nsISupports* aSubject, const char* aTopic,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool ContentParent::ShouldSyncPreference(const char* aPref) {
|
||||
#define PARENT_ONLY_PREF_LIST_ENTRY(s) \
|
||||
{ s, (sizeof(s) / sizeof(char)) - 1 }
|
||||
struct ParentOnlyPrefListEntry {
|
||||
const char* mPrefBranch;
|
||||
size_t mLen;
|
||||
};
|
||||
// These prefs are not useful in child processes.
|
||||
static const ParentOnlyPrefListEntry sParentOnlyPrefBranchList[] = {
|
||||
PARENT_ONLY_PREF_LIST_ENTRY("app.update.lastUpdateTime."),
|
||||
PARENT_ONLY_PREF_LIST_ENTRY("datareporting.policy."),
|
||||
PARENT_ONLY_PREF_LIST_ENTRY("browser.safebrowsing.provider."),
|
||||
PARENT_ONLY_PREF_LIST_ENTRY("browser.shell."),
|
||||
PARENT_ONLY_PREF_LIST_ENTRY("browser.slowStartup."),
|
||||
PARENT_ONLY_PREF_LIST_ENTRY("browser.startup."),
|
||||
PARENT_ONLY_PREF_LIST_ENTRY("extensions.getAddons.cache."),
|
||||
PARENT_ONLY_PREF_LIST_ENTRY("media.gmp-manager."),
|
||||
PARENT_ONLY_PREF_LIST_ENTRY("media.gmp-gmpopenh264."),
|
||||
PARENT_ONLY_PREF_LIST_ENTRY("privacy.sanitize."),
|
||||
};
|
||||
#undef PARENT_ONLY_PREF_LIST_ENTRY
|
||||
|
||||
for (const auto& entry : sParentOnlyPrefBranchList) {
|
||||
if (strncmp(entry.mPrefBranch, aPref, entry.mLen) == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ContentParent::UpdateNetworkLinkType() {
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsINetworkLinkService> nls =
|
||||
|
|
|
@ -1445,8 +1445,6 @@ class ContentParent final
|
|||
|
||||
void UpdateNetworkLinkType();
|
||||
|
||||
static bool ShouldSyncPreference(const char* aPref);
|
||||
|
||||
already_AddRefed<JSActor> InitJSActor(JS::HandleObject aMaybeActor,
|
||||
const nsACString& aName,
|
||||
ErrorResult& aRv) override;
|
||||
|
|
|
@ -23,6 +23,7 @@ union PrefValue {
|
|||
struct Pref {
|
||||
nsCString name;
|
||||
bool isLocked;
|
||||
bool isSanitized;
|
||||
PrefValue? defaultValue;
|
||||
PrefValue? userValue;
|
||||
};
|
||||
|
|
|
@ -471,7 +471,8 @@ Result<NavigationIsolationOptions, nsresult> IsolationOptionsForNavigation(
|
|||
CanonicalBrowsingContext* aTopBC, WindowGlobalParent* aParentWindow,
|
||||
nsIURI* aChannelCreationURI, nsIChannel* aChannel,
|
||||
const nsACString& aCurrentRemoteType, bool aHasCOOPMismatch,
|
||||
uint32_t aLoadStateLoadType, const Maybe<uint64_t>& aChannelId,
|
||||
bool aForNewTab, uint32_t aLoadStateLoadType,
|
||||
const Maybe<uint64_t>& aChannelId,
|
||||
const Maybe<nsCString>& aRemoteTypeOverride) {
|
||||
// Get the final principal, used to select which process to load into.
|
||||
nsCOMPtr<nsIPrincipal> resultPrincipal;
|
||||
|
@ -660,7 +661,7 @@ Result<NavigationIsolationOptions, nsresult> IsolationOptionsForNavigation(
|
|||
|
||||
// Check if we can put the previous document into the BFCache.
|
||||
if (mozilla::BFCacheInParent() && nsSHistory::GetMaxTotalViewers() > 0 &&
|
||||
!aParentWindow && !aTopBC->HadOriginalOpener() &&
|
||||
!aForNewTab && !aParentWindow && !aTopBC->HadOriginalOpener() &&
|
||||
behavior != IsolationBehavior::Parent &&
|
||||
(ExtensionPolicyService::GetSingleton().UseRemoteExtensions() ||
|
||||
behavior != IsolationBehavior::Extension) &&
|
||||
|
@ -765,9 +766,9 @@ Result<NavigationIsolationOptions, nsresult> IsolationOptionsForNavigation(
|
|||
// where we may have multiple documents with the same principal in different
|
||||
// processes. Those have been handled above, and will not be reaching here.
|
||||
//
|
||||
// If we're doing a replace load, we won't be staying in the same
|
||||
// BrowsingContext, so ignore this step.
|
||||
if (!options.mReplaceBrowsingContext) {
|
||||
// If we're doing a replace load or opening a new tab, we won't be staying in
|
||||
// the same BrowsingContextGroup, so ignore this step.
|
||||
if (!options.mReplaceBrowsingContext && !aForNewTab) {
|
||||
// Helper for efficiently determining if a given origin is same-site. This
|
||||
// will attempt to do a fast equality check, and will only fall back to
|
||||
// computing the site-origin for content principals.
|
||||
|
|
|
@ -55,7 +55,8 @@ Result<NavigationIsolationOptions, nsresult> IsolationOptionsForNavigation(
|
|||
CanonicalBrowsingContext* aTopBC, WindowGlobalParent* aParentWindow,
|
||||
nsIURI* aChannelCreationURI, nsIChannel* aChannel,
|
||||
const nsACString& aCurrentRemoteType, bool aHasCOOPMismatch,
|
||||
uint32_t aLoadStateLoadType, const Maybe<uint64_t>& aChannelId,
|
||||
bool aForNewTab, uint32_t aLoadStateLoadType,
|
||||
const Maybe<uint64_t>& aChannelId,
|
||||
const Maybe<nsCString>& aRemoteTypeOverride);
|
||||
|
||||
/**
|
||||
|
|
|
@ -46,9 +46,9 @@ bool RDDProcessHost::Launch(StringVector aExtraOpts) {
|
|||
MOZ_ASSERT(mLaunchPhase == LaunchPhase::Unlaunched);
|
||||
MOZ_ASSERT(!mRDDChild);
|
||||
|
||||
mPrefSerializer = MakeUnique<ipc::SharedPreferenceSerializer>(
|
||||
dom::ContentParent::ShouldSyncPreference);
|
||||
if (!mPrefSerializer->SerializeToSharedMemory()) {
|
||||
mPrefSerializer = MakeUnique<ipc::SharedPreferenceSerializer>();
|
||||
if (!mPrefSerializer->SerializeToSharedMemory(GeckoProcessType_RDD,
|
||||
/* remoteType */ ""_ns)) {
|
||||
return false;
|
||||
}
|
||||
mPrefSerializer->AddSharedPrefCmdLineArgs(*this, aExtraOpts);
|
||||
|
|
|
@ -96,13 +96,11 @@ void RDDProcessManager::OnPreferenceChange(const char16_t* aData) {
|
|||
// We know prefs are ASCII here.
|
||||
NS_LossyConvertUTF16toASCII strData(aData);
|
||||
|
||||
// A pref changed. If it is useful to do so, inform child processes.
|
||||
if (!dom::ContentParent::ShouldSyncPreference(strData.Data())) {
|
||||
return;
|
||||
}
|
||||
mozilla::dom::Pref pref(strData, /* isLocked */ false,
|
||||
/* isSanitized */ false, Nothing(), Nothing());
|
||||
|
||||
mozilla::dom::Pref pref(strData, /* isLocked */ false, Nothing(), Nothing());
|
||||
Preferences::GetPreference(&pref);
|
||||
Preferences::GetPreference(&pref, GeckoProcessType_RDD,
|
||||
/* remoteType */ ""_ns);
|
||||
if (!!mRDDChild) {
|
||||
MOZ_ASSERT(mQueuedPrefs.IsEmpty());
|
||||
mRDDChild->SendPreferenceUpdate(pref);
|
||||
|
|
|
@ -340,7 +340,8 @@ void AOMDecoder::OBUIterator::UpdateNext() {
|
|||
// begin obu_extension_header( ) (5.3.3)
|
||||
if (temp.mExtensionFlag) {
|
||||
if (br.BitsLeft() < 8) {
|
||||
NS_WARNING("Not enough bits left for an OBU extension header");
|
||||
mResult = MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
|
||||
"Not enough bits left for an OBU extension header");
|
||||
return;
|
||||
}
|
||||
br.ReadBits(3); // temporal_id
|
||||
|
@ -355,14 +356,16 @@ void AOMDecoder::OBUIterator::UpdateNext() {
|
|||
size_t size;
|
||||
if (hasSizeField) {
|
||||
if (br.BitsLeft() < 8) {
|
||||
NS_WARNING("Not enough bits left for an OBU size field");
|
||||
mResult = MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
|
||||
"Not enough bits left for an OBU size field");
|
||||
return;
|
||||
}
|
||||
CheckedUint32 checkedSize = br.ReadULEB128().toChecked<uint32_t>();
|
||||
// Spec requires that the value ULEB128 reads is (1 << 32) - 1 or below.
|
||||
// See leb128(): https://aomediacodec.github.io/av1-spec/#leb128
|
||||
if (!checkedSize.isValid()) {
|
||||
NS_WARNING("OBU size was too large");
|
||||
mResult =
|
||||
MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR, "OBU size was too large");
|
||||
return;
|
||||
}
|
||||
size = checkedSize.value();
|
||||
|
@ -375,10 +378,12 @@ void AOMDecoder::OBUIterator::UpdateNext() {
|
|||
}
|
||||
|
||||
if (br.BitsLeft() / 8 < size) {
|
||||
NS_WARNING(nsPrintfCString("Size specified by the OBU header (%zu) is more "
|
||||
"than the actual remaining OBU data (%zu)",
|
||||
size, br.BitsLeft() / 8)
|
||||
.get());
|
||||
mResult = MediaResult(
|
||||
NS_ERROR_DOM_MEDIA_DECODE_ERR,
|
||||
nsPrintfCString("Size specified by the OBU header (%zu) is more "
|
||||
"than the actual remaining OBU data (%zu)",
|
||||
size, br.BitsLeft() / 8)
|
||||
.get());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -391,6 +396,7 @@ void AOMDecoder::OBUIterator::UpdateNext() {
|
|||
|
||||
mPosition += bytes + size;
|
||||
resetExit.release();
|
||||
mResult = NS_OK;
|
||||
}
|
||||
|
||||
/* static */
|
||||
|
@ -413,8 +419,8 @@ already_AddRefed<MediaByteBuffer> AOMDecoder::CreateOBU(
|
|||
}
|
||||
|
||||
/* static */
|
||||
bool AOMDecoder::ReadSequenceHeaderInfo(const Span<const uint8_t>& aSample,
|
||||
AV1SequenceInfo& aDestInfo) {
|
||||
MediaResult AOMDecoder::ReadSequenceHeaderInfo(
|
||||
const Span<const uint8_t>& aSample, AV1SequenceInfo& aDestInfo) {
|
||||
// We need to get the last sequence header OBU, the specification does not
|
||||
// limit a temporal unit to one sequence header.
|
||||
OBUIterator iter = ReadOBUs(aSample);
|
||||
|
@ -422,6 +428,11 @@ bool AOMDecoder::ReadSequenceHeaderInfo(const Span<const uint8_t>& aSample,
|
|||
|
||||
while (true) {
|
||||
if (!iter.HasNext()) {
|
||||
// Pass along the error from parsing the OBU.
|
||||
MediaResult result = iter.GetResult();
|
||||
if (result.Code() != NS_OK) {
|
||||
return result;
|
||||
}
|
||||
break;
|
||||
}
|
||||
OBUInfo obu = iter.Next();
|
||||
|
@ -431,7 +442,7 @@ bool AOMDecoder::ReadSequenceHeaderInfo(const Span<const uint8_t>& aSample,
|
|||
}
|
||||
|
||||
if (seqOBU.mType != OBUType::SequenceHeader) {
|
||||
return false;
|
||||
return NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA;
|
||||
}
|
||||
|
||||
// Sequence header syntax is specified here:
|
||||
|
@ -448,9 +459,11 @@ bool AOMDecoder::ReadSequenceHeaderInfo(const Span<const uint8_t>& aSample,
|
|||
// https://aomediacodec.github.io/av1-spec/#general-sequence-header-obu-syntax
|
||||
tempInfo.mProfile = br.ReadBits(3);
|
||||
const bool stillPicture = br.ReadBit();
|
||||
bool reducedStillPicture = br.ReadBit();
|
||||
const bool reducedStillPicture = br.ReadBit();
|
||||
if (!stillPicture && reducedStillPicture) {
|
||||
NS_WARNING("reduced_still_picture is true while still_picture is false");
|
||||
return MediaResult(
|
||||
NS_ERROR_DOM_MEDIA_DECODE_ERR,
|
||||
"reduced_still_picture is true while still_picture is false");
|
||||
}
|
||||
|
||||
if (reducedStillPicture) {
|
||||
|
@ -527,7 +540,7 @@ bool AOMDecoder::ReadSequenceHeaderInfo(const Span<const uint8_t>& aSample,
|
|||
|
||||
if (reducedStillPicture) {
|
||||
aDestInfo = tempInfo;
|
||||
return true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
br.ReadBit(); // enable_interintra_compound
|
||||
|
@ -647,13 +660,13 @@ bool AOMDecoder::ReadSequenceHeaderInfo(const Span<const uint8_t>& aSample,
|
|||
correct &= br.ReadBits(8) == 0;
|
||||
}
|
||||
if (!correct) {
|
||||
NS_WARNING("AV1 sequence header was parsed incorrectly");
|
||||
return false;
|
||||
return MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
|
||||
"AV1 sequence header was parsed incorrectly");
|
||||
}
|
||||
// end trailing_bits( )
|
||||
|
||||
aDestInfo = tempInfo;
|
||||
return true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */
|
||||
|
@ -769,7 +782,7 @@ already_AddRefed<MediaByteBuffer> AOMDecoder::CreateSequenceHeader(
|
|||
NS_WARNING("Profile must be 2 for 12-bit");
|
||||
return nullptr;
|
||||
}
|
||||
if (aInfo.mProfile == 2) {
|
||||
if (aInfo.mProfile == 2 && highBitDepth) {
|
||||
bw.WriteBit(aInfo.mBitDepth == 12); // twelve_bit
|
||||
}
|
||||
|
||||
|
@ -873,10 +886,9 @@ already_AddRefed<MediaByteBuffer> AOMDecoder::CreateSequenceHeader(
|
|||
}
|
||||
|
||||
/* static */
|
||||
void AOMDecoder::ReadAV1CBox(const MediaByteBuffer* aBox,
|
||||
AV1SequenceInfo& aDestInfo, bool& aHadSeqHdr) {
|
||||
aHadSeqHdr = false;
|
||||
|
||||
void AOMDecoder::TryReadAV1CBox(const MediaByteBuffer* aBox,
|
||||
AV1SequenceInfo& aDestInfo,
|
||||
MediaResult& aSeqHdrResult) {
|
||||
// See av1C specification:
|
||||
// https://aomediacodec.github.io/av1-isobmff/#av1codecconfigurationbox-section
|
||||
BitReader br(aBox);
|
||||
|
@ -912,12 +924,13 @@ void AOMDecoder::ReadAV1CBox(const MediaByteBuffer* aBox,
|
|||
|
||||
// Minimum possible OBU header size
|
||||
if (obus.Length() < 1) {
|
||||
aSeqHdrResult = NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA;
|
||||
return;
|
||||
}
|
||||
|
||||
// If present, the sequence header will be redundant to some values, but any
|
||||
// values stored in it should be treated as more accurate than av1C.
|
||||
aHadSeqHdr = ReadSequenceHeaderInfo(obus, aDestInfo);
|
||||
aSeqHdrResult = ReadSequenceHeaderInfo(obus, aDestInfo);
|
||||
}
|
||||
|
||||
/* static */
|
||||
|
|
|
@ -82,7 +82,7 @@ class AOMDecoder : public MediaDataDecoder,
|
|||
struct OBUIterator {
|
||||
public:
|
||||
explicit OBUIterator(const Span<const uint8_t>& aData)
|
||||
: mData(aData), mPosition(0), mGoNext(true) {}
|
||||
: mData(aData), mPosition(0), mGoNext(true), mResult(NS_OK) {}
|
||||
bool HasNext() {
|
||||
UpdateNext();
|
||||
return !mGoNext;
|
||||
|
@ -92,12 +92,14 @@ class AOMDecoder : public MediaDataDecoder,
|
|||
mGoNext = true;
|
||||
return mCurrent;
|
||||
}
|
||||
MediaResult GetResult() const { return mResult; }
|
||||
|
||||
private:
|
||||
const Span<const uint8_t>& mData;
|
||||
size_t mPosition;
|
||||
OBUInfo mCurrent;
|
||||
bool mGoNext;
|
||||
MediaResult mResult;
|
||||
|
||||
// Used to fill mCurrent with the next OBU in the iterator.
|
||||
// mGoNext must be set to false if the next OBU is retrieved,
|
||||
|
@ -225,17 +227,33 @@ class AOMDecoder : public MediaDataDecoder,
|
|||
};
|
||||
|
||||
// Get a sequence header's info from a sample.
|
||||
// Returns false if the sample was not a sequence header.
|
||||
static bool ReadSequenceHeaderInfo(const Span<const uint8_t>& aSample,
|
||||
AV1SequenceInfo& aDestInfo);
|
||||
// Returns a MediaResult with codes:
|
||||
// NS_OK: Sequence header was successfully found and read.
|
||||
// NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA: Sequence header was not present.
|
||||
// Other errors will indicate that the data was corrupt.
|
||||
static MediaResult ReadSequenceHeaderInfo(const Span<const uint8_t>& aSample,
|
||||
AV1SequenceInfo& aDestInfo);
|
||||
// Writes a sequence header OBU to the buffer.
|
||||
static already_AddRefed<MediaByteBuffer> CreateSequenceHeader(
|
||||
const AV1SequenceInfo& aInfo, nsresult& aResult);
|
||||
|
||||
// Reads the raw data of an ISOBMFF-compatible av1 configuration box (av1C),
|
||||
// including any included sequence header.
|
||||
static void TryReadAV1CBox(const MediaByteBuffer* aBox,
|
||||
AV1SequenceInfo& aDestInfo,
|
||||
MediaResult& aSeqHdrResult);
|
||||
// Reads the raw data of an ISOBMFF-compatible av1 configuration box (av1C),
|
||||
// including any included sequence header.
|
||||
// This function should only be called for av1C boxes made by WriteAV1CBox, as
|
||||
// it will assert that the box and its contained OBUs are not corrupted.
|
||||
static void ReadAV1CBox(const MediaByteBuffer* aBox,
|
||||
AV1SequenceInfo& aDestInfo, bool& aHadSeqHdr);
|
||||
AV1SequenceInfo& aDestInfo, bool& aHadSeqHdr) {
|
||||
MediaResult seqHdrResult;
|
||||
TryReadAV1CBox(aBox, aDestInfo, seqHdrResult);
|
||||
nsresult code = seqHdrResult.Code();
|
||||
MOZ_ASSERT(code == NS_OK || code == NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA);
|
||||
aHadSeqHdr = code == NS_OK;
|
||||
}
|
||||
// Writes an ISOBMFF-compatible av1 configuration box (av1C) to the buffer.
|
||||
static void WriteAV1CBox(const AV1SequenceInfo& aInfo,
|
||||
MediaByteBuffer* aDestBox, bool& aHasSeqHdr);
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "WMFUtils.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "mozilla/mscom/COMWrappers.h"
|
||||
#include "mozilla/mscom/Utils.h"
|
||||
#include "PlatformDecoderModule.h"
|
||||
|
||||
|
@ -30,7 +31,7 @@ MFTDecoder::~MFTDecoder() {
|
|||
HRESULT MFTDecoder::Create(const GUID& aCLSID) {
|
||||
MOZ_ASSERT(mscom::IsCurrentThreadMTA());
|
||||
|
||||
HRESULT hr = CoCreateInstance(
|
||||
HRESULT hr = mscom::wrapped::CoCreateInstance(
|
||||
aCLSID, nullptr, CLSCTX_INPROC_SERVER,
|
||||
IID_PPV_ARGS(static_cast<IMFTransform**>(getter_AddRefs(mDecoder))));
|
||||
NS_WARNING_ASSERTION(SUCCEEDED(hr), "Failed to create MFT by CLSID");
|
||||
|
|
|
@ -170,6 +170,20 @@ int WMFDecoderModule::GetNumDecoderThreads() {
|
|||
/* static */
|
||||
HRESULT WMFDecoderModule::CreateMFTDecoder(const WMFStreamType& aType,
|
||||
RefPtr<MFTDecoder>& aDecoder) {
|
||||
// Do not expose any video decoder on utility process which is only for audio
|
||||
// decoding.
|
||||
if (XRE_IsUtilityProcess()) {
|
||||
switch (aType) {
|
||||
case WMFStreamType::H264:
|
||||
case WMFStreamType::VP8:
|
||||
case WMFStreamType::VP9:
|
||||
case WMFStreamType::AV1:
|
||||
return E_FAIL;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (aType) {
|
||||
case WMFStreamType::H264:
|
||||
return aDecoder->Create(CLSID_CMSH264DecoderMFT);
|
||||
|
@ -271,6 +285,20 @@ bool WMFDecoderModule::CanCreateMFTDecoder(const WMFStreamType& aType) {
|
|||
break;
|
||||
}
|
||||
|
||||
// Do not expose any video decoder on utility process which is only for audio
|
||||
// decoding.
|
||||
if (XRE_IsUtilityProcess()) {
|
||||
switch (aType) {
|
||||
case WMFStreamType::H264:
|
||||
case WMFStreamType::VP8:
|
||||
case WMFStreamType::VP9:
|
||||
case WMFStreamType::AV1:
|
||||
return false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return sSupportedTypes.contains(aType);
|
||||
}
|
||||
|
||||
|
|
|
@ -321,11 +321,12 @@ class AV1ChangeMonitor : public MediaChangeMonitor::CodecChangeMonitor {
|
|||
// If we're passed AV1 codec configuration, store it so that we can
|
||||
// instantiate a decoder in MediaChangeMonitor::Create.
|
||||
AOMDecoder::AV1SequenceInfo seqInfo;
|
||||
bool hadSeqHdr;
|
||||
AOMDecoder::ReadAV1CBox(mCurrentConfig.mExtraData, seqInfo, hadSeqHdr);
|
||||
MediaResult seqHdrResult;
|
||||
AOMDecoder::TryReadAV1CBox(mCurrentConfig.mExtraData, seqInfo,
|
||||
seqHdrResult);
|
||||
// If the av1C box doesn't include a sequence header specifying image
|
||||
// size, keep the one provided by VideoInfo.
|
||||
if (hadSeqHdr) {
|
||||
if (seqHdrResult.Code() != NS_OK) {
|
||||
seqInfo.mImage = mCurrentConfig.mImage;
|
||||
}
|
||||
|
||||
|
@ -365,6 +366,14 @@ class AV1ChangeMonitor : public MediaChangeMonitor::CodecChangeMonitor {
|
|||
mCurrentConfig.mDisplay = newDisplay;
|
||||
mCurrentConfig.ResetImageRect();
|
||||
}
|
||||
|
||||
bool wroteSequenceHeader = false;
|
||||
// Our headers should all be around the same size.
|
||||
mCurrentConfig.mExtraData->ClearAndRetainStorage();
|
||||
AOMDecoder::WriteAV1CBox(aInfo, mCurrentConfig.mExtraData.get(),
|
||||
wroteSequenceHeader);
|
||||
// Header should always be written ReadSequenceHeaderInfo succeeds.
|
||||
MOZ_ASSERT(wroteSequenceHeader);
|
||||
}
|
||||
|
||||
MediaResult CheckForChange(MediaRawData* aSample) override {
|
||||
|
@ -376,10 +385,17 @@ class AV1ChangeMonitor : public MediaChangeMonitor::CodecChangeMonitor {
|
|||
|
||||
// We don't trust the keyframe flag as set on the MediaRawData.
|
||||
AOMDecoder::AV1SequenceInfo info;
|
||||
|
||||
if (!AOMDecoder::ReadSequenceHeaderInfo(dataSpan, info)) {
|
||||
MediaResult seqHdrResult =
|
||||
AOMDecoder::ReadSequenceHeaderInfo(dataSpan, info);
|
||||
nsresult seqHdrCode = seqHdrResult.Code();
|
||||
if (seqHdrCode == NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA) {
|
||||
return NS_OK;
|
||||
}
|
||||
if (seqHdrCode != NS_OK) {
|
||||
LOG("AV1ChangeMonitor::CheckForChange read a corrupted sample: %s",
|
||||
seqHdrResult.Description().get());
|
||||
return seqHdrResult;
|
||||
}
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
if (mInfo.isSome() &&
|
||||
|
@ -398,12 +414,6 @@ class AV1ChangeMonitor : public MediaChangeMonitor::CodecChangeMonitor {
|
|||
rv = NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER;
|
||||
}
|
||||
|
||||
bool wroteSequenceHeader = false;
|
||||
// Our headers should all be around the same size.
|
||||
mCurrentConfig.mExtraData->ClearAndRetainStorage();
|
||||
AOMDecoder::WriteAV1CBox(info, mCurrentConfig.mExtraData.get(),
|
||||
wroteSequenceHeader);
|
||||
MOZ_ASSERT(wroteSequenceHeader);
|
||||
UpdateConfig(info);
|
||||
|
||||
if (rv == NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER) {
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<video src="1765842.webm" type="video/webm" autoplay="true" controls></video>
|
||||
</body>
|
||||
</html>
|
Двоичный файл не отображается.
|
@ -152,3 +152,4 @@ load 1734008.html
|
|||
load 1741677.html
|
||||
load 1748272.html
|
||||
load 1752917.html
|
||||
load 1765842.html
|
||||
|
|
|
@ -298,22 +298,28 @@ struct ParamTraits<mozilla::dom::RTCInboundRtpStreamStats> {
|
|||
static void Write(MessageWriter* aWriter, const paramType& aParam) {
|
||||
WriteParam(aWriter, aParam.mRemoteId);
|
||||
WriteParam(aWriter, aParam.mFramesDecoded);
|
||||
WriteParam(aWriter, aParam.mFrameWidth);
|
||||
WriteParam(aWriter, aParam.mFrameHeight);
|
||||
WriteParam(aWriter, aParam.mBytesReceived);
|
||||
WriteParam(aWriter, aParam.mNackCount);
|
||||
WriteParam(aWriter, aParam.mFirCount);
|
||||
WriteParam(aWriter, aParam.mPliCount);
|
||||
WriteParam(aWriter, aParam.mFramesPerSecond);
|
||||
WriteParam(aWriter, aParam.mFramesReceived);
|
||||
WriteRTCReceivedRtpStreamStats(aWriter, aParam);
|
||||
}
|
||||
|
||||
static bool Read(MessageReader* aReader, paramType* aResult) {
|
||||
return ReadParam(aReader, &(aResult->mRemoteId)) &&
|
||||
ReadParam(aReader, &(aResult->mFramesDecoded)) &&
|
||||
ReadParam(aReader, &(aResult->mFrameWidth)) &&
|
||||
ReadParam(aReader, &(aResult->mFrameHeight)) &&
|
||||
ReadParam(aReader, &(aResult->mBytesReceived)) &&
|
||||
ReadParam(aReader, &(aResult->mNackCount)) &&
|
||||
ReadParam(aReader, &(aResult->mFirCount)) &&
|
||||
ReadParam(aReader, &(aResult->mPliCount)) &&
|
||||
ReadParam(aReader, &(aResult->mFramesPerSecond)) &&
|
||||
ReadParam(aReader, &(aResult->mFramesReceived)) &&
|
||||
ReadRTCReceivedRtpStreamStats(aReader, aResult);
|
||||
}
|
||||
};
|
||||
|
@ -343,6 +349,9 @@ struct ParamTraits<mozilla::dom::RTCOutboundRtpStreamStats> {
|
|||
WriteParam(aWriter, aParam.mNackCount);
|
||||
WriteParam(aWriter, aParam.mFirCount);
|
||||
WriteParam(aWriter, aParam.mPliCount);
|
||||
WriteParam(aWriter, aParam.mFrameWidth);
|
||||
WriteParam(aWriter, aParam.mFrameHeight);
|
||||
WriteParam(aWriter, aParam.mFramesSent);
|
||||
WriteRTCSentRtpStreamStats(aWriter, aParam);
|
||||
}
|
||||
|
||||
|
@ -353,6 +362,9 @@ struct ParamTraits<mozilla::dom::RTCOutboundRtpStreamStats> {
|
|||
ReadParam(aReader, &(aResult->mNackCount)) &&
|
||||
ReadParam(aReader, &(aResult->mFirCount)) &&
|
||||
ReadParam(aReader, &(aResult->mPliCount)) &&
|
||||
ReadParam(aReader, &(aResult->mFrameWidth)) &&
|
||||
ReadParam(aReader, &(aResult->mFrameHeight)) &&
|
||||
ReadParam(aReader, &(aResult->mFramesSent)) &&
|
||||
ReadRTCSentRtpStreamStats(aReader, aResult);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -431,10 +431,13 @@ nsTArray<RefPtr<RTCStatsPromise>> RTCRtpReceiver::GetStatsInternal() {
|
|||
local.mFramesDecoded.Construct(videoStats->frames_decoded);
|
||||
|
||||
local.mFramesPerSecond.Construct(videoStats->decode_frame_rate);
|
||||
local.mFrameWidth.Construct(videoStats->width);
|
||||
local.mFrameHeight.Construct(videoStats->height);
|
||||
// XXX: key_frames + delta_frames may undercount frames because they were dropped in FrameBuffer::InsertFrame. (bug 1766553)
|
||||
local.mFramesReceived.Construct(videoStats->frame_counts.key_frames + videoStats->frame_counts.delta_frames);
|
||||
|
||||
/*
|
||||
* Potential new stats that are now available upstream.
|
||||
local.mFrameWidth.Construct(videoStats->width);
|
||||
local.mFrameheight.Construct(videoStats->height);
|
||||
if (videoStats->qp_sum) {
|
||||
local.mQpSum.Construct(*videoStats->qp_sum.value);
|
||||
}
|
||||
|
|
|
@ -369,6 +369,9 @@ nsTArray<RefPtr<dom::RTCStatsPromise>> RTCRtpSender::GetStatsInternal() {
|
|||
streamStats->rtp_stats.retransmitted.packets);
|
||||
local.mRetransmittedBytesSent.Construct(
|
||||
streamStats->rtp_stats.retransmitted.payload_bytes);
|
||||
local.mFramesSent.Construct(streamStats->frames_encoded);
|
||||
local.mFrameWidth.Construct(streamStats->width);
|
||||
local.mFrameHeight.Construct(streamStats->height);
|
||||
/*
|
||||
* Potential new stats that are now available upstream.
|
||||
local.mTargetBitrate.Construct(videoStats->target_media_bitrate_bps);
|
||||
|
|
|
@ -24,6 +24,10 @@
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
// QP scaling thresholds.
|
||||
static const int kLowH264QpThreshold = 24;
|
||||
static const int kHighH264QpThreshold = 37;
|
||||
|
||||
// Encoder.
|
||||
WebrtcGmpVideoEncoder::WebrtcGmpVideoEncoder(std::string aPCHandle)
|
||||
: mGMP(nullptr),
|
||||
|
@ -459,6 +463,18 @@ int32_t WebrtcGmpVideoEncoder::SetRates(
|
|||
return WEBRTC_VIDEO_CODEC_OK;
|
||||
}
|
||||
|
||||
WebrtcVideoEncoder::EncoderInfo WebrtcGmpVideoEncoder::GetEncoderInfo() const {
|
||||
|
||||
WebrtcVideoEncoder::EncoderInfo info;
|
||||
info.supports_native_handle = false;
|
||||
info.implementation_name = "GMPOpenH264";
|
||||
info.scaling_settings =
|
||||
WebrtcVideoEncoder::ScalingSettings(kLowH264QpThreshold, kHighH264QpThreshold);
|
||||
info.is_hardware_accelerated = false;
|
||||
info.supports_simulcast = false;
|
||||
return info;
|
||||
}
|
||||
|
||||
/* static */
|
||||
int32_t WebrtcGmpVideoEncoder::SetRates_g(RefPtr<WebrtcGmpVideoEncoder> aThis,
|
||||
uint32_t aNewBitRateKbps,
|
||||
|
|
|
@ -145,6 +145,8 @@ class RefCountedWebrtcVideoEncoder {
|
|||
|
||||
virtual MediaEventSource<uint64_t>* ReleasePluginEvent() = 0;
|
||||
|
||||
virtual WebrtcVideoEncoder::EncoderInfo GetEncoderInfo() const = 0;
|
||||
|
||||
protected:
|
||||
virtual ~RefCountedWebrtcVideoEncoder() = default;
|
||||
};
|
||||
|
@ -171,6 +173,8 @@ class WebrtcGmpVideoEncoder : public GMPVideoEncoderCallbackProxy,
|
|||
int32_t SetRates(
|
||||
const webrtc::VideoEncoder::RateControlParameters& aParameters) override;
|
||||
|
||||
WebrtcVideoEncoder::EncoderInfo GetEncoderInfo() const override;
|
||||
|
||||
MediaEventSource<uint64_t>* InitPluginEvent() override {
|
||||
return &mInitPluginEvent;
|
||||
}
|
||||
|
@ -341,6 +345,10 @@ class WebrtcVideoEncoderProxy : public WebrtcVideoEncoder {
|
|||
mEncoderImpl->SetRates(aParameters);
|
||||
}
|
||||
|
||||
EncoderInfo GetEncoderInfo() const override {
|
||||
return mEncoderImpl->GetEncoderInfo();
|
||||
}
|
||||
|
||||
private:
|
||||
const RefPtr<RefCountedWebrtcVideoEncoder> mEncoderImpl;
|
||||
};
|
||||
|
|
|
@ -261,6 +261,15 @@ already_AddRefed<MediaDataEncoder> WebrtcMediaDataEncoder::CreateEncoder(
|
|||
return mFactory->CreateEncoder(params);
|
||||
}
|
||||
|
||||
WebrtcVideoEncoder::EncoderInfo WebrtcMediaDataEncoder::GetEncoderInfo() const {
|
||||
WebrtcVideoEncoder::EncoderInfo info;
|
||||
info.supports_native_handle = false;
|
||||
info.implementation_name = "MediaDataEncoder";
|
||||
info.is_hardware_accelerated = false;
|
||||
info.supports_simulcast = false;
|
||||
return info;
|
||||
}
|
||||
|
||||
int32_t WebrtcMediaDataEncoder::RegisterEncodeCompleteCallback(
|
||||
webrtc::EncodedImageCallback* aCallback) {
|
||||
MutexAutoLock lock(mCallbackMutex);
|
||||
|
|
|
@ -41,6 +41,7 @@ class WebrtcMediaDataEncoder : public RefCountedWebrtcVideoEncoder {
|
|||
int32_t SetRates(
|
||||
const webrtc::VideoEncoder::RateControlParameters& aParameters) override;
|
||||
|
||||
WebrtcVideoEncoder::EncoderInfo GetEncoderInfo() const override;
|
||||
MediaEventSource<uint64_t>* InitPluginEvent() override { return nullptr; }
|
||||
|
||||
MediaEventSource<uint64_t>* ReleasePluginEvent() override { return nullptr; }
|
||||
|
|
|
@ -27,6 +27,9 @@ const statsExpectedByType = {
|
|||
"framesDecoded",
|
||||
"discardedPackets",
|
||||
"framesPerSecond",
|
||||
"frameWidth",
|
||||
"frameHeight",
|
||||
"framesReceived",
|
||||
],
|
||||
unimplemented: [
|
||||
"mediaTrackId",
|
||||
|
@ -62,7 +65,14 @@ const statsExpectedByType = {
|
|||
"retransmittedBytesSent",
|
||||
],
|
||||
optional: ["nackCount", "qpSum"],
|
||||
localVideoOnly: ["framesEncoded", "firCount", "pliCount"],
|
||||
localVideoOnly: [
|
||||
"framesEncoded",
|
||||
"firCount",
|
||||
"pliCount",
|
||||
"frameWidth",
|
||||
"frameHeight",
|
||||
"framesSent",
|
||||
],
|
||||
unimplemented: ["mediaTrackId", "transportId", "sliCount", "targetBitrate"],
|
||||
deprecated: ["isRemote"],
|
||||
},
|
||||
|
@ -517,6 +527,27 @@ function pedanticChecks(report) {
|
|||
`${stat.type}.framesDecoded is a sane number for a short ` +
|
||||
`${stat.kind} test. value=${stat.framesDecoded}`
|
||||
);
|
||||
|
||||
// frameWidth
|
||||
ok(
|
||||
stat.frameWidth > 0 && stat.frameWidth < 100000,
|
||||
`${stat.type}.frameWidth is a sane number for a short ` +
|
||||
`${stat.kind} test. value=${stat.framesSent}`
|
||||
);
|
||||
|
||||
// frameHeight
|
||||
ok(
|
||||
stat.frameHeight > 0 && stat.frameHeight < 100000,
|
||||
`${stat.type}.frameHeight is a sane number for a short ` +
|
||||
`${stat.kind} test. value=${stat.frameHeight}`
|
||||
);
|
||||
|
||||
// framesReceived
|
||||
ok(
|
||||
stat.framesReceived >= 0 && stat.framesReceived < 100000,
|
||||
`${stat.type}.framesReceived is a sane number for a short ` +
|
||||
`${stat.kind} test. value=${stat.framesReceived}`
|
||||
);
|
||||
}
|
||||
} else if (stat.type == "remote-inbound-rtp") {
|
||||
// roundTripTime
|
||||
|
@ -664,6 +695,27 @@ function pedanticChecks(report) {
|
|||
`${stat.type}.framesEncoded is a sane number for a short ` +
|
||||
`${stat.kind} test. value=${stat.framesEncoded}`
|
||||
);
|
||||
|
||||
// frameWidth
|
||||
ok(
|
||||
stat.frameWidth >= 0 && stat.frameWidth < 100000,
|
||||
`${stat.type}.frameWidth is a sane number for a short ` +
|
||||
`${stat.kind} test. value=${stat.frameWidth}`
|
||||
);
|
||||
|
||||
// frameHeight
|
||||
ok(
|
||||
stat.frameHeight >= 0 && stat.frameHeight < 100000,
|
||||
`${stat.type}.frameHeight is a sane number for a short ` +
|
||||
`${stat.kind} test. value=${stat.frameHeight}`
|
||||
);
|
||||
|
||||
// framesSent
|
||||
ok(
|
||||
stat.framesSent >= 0 && stat.framesSent < 100000,
|
||||
`${stat.type}.framesSent is a sane number for a short ` +
|
||||
`${stat.kind} test. value=${stat.framesSent}`
|
||||
);
|
||||
}
|
||||
} else if (stat.type == "remote-outbound-rtp") {
|
||||
//
|
||||
|
|
|
@ -34,7 +34,8 @@ add_task(async function() {
|
|||
});
|
||||
|
||||
// Top-Level scheme: HTTP
|
||||
testSet.push(
|
||||
// NOTE(freddyb): Test case temporarily disabled. See bug 1735565
|
||||
/*testSet.push(
|
||||
runTest({
|
||||
queryString: "test1.1",
|
||||
topLevelScheme: "http",
|
||||
|
@ -43,7 +44,7 @@ add_task(async function() {
|
|||
expectedSameOrigin: "http",
|
||||
expectedCrossOrigin: "http",
|
||||
})
|
||||
);
|
||||
);*/
|
||||
// Top-Level scheme: HTTPS
|
||||
testSet.push(
|
||||
runTest({
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>nsIDOMWindowUtils::nodesFromRect test - bug 489127</title>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
<script type="application/javascript">
|
||||
|
||||
<title>nsIDOMWindowUtils::nodesFromRect test - bug 489127</title>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
<script>
|
||||
var SimpleTest = window.opener.SimpleTest;
|
||||
function ok() { window.opener.ok.apply(window.opener, arguments); }
|
||||
function done() { window.opener.done.apply(window.opener, arguments); }
|
||||
function info() { window.opener.info.apply(window.opener, arguments); }
|
||||
|
||||
let e = {};
|
||||
|
||||
let dwu = window.windowUtils;
|
||||
|
@ -30,9 +29,8 @@
|
|||
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
if (nodes[i] != list[i]) {
|
||||
ok(false, "Unexpected node #" + i + " for rect " +
|
||||
"[" + x + "," + y + "], " +
|
||||
"[" + top + "," + right + "," + bottom + "," + left + "]");
|
||||
ok(false, `Unexpected node #${i} (${nodes[i].id} vs. ${list[i].id}) ` +
|
||||
`[${x}, ${y}] [${top}, ${right}, ${bottom}, ${left}]`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -42,14 +40,21 @@
|
|||
}
|
||||
|
||||
function doTest() {
|
||||
|
||||
// Set up shortcut access to elements
|
||||
e.html = document.getElementsByTagName("html")[0];
|
||||
e.html = document.documentElement;
|
||||
['h1', 'd1', 'd2', 'p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'span',
|
||||
'a1', 'a2', 'a3', 'transf', 'iframe1', 'body', 'opacity'].forEach(function(a) {
|
||||
'a1', 'a2', 'a3', 'transf', 'iframe1', 'body', 'opacity', 'host'].forEach(function(a) {
|
||||
e[a] = document.getElementById(a);
|
||||
});
|
||||
|
||||
let shadow = e.host.attachShadow({ mode: "open" });
|
||||
shadow.innerHTML = `
|
||||
<style>
|
||||
#host-inner { height: 100px; width: 100px; background-color: blue }
|
||||
</style>
|
||||
<div id="host-inner"></div>
|
||||
`;
|
||||
|
||||
window.scrollTo(0, 0);
|
||||
|
||||
// Top, Right, Bottom, Left directions:
|
||||
|
@ -102,8 +107,8 @@
|
|||
check(61, 671, 0, 30, 0, 10, false, [e.transf]);
|
||||
check(61, 671, 0, 30, 90, 10, false, [e.transf.firstChild, e.transf]);
|
||||
|
||||
// opacity: with and without aVisibleOnly = true
|
||||
{
|
||||
info("opacity: with and without aVisibleOnly = true");
|
||||
let [x, y] = getCenterFor(e.opacity);
|
||||
check(x, y, 1, 1, 1, 1, false, [e.opacity]);
|
||||
check(x, y, 1, 1, 1, 1, true, []);
|
||||
|
@ -112,6 +117,7 @@
|
|||
// Check elements behind opaque backgrounds of other elements don't show up
|
||||
// in the list when aVisibleOnly = true.
|
||||
{
|
||||
info("elements behind opaque backgrounds of other elements with aVisibleOnly=true");
|
||||
let container = document.getElementById('obscured-test');
|
||||
let fg = document.getElementById('obscured-test-foreground');
|
||||
let bg = document.getElementById('obscured-test-background');
|
||||
|
@ -124,8 +130,7 @@
|
|||
check(x, y, 1, 1, 1, 1, true, [fg], kListIsComplete);
|
||||
check(x, y, 1, 1, 1, 1, true, [fg], kListIsComplete, 0.5);
|
||||
|
||||
// Occluded with different opacity thresholds, with background colors and
|
||||
// opacity.
|
||||
info("Occluded with different opacity thresholds, with background colors and opacity");
|
||||
fg.style.backgroundColor = "rgba(0, 255, 0, 0.5)";
|
||||
check(x, y, 1, 1, 1, 1, true, [fg], kListIsComplete, 0.4);
|
||||
check(x, y, 1, 1, 1, 1, true, [fg, bg], kListIsComplete, 0.6);
|
||||
|
@ -137,6 +142,14 @@
|
|||
check(x, y, 1, 1, 1, 1, true, [fg, bg], kListIsComplete, 0.6);
|
||||
}
|
||||
|
||||
{
|
||||
info("Shadow DOM retargeting");
|
||||
|
||||
let [x, y] = getCenterFor(e.host);
|
||||
let inner = shadow.getElementById("host-inner");
|
||||
check(x, y, 1, 1, 1, 1, false, [inner, e.host]);
|
||||
}
|
||||
|
||||
done();
|
||||
}
|
||||
|
||||
|
@ -147,8 +160,7 @@
|
|||
|
||||
addLoadEvent(doTest);
|
||||
</script>
|
||||
<style type="text/css">
|
||||
|
||||
<style>
|
||||
body {
|
||||
margin: 8px;
|
||||
padding: 0;
|
||||
|
@ -215,7 +227,6 @@ span {
|
|||
background: green;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body id="body">
|
||||
<h1 id="h1"></h1>
|
||||
<div id="d1"></div>
|
||||
|
@ -248,9 +259,10 @@ span {
|
|||
|
||||
<div id="opacity">text</div>
|
||||
|
||||
<div id="host"></div>
|
||||
|
||||
<div id="obscured-test">
|
||||
<div id="obscured-test-background"></div>
|
||||
<div id="obscured-test-foreground"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -5,9 +5,12 @@
|
|||
|
||||
#include "mozilla/dom/WebGPUBinding.h"
|
||||
#include "CanvasContext.h"
|
||||
#include "nsDisplayList.h"
|
||||
#include "gfxUtils.h"
|
||||
#include "LayerUserData.h"
|
||||
#include "nsDisplayList.h"
|
||||
#include "mozilla/dom/HTMLCanvasElement.h"
|
||||
#include "mozilla/gfx/CanvasManagerChild.h"
|
||||
#include "mozilla/layers/CanvasRenderer.h"
|
||||
#include "mozilla/layers/CompositableInProcessManager.h"
|
||||
#include "mozilla/layers/ImageDataSerializer.h"
|
||||
#include "mozilla/layers/LayersSurfaces.h"
|
||||
|
@ -78,6 +81,7 @@ void CanvasContext::Configure(const dom::GPUCanvasConfiguration& aDesc) {
|
|||
} else if (mOffscreenCanvas) {
|
||||
dom::OffscreenCanvasDisplayData data;
|
||||
data.mSize = {mWidth, mHeight};
|
||||
data.mIsOpaque = false;
|
||||
data.mHandle = mHandle;
|
||||
mOffscreenCanvas->UpdateDisplayData(data);
|
||||
}
|
||||
|
@ -123,5 +127,69 @@ void CanvasContext::SwapChainPresent() {
|
|||
}
|
||||
}
|
||||
|
||||
bool CanvasContext::InitializeCanvasRenderer(
|
||||
nsDisplayListBuilder* aBuilder, layers::CanvasRenderer* aRenderer) {
|
||||
// This path is only used for rendering when we use the fallback Paint path,
|
||||
// used by reftest-snapshot, printing and Firefox Screenshot.
|
||||
if (!mHandle) {
|
||||
return false;
|
||||
}
|
||||
|
||||
layers::CanvasRendererData data;
|
||||
data.mContext = this;
|
||||
data.mSize = mGfxSize;
|
||||
data.mIsOpaque = false;
|
||||
|
||||
aRenderer->Initialize(data);
|
||||
aRenderer->SetDirty();
|
||||
return true;
|
||||
}
|
||||
|
||||
mozilla::UniquePtr<uint8_t[]> CanvasContext::GetImageBuffer(int32_t* aFormat) {
|
||||
gfxAlphaType any;
|
||||
RefPtr<gfx::SourceSurface> snapshot = GetSurfaceSnapshot(&any);
|
||||
if (!snapshot) {
|
||||
*aFormat = 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<gfx::DataSourceSurface> dataSurface = snapshot->GetDataSurface();
|
||||
return gfxUtils::GetImageBuffer(dataSurface, /* aIsAlphaPremultiplied */ true,
|
||||
aFormat);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP CanvasContext::GetInputStream(const char* aMimeType,
|
||||
const nsAString& aEncoderOptions,
|
||||
nsIInputStream** aStream) {
|
||||
gfxAlphaType any;
|
||||
RefPtr<gfx::SourceSurface> snapshot = GetSurfaceSnapshot(&any);
|
||||
if (!snapshot) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
RefPtr<gfx::DataSourceSurface> dataSurface = snapshot->GetDataSurface();
|
||||
return gfxUtils::GetInputStream(dataSurface, /* aIsAlphaPremultiplied */ true,
|
||||
aMimeType, aEncoderOptions, aStream);
|
||||
}
|
||||
|
||||
already_AddRefed<mozilla::gfx::SourceSurface> CanvasContext::GetSurfaceSnapshot(
|
||||
gfxAlphaType* aOutAlphaType) {
|
||||
if (aOutAlphaType) {
|
||||
*aOutAlphaType = gfxAlphaType::Premult;
|
||||
}
|
||||
|
||||
auto* const cm = gfx::CanvasManagerChild::Get();
|
||||
if (!cm) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!mBridge || !mBridge->IsOpen() || !mHandle) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return cm->GetSnapshot(cm->Id(), mBridge->Id(), mHandle, mGfxFormat,
|
||||
/* aPremultiply */ false, /* aYFlip */ false);
|
||||
}
|
||||
|
||||
} // namespace webgpu
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -54,20 +54,14 @@ class CanvasContext final : public nsICanvasRenderingContextInternal,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
mozilla::UniquePtr<uint8_t[]> GetImageBuffer(int32_t* aFormat) override {
|
||||
MOZ_CRASH("todo");
|
||||
}
|
||||
bool InitializeCanvasRenderer(nsDisplayListBuilder* aBuilder,
|
||||
layers::CanvasRenderer* aRenderer) override;
|
||||
mozilla::UniquePtr<uint8_t[]> GetImageBuffer(int32_t* aFormat) override;
|
||||
NS_IMETHOD GetInputStream(const char* aMimeType,
|
||||
const nsAString& aEncoderOptions,
|
||||
nsIInputStream** aStream) override {
|
||||
*aStream = nullptr;
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
nsIInputStream** aStream) override;
|
||||
already_AddRefed<mozilla::gfx::SourceSurface> GetSurfaceSnapshot(
|
||||
gfxAlphaType* aOutAlphaType) override {
|
||||
return nullptr;
|
||||
}
|
||||
gfxAlphaType* aOutAlphaType) override;
|
||||
|
||||
void SetOpaqueValueFromOpaqueAttr(bool aOpaqueAttrValue) override {}
|
||||
bool GetIsOpaque() override { return true; }
|
||||
|
|
|
@ -640,6 +640,36 @@ static void PresentCallback(ffi::WGPUBufferMapAsyncStatus status,
|
|||
delete req;
|
||||
}
|
||||
|
||||
ipc::IPCResult WebGPUParent::GetFrontBufferSnapshot(
|
||||
IProtocol* aProtocol, const CompositableHandle& aHandle,
|
||||
Maybe<Shmem>& aShmem, gfx::IntSize& aSize) {
|
||||
const auto& lookup = mCanvasMap.find(aHandle.Value());
|
||||
if (lookup == mCanvasMap.end()) {
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
RefPtr<PresentationData> data = lookup->second.get();
|
||||
aSize = data->mTextureHost->GetSize();
|
||||
uint32_t stride =
|
||||
aSize.width * BytesPerPixel(data->mTextureHost->GetFormat());
|
||||
uint32_t len = data->mRowCount * stride;
|
||||
Shmem shmem;
|
||||
if (!AllocShmem(len, ipc::Shmem::SharedMemory::TYPE_BASIC, &shmem)) {
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
uint8_t* dst = shmem.get<uint8_t>();
|
||||
uint8_t* src = data->mTextureHost->GetBuffer();
|
||||
for (uint32_t row = 0; row < data->mRowCount; ++row) {
|
||||
memcpy(dst, src, stride);
|
||||
src += data->mTargetPitch;
|
||||
dst += stride;
|
||||
}
|
||||
|
||||
aShmem.emplace(std::move(shmem));
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
ipc::IPCResult WebGPUParent::RecvSwapChainPresent(
|
||||
const CompositableHandle& aHandle, RawId aTextureId,
|
||||
RawId aCommandEncoderId) {
|
||||
|
|
|
@ -11,8 +11,7 @@
|
|||
#include "WebGPUTypes.h"
|
||||
#include "base/timer.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace webgpu {
|
||||
namespace mozilla::webgpu {
|
||||
class ErrorBuffer;
|
||||
class PresentationData;
|
||||
|
||||
|
@ -90,6 +89,11 @@ class WebGPUParent final : public PWebGPUParent {
|
|||
ipc::IPCResult RecvDevicePopErrorScope(
|
||||
RawId aSelfId, DevicePopErrorScopeResolver&& aResolver);
|
||||
|
||||
ipc::IPCResult GetFrontBufferSnapshot(IProtocol* aProtocol,
|
||||
const CompositableHandle& aHandle,
|
||||
Maybe<Shmem>& aShmem,
|
||||
gfx::IntSize& aSize);
|
||||
|
||||
void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
|
||||
private:
|
||||
|
@ -109,7 +113,6 @@ class WebGPUParent final : public PWebGPUParent {
|
|||
std::unordered_map<uint64_t, ErrorScopeStack> mErrorScopeMap;
|
||||
};
|
||||
|
||||
} // namespace webgpu
|
||||
} // namespace mozilla
|
||||
} // namespace mozilla::webgpu
|
||||
|
||||
#endif // WEBGPU_PARENT_H_
|
||||
|
|
|
@ -39,8 +39,6 @@ interface HTMLCanvasElement : HTMLElement {
|
|||
partial interface HTMLCanvasElement {
|
||||
[Pure, SetterThrows]
|
||||
attribute boolean mozOpaque;
|
||||
[Throws, NeedsSubjectPrincipal, Pref="canvas.mozgetasfile.enabled"]
|
||||
File mozGetAsFile(DOMString name, optional DOMString? type = null);
|
||||
// A Mozilla-only extension to get a canvas context backed by double-buffered
|
||||
// shared memory. Only privileged callers can call this.
|
||||
[ChromeOnly, Throws]
|
||||
|
|
|
@ -64,11 +64,14 @@ dictionary RTCReceivedRtpStreamStats: RTCRtpStreamStats {
|
|||
dictionary RTCInboundRtpStreamStats : RTCReceivedRtpStreamStats {
|
||||
DOMString remoteId;
|
||||
unsigned long framesDecoded;
|
||||
unsigned long frameWidth;
|
||||
unsigned long frameHeight;
|
||||
unsigned long long bytesReceived;
|
||||
unsigned long nackCount;
|
||||
unsigned long firCount;
|
||||
unsigned long pliCount;
|
||||
double framesPerSecond;
|
||||
unsigned long framesReceived;
|
||||
};
|
||||
|
||||
dictionary RTCRemoteInboundRtpStreamStats : RTCReceivedRtpStreamStats {
|
||||
|
@ -91,6 +94,9 @@ dictionary RTCOutboundRtpStreamStats : RTCSentRtpStreamStats {
|
|||
unsigned long long headerBytesSent;
|
||||
unsigned long long retransmittedPacketsSent;
|
||||
unsigned long long retransmittedBytesSent;
|
||||
unsigned long frameWidth;
|
||||
unsigned long frameHeight;
|
||||
unsigned long framesSent;
|
||||
};
|
||||
|
||||
dictionary RTCRemoteOutboundRtpStreamStats : RTCSentRtpStreamStats {
|
||||
|
|
|
@ -66,7 +66,22 @@ already_AddRefed<SharedWorker> SharedWorker::Constructor(
|
|||
do_QueryInterface(aGlobal.GetAsSupports());
|
||||
MOZ_ASSERT(window);
|
||||
|
||||
auto storageAllowed = StorageAllowedForWindow(window);
|
||||
// Our current idiom is that storage-related APIs specialize for the system
|
||||
// principal themselves, which is consistent with StorageAllowedForwindow not
|
||||
// specializing for the system principal. Without this specialization we
|
||||
// would end up with ePrivateBrowsing for system principaled private browsing
|
||||
// windows which is explicitly not what we want. System Principal code always
|
||||
// should have access to storage. It may make sense to enhance
|
||||
// StorageAllowedForWindow in the future to handle this after comprehensive
|
||||
// auditing.
|
||||
nsCOMPtr<nsIPrincipal> principal = aGlobal.GetSubjectPrincipal();
|
||||
StorageAccess storageAllowed;
|
||||
if (principal && principal->IsSystemPrincipal()) {
|
||||
storageAllowed = StorageAccess::eAllow;
|
||||
} else {
|
||||
storageAllowed = StorageAllowedForWindow(window);
|
||||
}
|
||||
|
||||
if (storageAllowed == StorageAccess::eDeny) {
|
||||
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return nullptr;
|
||||
|
@ -83,8 +98,6 @@ already_AddRefed<SharedWorker> SharedWorker::Constructor(
|
|||
// StorageAccess value.
|
||||
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
||||
if (storageAllowed == StorageAccess::ePrivateBrowsing) {
|
||||
nsCOMPtr<Document> doc = window->GetExtantDoc();
|
||||
nsCOMPtr<nsIPrincipal> principal = doc ? doc->NodePrincipal() : nullptr;
|
||||
uint32_t privateBrowsingId = 0;
|
||||
if (principal) {
|
||||
MOZ_ALWAYS_SUCCEEDS(principal->GetPrivateBrowsingId(&privateBrowsingId));
|
||||
|
|
|
@ -61,9 +61,21 @@ function doTests() {
|
|||
});
|
||||
}
|
||||
|
||||
function doSystemSharedWorkerTest() {
|
||||
try {
|
||||
let chromeShared =
|
||||
new wP.SharedWorker("chrome://mochitests/content/dom/workers/test/sharedWorker_privateBrowsing.js");
|
||||
ok(true, "system SharedWorker created without throwing or crashing!");
|
||||
} catch (_ex) {
|
||||
ok(false, "system SharedWorker should not throw or crash");
|
||||
}
|
||||
runTest();
|
||||
}
|
||||
|
||||
var steps = [
|
||||
setupWindow,
|
||||
doTests
|
||||
doTests,
|
||||
doSystemSharedWorkerTest,
|
||||
];
|
||||
|
||||
function runTest() {
|
||||
|
|
259
gfx/2d/2D.h
259
gfx/2d/2D.h
|
@ -251,7 +251,7 @@ struct ShadowOptions {
|
|||
* matching DrawTarget. Not adhering to this condition will make a draw call
|
||||
* fail.
|
||||
*/
|
||||
class GradientStops : public external::AtomicRefCounted<GradientStops> {
|
||||
class GradientStops : public SupportsThreadSafeWeakPtr<GradientStops> {
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStops)
|
||||
virtual ~GradientStops() = default;
|
||||
|
@ -275,8 +275,15 @@ class Pattern {
|
|||
|
||||
virtual PatternType GetType() const = 0;
|
||||
|
||||
/** Instantiate a new clone with the same pattern type and values. */
|
||||
virtual Pattern* Clone() const { return nullptr; }
|
||||
/** Instantiate a new clone with the same pattern type and values. Any
|
||||
* internal strong references will be converted to weak references. */
|
||||
virtual Pattern* CloneWeak() const { return nullptr; }
|
||||
|
||||
/** Whether the pattern holds an internal weak reference. */
|
||||
virtual bool IsWeak() const { return false; }
|
||||
|
||||
/** Whether any internal weak references still point to a target. */
|
||||
virtual bool IsValid() const { return true; }
|
||||
|
||||
/** Determine if the pattern type and values exactly match. */
|
||||
virtual bool operator==(const Pattern& aOther) const = 0;
|
||||
|
@ -285,6 +292,19 @@ class Pattern {
|
|||
|
||||
protected:
|
||||
Pattern() = default;
|
||||
|
||||
// Utility functions to check if a weak reference is still valid.
|
||||
template <typename T>
|
||||
static inline bool IsRefValid(const RefPtr<T>& aPtr) {
|
||||
// RefPtrs are always valid.
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline bool IsRefValid(const ThreadSafeWeakPtr<T>& aPtr) {
|
||||
// Weak refs are only valid if they aren't dead.
|
||||
return !aPtr.IsDead();
|
||||
}
|
||||
};
|
||||
|
||||
class ColorPattern : public Pattern {
|
||||
|
@ -295,7 +315,7 @@ class ColorPattern : public Pattern {
|
|||
|
||||
PatternType GetType() const override { return PatternType::COLOR; }
|
||||
|
||||
Pattern* Clone() const override { return new ColorPattern(mColor); }
|
||||
Pattern* CloneWeak() const override { return new ColorPattern(mColor); }
|
||||
|
||||
bool operator==(const Pattern& aOther) const override {
|
||||
if (aOther.GetType() != PatternType::COLOR) {
|
||||
|
@ -313,150 +333,201 @@ class ColorPattern : public Pattern {
|
|||
* stored in a separate object and are backend dependent. This class itself
|
||||
* may be used on the stack.
|
||||
*/
|
||||
class LinearGradientPattern : public Pattern {
|
||||
template <template <typename> typename REF = RefPtr>
|
||||
class LinearGradientPatternT : public Pattern {
|
||||
typedef LinearGradientPatternT<ThreadSafeWeakPtr> Weak;
|
||||
|
||||
public:
|
||||
/// For constructor parameter description, see member data documentation.
|
||||
LinearGradientPattern(const Point& aBegin, const Point& aEnd,
|
||||
already_AddRefed<GradientStops> aStops,
|
||||
const Matrix& aMatrix = Matrix())
|
||||
: mBegin(aBegin), mEnd(aEnd), mStops(aStops), mMatrix(aMatrix) {}
|
||||
LinearGradientPatternT(const Point& aBegin, const Point& aEnd,
|
||||
RefPtr<GradientStops> aStops,
|
||||
const Matrix& aMatrix = Matrix())
|
||||
: mBegin(aBegin),
|
||||
mEnd(aEnd),
|
||||
mStops(std::move(aStops)),
|
||||
mMatrix(aMatrix) {}
|
||||
|
||||
PatternType GetType() const override { return PatternType::LINEAR_GRADIENT; }
|
||||
|
||||
Pattern* Clone() const override {
|
||||
return new LinearGradientPattern(mBegin, mEnd, do_AddRef(mStops), mMatrix);
|
||||
Pattern* CloneWeak() const override {
|
||||
return new Weak(mBegin, mEnd, do_AddRef(mStops), mMatrix);
|
||||
}
|
||||
|
||||
bool IsWeak() const override {
|
||||
return std::is_same<decltype(*this), Weak>::value;
|
||||
}
|
||||
|
||||
bool IsValid() const override { return IsRefValid(mStops); }
|
||||
|
||||
template <template <typename> typename T>
|
||||
bool operator==(const LinearGradientPatternT<T>& aOther) const {
|
||||
return mBegin == aOther.mBegin && mEnd == aOther.mEnd &&
|
||||
mStops == aOther.mStops && mMatrix.ExactlyEquals(aOther.mMatrix);
|
||||
}
|
||||
|
||||
bool operator==(const Pattern& aOther) const override {
|
||||
if (aOther.GetType() != PatternType::LINEAR_GRADIENT) {
|
||||
return false;
|
||||
}
|
||||
const LinearGradientPattern& other =
|
||||
static_cast<const LinearGradientPattern&>(aOther);
|
||||
return mBegin == other.mBegin && mEnd == other.mEnd &&
|
||||
mStops == other.mStops && mMatrix.ExactlyEquals(other.mMatrix);
|
||||
return aOther.IsWeak()
|
||||
? *this == static_cast<const Weak&>(aOther)
|
||||
: *this == static_cast<const LinearGradientPatternT<>&>(aOther);
|
||||
}
|
||||
|
||||
Point mBegin; //!< Start of the linear gradient
|
||||
Point mEnd; /**< End of the linear gradient - NOTE: In the case
|
||||
of a zero length gradient it will act as the
|
||||
color of the last stop. */
|
||||
RefPtr<GradientStops>
|
||||
mStops; /**< GradientStops object for this gradient, this
|
||||
should match the backend type of the draw
|
||||
target this pattern will be used with. */
|
||||
Matrix mMatrix; /**< A matrix that transforms the pattern into
|
||||
user space */
|
||||
Point mBegin; //!< Start of the linear gradient
|
||||
Point mEnd; /**< End of the linear gradient - NOTE: In the case
|
||||
of a zero length gradient it will act as the
|
||||
color of the last stop. */
|
||||
REF<GradientStops> mStops; /**< GradientStops object for this gradient, this
|
||||
should match the backend type of the draw
|
||||
target this pattern will be used with. */
|
||||
Matrix mMatrix; /**< A matrix that transforms the pattern into
|
||||
user space */
|
||||
};
|
||||
|
||||
typedef LinearGradientPatternT<> LinearGradientPattern;
|
||||
|
||||
/**
|
||||
* This class is used for Radial Gradient Patterns, the gradient stops are
|
||||
* stored in a separate object and are backend dependent. This class itself
|
||||
* may be used on the stack.
|
||||
*/
|
||||
class RadialGradientPattern : public Pattern {
|
||||
template <template <typename> typename REF = RefPtr>
|
||||
class RadialGradientPatternT : public Pattern {
|
||||
typedef RadialGradientPatternT<ThreadSafeWeakPtr> Weak;
|
||||
|
||||
public:
|
||||
/// For constructor parameter description, see member data documentation.
|
||||
RadialGradientPattern(const Point& aCenter1, const Point& aCenter2,
|
||||
Float aRadius1, Float aRadius2,
|
||||
already_AddRefed<GradientStops> aStops,
|
||||
const Matrix& aMatrix = Matrix())
|
||||
RadialGradientPatternT(const Point& aCenter1, const Point& aCenter2,
|
||||
Float aRadius1, Float aRadius2,
|
||||
RefPtr<GradientStops> aStops,
|
||||
const Matrix& aMatrix = Matrix())
|
||||
: mCenter1(aCenter1),
|
||||
mCenter2(aCenter2),
|
||||
mRadius1(aRadius1),
|
||||
mRadius2(aRadius2),
|
||||
mStops(aStops),
|
||||
mStops(std::move(aStops)),
|
||||
mMatrix(aMatrix) {}
|
||||
|
||||
PatternType GetType() const override { return PatternType::RADIAL_GRADIENT; }
|
||||
|
||||
Pattern* Clone() const override {
|
||||
return new RadialGradientPattern(mCenter1, mCenter2, mRadius1, mRadius2,
|
||||
do_AddRef(mStops), mMatrix);
|
||||
Pattern* CloneWeak() const override {
|
||||
return new Weak(mCenter1, mCenter2, mRadius1, mRadius2, do_AddRef(mStops),
|
||||
mMatrix);
|
||||
}
|
||||
|
||||
bool IsWeak() const override {
|
||||
return std::is_same<decltype(*this), Weak>::value;
|
||||
}
|
||||
|
||||
bool IsValid() const override { return IsRefValid(mStops); }
|
||||
|
||||
template <template <typename> typename T>
|
||||
bool operator==(const RadialGradientPatternT<T>& aOther) const {
|
||||
return mCenter1 == aOther.mCenter1 && mCenter2 == aOther.mCenter2 &&
|
||||
mRadius1 == aOther.mRadius1 && mRadius2 == aOther.mRadius2 &&
|
||||
mStops == aOther.mStops && mMatrix.ExactlyEquals(aOther.mMatrix);
|
||||
}
|
||||
|
||||
bool operator==(const Pattern& aOther) const override {
|
||||
if (aOther.GetType() != PatternType::RADIAL_GRADIENT) {
|
||||
return false;
|
||||
}
|
||||
const RadialGradientPattern& other =
|
||||
static_cast<const RadialGradientPattern&>(aOther);
|
||||
return mCenter1 == other.mCenter1 && mCenter2 == other.mCenter2 &&
|
||||
mRadius1 == other.mRadius1 && mRadius2 == other.mRadius2 &&
|
||||
mStops == other.mStops && mMatrix.ExactlyEquals(other.mMatrix);
|
||||
return aOther.IsWeak()
|
||||
? *this == static_cast<const Weak&>(aOther)
|
||||
: *this == static_cast<const RadialGradientPatternT<>&>(aOther);
|
||||
}
|
||||
|
||||
Point mCenter1; //!< Center of the inner (focal) circle.
|
||||
Point mCenter2; //!< Center of the outer circle.
|
||||
Float mRadius1; //!< Radius of the inner (focal) circle.
|
||||
Float mRadius2; //!< Radius of the outer circle.
|
||||
RefPtr<GradientStops>
|
||||
mStops; /**< GradientStops object for this gradient, this
|
||||
should match the backend type of the draw target
|
||||
this pattern will be used with. */
|
||||
Point mCenter1; //!< Center of the inner (focal) circle.
|
||||
Point mCenter2; //!< Center of the outer circle.
|
||||
Float mRadius1; //!< Radius of the inner (focal) circle.
|
||||
Float mRadius2; //!< Radius of the outer circle.
|
||||
REF<GradientStops> mStops; /**< GradientStops object for this gradient, this
|
||||
should match the backend type of the draw
|
||||
target this pattern will be used with. */
|
||||
Matrix mMatrix; //!< A matrix that transforms the pattern into user space
|
||||
};
|
||||
|
||||
typedef RadialGradientPatternT<> RadialGradientPattern;
|
||||
|
||||
/**
|
||||
* This class is used for Conic Gradient Patterns, the gradient stops are
|
||||
* stored in a separate object and are backend dependent. This class itself
|
||||
* may be used on the stack.
|
||||
*/
|
||||
class ConicGradientPattern : public Pattern {
|
||||
template <template <typename> typename REF = RefPtr>
|
||||
class ConicGradientPatternT : public Pattern {
|
||||
typedef ConicGradientPatternT<ThreadSafeWeakPtr> Weak;
|
||||
|
||||
public:
|
||||
/// For constructor parameter description, see member data documentation.
|
||||
ConicGradientPattern(const Point& aCenter, Float aAngle, Float aStartOffset,
|
||||
Float aEndOffset, already_AddRefed<GradientStops> aStops,
|
||||
const Matrix& aMatrix = Matrix())
|
||||
ConicGradientPatternT(const Point& aCenter, Float aAngle, Float aStartOffset,
|
||||
Float aEndOffset, RefPtr<GradientStops> aStops,
|
||||
const Matrix& aMatrix = Matrix())
|
||||
: mCenter(aCenter),
|
||||
mAngle(aAngle),
|
||||
mStartOffset(aStartOffset),
|
||||
mEndOffset(aEndOffset),
|
||||
mStops(aStops),
|
||||
mStops(std::move(aStops)),
|
||||
mMatrix(aMatrix) {}
|
||||
|
||||
PatternType GetType() const override { return PatternType::CONIC_GRADIENT; }
|
||||
|
||||
Pattern* Clone() const override {
|
||||
return new ConicGradientPattern(mCenter, mAngle, mStartOffset, mEndOffset,
|
||||
do_AddRef(mStops), mMatrix);
|
||||
Pattern* CloneWeak() const override {
|
||||
return new Weak(mCenter, mAngle, mStartOffset, mEndOffset,
|
||||
do_AddRef(mStops), mMatrix);
|
||||
}
|
||||
|
||||
bool IsWeak() const override {
|
||||
return std::is_same<decltype(*this), Weak>::value;
|
||||
}
|
||||
|
||||
bool IsValid() const override { return IsRefValid(mStops); }
|
||||
|
||||
template <template <typename> typename T>
|
||||
bool operator==(const ConicGradientPatternT<T>& aOther) const {
|
||||
return mCenter == aOther.mCenter && mAngle == aOther.mAngle &&
|
||||
mStartOffset == aOther.mStartOffset &&
|
||||
mEndOffset == aOther.mEndOffset && mStops == aOther.mStops &&
|
||||
mMatrix.ExactlyEquals(aOther.mMatrix);
|
||||
}
|
||||
|
||||
bool operator==(const Pattern& aOther) const override {
|
||||
if (aOther.GetType() != PatternType::CONIC_GRADIENT) {
|
||||
return false;
|
||||
}
|
||||
const ConicGradientPattern& other =
|
||||
static_cast<const ConicGradientPattern&>(aOther);
|
||||
return mCenter == other.mCenter && mAngle == other.mAngle &&
|
||||
mStartOffset == other.mStartOffset &&
|
||||
mEndOffset == other.mEndOffset && mStops == other.mStops &&
|
||||
mMatrix.ExactlyEquals(other.mMatrix);
|
||||
return aOther.IsWeak()
|
||||
? *this == static_cast<const Weak&>(aOther)
|
||||
: *this == static_cast<const ConicGradientPatternT<>&>(aOther);
|
||||
}
|
||||
|
||||
Point mCenter; //!< Center of the gradient
|
||||
Float mAngle; //!< Start angle of gradient
|
||||
Float mStartOffset; // Offset of first stop
|
||||
Float mEndOffset; // Offset of last stop
|
||||
RefPtr<GradientStops>
|
||||
mStops; /**< GradientStops object for this gradient, this
|
||||
should match the backend type of the draw target
|
||||
this pattern will be used with. */
|
||||
Point mCenter; //!< Center of the gradient
|
||||
Float mAngle; //!< Start angle of gradient
|
||||
Float mStartOffset; // Offset of first stop
|
||||
Float mEndOffset; // Offset of last stop
|
||||
REF<GradientStops> mStops; /**< GradientStops object for this gradient, this
|
||||
should match the backend type of the draw
|
||||
target this pattern will be used with. */
|
||||
Matrix mMatrix; //!< A matrix that transforms the pattern into user space
|
||||
};
|
||||
|
||||
typedef ConicGradientPatternT<> ConicGradientPattern;
|
||||
|
||||
/**
|
||||
* This class is used for Surface Patterns, they wrap a surface and a
|
||||
* repetition mode for the surface. This may be used on the stack.
|
||||
*/
|
||||
class SurfacePattern : public Pattern {
|
||||
template <template <typename> typename REF = RefPtr>
|
||||
class SurfacePatternT : public Pattern {
|
||||
typedef SurfacePatternT<ThreadSafeWeakPtr> Weak;
|
||||
|
||||
public:
|
||||
/// For constructor parameter description, see member data documentation.
|
||||
SurfacePattern(SourceSurface* aSourceSurface, ExtendMode aExtendMode,
|
||||
const Matrix& aMatrix = Matrix(),
|
||||
SamplingFilter aSamplingFilter = SamplingFilter::GOOD,
|
||||
const IntRect& aSamplingRect = IntRect())
|
||||
: mSurface(aSourceSurface),
|
||||
SurfacePatternT(RefPtr<SourceSurface> aSourceSurface, ExtendMode aExtendMode,
|
||||
const Matrix& aMatrix = Matrix(),
|
||||
SamplingFilter aSamplingFilter = SamplingFilter::GOOD,
|
||||
const IntRect& aSamplingRect = IntRect())
|
||||
: mSurface(std::move(aSourceSurface)),
|
||||
mExtendMode(aExtendMode),
|
||||
mSamplingFilter(aSamplingFilter),
|
||||
mMatrix(aMatrix),
|
||||
|
@ -464,25 +535,37 @@ class SurfacePattern : public Pattern {
|
|||
|
||||
PatternType GetType() const override { return PatternType::SURFACE; }
|
||||
|
||||
Pattern* Clone() const override {
|
||||
return new SurfacePattern(mSurface, mExtendMode, mMatrix, mSamplingFilter,
|
||||
mSamplingRect);
|
||||
Pattern* CloneWeak() const override {
|
||||
return new Weak(do_AddRef(mSurface), mExtendMode, mMatrix, mSamplingFilter,
|
||||
mSamplingRect);
|
||||
}
|
||||
|
||||
bool IsWeak() const override {
|
||||
return std::is_same<decltype(*this), Weak>::value;
|
||||
}
|
||||
|
||||
bool IsValid() const override { return IsRefValid(mSurface); }
|
||||
|
||||
template <template <typename> typename T>
|
||||
bool operator==(const SurfacePatternT<T>& aOther) const {
|
||||
return mSurface == aOther.mSurface && mExtendMode == aOther.mExtendMode &&
|
||||
mSamplingFilter == aOther.mSamplingFilter &&
|
||||
mMatrix.ExactlyEquals(aOther.mMatrix) &&
|
||||
mSamplingRect.IsEqualEdges(aOther.mSamplingRect);
|
||||
}
|
||||
|
||||
bool operator==(const Pattern& aOther) const override {
|
||||
if (aOther.GetType() != PatternType::SURFACE) {
|
||||
return false;
|
||||
}
|
||||
const SurfacePattern& other = static_cast<const SurfacePattern&>(aOther);
|
||||
return mSurface == other.mSurface && mExtendMode == other.mExtendMode &&
|
||||
mSamplingFilter == other.mSamplingFilter &&
|
||||
mMatrix.ExactlyEquals(other.mMatrix) &&
|
||||
mSamplingRect.IsEqualEdges(other.mSamplingRect);
|
||||
return aOther.IsWeak()
|
||||
? *this == static_cast<const Weak&>(aOther)
|
||||
: *this == static_cast<const SurfacePatternT<>&>(aOther);
|
||||
}
|
||||
|
||||
RefPtr<SourceSurface> mSurface; //!< Surface to use for drawing
|
||||
ExtendMode mExtendMode; /**< This determines how the image is extended
|
||||
outside the bounds of the image */
|
||||
REF<SourceSurface> mSurface; //!< Surface to use for drawing
|
||||
ExtendMode mExtendMode; /**< This determines how the image is extended
|
||||
outside the bounds of the image */
|
||||
SamplingFilter
|
||||
mSamplingFilter; //!< Resampling filter for resampling the image.
|
||||
Matrix mMatrix; //!< Transforms the pattern into user space
|
||||
|
@ -492,6 +575,8 @@ class SurfacePattern : public Pattern {
|
|||
specified. */
|
||||
};
|
||||
|
||||
typedef SurfacePatternT<> SurfacePattern;
|
||||
|
||||
class StoredPattern;
|
||||
|
||||
static const int32_t kReasonableSurfaceSize = 8192;
|
||||
|
@ -506,7 +591,7 @@ static const int32_t kReasonableSurfaceSize = 8192;
|
|||
* used on random threads now. This will be fixed in the future. Eventually
|
||||
* all SourceSurface should be thread-safe.
|
||||
*/
|
||||
class SourceSurface : public external::AtomicRefCounted<SourceSurface> {
|
||||
class SourceSurface : public SupportsThreadSafeWeakPtr<SourceSurface> {
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurface)
|
||||
virtual ~SourceSurface() = default;
|
||||
|
|
|
@ -144,13 +144,15 @@ RefPtr<webgpu::WebGPUChild> CanvasManagerChild::GetWebGPUChild() {
|
|||
}
|
||||
|
||||
already_AddRefed<DataSourceSurface> CanvasManagerChild::GetSnapshot(
|
||||
uint32_t aManagerId, int32_t aProtocolId, bool aHasAlpha) {
|
||||
uint32_t aManagerId, int32_t aProtocolId,
|
||||
const layers::CompositableHandle& aHandle, SurfaceFormat aFormat,
|
||||
bool aPremultiply, bool aYFlip) {
|
||||
if (!CanSend()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
webgl::FrontBufferSnapshotIpc res;
|
||||
if (!SendGetSnapshot(aManagerId, aProtocolId, &res)) {
|
||||
if (!SendGetSnapshot(aManagerId, aProtocolId, aHandle, &res)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -178,7 +180,7 @@ already_AddRefed<DataSourceSurface> CanvasManagerChild::GetSnapshot(
|
|||
}
|
||||
|
||||
SurfaceFormat format =
|
||||
aHasAlpha ? SurfaceFormat::B8G8R8A8 : SurfaceFormat::B8G8R8X8;
|
||||
IsOpaque(aFormat) ? SurfaceFormat::B8G8R8X8 : SurfaceFormat::B8G8R8A8;
|
||||
RefPtr<DataSourceSurface> surface =
|
||||
Factory::CreateDataSourceSurfaceWithStride(size, format, stride.value(),
|
||||
/* aZero */ false);
|
||||
|
@ -192,21 +194,32 @@ already_AddRefed<DataSourceSurface> CanvasManagerChild::GetSnapshot(
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
// The buffer we read back from WebGL is R8G8B8A8, not premultiplied and has
|
||||
// its rows inverted. For the general case, we want surfaces represented as
|
||||
// premultiplied B8G8R8A8, with its rows ordered top to bottom. Given this
|
||||
// path is used for screenshots/SurfaceFromElement, that's the representation
|
||||
// we need.
|
||||
if (aHasAlpha) {
|
||||
if (!PremultiplyYFlipData(res.shmem->get<uint8_t>(), stride.value(),
|
||||
SurfaceFormat::R8G8B8A8, map.GetData(),
|
||||
map.GetStride(), format, size)) {
|
||||
// The buffer we may readback from the canvas could be R8G8B8A8, not
|
||||
// premultiplied, and/or has its rows iverted. For the general case, we want
|
||||
// surfaces represented as premultiplied B8G8R8A8, with its rows ordered top
|
||||
// to bottom. Given this path is used for screenshots/SurfaceFromElement,
|
||||
// that's the representation we need.
|
||||
if (aYFlip) {
|
||||
if (aPremultiply) {
|
||||
if (!PremultiplyYFlipData(res.shmem->get<uint8_t>(), stride.value(),
|
||||
aFormat, map.GetData(), map.GetStride(), format,
|
||||
size)) {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
if (!SwizzleYFlipData(res.shmem->get<uint8_t>(), stride.value(), aFormat,
|
||||
map.GetData(), map.GetStride(), format, size)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
} else if (aPremultiply) {
|
||||
if (!PremultiplyData(res.shmem->get<uint8_t>(), stride.value(), aFormat,
|
||||
map.GetData(), map.GetStride(), format, size)) {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
if (!SwizzleYFlipData(res.shmem->get<uint8_t>(), stride.value(),
|
||||
SurfaceFormat::R8G8B8X8, map.GetData(),
|
||||
map.GetStride(), format, size)) {
|
||||
if (!SwizzleData(res.shmem->get<uint8_t>(), stride.value(), aFormat,
|
||||
map.GetData(), map.GetStride(), format, size)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/gfx/PCanvasManagerChild.h"
|
||||
#include "mozilla/gfx/Types.h"
|
||||
#include "mozilla/ThreadLocal.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -29,9 +30,10 @@ class CanvasManagerChild final : public PCanvasManagerChild {
|
|||
|
||||
explicit CanvasManagerChild(uint32_t aId);
|
||||
uint32_t Id() const { return mId; }
|
||||
already_AddRefed<DataSourceSurface> GetSnapshot(uint32_t aManagerId,
|
||||
int32_t aProtocolId,
|
||||
bool aHasAlpha);
|
||||
already_AddRefed<DataSourceSurface> GetSnapshot(
|
||||
uint32_t aManagerId, int32_t aProtocolId,
|
||||
const layers::CompositableHandle& aHandle, SurfaceFormat aFormat,
|
||||
bool aPremultiply, bool aYFlip);
|
||||
void ActorDestroy(ActorDestroyReason aReason) override;
|
||||
|
||||
static CanvasManagerChild* Get();
|
||||
|
|
|
@ -111,7 +111,7 @@ mozilla::ipc::IPCResult CanvasManagerParent::RecvInitialize(
|
|||
|
||||
mozilla::ipc::IPCResult CanvasManagerParent::RecvGetSnapshot(
|
||||
const uint32_t& aManagerId, const int32_t& aProtocolId,
|
||||
webgl::FrontBufferSnapshotIpc* aResult) {
|
||||
const CompositableHandle& aHandle, webgl::FrontBufferSnapshotIpc* aResult) {
|
||||
if (!aManagerId) {
|
||||
return IPC_FAIL(this, "invalid id");
|
||||
}
|
||||
|
@ -129,16 +129,33 @@ mozilla::ipc::IPCResult CanvasManagerParent::RecvGetSnapshot(
|
|||
return IPC_FAIL(this, "invalid actor");
|
||||
}
|
||||
|
||||
if (actor->GetProtocolId() != ProtocolId::PWebGLMsgStart ||
|
||||
actor->GetSide() != mozilla::ipc::Side::ParentSide) {
|
||||
if (actor->GetSide() != mozilla::ipc::Side::ParentSide) {
|
||||
return IPC_FAIL(this, "unsupported actor");
|
||||
}
|
||||
|
||||
RefPtr<dom::WebGLParent> webgl = static_cast<dom::WebGLParent*>(actor);
|
||||
webgl::FrontBufferSnapshotIpc buffer;
|
||||
mozilla::ipc::IPCResult rv = webgl->GetFrontBufferSnapshot(&buffer, this);
|
||||
if (!rv) {
|
||||
return rv;
|
||||
switch (actor->GetProtocolId()) {
|
||||
case ProtocolId::PWebGLMsgStart: {
|
||||
RefPtr<dom::WebGLParent> webgl = static_cast<dom::WebGLParent*>(actor);
|
||||
mozilla::ipc::IPCResult rv = webgl->GetFrontBufferSnapshot(&buffer, this);
|
||||
if (!rv) {
|
||||
return rv;
|
||||
}
|
||||
} break;
|
||||
case ProtocolId::PWebGPUMsgStart: {
|
||||
RefPtr<webgpu::WebGPUParent> webgpu =
|
||||
static_cast<webgpu::WebGPUParent*>(actor);
|
||||
IntSize size;
|
||||
mozilla::ipc::IPCResult rv =
|
||||
webgpu->GetFrontBufferSnapshot(this, aHandle, buffer.shmem, size);
|
||||
if (!rv) {
|
||||
return rv;
|
||||
}
|
||||
buffer.surfSize.x = static_cast<uint32_t>(size.width);
|
||||
buffer.surfSize.y = static_cast<uint32_t>(size.height);
|
||||
} break;
|
||||
default:
|
||||
return IPC_FAIL(this, "unsupported protocol");
|
||||
}
|
||||
|
||||
*aResult = std::move(buffer);
|
||||
|
|
|
@ -30,6 +30,7 @@ class CanvasManagerParent final : public PCanvasManagerParent {
|
|||
mozilla::ipc::IPCResult RecvInitialize(const uint32_t& aId);
|
||||
mozilla::ipc::IPCResult RecvGetSnapshot(
|
||||
const uint32_t& aManagerId, const int32_t& aProtocolId,
|
||||
const CompositableHandle& aHandle,
|
||||
webgl::FrontBufferSnapshotIpc* aResult);
|
||||
|
||||
private:
|
||||
|
|
|
@ -42,9 +42,9 @@ bool GPUProcessHost::Launch(StringVector aExtraOpts) {
|
|||
MOZ_ASSERT(!mGPUChild);
|
||||
MOZ_ASSERT(!gfxPlatform::IsHeadless());
|
||||
|
||||
mPrefSerializer = MakeUnique<ipc::SharedPreferenceSerializer>(
|
||||
dom::ContentParent::ShouldSyncPreference);
|
||||
if (!mPrefSerializer->SerializeToSharedMemory()) {
|
||||
mPrefSerializer = MakeUnique<ipc::SharedPreferenceSerializer>();
|
||||
if (!mPrefSerializer->SerializeToSharedMemory(GeckoProcessType_GPU,
|
||||
/* remoteType */ ""_ns)) {
|
||||
return false;
|
||||
}
|
||||
mPrefSerializer->AddSharedPrefCmdLineArgs(*this, aExtraOpts);
|
||||
|
|
|
@ -163,13 +163,11 @@ void GPUProcessManager::OnPreferenceChange(const char16_t* aData) {
|
|||
// We know prefs are ASCII here.
|
||||
NS_LossyConvertUTF16toASCII strData(aData);
|
||||
|
||||
// A pref changed. If it is useful to do so, inform child processes.
|
||||
if (!dom::ContentParent::ShouldSyncPreference(strData.Data())) {
|
||||
return;
|
||||
}
|
||||
mozilla::dom::Pref pref(strData, /* isLocked */ false,
|
||||
/* isSanitized */ false, Nothing(), Nothing());
|
||||
|
||||
mozilla::dom::Pref pref(strData, /* isLocked */ false, Nothing(), Nothing());
|
||||
Preferences::GetPreference(&pref);
|
||||
Preferences::GetPreference(&pref, GeckoProcessType_GPU,
|
||||
/* remoteType */ ""_ns);
|
||||
if (!!mGPUChild) {
|
||||
MOZ_ASSERT(mQueuedPrefs.IsEmpty());
|
||||
mGPUChild->SendPreferenceUpdate(pref);
|
||||
|
|
|
@ -5,9 +5,11 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
include "mozilla/layers/LayersMessageUtils.h";
|
||||
include protocol PWebGL;
|
||||
include protocol PWebGPU;
|
||||
|
||||
using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
|
||||
using mozilla::webgl::FrontBufferSnapshotIpc from "mozilla/dom/WebGLIpdl.h";
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -39,7 +41,7 @@ parent:
|
|||
// intended to be used by the main thread in the content process to block
|
||||
// reading without having to block on the worker thread that owns the context
|
||||
// instance.
|
||||
sync GetSnapshot(uint32_t aManagerId, int32_t aProtocolId) returns (FrontBufferSnapshotIpc ret);
|
||||
sync GetSnapshot(uint32_t aManagerId, int32_t aProtocolId, CompositableHandle aHandle) returns (FrontBufferSnapshotIpc ret);
|
||||
};
|
||||
|
||||
} // gfx
|
||||
|
|
|
@ -1728,6 +1728,8 @@ bool gfxPlatformFontList::InitializeFamily(fontlist::Family* aFamily,
|
|||
gfxFontEntry* gfxPlatformFontList::FindFontForFamily(
|
||||
nsPresContext* aPresContext, const nsACString& aFamily,
|
||||
const gfxFontStyle* aStyle) {
|
||||
AutoLock lock(mLock);
|
||||
|
||||
nsAutoCString key;
|
||||
GenerateFontListKey(aFamily, key);
|
||||
|
||||
|
|
|
@ -331,7 +331,7 @@ class gfxPlatformFontList : public gfxFontInfoLoader {
|
|||
|
||||
gfxFontEntry* FindFontForFamily(nsPresContext* aPresContext,
|
||||
const nsACString& aFamily,
|
||||
const gfxFontStyle* aStyle) REQUIRES(mLock);
|
||||
const gfxFontStyle* aStyle);
|
||||
|
||||
mozilla::fontlist::FontList* SharedFontList() const {
|
||||
return mSharedFontList.get();
|
||||
|
|
|
@ -216,13 +216,11 @@ void VRProcessManager::OnPreferenceChange(const char16_t* aData) {
|
|||
// We know prefs are ASCII here.
|
||||
NS_LossyConvertUTF16toASCII strData(aData);
|
||||
|
||||
// A pref changed. If it is useful to do so, inform child processes.
|
||||
if (!dom::ContentParent::ShouldSyncPreference(strData.Data())) {
|
||||
return;
|
||||
}
|
||||
mozilla::dom::Pref pref(strData, /* isLocked */ false,
|
||||
/* isSanitized */ false, Nothing(), Nothing());
|
||||
|
||||
mozilla::dom::Pref pref(strData, /* isLocked */ false, Nothing(), Nothing());
|
||||
Preferences::GetPreference(&pref);
|
||||
Preferences::GetPreference(&pref, GeckoProcessType_VR,
|
||||
/* remoteType */ ""_ns);
|
||||
if (!!mVRChild) {
|
||||
MOZ_ASSERT(mQueuedPrefs.IsEmpty());
|
||||
mVRChild->SendPreferenceUpdate(pref);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "mozilla/ipc/ProcessUtils.h"
|
||||
#include "mozilla/ipc/ProtocolTypes.h"
|
||||
#include "mozilla/ipc/ProtocolUtils.h" // for IToplevelProtocol
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/StaticPrefs_dom.h"
|
||||
#include "mozilla/TimeStamp.h" // for TimeStamp
|
||||
#include "mozilla/Unused.h"
|
||||
|
@ -54,9 +55,9 @@ bool VRProcessParent::Launch() {
|
|||
std::vector<std::string> extraArgs;
|
||||
ProcessChild::AddPlatformBuildID(extraArgs);
|
||||
|
||||
mPrefSerializer = MakeUnique<ipc::SharedPreferenceSerializer>(
|
||||
dom::ContentParent::ShouldSyncPreference);
|
||||
if (!mPrefSerializer->SerializeToSharedMemory()) {
|
||||
mPrefSerializer = MakeUnique<ipc::SharedPreferenceSerializer>();
|
||||
if (!mPrefSerializer->SerializeToSharedMemory(GeckoProcessType_VR,
|
||||
/* remoteType */ ""_ns)) {
|
||||
return false;
|
||||
}
|
||||
mPrefSerializer->AddSharedPrefCmdLineArgs(*this, extraArgs);
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -21,13 +21,7 @@ opt-level = 2
|
|||
[profile.release.package.glsl]
|
||||
opt-level = 2
|
||||
|
||||
# Running wrench on android built with master cargo-apk results in a crash
|
||||
# due to a mismatched version of android_glue (a dependency of winit).
|
||||
# Override it to use a suitable version of android_glue.
|
||||
# See https://github.com/rust-windowing/android-rs-glue/issues/239.
|
||||
# This can be removed once a new version of android_glue is published to crates.io.
|
||||
[patch.crates-io]
|
||||
android_glue = { git = "https://github.com/rust-windowing/android-rs-glue.git", rev = "e3ac6edea5814e1faca0c31ea8fac6877cb929ea" }
|
||||
# this is a version that fixes some incompatibilites with newer rust/aarch64
|
||||
winit = { version = "0.19", git = "https://github.com/jrmuizel/winit", branch="wr" }
|
||||
fog = { path = "fog" }
|
||||
# use a patched version of glutin that works on android
|
||||
glutin = { version = "0.28", git = "https://github.com/jamienicol/glutin", branch="wr" }
|
||||
|
|
|
@ -58,10 +58,10 @@ app_units = "0.7"
|
|||
env_logger = "0.5"
|
||||
euclid = "0.22"
|
||||
gleam = "0.13"
|
||||
glutin = "0.21"
|
||||
glutin = "0.28"
|
||||
rayon = "1"
|
||||
webrender = { path = "../webrender" }
|
||||
winit = "0.19"
|
||||
winit = "0.26"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
core-foundation = "0.7"
|
||||
|
|
|
@ -54,25 +54,26 @@ impl Example for App {
|
|||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
event: winit::WindowEvent,
|
||||
event: winit::event::WindowEvent,
|
||||
_window: &winit::window::Window,
|
||||
_api: &mut RenderApi,
|
||||
_document_id: DocumentId
|
||||
_document_id: DocumentId,
|
||||
) -> bool {
|
||||
match event {
|
||||
winit::WindowEvent::KeyboardInput {
|
||||
input: winit::KeyboardInput {
|
||||
state: winit::ElementState::Pressed,
|
||||
winit::event::WindowEvent::KeyboardInput {
|
||||
input: winit::event::KeyboardInput {
|
||||
state: winit::event::ElementState::Pressed,
|
||||
virtual_keycode: Some(key),
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
match key {
|
||||
winit::VirtualKeyCode::Right => {
|
||||
winit::event::VirtualKeyCode::Right => {
|
||||
self.rect_count += 1;
|
||||
println!("rects = {}", self.rect_count);
|
||||
}
|
||||
winit::VirtualKeyCode::Left => {
|
||||
winit::event::VirtualKeyCode::Left => {
|
||||
self.rect_count = cmp::max(self.rect_count, 1) - 1;
|
||||
println!("rects = {}", self.rect_count);
|
||||
}
|
||||
|
|
|
@ -141,24 +141,30 @@ impl Example for App {
|
|||
self.add_rounded_rect(bounds, ColorF::new(0.0, 0.0, 1.0, 0.5), builder, pipeline_id, key2, None);
|
||||
}
|
||||
|
||||
fn on_event(&mut self, win_event: winit::WindowEvent, api: &mut RenderApi, document_id: DocumentId) -> bool {
|
||||
fn on_event(
|
||||
&mut self,
|
||||
win_event: winit::event::WindowEvent,
|
||||
_window: &winit::window::Window,
|
||||
api: &mut RenderApi,
|
||||
document_id: DocumentId
|
||||
) -> bool {
|
||||
let mut rebuild_display_list = false;
|
||||
|
||||
match win_event {
|
||||
winit::WindowEvent::KeyboardInput {
|
||||
input: winit::KeyboardInput {
|
||||
state: winit::ElementState::Pressed,
|
||||
winit::event::WindowEvent::KeyboardInput {
|
||||
input: winit::event::KeyboardInput {
|
||||
state: winit::event::ElementState::Pressed,
|
||||
virtual_keycode: Some(key),
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
let (delta_angle, delta_opacity) = match key {
|
||||
winit::VirtualKeyCode::Down => (0.0, -0.1),
|
||||
winit::VirtualKeyCode::Up => (0.0, 0.1),
|
||||
winit::VirtualKeyCode::Right => (1.0, 0.0),
|
||||
winit::VirtualKeyCode::Left => (-1.0, 0.0),
|
||||
winit::VirtualKeyCode::R => {
|
||||
winit::event::VirtualKeyCode::Down => (0.0, -0.1),
|
||||
winit::event::VirtualKeyCode::Up => (0.0, 0.1),
|
||||
winit::event::VirtualKeyCode::Right => (1.0, 0.0),
|
||||
winit::event::VirtualKeyCode::Left => (-1.0, 0.0),
|
||||
winit::event::VirtualKeyCode::R => {
|
||||
rebuild_display_list = true;
|
||||
(0.0, 0.0)
|
||||
}
|
||||
|
|
|
@ -8,17 +8,18 @@ use std::env;
|
|||
use std::path::PathBuf;
|
||||
use webrender;
|
||||
use winit;
|
||||
use winit::platform::run_return::EventLoopExtRunReturn;
|
||||
use webrender::{DebugFlags, ShaderPrecacheFlags};
|
||||
use webrender::api::*;
|
||||
use webrender::render_api::*;
|
||||
use webrender::api::units::*;
|
||||
|
||||
struct Notifier {
|
||||
events_proxy: winit::EventsLoopProxy,
|
||||
events_proxy: winit::event_loop::EventLoopProxy<()>,
|
||||
}
|
||||
|
||||
impl Notifier {
|
||||
fn new(events_proxy: winit::EventsLoopProxy) -> Notifier {
|
||||
fn new(events_proxy: winit::event_loop::EventLoopProxy<()>) -> Notifier {
|
||||
Notifier { events_proxy }
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +33,7 @@ impl RenderNotifier for Notifier {
|
|||
|
||||
fn wake_up(&self, _composite_needed: bool) {
|
||||
#[cfg(not(target_os = "android"))]
|
||||
let _ = self.events_proxy.wakeup();
|
||||
let _ = self.events_proxy.send_event(());
|
||||
}
|
||||
|
||||
fn new_frame_ready(&self,
|
||||
|
@ -83,7 +84,8 @@ pub trait Example {
|
|||
);
|
||||
fn on_event(
|
||||
&mut self,
|
||||
_: winit::WindowEvent,
|
||||
_: winit::event::WindowEvent,
|
||||
_: &winit::window::Window,
|
||||
_: &mut RenderApi,
|
||||
_: DocumentId,
|
||||
) -> bool {
|
||||
|
@ -123,11 +125,10 @@ pub fn main_wrapper<E: Example>(
|
|||
None
|
||||
};
|
||||
|
||||
let mut events_loop = winit::EventsLoop::new();
|
||||
let window_builder = winit::WindowBuilder::new()
|
||||
let mut events_loop = winit::event_loop::EventLoop::new();
|
||||
let window_builder = winit::window::WindowBuilder::new()
|
||||
.with_title(E::TITLE)
|
||||
.with_multitouch()
|
||||
.with_dimensions(winit::dpi::LogicalSize::new(E::WIDTH as f64, E::HEIGHT as f64));
|
||||
.with_inner_size(winit::dpi::LogicalSize::new(E::WIDTH as f64, E::HEIGHT as f64));
|
||||
let windowed_context = glutin::ContextBuilder::new()
|
||||
.with_gl(glutin::GlRequest::GlThenGles {
|
||||
opengl_version: (3, 2),
|
||||
|
@ -154,7 +155,7 @@ pub fn main_wrapper<E: Example>(
|
|||
|
||||
println!("OpenGL version {}", gl.get_string(gl::VERSION));
|
||||
println!("Shader resource path: {:?}", res_path);
|
||||
let device_pixel_ratio = windowed_context.window().get_hidpi_factor() as f32;
|
||||
let device_pixel_ratio = windowed_context.window().scale_factor() as f32;
|
||||
println!("Device pixel ratio: {}", device_pixel_ratio);
|
||||
|
||||
println!("Loading shaders...");
|
||||
|
@ -171,9 +172,7 @@ pub fn main_wrapper<E: Example>(
|
|||
let device_size = {
|
||||
let size = windowed_context
|
||||
.window()
|
||||
.get_inner_size()
|
||||
.unwrap()
|
||||
.to_physical(device_pixel_ratio as f64);
|
||||
.inner_size();
|
||||
DeviceIntSize::new(size.width as i32, size.height as i32)
|
||||
};
|
||||
let notifier = Box::new(Notifier::new(events_loop.create_proxy()));
|
||||
|
@ -218,48 +217,55 @@ pub fn main_wrapper<E: Example>(
|
|||
api.send_transaction(document_id, txn);
|
||||
|
||||
println!("Entering event loop");
|
||||
events_loop.run_forever(|global_event| {
|
||||
events_loop.run_return(|global_event, _elwt, control_flow| {
|
||||
let mut txn = Transaction::new();
|
||||
let mut custom_event = true;
|
||||
|
||||
let old_flags = debug_flags;
|
||||
let win_event = match global_event {
|
||||
winit::Event::WindowEvent { event, .. } => event,
|
||||
_ => return winit::ControlFlow::Continue,
|
||||
winit::event::Event::WindowEvent { event, .. } => event,
|
||||
_ => return,
|
||||
};
|
||||
match win_event {
|
||||
winit::WindowEvent::CloseRequested => return winit::ControlFlow::Break,
|
||||
winit::WindowEvent::AxisMotion { .. } |
|
||||
winit::WindowEvent::CursorMoved { .. } => {
|
||||
winit::event::WindowEvent::CloseRequested => {
|
||||
*control_flow = winit::event_loop::ControlFlow::Exit;
|
||||
return;
|
||||
}
|
||||
winit::event::WindowEvent::AxisMotion { .. } |
|
||||
winit::event::WindowEvent::CursorMoved { .. } => {
|
||||
custom_event = example.on_event(
|
||||
win_event,
|
||||
&mut api,
|
||||
document_id,
|
||||
);
|
||||
win_event,
|
||||
windowed_context.window(),
|
||||
&mut api,
|
||||
document_id,
|
||||
);
|
||||
// skip high-frequency events from triggering a frame draw.
|
||||
if !custom_event {
|
||||
return winit::ControlFlow::Continue;
|
||||
return;
|
||||
}
|
||||
},
|
||||
winit::WindowEvent::KeyboardInput {
|
||||
input: winit::KeyboardInput {
|
||||
state: winit::ElementState::Pressed,
|
||||
winit::event::WindowEvent::KeyboardInput {
|
||||
input: winit::event::KeyboardInput {
|
||||
state: winit::event::ElementState::Pressed,
|
||||
virtual_keycode: Some(key),
|
||||
..
|
||||
},
|
||||
..
|
||||
} => match key {
|
||||
winit::VirtualKeyCode::Escape => return winit::ControlFlow::Break,
|
||||
winit::VirtualKeyCode::P => debug_flags.toggle(DebugFlags::PROFILER_DBG),
|
||||
winit::VirtualKeyCode::O => debug_flags.toggle(DebugFlags::RENDER_TARGET_DBG),
|
||||
winit::VirtualKeyCode::I => debug_flags.toggle(DebugFlags::TEXTURE_CACHE_DBG),
|
||||
winit::VirtualKeyCode::T => debug_flags.toggle(DebugFlags::PICTURE_CACHING_DBG),
|
||||
winit::VirtualKeyCode::Q => debug_flags.toggle(
|
||||
winit::event::VirtualKeyCode::Escape => {
|
||||
*control_flow = winit::event_loop::ControlFlow::Exit;
|
||||
return;
|
||||
}
|
||||
winit::event::VirtualKeyCode::P => debug_flags.toggle(DebugFlags::PROFILER_DBG),
|
||||
winit::event::VirtualKeyCode::O => debug_flags.toggle(DebugFlags::RENDER_TARGET_DBG),
|
||||
winit::event::VirtualKeyCode::I => debug_flags.toggle(DebugFlags::TEXTURE_CACHE_DBG),
|
||||
winit::event::VirtualKeyCode::T => debug_flags.toggle(DebugFlags::PICTURE_CACHING_DBG),
|
||||
winit::event::VirtualKeyCode::Q => debug_flags.toggle(
|
||||
DebugFlags::GPU_TIME_QUERIES | DebugFlags::GPU_SAMPLE_QUERIES
|
||||
),
|
||||
winit::VirtualKeyCode::G => debug_flags.toggle(DebugFlags::GPU_CACHE_DBG),
|
||||
winit::VirtualKeyCode::M => api.notify_memory_pressure(),
|
||||
winit::VirtualKeyCode::C => {
|
||||
winit::event::VirtualKeyCode::G => debug_flags.toggle(DebugFlags::GPU_CACHE_DBG),
|
||||
winit::event::VirtualKeyCode::M => api.notify_memory_pressure(),
|
||||
winit::event::VirtualKeyCode::C => {
|
||||
let path: PathBuf = "../captures/example".into();
|
||||
//TODO: switch between SCENE/FRAME capture types
|
||||
// based on "shift" modifier, when `glutin` is updated.
|
||||
|
@ -269,6 +275,7 @@ pub fn main_wrapper<E: Example>(
|
|||
_ => {
|
||||
custom_event = example.on_event(
|
||||
win_event,
|
||||
windowed_context.window(),
|
||||
&mut api,
|
||||
document_id,
|
||||
)
|
||||
|
@ -276,6 +283,7 @@ pub fn main_wrapper<E: Example>(
|
|||
},
|
||||
other => custom_event = example.on_event(
|
||||
other,
|
||||
windowed_context.window(),
|
||||
&mut api,
|
||||
document_id,
|
||||
),
|
||||
|
@ -313,7 +321,7 @@ pub fn main_wrapper<E: Example>(
|
|||
example.draw_custom(&*gl);
|
||||
windowed_context.swap_buffers().ok();
|
||||
|
||||
winit::ControlFlow::Continue
|
||||
*control_flow = winit::event_loop::ControlFlow::Wait;
|
||||
});
|
||||
|
||||
renderer.deinit();
|
||||
|
|
|
@ -77,12 +77,18 @@ impl Example for App {
|
|||
builder.pop_stacking_context();
|
||||
}
|
||||
|
||||
fn on_event(&mut self, event: winit::WindowEvent, api: &mut RenderApi, document_id: DocumentId) -> bool {
|
||||
fn on_event(
|
||||
&mut self,
|
||||
event: winit::event::WindowEvent,
|
||||
_window: &winit::window::Window,
|
||||
api: &mut RenderApi,
|
||||
document_id: DocumentId,
|
||||
) -> bool {
|
||||
match event {
|
||||
winit::WindowEvent::KeyboardInput {
|
||||
input: winit::KeyboardInput {
|
||||
state: winit::ElementState::Pressed,
|
||||
virtual_keycode: Some(winit::VirtualKeyCode::Space),
|
||||
winit::event::WindowEvent::KeyboardInput {
|
||||
input: winit::event::KeyboardInput {
|
||||
state: winit::event::ElementState::Pressed,
|
||||
virtual_keycode: Some(winit::event::VirtualKeyCode::Space),
|
||||
..
|
||||
},
|
||||
..
|
||||
|
|
|
@ -17,13 +17,14 @@ use webrender::api::units::*;
|
|||
use webrender::render_api::*;
|
||||
use webrender::DebugFlags;
|
||||
use winit::dpi::LogicalSize;
|
||||
use winit::platform::run_return::EventLoopExtRunReturn;
|
||||
|
||||
struct Notifier {
|
||||
events_proxy: winit::EventsLoopProxy,
|
||||
events_proxy: winit::event_loop::EventLoopProxy<()>,
|
||||
}
|
||||
|
||||
impl Notifier {
|
||||
fn new(events_proxy: winit::EventsLoopProxy) -> Notifier {
|
||||
fn new(events_proxy: winit::event_loop::EventLoopProxy<()>) -> Notifier {
|
||||
Notifier { events_proxy }
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +38,7 @@ impl RenderNotifier for Notifier {
|
|||
|
||||
fn wake_up(&self, _composite_needed: bool) {
|
||||
#[cfg(not(target_os = "android"))]
|
||||
let _ = self.events_proxy.wakeup();
|
||||
let _ = self.events_proxy.send_event(());
|
||||
}
|
||||
|
||||
fn new_frame_ready(&self,
|
||||
|
@ -50,7 +51,7 @@ impl RenderNotifier for Notifier {
|
|||
}
|
||||
|
||||
struct Window {
|
||||
events_loop: winit::EventsLoop, //TODO: share events loop?
|
||||
events_loop: winit::event_loop::EventLoop<()>, //TODO: share events loop?
|
||||
context: Option<glutin::WindowedContext<NotCurrent>>,
|
||||
renderer: webrender::Renderer,
|
||||
name: &'static str,
|
||||
|
@ -63,11 +64,10 @@ struct Window {
|
|||
|
||||
impl Window {
|
||||
fn new(name: &'static str, clear_color: ColorF) -> Self {
|
||||
let events_loop = winit::EventsLoop::new();
|
||||
let window_builder = winit::WindowBuilder::new()
|
||||
let events_loop = winit::event_loop::EventLoop::new();
|
||||
let window_builder = winit::window::WindowBuilder::new()
|
||||
.with_title(name)
|
||||
.with_multitouch()
|
||||
.with_dimensions(LogicalSize::new(800., 600.));
|
||||
.with_inner_size(LogicalSize::new(800. as f64, 600. as f64));
|
||||
let context = glutin::ContextBuilder::new()
|
||||
.with_gl(glutin::GlRequest::GlThenGles {
|
||||
opengl_version: (3, 2),
|
||||
|
@ -88,8 +88,6 @@ impl Window {
|
|||
glutin::Api::WebGl => unimplemented!(),
|
||||
};
|
||||
|
||||
let device_pixel_ratio = context.window().get_hidpi_factor() as f32;
|
||||
|
||||
let opts = webrender::RendererOptions {
|
||||
clear_color,
|
||||
..webrender::RendererOptions::default()
|
||||
|
@ -98,9 +96,7 @@ impl Window {
|
|||
let device_size = {
|
||||
let size = context
|
||||
.window()
|
||||
.get_inner_size()
|
||||
.unwrap()
|
||||
.to_physical(device_pixel_ratio as f64);
|
||||
.inner_size();
|
||||
DeviceIntSize::new(size.width as i32, size.height as i32)
|
||||
};
|
||||
let notifier = Box::new(Notifier::new(events_loop.create_proxy()));
|
||||
|
@ -140,45 +136,46 @@ impl Window {
|
|||
let renderer = &mut self.renderer;
|
||||
let api = &mut self.api;
|
||||
|
||||
self.events_loop.poll_events(|global_event| match global_event {
|
||||
winit::Event::WindowEvent { event, .. } => match event {
|
||||
winit::WindowEvent::CloseRequested |
|
||||
winit::WindowEvent::KeyboardInput {
|
||||
input: winit::KeyboardInput {
|
||||
virtual_keycode: Some(winit::VirtualKeyCode::Escape),
|
||||
self.events_loop.run_return(|global_event, _elwt, control_flow| {
|
||||
*control_flow = winit::event_loop::ControlFlow::Exit;
|
||||
match global_event {
|
||||
winit::event::Event::WindowEvent { event, .. } => match event {
|
||||
winit::event::WindowEvent::CloseRequested |
|
||||
winit::event::WindowEvent::KeyboardInput {
|
||||
input: winit::event::KeyboardInput {
|
||||
virtual_keycode: Some(winit::event::VirtualKeyCode::Escape),
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
do_exit = true
|
||||
}
|
||||
winit::WindowEvent::KeyboardInput {
|
||||
input: winit::KeyboardInput {
|
||||
state: winit::ElementState::Pressed,
|
||||
virtual_keycode: Some(winit::VirtualKeyCode::P),
|
||||
} => {
|
||||
do_exit = true
|
||||
}
|
||||
winit::event::WindowEvent::KeyboardInput {
|
||||
input: winit::event::KeyboardInput {
|
||||
state: winit::event::ElementState::Pressed,
|
||||
virtual_keycode: Some(winit::event::VirtualKeyCode::P),
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
println!("set flags {}", my_name);
|
||||
api.send_debug_cmd(DebugCommand::SetFlags(DebugFlags::PROFILER_DBG))
|
||||
} => {
|
||||
println!("set flags {}", my_name);
|
||||
api.send_debug_cmd(DebugCommand::SetFlags(DebugFlags::PROFILER_DBG))
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
_ => {}
|
||||
});
|
||||
if do_exit {
|
||||
return true
|
||||
}
|
||||
|
||||
let context = unsafe { self.context.take().unwrap().make_current().unwrap() };
|
||||
let device_pixel_ratio = context.window().get_hidpi_factor() as f32;
|
||||
let device_pixel_ratio = context.window().scale_factor() as f32;
|
||||
let device_size = {
|
||||
let size = context
|
||||
.window()
|
||||
.get_inner_size()
|
||||
.unwrap()
|
||||
.to_physical(device_pixel_ratio as f64);
|
||||
.inner_size();
|
||||
DeviceIntSize::new(size.width as i32, size.height as i32)
|
||||
};
|
||||
let layout_size = device_size.to_f32() / euclid::Scale::new(device_pixel_ratio);
|
||||
|
|
|
@ -165,22 +165,28 @@ impl Example for App {
|
|||
builder.pop_stacking_context();
|
||||
}
|
||||
|
||||
fn on_event(&mut self, event: winit::WindowEvent, api: &mut RenderApi, document_id: DocumentId) -> bool {
|
||||
fn on_event(
|
||||
&mut self,
|
||||
event: winit::event::WindowEvent,
|
||||
window: &winit::window::Window,
|
||||
api: &mut RenderApi,
|
||||
document_id: DocumentId,
|
||||
) -> bool {
|
||||
let mut txn = Transaction::new();
|
||||
match event {
|
||||
winit::WindowEvent::KeyboardInput {
|
||||
input: winit::KeyboardInput {
|
||||
state: winit::ElementState::Pressed,
|
||||
winit::event::WindowEvent::KeyboardInput {
|
||||
input: winit::event::KeyboardInput {
|
||||
state: winit::event::ElementState::Pressed,
|
||||
virtual_keycode: Some(key),
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
let offset = match key {
|
||||
winit::VirtualKeyCode::Down => Some(LayoutVector2D::new(0.0, -10.0)),
|
||||
winit::VirtualKeyCode::Up => Some(LayoutVector2D::new(0.0, 10.0)),
|
||||
winit::VirtualKeyCode::Right => Some(LayoutVector2D::new(-10.0, 0.0)),
|
||||
winit::VirtualKeyCode::Left => Some(LayoutVector2D::new(10.0, 0.0)),
|
||||
winit::event::VirtualKeyCode::Down => Some(LayoutVector2D::new(0.0, -10.0)),
|
||||
winit::event::VirtualKeyCode::Up => Some(LayoutVector2D::new(0.0, 10.0)),
|
||||
winit::event::VirtualKeyCode::Right => Some(LayoutVector2D::new(-10.0, 0.0)),
|
||||
winit::event::VirtualKeyCode::Left => Some(LayoutVector2D::new(10.0, 0.0)),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
|
@ -197,14 +203,15 @@ impl Example for App {
|
|||
txn.generate_frame(0, RenderReasons::empty());
|
||||
}
|
||||
}
|
||||
winit::WindowEvent::CursorMoved { position: LogicalPosition { x, y }, .. } => {
|
||||
self.cursor_position = WorldPoint::new(x as f32, y as f32);
|
||||
winit::event::WindowEvent::CursorMoved { position, .. } => {
|
||||
let pos: LogicalPosition<f32> = position.to_logical(window.scale_factor());
|
||||
self.cursor_position = WorldPoint::new(pos.x, pos.y);
|
||||
}
|
||||
winit::WindowEvent::MouseWheel { delta, .. } => {
|
||||
winit::event::WindowEvent::MouseWheel { delta, .. } => {
|
||||
const LINE_HEIGHT: f32 = 38.0;
|
||||
let (dx, dy) = match delta {
|
||||
winit::MouseScrollDelta::LineDelta(dx, dy) => (dx, dy * LINE_HEIGHT),
|
||||
winit::MouseScrollDelta::PixelDelta(pos) => (pos.x as f32, pos.y as f32),
|
||||
winit::event::MouseScrollDelta::LineDelta(dx, dy) => (dx, dy * LINE_HEIGHT),
|
||||
winit::event::MouseScrollDelta::PixelDelta(pos) => (pos.x as f32, pos.y as f32),
|
||||
};
|
||||
|
||||
self.scroll_offset += LayoutVector2D::new(dx, dy);
|
||||
|
@ -219,7 +226,7 @@ impl Example for App {
|
|||
|
||||
txn.generate_frame(0, RenderReasons::empty());
|
||||
}
|
||||
winit::WindowEvent::MouseInput { .. } => {
|
||||
winit::event::WindowEvent::MouseInput { .. } => {
|
||||
let results = api.hit_test(
|
||||
document_id,
|
||||
self.cursor_position,
|
||||
|
|
|
@ -192,14 +192,15 @@ impl Example for App {
|
|||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
event: winit::WindowEvent,
|
||||
event: winit::event::WindowEvent,
|
||||
_window: &winit::window::Window,
|
||||
api: &mut RenderApi,
|
||||
document_id: DocumentId,
|
||||
) -> bool {
|
||||
match event {
|
||||
winit::WindowEvent::KeyboardInput {
|
||||
input: winit::KeyboardInput {
|
||||
state: winit::ElementState::Pressed,
|
||||
winit::event::WindowEvent::KeyboardInput {
|
||||
input: winit::event::KeyboardInput {
|
||||
state: winit::event::ElementState::Pressed,
|
||||
virtual_keycode: Some(key),
|
||||
..
|
||||
},
|
||||
|
@ -208,7 +209,7 @@ impl Example for App {
|
|||
let mut txn = Transaction::new();
|
||||
|
||||
match key {
|
||||
winit::VirtualKeyCode::S => {
|
||||
winit::event::VirtualKeyCode::S => {
|
||||
self.stress_keys.clear();
|
||||
|
||||
for _ in 0 .. 16 {
|
||||
|
@ -235,10 +236,10 @@ impl Example for App {
|
|||
}
|
||||
}
|
||||
}
|
||||
winit::VirtualKeyCode::D => if let Some(image_key) = self.image_key.take() {
|
||||
winit::event::VirtualKeyCode::D => if let Some(image_key) = self.image_key.take() {
|
||||
txn.delete_image(image_key);
|
||||
},
|
||||
winit::VirtualKeyCode::U => if let Some(image_key) = self.image_key {
|
||||
winit::event::VirtualKeyCode::U => if let Some(image_key) = self.image_key {
|
||||
let size = 128;
|
||||
self.image_generator.generate_image(size);
|
||||
|
||||
|
@ -249,7 +250,7 @@ impl Example for App {
|
|||
&DirtyRect::All,
|
||||
);
|
||||
},
|
||||
winit::VirtualKeyCode::E => {
|
||||
winit::event::VirtualKeyCode::E => {
|
||||
if let Some(image_key) = self.image_key.take() {
|
||||
txn.delete_image(image_key);
|
||||
}
|
||||
|
@ -272,7 +273,7 @@ impl Example for App {
|
|||
|
||||
self.image_key = Some(image_key);
|
||||
}
|
||||
winit::VirtualKeyCode::R => {
|
||||
winit::event::VirtualKeyCode::R => {
|
||||
if let Some(image_key) = self.image_key.take() {
|
||||
txn.delete_image(image_key);
|
||||
}
|
||||
|
|
|
@ -189,7 +189,8 @@ impl Example for App {
|
|||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
_event: winit::WindowEvent,
|
||||
_event: winit::event::WindowEvent,
|
||||
_window: &winit::window::Window,
|
||||
_api: &mut RenderApi,
|
||||
_document_id: DocumentId,
|
||||
) -> bool {
|
||||
|
|
|
@ -11,17 +11,15 @@ packages = [
|
|||
"core-foundation-sys",
|
||||
"core-graphics",
|
||||
"gl_generator",
|
||||
"gleam",
|
||||
# glsl requires 5.1, and xcursor (required by winit) requires 7.1.
|
||||
# when a version of glsl depending on 7.1 is published we can update.
|
||||
"nom",
|
||||
"rand_core",
|
||||
# https://github.com/trimental/andrew/issues/5
|
||||
"rusttype",
|
||||
# https://bugzilla.mozilla.org/show_bug.cgi?id=1615148
|
||||
"smallvec",
|
||||
# Can be fixed by updating clap dependency - see bug 1765326
|
||||
"strsim",
|
||||
"yaml-rust",
|
||||
# These are tracked in bug 1587468, see there for pending work.
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
# Can be fixed by removing time dependency - see bug 1765324
|
||||
"wasi",
|
||||
]
|
||||
|
||||
# Files that are ignored for all tidy and lint checks.
|
||||
|
|
|
@ -6,11 +6,16 @@ build = "build.rs"
|
|||
license = "MPL-2.0"
|
||||
edition = "2018"
|
||||
|
||||
# Required by cargo-apk to build for Android
|
||||
[lib]
|
||||
crate-type = ["lib", "cdylib"]
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.13"
|
||||
env_logger = { version = "0.5", optional = true }
|
||||
gleam = "0.13"
|
||||
glutin = "0.21"
|
||||
glutin = "0.28"
|
||||
clap = { version = "2", features = ["yaml"] }
|
||||
glsl = "4.0"
|
||||
log = "0.4"
|
||||
|
@ -22,7 +27,7 @@ crossbeam = "0.2"
|
|||
osmesa-sys = { version = "0.1.2", optional = true }
|
||||
osmesa-src = { version = "0.2", git = "https://github.com/servo/osmesa-src", optional = true }
|
||||
webrender = { path = "../webrender", features = ["capture", "replay", "png", "profiler", "no_static_freetype", "leak_checks"] }
|
||||
winit = "0.19"
|
||||
winit = "0.26"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
semver = "0.9.0"
|
||||
swgl = { path = "../swgl", optional = true }
|
||||
|
@ -46,25 +51,23 @@ software = [ "swgl" ]
|
|||
dwrote = "0.11"
|
||||
mozangle = { version = "0.3.2", features = ["egl"] }
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
ndk-glue = "0.5"
|
||||
|
||||
[target.'cfg(all(unix, not(target_os = "android")))'.dependencies]
|
||||
font-loader = "0.11"
|
||||
|
||||
# Configuration information used when building wrench as an APK.
|
||||
[package.metadata.android]
|
||||
package_name = "org.mozilla.wrench"
|
||||
label = "Wrench"
|
||||
# keep it in sync with android-sdk-version in android-sdk.configure
|
||||
android_version = 31
|
||||
target_sdk_version = 18
|
||||
min_sdk_version = 18
|
||||
fullscreen = true
|
||||
package = "org.mozilla.wrench"
|
||||
build_targets = [ "armv7-linux-androideabi", "i686-linux-android" ]
|
||||
opengles_version_major = 3
|
||||
opengles_version_minor = 0
|
||||
[package.metadata.android.application_attributes]
|
||||
"android:hardwareAccelerated" = "true"
|
||||
[package.metadata.android.activity_attributes]
|
||||
"android:screenOrientation" = "unspecified"
|
||||
"android:uiOptions" = "none"
|
||||
[[package.metadata.android.permission]]
|
||||
name = "android.permission.READ_EXTERNAL_STORAGE"
|
||||
|
||||
[package.metadata.android.sdk]
|
||||
# keep it in sync with android-sdk-version in android-sdk.configure
|
||||
target_sdk_version = 31
|
||||
min_sdk_version = 18
|
||||
|
||||
[package.metadata.android.application]
|
||||
label = "Wrench"
|
||||
|
|
|
@ -10,10 +10,11 @@ Follow the steps at https://github.com/rust-windowing/android-rs-glue#setting-up
|
|||
- Install both the i686-linux-android and armv7-linux-androideabi rust
|
||||
targets, as the APK will include native libraries with both architectures.
|
||||
|
||||
- Don't install currently published version of cargo-apk as it doesn't work with the
|
||||
version of winit and glutin we are using.
|
||||
Instead, install the git master version of our fork like so:
|
||||
cargo install --git https://github.com/jamienicol/android-rs-glue cargo-apk
|
||||
- Don't install currently published version of cargo-apk, as it is missing the following
|
||||
required patch: https://github.com/rust-windowing/android-ndk-rs/pull/236
|
||||
|
||||
Instead, install the git master version like so:
|
||||
cargo install --git https://github.com/rust-windowing/android-ndk-rs cargo-apk
|
||||
|
||||
- Consider adding ~/.mozbuild/android-sdk-linux/platform-tools to your path, for the adb commands below.
|
||||
|
||||
|
@ -22,23 +23,23 @@ Compiling and running:
|
|||
|
||||
Compile wrench:
|
||||
cd wrench
|
||||
export ANDROID_HOME=$HOME/.mozbuild/android-sdk-linux # exact path may vary
|
||||
export NDK_HOME=$HOME/.mozbuild/android-ndk-r17b # exact path may vary
|
||||
export ANDROID_SDK_ROOT=$HOME/.mozbuild/android-sdk-linux # exact path may vary
|
||||
export ANDROID_NDK_ROOT=$HOME/.mozbuild/android-ndk-r21d # exact path may vary
|
||||
cargo apk build
|
||||
|
||||
Install the APK:
|
||||
adb install -r ../target/android-artifacts/debug/apk/wrench.apk
|
||||
adb install -r ../target/debug/apk/wrench.apk
|
||||
|
||||
Set command line arguments and env vars for wrench:
|
||||
adb shell
|
||||
mkdir /sdcard/wrench
|
||||
echo "load reftests/aa/rounded-rects.yaml" >/sdcard/wrench/args
|
||||
echo "env: WRENCH_REFTEST_CONDITION_EMULATOR=1" >>/sdcard/wrench/args # If you're using the emulator
|
||||
echo "env: WRENCH_REFTEST_CONDITION_DEVICE=1" >>/sdcard/wrench/args # If you're using a device
|
||||
mkdir /storage/emulated/0/Android/data/org.mozilla.wrench/files/wrench
|
||||
echo "load reftests/aa/rounded-rects.yaml" >/storage/emulated/0/Android/data/org.mozilla.wrench/files/wrench/args
|
||||
echo "env: WRENCH_REFTEST_CONDITION_EMULATOR=1" >>/storage/emulated/0/Android/data/org.mozilla.wrench/files/wrench/args # If you're using the emulator
|
||||
echo "env: WRENCH_REFTEST_CONDITION_DEVICE=1" >>/storage/emulated/0/Android/data/org.mozilla.wrench/files/wrench/args # If you're using a device
|
||||
exit
|
||||
|
||||
Push reftests (if you need these files for what you're doing):
|
||||
adb push reftests /sdcard/wrench/
|
||||
adb push reftests /storage/emulated/0/Android/data/org.mozilla.wrench/files/wrench/
|
||||
|
||||
Run the application:
|
||||
adb shell am start -n org.mozilla.wrench/android.app.NativeActivity
|
||||
|
@ -49,11 +50,11 @@ Release mode:
|
|||
|
||||
Building in release does work as well. Use the following steps to compile wrench:
|
||||
cd wrench
|
||||
export ANDROID_HOME=$HOME/.mozbuild/android-sdk-linux # exact path may vary
|
||||
export NDK_HOME=$HOME/.mozbuild/android-ndk # exact path may vary
|
||||
export ANDROID_SDK_ROOT=$HOME/.mozbuild/android-sdk-linux # exact path may vary
|
||||
export ANDROID_NDK_ROOT=$HOME/.mozbuild/android-ndk-r21d # exact path may vary
|
||||
cargo apk build --release
|
||||
|
||||
Now the APK at ../target/android-artifacts/release/apk/wrench.apk
|
||||
Now the APK at ../target/release/apk/wrench.apk
|
||||
should be signed and installable (you may need to uninstall the debug APK first if you
|
||||
have that installed).
|
||||
|
||||
|
@ -66,7 +67,7 @@ Running reftests like a boss (on a local emulator):
|
|||
This will automatically do the following:
|
||||
- Download the blessed android AVDs from taskcluster
|
||||
- Start the emulator (using your ~/.mozbuild/android-sdk-linux emulator binaries)
|
||||
- Install the debug APK (from gfx/wr/wrench/target/android-artifacts/debug/apk/wrench.apk)
|
||||
- Install the debug APK (from gfx/wr/wrench/target/debug/apk/wrench.apk)
|
||||
- Copy the reftests to the sdcard
|
||||
- Write an args file to the sdcard
|
||||
- Run wrench
|
||||
|
@ -80,7 +81,7 @@ Running reftests like a boss (on a local emulator):
|
|||
If you want to use a release APK (runs much faster), build it as per the "Release mode"
|
||||
instructions above and set the WRENCH_APK env var to point to the APK:
|
||||
to point to it:
|
||||
export WRENCH_APK=gfx/wr/target/android-artifacts/release/apk/wrench.apk
|
||||
export WRENCH_APK=gfx/wr/target/release/apk/wrench.apk
|
||||
./mach python testing/mozharness/scripts/android_wrench.py --config testing/mozharness/configs/android/wrench.py
|
||||
|
||||
Running reftests like a boss (on a local device):
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use glutin::{self, ContextBuilder, ContextCurrentState, CreationError};
|
||||
use winit::{EventsLoop, Window, WindowBuilder};
|
||||
use winit::{event_loop::EventLoop, window::Window, window::WindowBuilder};
|
||||
|
||||
#[cfg(not(windows))]
|
||||
pub enum Context {}
|
||||
|
@ -16,7 +16,7 @@ impl Context {
|
|||
pub fn with_window<T: ContextCurrentState>(
|
||||
_: WindowBuilder,
|
||||
_: ContextBuilder<'_, T>,
|
||||
_: &EventsLoop,
|
||||
_: &EventLoop<()>,
|
||||
) -> Result<(Window, Self), CreationError> {
|
||||
Err(CreationError::PlatformSpecific(
|
||||
"ANGLE rendering is only supported on Windows".into(),
|
||||
|
@ -27,16 +27,16 @@ impl Context {
|
|||
pub fn with_window<T: ContextCurrentState>(
|
||||
window_builder: WindowBuilder,
|
||||
context_builder: ContextBuilder<'_, T>,
|
||||
events_loop: &EventsLoop,
|
||||
events_loop: &EventLoop<()>,
|
||||
) -> Result<(Window, Self), CreationError> {
|
||||
use winit::os::windows::WindowExt;
|
||||
use winit::platform::windows::WindowExtWindows;
|
||||
|
||||
// FIXME: &context_builder.pf_reqs https://github.com/tomaka/glutin/pull/1002
|
||||
let pf_reqs = &glutin::PixelFormatRequirements::default();
|
||||
let gl_attr = &context_builder.gl_attr.map_sharing(|_| unimplemented!());
|
||||
let window = window_builder.build(events_loop)?;
|
||||
Self::new(pf_reqs, gl_attr)
|
||||
.and_then(|p| p.finish(window.get_hwnd() as _))
|
||||
.and_then(|p| p.finish(window.hwnd() as _))
|
||||
.map(|context| (window, context))
|
||||
}
|
||||
|
||||
|
|
|
@ -30,10 +30,8 @@ use gleam::gl;
|
|||
#[cfg(feature = "software")]
|
||||
use gleam::gl::Gl;
|
||||
use crate::perf::PerfHarness;
|
||||
use crate::png::save_flipped;
|
||||
use crate::rawtest::RawtestHarness;
|
||||
use crate::reftest::{ReftestHarness, ReftestOptions};
|
||||
use std::fs;
|
||||
#[cfg(feature = "headless")]
|
||||
use std::ffi::CString;
|
||||
#[cfg(feature = "headless")]
|
||||
|
@ -51,7 +49,8 @@ use webrender::api::*;
|
|||
use webrender::render_api::*;
|
||||
use webrender::api::units::*;
|
||||
use winit::dpi::{LogicalPosition, LogicalSize};
|
||||
use winit::VirtualKeyCode;
|
||||
use winit::event::VirtualKeyCode;
|
||||
use winit::platform::run_return::EventLoopExtRunReturn;
|
||||
use crate::wrench::{CapturedSequence, Wrench, WrenchThing};
|
||||
use crate::yaml_frame_reader::YamlFrameReader;
|
||||
|
||||
|
@ -138,7 +137,7 @@ mod swgl {
|
|||
|
||||
pub enum WindowWrapper {
|
||||
WindowedContext(glutin::WindowedContext<glutin::PossiblyCurrent>, Rc<dyn gl::Gl>, Option<swgl::Context>),
|
||||
Angle(winit::Window, angle::Context, Rc<dyn gl::Gl>, Option<swgl::Context>),
|
||||
Angle(winit::window::Window, angle::Context, Rc<dyn gl::Gl>, Option<swgl::Context>),
|
||||
Headless(HeadlessContext, Rc<dyn gl::Gl>, Option<swgl::Context>),
|
||||
}
|
||||
|
||||
|
@ -184,11 +183,8 @@ impl WindowWrapper {
|
|||
}
|
||||
|
||||
fn get_inner_size(&self) -> DeviceIntSize {
|
||||
fn inner_size(window: &winit::Window) -> DeviceIntSize {
|
||||
let size = window
|
||||
.get_inner_size()
|
||||
.unwrap()
|
||||
.to_physical(window.get_hidpi_factor());
|
||||
fn inner_size(window: &winit::window::Window) -> DeviceIntSize {
|
||||
let size = window.inner_size();
|
||||
DeviceIntSize::new(size.width as i32, size.height as i32)
|
||||
}
|
||||
match *self {
|
||||
|
@ -203,9 +199,9 @@ impl WindowWrapper {
|
|||
fn hidpi_factor(&self) -> f32 {
|
||||
match *self {
|
||||
WindowWrapper::WindowedContext(ref windowed_context, ..) => {
|
||||
windowed_context.window().get_hidpi_factor() as f32
|
||||
windowed_context.window().scale_factor() as f32
|
||||
}
|
||||
WindowWrapper::Angle(ref window, ..) => window.get_hidpi_factor() as f32,
|
||||
WindowWrapper::Angle(ref window, ..) => window.scale_factor() as f32,
|
||||
WindowWrapper::Headless(..) => 1.0,
|
||||
}
|
||||
}
|
||||
|
@ -317,7 +313,7 @@ fn make_software_context() -> swgl::Context {
|
|||
fn make_window(
|
||||
size: DeviceIntSize,
|
||||
vsync: bool,
|
||||
events_loop: &Option<winit::EventsLoop>,
|
||||
events_loop: &Option<winit::event_loop::EventLoop<()>>,
|
||||
angle: bool,
|
||||
gl_request: glutin::GlRequest,
|
||||
software: bool,
|
||||
|
@ -331,11 +327,12 @@ fn make_window(
|
|||
let wrapper = if let Some(events_loop) = events_loop {
|
||||
let context_builder = glutin::ContextBuilder::new()
|
||||
.with_gl(gl_request)
|
||||
.with_vsync(vsync);
|
||||
let window_builder = winit::WindowBuilder::new()
|
||||
// Glutin can fail to create a context on Android if vsync is not set
|
||||
.with_vsync(vsync || cfg!(target_os = "android"));
|
||||
|
||||
let window_builder = winit::window::WindowBuilder::new()
|
||||
.with_title("WRench")
|
||||
.with_multitouch()
|
||||
.with_dimensions(LogicalSize::new(size.width as f64, size.height as f64));
|
||||
.with_inner_size(LogicalSize::new(size.width as f64, size.height as f64));
|
||||
|
||||
if angle {
|
||||
angle::Context::with_window(
|
||||
|
@ -492,11 +489,18 @@ fn reftest<'a>(
|
|||
rx: Receiver<NotifierEvent>
|
||||
) -> usize {
|
||||
let dim = window.get_inner_size();
|
||||
let base_manifest = if cfg!(target_os = "android") {
|
||||
Path::new("/sdcard/wrench/reftests/reftest.list")
|
||||
} else {
|
||||
Path::new("reftests/reftest.list")
|
||||
#[cfg(target_os = "android")]
|
||||
let base_manifest = {
|
||||
let mut list_path = PathBuf::new();
|
||||
list_path.push(ndk_glue::native_activity().external_data_path().to_str().unwrap());
|
||||
list_path.push("wrench");
|
||||
list_path.push("reftests");
|
||||
list_path.push("reftest.list");
|
||||
list_path
|
||||
};
|
||||
#[cfg(not(target_os = "android"))]
|
||||
let base_manifest = Path::new("reftests/reftest.list").to_owned();
|
||||
|
||||
let specific_reftest = subargs.value_of("REFTEST").map(Path::new);
|
||||
let mut reftest_options = ReftestOptions::default();
|
||||
if let Some(allow_max_diff) = subargs.value_of("fuzz_tolerance") {
|
||||
|
@ -504,12 +508,13 @@ fn reftest<'a>(
|
|||
reftest_options.allow_num_differences = dim.width as usize * dim.height as usize;
|
||||
}
|
||||
let num_failures = ReftestHarness::new(&mut wrench, window, &rx)
|
||||
.run(base_manifest, specific_reftest, &reftest_options);
|
||||
.run(&base_manifest, specific_reftest, &reftest_options);
|
||||
wrench.shut_down(rx);
|
||||
num_failures
|
||||
}
|
||||
|
||||
fn main() {
|
||||
#[cfg_attr(target_os = "android", ndk_glue::main)]
|
||||
pub fn main() {
|
||||
#[cfg(feature = "env_logger")]
|
||||
env_logger::init();
|
||||
|
||||
|
@ -528,16 +533,22 @@ fn main() {
|
|||
let clap = clap::App::from_yaml(args_yaml)
|
||||
.setting(clap::AppSettings::ArgRequiredElseHelp);
|
||||
|
||||
// On android devices, attempt to read command line arguments
|
||||
// from a text file located at /sdcard/wrench/args.
|
||||
let args = if cfg!(target_os = "android") {
|
||||
// On android devices, attempt to read command line arguments from a text
|
||||
// file located at <external_data_dir>/wrench/args.
|
||||
#[cfg(target_os = "android")]
|
||||
let args = {
|
||||
// get full backtraces by default because it's hard to request
|
||||
// externally on android
|
||||
std::env::set_var("RUST_BACKTRACE", "full");
|
||||
|
||||
let mut args = vec!["wrench".to_string()];
|
||||
|
||||
if let Ok(wrench_args) = fs::read_to_string("/sdcard/wrench/args") {
|
||||
let mut args_path = PathBuf::new();
|
||||
args_path.push(ndk_glue::native_activity().external_data_path().to_str().unwrap());
|
||||
args_path.push("wrench");
|
||||
args_path.push("args");
|
||||
|
||||
if let Ok(wrench_args) = std::fs::read_to_string(&args_path) {
|
||||
for line in wrench_args.lines() {
|
||||
if let Some(envvar) = line.strip_prefix("env: ") {
|
||||
if let Some((lhs, rhs)) = envvar.split_once('=') {
|
||||
|
@ -555,10 +566,11 @@ fn main() {
|
|||
}
|
||||
|
||||
clap.get_matches_from(&args)
|
||||
} else {
|
||||
clap.get_matches()
|
||||
};
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
let args = clap.get_matches();
|
||||
|
||||
// handle some global arguments
|
||||
let res_path = args.value_of("shaders").map(PathBuf::from);
|
||||
let size = args.value_of("size")
|
||||
|
@ -602,7 +614,7 @@ fn main() {
|
|||
let mut events_loop = if args.is_present("headless") {
|
||||
None
|
||||
} else {
|
||||
Some(winit::EventsLoop::new())
|
||||
Some(winit::event_loop::EventLoop::new())
|
||||
};
|
||||
|
||||
let gl_request = match args.value_of("renderer") {
|
||||
|
@ -625,6 +637,21 @@ fn main() {
|
|||
|
||||
let software = args.is_present("software");
|
||||
|
||||
// On Android we can only create an OpenGL context when we have a
|
||||
// native_window handle, so wait here until we are resumed and have a
|
||||
// handle. If the app gets minimized this will no longer be valid, but
|
||||
// that's okay for wrench's usage.
|
||||
#[cfg(target_os = "android")]
|
||||
{
|
||||
events_loop.as_mut().unwrap().run_return(|event, _elwt, control_flow| {
|
||||
if let winit::event::Event::Resumed = event {
|
||||
if ndk_glue::native_window().is_some() {
|
||||
*control_flow = winit::event_loop::ControlFlow::Exit;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let mut window = make_window(
|
||||
size,
|
||||
args.is_present("vsync"),
|
||||
|
@ -681,8 +708,7 @@ fn main() {
|
|||
render(
|
||||
&mut wrench,
|
||||
&mut window,
|
||||
size,
|
||||
&mut events_loop,
|
||||
events_loop.as_mut().expect("`wrench show` is not supported in headless mode"),
|
||||
subargs,
|
||||
no_block,
|
||||
no_batch,
|
||||
|
@ -766,8 +792,7 @@ fn main() {
|
|||
fn render<'a>(
|
||||
wrench: &mut Wrench,
|
||||
window: &mut WindowWrapper,
|
||||
size: DeviceIntSize,
|
||||
events_loop: &mut Option<winit::EventsLoop>,
|
||||
events_loop: &mut winit::event_loop::EventLoop<()>,
|
||||
subargs: &clap::ArgMatches<'a>,
|
||||
no_block: bool,
|
||||
no_batch: bool,
|
||||
|
@ -798,10 +823,6 @@ fn render<'a>(
|
|||
}
|
||||
};
|
||||
|
||||
let mut show_help = false;
|
||||
let mut do_loop = false;
|
||||
let mut cursor_position = WorldPoint::zero();
|
||||
|
||||
window.update(wrench);
|
||||
thing.do_frame(wrench);
|
||||
|
||||
|
@ -818,197 +839,178 @@ fn render<'a>(
|
|||
wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
|
||||
}
|
||||
|
||||
let mut body = |wrench: &mut Wrench, events: Vec<winit::Event>| {
|
||||
let mut do_frame = false;
|
||||
let mut do_render = !events.is_empty();
|
||||
let mut show_help = false;
|
||||
let mut do_loop = false;
|
||||
let mut cursor_position = WorldPoint::zero();
|
||||
let mut do_render = false;
|
||||
let mut do_frame = false;
|
||||
|
||||
for event in events {
|
||||
match event {
|
||||
winit::Event::Awakened => {}
|
||||
winit::Event::WindowEvent { event, .. } => match event {
|
||||
winit::WindowEvent::CloseRequested => {
|
||||
return winit::ControlFlow::Break;
|
||||
}
|
||||
winit::WindowEvent::Refresh |
|
||||
winit::WindowEvent::Focused(..) => {}
|
||||
winit::WindowEvent::CursorMoved { position: LogicalPosition { x, y }, .. } => {
|
||||
cursor_position = WorldPoint::new(x as f32, y as f32);
|
||||
wrench.renderer.set_cursor_position(
|
||||
DeviceIntPoint::new(
|
||||
cursor_position.x.round() as i32,
|
||||
cursor_position.y.round() as i32,
|
||||
),
|
||||
);
|
||||
}
|
||||
winit::WindowEvent::KeyboardInput {
|
||||
input: winit::KeyboardInput {
|
||||
state: winit::ElementState::Pressed,
|
||||
virtual_keycode: Some(vk),
|
||||
..
|
||||
},
|
||||
events_loop.run_return(|event, _elwt, control_flow| {
|
||||
// By default after each iteration of the event loop we block the thread until the next
|
||||
// events arrive. --no-block can be used to run the event loop as quickly as possible.
|
||||
// On Android, we are generally profiling when running wrench, and don't want to block
|
||||
// on UI events.
|
||||
if !no_block && cfg!(not(target_os = "android")) {
|
||||
*control_flow = winit::event_loop::ControlFlow::Wait;
|
||||
} else {
|
||||
*control_flow = winit::event_loop::ControlFlow::Poll;
|
||||
}
|
||||
|
||||
match event {
|
||||
winit::event::Event::UserEvent(_) => {
|
||||
do_render = true;
|
||||
}
|
||||
winit::event::Event::WindowEvent { event, .. } => match event {
|
||||
winit::event::WindowEvent::CloseRequested => {
|
||||
*control_flow = winit::event_loop::ControlFlow::Exit;
|
||||
}
|
||||
winit::event::WindowEvent::Focused(..) => do_render = true,
|
||||
winit::event::WindowEvent::CursorMoved { position, .. } => {
|
||||
let pos: LogicalPosition<f32> = position.to_logical(window.hidpi_factor() as f64);
|
||||
cursor_position = WorldPoint::new(pos.x, pos.y);
|
||||
wrench.renderer.set_cursor_position(
|
||||
DeviceIntPoint::new(
|
||||
cursor_position.x.round() as i32,
|
||||
cursor_position.y.round() as i32,
|
||||
),
|
||||
);
|
||||
do_render = true;
|
||||
}
|
||||
winit::event::WindowEvent::KeyboardInput {
|
||||
input: winit::event::KeyboardInput {
|
||||
state: winit::event::ElementState::Pressed,
|
||||
virtual_keycode: Some(vk),
|
||||
..
|
||||
} => match vk {
|
||||
VirtualKeyCode::Escape => {
|
||||
return winit::ControlFlow::Break;
|
||||
}
|
||||
VirtualKeyCode::B => {
|
||||
debug_flags.toggle(DebugFlags::INVALIDATION_DBG);
|
||||
wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
|
||||
}
|
||||
VirtualKeyCode::P => {
|
||||
debug_flags.toggle(DebugFlags::PROFILER_DBG);
|
||||
wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
|
||||
}
|
||||
VirtualKeyCode::O => {
|
||||
debug_flags.toggle(DebugFlags::RENDER_TARGET_DBG);
|
||||
wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
|
||||
}
|
||||
VirtualKeyCode::I => {
|
||||
debug_flags.toggle(DebugFlags::TEXTURE_CACHE_DBG);
|
||||
wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
|
||||
}
|
||||
VirtualKeyCode::D => {
|
||||
debug_flags.toggle(DebugFlags::PICTURE_CACHING_DBG);
|
||||
wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
|
||||
}
|
||||
VirtualKeyCode::Q => {
|
||||
debug_flags.toggle(DebugFlags::GPU_TIME_QUERIES | DebugFlags::GPU_SAMPLE_QUERIES);
|
||||
wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
|
||||
}
|
||||
VirtualKeyCode::V => {
|
||||
debug_flags.toggle(DebugFlags::SHOW_OVERDRAW);
|
||||
wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
|
||||
}
|
||||
VirtualKeyCode::G => {
|
||||
debug_flags.toggle(DebugFlags::GPU_CACHE_DBG);
|
||||
wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
|
||||
|
||||
// force scene rebuild to see the full set of used GPU cache entries
|
||||
let mut txn = Transaction::new();
|
||||
txn.set_root_pipeline(wrench.root_pipeline_id);
|
||||
wrench.api.send_transaction(wrench.document_id, txn);
|
||||
|
||||
do_render = false;
|
||||
do_frame = true;
|
||||
}
|
||||
VirtualKeyCode::M => {
|
||||
wrench.api.notify_memory_pressure();
|
||||
}
|
||||
VirtualKeyCode::L => {
|
||||
do_loop = !do_loop;
|
||||
}
|
||||
VirtualKeyCode::Left => {
|
||||
thing.prev_frame();
|
||||
do_render = false;
|
||||
do_frame = true;
|
||||
}
|
||||
VirtualKeyCode::Right => {
|
||||
thing.next_frame();
|
||||
do_render = false;
|
||||
do_frame = true;
|
||||
}
|
||||
VirtualKeyCode::H => {
|
||||
show_help = !show_help;
|
||||
}
|
||||
VirtualKeyCode::C => {
|
||||
let path = PathBuf::from("../captures/wrench");
|
||||
wrench.api.save_capture(path, CaptureBits::all());
|
||||
do_render = false;
|
||||
}
|
||||
VirtualKeyCode::X => {
|
||||
let results = wrench.api.hit_test(
|
||||
wrench.document_id,
|
||||
cursor_position,
|
||||
);
|
||||
|
||||
println!("Hit test results:");
|
||||
for item in &results.items {
|
||||
println!(" • {:?}", item);
|
||||
}
|
||||
println!();
|
||||
do_render = false;
|
||||
}
|
||||
VirtualKeyCode::Z => {
|
||||
debug_flags.toggle(DebugFlags::ZOOM_DBG);
|
||||
wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
|
||||
}
|
||||
VirtualKeyCode::Y => {
|
||||
println!("Clearing all caches...");
|
||||
wrench.api.send_debug_cmd(DebugCommand::ClearCaches(ClearCache::all()));
|
||||
do_frame = true;
|
||||
}
|
||||
_other_virtual_keycode => { do_render = false; }
|
||||
},
|
||||
..
|
||||
} => match vk {
|
||||
VirtualKeyCode::Escape => {
|
||||
*control_flow = winit::event_loop::ControlFlow::Exit;
|
||||
}
|
||||
_other_window_event => { do_render = false; }
|
||||
},
|
||||
_other_event => { do_render = false; }
|
||||
VirtualKeyCode::B => {
|
||||
debug_flags.toggle(DebugFlags::INVALIDATION_DBG);
|
||||
wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
|
||||
do_render = true;
|
||||
}
|
||||
VirtualKeyCode::P => {
|
||||
debug_flags.toggle(DebugFlags::PROFILER_DBG);
|
||||
wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
|
||||
do_render = true;
|
||||
}
|
||||
VirtualKeyCode::O => {
|
||||
debug_flags.toggle(DebugFlags::RENDER_TARGET_DBG);
|
||||
wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
|
||||
do_render = true;
|
||||
}
|
||||
VirtualKeyCode::I => {
|
||||
debug_flags.toggle(DebugFlags::TEXTURE_CACHE_DBG);
|
||||
wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
|
||||
do_render = true;
|
||||
}
|
||||
VirtualKeyCode::D => {
|
||||
debug_flags.toggle(DebugFlags::PICTURE_CACHING_DBG);
|
||||
wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
|
||||
do_render = true;
|
||||
}
|
||||
VirtualKeyCode::Q => {
|
||||
debug_flags.toggle(DebugFlags::GPU_TIME_QUERIES | DebugFlags::GPU_SAMPLE_QUERIES);
|
||||
wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
|
||||
do_render = true;
|
||||
}
|
||||
VirtualKeyCode::V => {
|
||||
debug_flags.toggle(DebugFlags::SHOW_OVERDRAW);
|
||||
wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
|
||||
do_render = true;
|
||||
}
|
||||
VirtualKeyCode::G => {
|
||||
debug_flags.toggle(DebugFlags::GPU_CACHE_DBG);
|
||||
wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
|
||||
|
||||
// force scene rebuild to see the full set of used GPU cache entries
|
||||
let mut txn = Transaction::new();
|
||||
txn.set_root_pipeline(wrench.root_pipeline_id);
|
||||
wrench.api.send_transaction(wrench.document_id, txn);
|
||||
|
||||
do_frame = true;
|
||||
}
|
||||
VirtualKeyCode::M => {
|
||||
wrench.api.notify_memory_pressure();
|
||||
do_render = true;
|
||||
}
|
||||
VirtualKeyCode::L => {
|
||||
do_loop = !do_loop;
|
||||
do_render = true;
|
||||
}
|
||||
VirtualKeyCode::Left => {
|
||||
thing.prev_frame();
|
||||
do_frame = true;
|
||||
}
|
||||
VirtualKeyCode::Right => {
|
||||
thing.next_frame();
|
||||
do_frame = true;
|
||||
}
|
||||
VirtualKeyCode::H => {
|
||||
show_help = !show_help;
|
||||
do_render = true;
|
||||
}
|
||||
VirtualKeyCode::C => {
|
||||
let path = PathBuf::from("../captures/wrench");
|
||||
wrench.api.save_capture(path, CaptureBits::all());
|
||||
}
|
||||
VirtualKeyCode::X => {
|
||||
let results = wrench.api.hit_test(
|
||||
wrench.document_id,
|
||||
cursor_position,
|
||||
);
|
||||
|
||||
println!("Hit test results:");
|
||||
for item in &results.items {
|
||||
println!(" • {:?}", item);
|
||||
}
|
||||
println!();
|
||||
}
|
||||
VirtualKeyCode::Z => {
|
||||
debug_flags.toggle(DebugFlags::ZOOM_DBG);
|
||||
wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
|
||||
do_render = true;
|
||||
}
|
||||
VirtualKeyCode::Y => {
|
||||
println!("Clearing all caches...");
|
||||
wrench.api.send_debug_cmd(DebugCommand::ClearCaches(ClearCache::all()));
|
||||
do_frame = true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
winit::event::Event::MainEventsCleared => {
|
||||
window.update(wrench);
|
||||
|
||||
if do_frame {
|
||||
do_frame = false;
|
||||
let frame_num = thing.do_frame(wrench);
|
||||
unsafe {
|
||||
CURRENT_FRAME_NUMBER = frame_num;
|
||||
}
|
||||
}
|
||||
|
||||
if do_render {
|
||||
do_render = false;
|
||||
|
||||
if show_help {
|
||||
wrench.show_onscreen_help();
|
||||
}
|
||||
|
||||
wrench.render();
|
||||
window.upload_software_to_native();
|
||||
window.swap_buffers();
|
||||
|
||||
if do_loop {
|
||||
thing.next_frame();
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
window.update(wrench);
|
||||
|
||||
if do_frame {
|
||||
let frame_num = thing.do_frame(wrench);
|
||||
unsafe {
|
||||
CURRENT_FRAME_NUMBER = frame_num;
|
||||
}
|
||||
}
|
||||
|
||||
if do_render {
|
||||
if show_help {
|
||||
wrench.show_onscreen_help();
|
||||
}
|
||||
|
||||
wrench.render();
|
||||
window.upload_software_to_native();
|
||||
window.swap_buffers();
|
||||
|
||||
if do_loop {
|
||||
thing.next_frame();
|
||||
}
|
||||
}
|
||||
|
||||
winit::ControlFlow::Continue
|
||||
};
|
||||
|
||||
if let Some(events_loop) = events_loop.as_mut() {
|
||||
// We want to ensure that we:
|
||||
//
|
||||
// (a) Block the thread when no events are present (for CPU/battery purposes)
|
||||
// (b) Don't lag the input events by having the event queue back up.
|
||||
loop {
|
||||
let mut pending_events = Vec::new();
|
||||
|
||||
// Block the thread until at least one event arrives
|
||||
// On Android, we are generally profiling when running
|
||||
// wrench, and don't want to block on UI events.
|
||||
if !no_block && cfg!(not(target_os = "android")) {
|
||||
events_loop.run_forever(|event| {
|
||||
pending_events.push(event);
|
||||
winit::ControlFlow::Break
|
||||
});
|
||||
}
|
||||
|
||||
// Collect any other pending events that are also available
|
||||
events_loop.poll_events(|event| {
|
||||
pending_events.push(event);
|
||||
});
|
||||
|
||||
// Ensure there is at least one event present so that the
|
||||
// frame gets rendered.
|
||||
if pending_events.is_empty() {
|
||||
pending_events.push(winit::Event::Awakened);
|
||||
}
|
||||
|
||||
// Process all of those pending events in the next vsync period
|
||||
if body(wrench, pending_events) == winit::ControlFlow::Break {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while body(wrench, vec![winit::Event::Awakened]) == winit::ControlFlow::Continue {}
|
||||
let fb_rect = FramebufferIntSize::new(size.width, size.height).into();
|
||||
let pixels = wrench.renderer.read_pixels_rgba8(fb_rect);
|
||||
save_flipped("screenshot.png", pixels, size);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ use crossbeam::sync::chase_lev;
|
|||
use dwrote;
|
||||
#[cfg(all(unix, not(target_os = "android")))]
|
||||
use font_loader::system_fonts;
|
||||
use winit::EventsLoopProxy;
|
||||
use winit::event_loop::EventLoopProxy;
|
||||
use std::collections::HashMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
@ -36,7 +36,7 @@ pub enum FontDescriptor {
|
|||
}
|
||||
|
||||
struct NotifierData {
|
||||
events_loop_proxy: Option<EventsLoopProxy>,
|
||||
events_loop_proxy: Option<EventLoopProxy<()>>,
|
||||
frames_notified: u32,
|
||||
timing_receiver: chase_lev::Stealer<time::SteadyTime>,
|
||||
verbose: bool,
|
||||
|
@ -44,7 +44,7 @@ struct NotifierData {
|
|||
|
||||
impl NotifierData {
|
||||
fn new(
|
||||
events_loop_proxy: Option<EventsLoopProxy>,
|
||||
events_loop_proxy: Option<EventLoopProxy<()>>,
|
||||
timing_receiver: chase_lev::Stealer<time::SteadyTime>,
|
||||
verbose: bool,
|
||||
) -> Self {
|
||||
|
@ -84,7 +84,7 @@ impl Notifier {
|
|||
|
||||
if let Some(ref _elp) = data.events_loop_proxy {
|
||||
#[cfg(not(target_os = "android"))]
|
||||
let _ = _elp.wakeup();
|
||||
let _ = _elp.send_event(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -217,7 +217,7 @@ impl Wrench {
|
|||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
window: &mut WindowWrapper,
|
||||
proxy: Option<EventsLoopProxy>,
|
||||
proxy: Option<EventLoopProxy<()>>,
|
||||
shader_override_path: Option<PathBuf>,
|
||||
use_optimized_shaders: bool,
|
||||
size: DeviceIntSize,
|
||||
|
@ -268,7 +268,7 @@ impl Wrench {
|
|||
// put an Awakened event into the queue to kick off the first frame
|
||||
if let Some(ref _elp) = proxy {
|
||||
#[cfg(not(target_os = "android"))]
|
||||
let _ = _elp.wakeup();
|
||||
let _ = _elp.send_event(());
|
||||
}
|
||||
|
||||
let (timing_sender, timing_receiver) = chase_lev::deque();
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<canvas id="crashtest" width="50000" height="50000" />
|
||||
<script>
|
||||
document.getElementById("crashtest").mozGetAsFile("foo.png", "image/png");
|
||||
document.getElementById("crashtest").mozGetAsFile("foo.jpeg", "image/jpeg");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -9,7 +9,6 @@ load 570451.png
|
|||
# Bug 1390704 - Skip on debug because it triggers a quadratic behavior that makes it take
|
||||
# so much time that it can trip on the reftest timeout of 5 minutes.
|
||||
skip-if(Android||isDebugBuild||ThreadSanitizer) load 694165-1.xhtml
|
||||
pref(canvas.mozgetasfile.enabled,true) load 681190.html
|
||||
load 732319-1.html
|
||||
load 844403-1.html
|
||||
load 856616.gif
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#include "mozilla/ipc/FileDescriptor.h"
|
||||
#include "base/shared_memory.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
@ -25,12 +27,12 @@ void SetThisProcessName(const char* aName);
|
|||
|
||||
class SharedPreferenceSerializer final {
|
||||
public:
|
||||
explicit SharedPreferenceSerializer(
|
||||
std::function<bool(const char*)>&& aShouldSerializeFn);
|
||||
explicit SharedPreferenceSerializer();
|
||||
SharedPreferenceSerializer(SharedPreferenceSerializer&& aOther);
|
||||
~SharedPreferenceSerializer();
|
||||
|
||||
bool SerializeToSharedMemory();
|
||||
bool SerializeToSharedMemory(const GeckoProcessType aDestinationProcessType,
|
||||
const nsACString& aDestinationRemoteType);
|
||||
|
||||
size_t GetPrefMapSize() const { return mPrefMapSize; }
|
||||
size_t GetPrefsLength() const { return mPrefsLength; }
|
||||
|
@ -48,7 +50,6 @@ class SharedPreferenceSerializer final {
|
|||
size_t mPrefsLength;
|
||||
UniqueFileHandle mPrefMapHandle;
|
||||
UniqueFileHandle mPrefsHandle;
|
||||
std::function<bool(const char*)> mShouldSerializeFn;
|
||||
};
|
||||
|
||||
class SharedPreferenceDeserializer final {
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/GeckoArgs.h"
|
||||
#include "mozilla/dom/RemoteType.h"
|
||||
#include "mozilla/ipc/GeckoChildProcessHost.h"
|
||||
#include "mozilla/UniquePtrExtensions.h"
|
||||
#include "nsPrintfCString.h"
|
||||
|
@ -17,9 +18,8 @@
|
|||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
SharedPreferenceSerializer::SharedPreferenceSerializer(
|
||||
std::function<bool(const char*)>&& aShouldSerializeFn)
|
||||
: mPrefMapSize(0), mPrefsLength(0), mShouldSerializeFn(aShouldSerializeFn) {
|
||||
SharedPreferenceSerializer::SharedPreferenceSerializer()
|
||||
: mPrefMapSize(0), mPrefsLength(0) {
|
||||
MOZ_COUNT_CTOR(SharedPreferenceSerializer);
|
||||
}
|
||||
|
||||
|
@ -36,13 +36,20 @@ SharedPreferenceSerializer::SharedPreferenceSerializer(
|
|||
MOZ_COUNT_CTOR(SharedPreferenceSerializer);
|
||||
}
|
||||
|
||||
bool SharedPreferenceSerializer::SerializeToSharedMemory() {
|
||||
bool SharedPreferenceSerializer::SerializeToSharedMemory(
|
||||
const GeckoProcessType aDestinationProcessType,
|
||||
const nsACString& aDestinationRemoteType) {
|
||||
mPrefMapHandle =
|
||||
Preferences::EnsureSnapshot(&mPrefMapSize).TakePlatformHandle();
|
||||
|
||||
bool destIsWebContent =
|
||||
aDestinationProcessType == GeckoProcessType_Content &&
|
||||
(StringBeginsWith(aDestinationRemoteType, WEB_REMOTE_TYPE) ||
|
||||
StringBeginsWith(aDestinationRemoteType, PREALLOC_REMOTE_TYPE));
|
||||
|
||||
// Serialize the early prefs.
|
||||
nsAutoCStringN<1024> prefs;
|
||||
Preferences::SerializePreferences(prefs, mShouldSerializeFn);
|
||||
Preferences::SerializePreferences(prefs, destIsWebContent);
|
||||
mPrefsLength = prefs.Length();
|
||||
|
||||
base::SharedMemory shm;
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче