author | Dorel Luca <dluca@mozilla.com> |
Tue, 21 Aug 2018 12:54:24 +0300 | |
changeset 432546 | a955df76e2b636889a1c37a7863739d6306520eb |
parent 432479 | 6cc53c0f6efec1c590a5b3a6c0019a26e11e6843 (current diff) |
parent 432545 | 6c3db80981da2613b3e58c4904dec3504b7476d8 (diff) |
child 432557 | c3b06d8fd0011fee94007ef05a202bf56343e460 |
child 432575 | 88803cf0dec1cffe6990cc48705d18591aaa9e2b |
push id | 34478 |
push user | dluca@mozilla.com |
push date | Tue, 21 Aug 2018 09:54:49 +0000 |
treeherder | mozilla-central@a955df76e2b6 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 63.0a1 |
first release with | nightly linux32
a955df76e2b6
/
63.0a1
/
20180821100053
/
files
nightly linux64
a955df76e2b6
/
63.0a1
/
20180821100053
/
files
nightly mac
a955df76e2b6
/
63.0a1
/
20180821100053
/
files
nightly win32
a955df76e2b6
/
63.0a1
/
20180821100053
/
files
nightly win64
a955df76e2b6
/
63.0a1
/
20180821100053
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
63.0a1
/
20180821100053
/
pushlog to previous
nightly linux64
63.0a1
/
20180821100053
/
pushlog to previous
nightly mac
63.0a1
/
20180821100053
/
pushlog to previous
nightly win32
63.0a1
/
20180821100053
/
pushlog to previous
nightly win64
63.0a1
/
20180821100053
/
pushlog to previous
|
layout/reftests/bugs/reftest.list | file | annotate | diff | comparison | revisions | |
testing/xpcshell/example/unit/import_sub_module.jsm | file | annotate | diff | comparison | revisions |
--- a/accessible/ipc/win/DocAccessibleChild.cpp +++ b/accessible/ipc/win/DocAccessibleChild.cpp @@ -269,16 +269,36 @@ DocAccessibleChild::SendRoleChangedEvent return PDocAccessibleChild::SendRoleChangedEvent(aRole); } PushDeferredEvent(MakeUnique<SerializedRoleChanged>(this, aRole)); return true; } bool +DocAccessibleChild::SendScrollingEvent(const uint64_t& aID, + const uint64_t& aType, + const uint32_t& aScrollX, + const uint32_t& aScrollY, + const uint32_t& aMaxScrollX, + const uint32_t& aMaxScrollY) +{ + if (IsConstructedInParentProcess()) { + return PDocAccessibleChild::SendScrollingEvent(aID, aType, + aScrollX, aScrollY, + aMaxScrollX, aMaxScrollY); + } + + PushDeferredEvent(MakeUnique<SerializedScrolling>(this, aID, aType, + aScrollX, aScrollY, + aMaxScrollX, aMaxScrollY)); + return true; +} + +bool DocAccessibleChild::ConstructChildDocInParentProcess( DocAccessibleChild* aNewChildDoc, uint64_t aUniqueID, uint32_t aMsaaID) { if (IsConstructedInParentProcess()) { // We may send the constructor immediately auto tabChild = static_cast<dom::TabChild*>(Manager()); MOZ_ASSERT(tabChild);
--- a/accessible/ipc/win/DocAccessibleChild.h +++ b/accessible/ipc/win/DocAccessibleChild.h @@ -53,16 +53,20 @@ public: const LayoutDeviceIntRect& aCaretRect); bool SendTextChangeEvent(const uint64_t& aID, const nsString& aStr, const int32_t& aStart, const uint32_t& aLen, const bool& aIsInsert, const bool& aFromUser, const bool aDoSyncCheck = true); bool SendSelectionEvent(const uint64_t& aID, const uint64_t& aWidgetID, const uint32_t& aType); bool SendRoleChangedEvent(const a11y::role& aRole); + bool SendScrollingEvent(const uint64_t& aID, const uint64_t& aType, + const uint32_t& aScrollX, const uint32_t& aScrollY, + const uint32_t& aMaxScrollX, + const uint32_t& aMaxScrollY); bool ConstructChildDocInParentProcess(DocAccessibleChild* aNewChildDoc, uint64_t aUniqueID, uint32_t aMsaaID); bool SendBindChildDoc(DocAccessibleChild* aChildDoc, const uint64_t& aNewParentID); protected: @@ -264,16 +268,45 @@ private: void Dispatch(DocAccessibleChild* aIPCDoc) override { Unused << aIPCDoc->SendRoleChangedEvent(mRole); } a11y::role mRole; }; + struct SerializedScrolling final : public DeferredEvent + { + explicit SerializedScrolling(DocAccessibleChild* aTarget, + uint64_t aID, uint64_t aType, + uint32_t aScrollX, uint32_t aScrollY, + uint32_t aMaxScrollX, uint32_t aMaxScrollY) + : DeferredEvent(aTarget) + , mID(aID) + , mType(aType) + , mScrollX(aScrollX) + , mScrollY(aScrollY) + , mMaxScrollX(aMaxScrollX) + , mMaxScrollY(aMaxScrollY) + {} + + void Dispatch(DocAccessibleChild* aIPCDoc) override + { + Unused << aIPCDoc->SendScrollingEvent(mID, mType, mScrollX, mScrollY, + mMaxScrollX, mMaxScrollY); + } + + uint64_t mID; + uint64_t mType; + uint32_t mScrollX; + uint32_t mScrollY; + uint32_t mMaxScrollX; + uint32_t mMaxScrollY; + }; + struct SerializedEvent final : public DeferredEvent { SerializedEvent(DocAccessibleChild* aTarget, uint64_t aID, uint32_t aType) : DeferredEvent(aTarget) , mID(aID) , mType(aType) {}
--- a/browser/base/content/tabbrowser.js +++ b/browser/base/content/tabbrowser.js @@ -770,42 +770,48 @@ window._gBrowser = { ).URI; return resolvedURI.schemeIs("jar") || resolvedURI.schemeIs("file"); } catch (ex) { // aURI might be invalid. return false; } }, - setIcon(aTab, aIconURL = "", aOriginalURL = aIconURL) { + setIcon(aTab, aIconURL = "", aOriginalURL = aIconURL, aLoadingPrincipal = null) { let makeString = (url) => url instanceof Ci.nsIURI ? url.spec : url; aIconURL = makeString(aIconURL); aOriginalURL = makeString(aOriginalURL); let LOCAL_PROTOCOLS = [ "chrome:", "about:", "resource:", "data:", ]; - if (aIconURL && !LOCAL_PROTOCOLS.some(protocol => aIconURL.startsWith(protocol))) { - console.error(`Attempt to set a remote URL ${aIconURL} as a tab icon.`); + if (aIconURL && !aLoadingPrincipal && !LOCAL_PROTOCOLS.some(protocol => aIconURL.startsWith(protocol))) { + console.error(`Attempt to set a remote URL ${aIconURL} as a tab icon without a loading principal.`); return; } let browser = this.getBrowserForTab(aTab); browser.mIconURL = aIconURL; if (aIconURL != aTab.getAttribute("image")) { if (aIconURL) { + if (aLoadingPrincipal) { + aTab.setAttribute("iconloadingprincipal", aLoadingPrincipal); + } else { + aTab.removeAttribute("iconloadingprincipal"); + } aTab.setAttribute("image", aIconURL); } else { aTab.removeAttribute("image"); + aTab.removeAttribute("iconloadingprincipal"); } this._tabAttrModified(aTab, ["image"]); } // The aOriginalURL argument is currently only used by tests. this._callProgressListeners(browser, "onLinkIconAvailable", [aIconURL, aOriginalURL]); },
--- a/browser/base/content/test/chrome/test_aboutRestartRequired.xul +++ b/browser/base/content/test/chrome/test_aboutRestartRequired.xul @@ -8,17 +8,16 @@ <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> <iframe type="content" id="frame1"/> <iframe type="content" id="frame2" onload="doTest()"/> <script type="application/javascript"><![CDATA[ ChromeUtils.import("resource://gre/modules/Services.jsm"); - ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); SimpleTest.waitForExplicitFinish(); // Load error pages do not fire "load" events, so let's use a progressListener. function waitForErrorPage(frame) { return new Promise(resolve => { let progressListener = { onLocationChange: function(aWebProgress, aRequest, aLocation, aFlags) { @@ -27,18 +26,18 @@ .getInterface(Ci.nsIWebProgress) .removeProgressListener(progressListener, Ci.nsIWebProgress.NOTIFY_LOCATION); resolve(); } }, - QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener, - Ci.nsISupportsWeakReference]) + QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener, + Ci.nsISupportsWeakReference]) }; frame.docShell.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIWebProgress) .addProgressListener(progressListener, Ci.nsIWebProgress.NOTIFY_LOCATION); }); }
--- a/browser/components/payments/content/paymentDialogFrameScript.js +++ b/browser/components/payments/content/paymentDialogFrameScript.js @@ -71,18 +71,18 @@ let PaymentFrameScript = { * Expose privileged utility functions to the unprivileged page. */ exposeUtilityFunctions() { let waivedContent = Cu.waiveXrays(content); let PaymentDialogUtils = { DEFAULT_REGION: FormAutofill.DEFAULT_REGION, supportedCountries: FormAutofill.supportedCountries, - getAddressLabel(address) { - return FormAutofillUtils.getAddressLabel(address); + getAddressLabel(address, addressFields = null) { + return FormAutofillUtils.getAddressLabel(address, addressFields); }, isCCNumber(value) { return FormAutofillUtils.isCCNumber(value); }, getFormFormat(country) { let format = FormAutofillUtils.getFormFormat(country);
--- a/browser/components/payments/res/components/address-option.js +++ b/browser/components/payments/res/components/address-option.js @@ -52,18 +52,18 @@ export default class AddressOption exten connectedCallback() { for (let name of ["name", "street-address", "email", "tel"]) { this.appendChild(this[`_${name}`]); } super.connectedCallback(); } - static formatSingleLineLabel(address) { - return PaymentDialogUtils.getAddressLabel(address); + static formatSingleLineLabel(address, addressFields) { + return PaymentDialogUtils.getAddressLabel(address, addressFields); } render() { // Fall back to empty strings to prevent 'null' from appearing. this._name.textContent = this.name || ""; this["_street-address"].textContent = `${this.streetAddress || ""} ${this.addressLevel2 || ""} ` + `${this.addressLevel1 || ""} ${this.postalCode || ""} ${this.country || ""}`;
--- a/browser/components/payments/res/containers/address-picker.js +++ b/browser/components/payments/res/containers/address-picker.js @@ -95,17 +95,20 @@ export default class AddressPicker exten let val = address[key]; if (val) { optionEl.setAttribute(key, val); } else { optionEl.removeAttribute(key); } } - optionEl.textContent = AddressOption.formatSingleLineLabel(address); + // fieldNames getter is not used here because it returns a default array with + // attributes even when "address-fields" observed attribute is null. + let addressFields = this.getAttribute("address-fields"); + optionEl.textContent = AddressOption.formatSingleLineLabel(address, addressFields); desiredOptions.push(optionEl); } this.dropdown.popupBox.textContent = ""; for (let option of desiredOptions) { this.dropdown.popupBox.appendChild(option); }
--- a/browser/components/payments/res/unprivileged-fallbacks.js +++ b/browser/components/payments/res/unprivileged-fallbacks.js @@ -17,17 +17,22 @@ var log = { error: console.error.bind(console, "paymentRequest.xhtml:"), warn: console.warn.bind(console, "paymentRequest.xhtml:"), info: console.info.bind(console, "paymentRequest.xhtml:"), debug: console.debug.bind(console, "paymentRequest.xhtml:"), }; var PaymentDialogUtils = { - getAddressLabel(address) { + getAddressLabel(address, addressFields = null) { + if (addressFields) { + let requestedFields = addressFields.trim().split(/\s+/); + return requestedFields.filter(f => f && address[f]).map(f => address[f]).join(", ") + + ` (${address.guid})`; + } return `${address.name} (${address.guid})`; }, isCCNumber(str) { return !!str.replace(/[-\s]/g, "").match(/^\d{9,}$/); }, DEFAULT_REGION: "US", supportedCountries: ["US", "CA"], getFormFormat(country) {
--- a/browser/components/payments/test/browser/browser_address_edit.js +++ b/browser/components/payments/test/browser/browser_address_edit.js @@ -370,16 +370,99 @@ add_task(async function test_edit_payer_ info("clicking cancel"); spawnPaymentDialogTask(frame, PTU.DialogContentTasks.manuallyClickCancel); await BrowserTestUtils.waitForCondition(() => win.closed, "dialog should be closed"); }); }); +add_task(async function test_shipping_address_picker() { + await setup(); + await BrowserTestUtils.withNewTab({ + gBrowser, + url: BLANK_PAGE_URL, + }, async browser => { + let {win, frame} = + await setupPaymentDialog(browser, { + methodData: [PTU.MethodData.basicCard], + details: PTU.Details.total60USD, + options: PTU.Options.requestShippingOption, + merchantTaskFn: PTU.ContentTasks.createAndShowRequest, + } + ); + + await spawnPaymentDialogTask(frame, async function test_picker_option_label(address) { + let { + PaymentTestUtils: PTU, + } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {}); + ChromeUtils.import("resource://formautofill/FormAutofillUtils.jsm"); + + let state = await PTU.DialogContentUtils.waitForState(content, (state) => { + return Object.keys(state.savedAddresses).length == 1; + }, "One saved addresses when starting test"); + let savedAddress = Object.values(state.savedAddresses)[0]; + + let selector = "address-picker[selected-state-key='selectedShippingAddress']"; + let picker = content.document.querySelector(selector); + let option = Cu.waiveXrays(picker).dropdown.popupBox.children[0]; + ok(option.textContent, + FormAutofillUtils.getAddressLabel(savedAddress, null), + "Shows correct shipping option label"); + }); + + info("clicking cancel"); + spawnPaymentDialogTask(frame, PTU.DialogContentTasks.manuallyClickCancel); + + await BrowserTestUtils.waitForCondition(() => win.closed, "dialog should be closed"); + }); +}); + +add_task(async function test_payer_address_picker() { + await BrowserTestUtils.withNewTab({ + gBrowser, + url: BLANK_PAGE_URL, + }, async browser => { + let {win, frame} = + await setupPaymentDialog(browser, { + methodData: [PTU.MethodData.basicCard], + details: PTU.Details.total60USD, + options: PTU.Options.requestPayerNameEmailAndPhone, + merchantTaskFn: PTU.ContentTasks.createAndShowRequest, + } + ); + + await spawnPaymentDialogTask(frame, async function test_picker_option_label(address) { + let { + PaymentTestUtils: PTU, + } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {}); + ChromeUtils.import("resource://formautofill/FormAutofillUtils.jsm"); + + let state = await PTU.DialogContentUtils.waitForState(content, (state) => { + return Object.keys(state.savedAddresses).length == 1; + }, "One saved addresses when starting test"); + let savedAddress = Object.values(state.savedAddresses)[0]; + + let selector = "address-picker[selected-state-key='selectedPayerAddress']"; + let picker = content.document.querySelector(selector); + let option = Cu.waiveXrays(picker).dropdown.popupBox.children[0]; + is(option.textContent.includes("32 Vassar Street"), false, + "Payer option label does not contain street address"); + ok(option.textContent, + FormAutofillUtils.getAddressLabel(savedAddress, "name tel email"), + "Shows correct payer option label"); + }); + + info("clicking cancel"); + spawnPaymentDialogTask(frame, PTU.DialogContentTasks.manuallyClickCancel); + + await BrowserTestUtils.waitForCondition(() => win.closed, "dialog should be closed"); + }); +}); + /* * Test that we can correctly add an address from a private window */ add_task(async function test_private_persist_addresses() { let prefilledGuids = await setup(); is((await formAutofillStorage.addresses.getAll()).length, 1, "Setup results in 1 stored address at start of test");
--- a/browser/components/sessionstore/SessionStore.jsm +++ b/browser/components/sessionstore/SessionStore.jsm @@ -1977,17 +1977,16 @@ var SessionStoreInternal = { let tabTitle = aTab.label; let {permanentKey} = aTab.linkedBrowser; let tabData = { permanentKey, state: tabState, title: tabTitle, image: tabbrowser.getIcon(aTab), - iconLoadingPrincipal: Utils.serializePrincipal(aTab.linkedBrowser.contentPrincipal), pos: aTab._tPos, closedAt: Date.now() }; let closedTabs = this._windows[aWindow.__SSi]._closedTabs; // Determine whether the tab contains any information worth saving. Note // that there might be pending state changes queued in the child that @@ -2774,19 +2773,17 @@ var SessionStoreInternal = { win.gBrowser.setInitialTabTitle(tab, activePageData.title, { isContentTitle: true }); } else if (activePageData.url != "about:blank") { win.gBrowser.setInitialTabTitle(tab, activePageData.url); } } // Restore the tab icon. if ("image" in tabData) { - // Use the serialized contentPrincipal with the new icon load. - let loadingPrincipal = Utils.deserializePrincipal(tabData.iconLoadingPrincipal); - win.gBrowser.setIcon(tab, tabData.image, loadingPrincipal); + win.gBrowser.setIcon(tab, tabData.image, undefined, tabData.iconLoadingPrincipal); TabStateCache.update(browser, { image: null, iconLoadingPrincipal: null }); } }, // This method deletes all the closedTabs matching userContextId. _forgetTabsWithUserContextId(userContextId) { let windowsEnum = Services.wm.getEnumerator("navigator:browser"); while (windowsEnum.hasMoreElements()) {
--- a/browser/components/sessionstore/TabAttributes.jsm +++ b/browser/components/sessionstore/TabAttributes.jsm @@ -7,20 +7,17 @@ var EXPORTED_SYMBOLS = ["TabAttributes"]; // We never want to directly read or write these attributes. // 'image' should not be accessed directly but handled by using the // gBrowser.getIcon()/setIcon() methods. // 'muted' should not be accessed directly but handled by using the // tab.linkedBrowser.audioMuted/toggleMuteAudio methods. // 'pending' is used internal by sessionstore and managed accordingly. -// 'iconloadingprincipal' is same as 'image' that it should be handled by -// using the gBrowser.getIcon()/setIcon() methods. -const ATTRIBUTES_TO_SKIP = new Set(["image", "muted", "pending", "iconloadingprincipal", - "skipbackgroundnotify"]); +const ATTRIBUTES_TO_SKIP = new Set(["image", "muted", "pending", "skipbackgroundnotify"]); // A set of tab attributes to persist. We will read a given list of tab // attributes when collecting tab data and will re-set those attributes when // the given tab data is restored to a new tab. var TabAttributes = Object.freeze({ persist(name) { return TabAttributesInternal.persist(name); },
--- a/browser/components/sessionstore/TabState.jsm +++ b/browser/components/sessionstore/TabState.jsm @@ -7,18 +7,16 @@ var EXPORTED_SYMBOLS = ["TabState"]; ChromeUtils.defineModuleGetter(this, "PrivacyFilter", "resource://gre/modules/sessionstore/PrivacyFilter.jsm"); ChromeUtils.defineModuleGetter(this, "TabStateCache", "resource:///modules/sessionstore/TabStateCache.jsm"); ChromeUtils.defineModuleGetter(this, "TabAttributes", "resource:///modules/sessionstore/TabAttributes.jsm"); -ChromeUtils.defineModuleGetter(this, "Utils", - "resource://gre/modules/sessionstore/Utils.jsm"); /** * Module that contains tab state collection methods. */ var TabState = Object.freeze({ update(browser, data) { TabStateInternal.update(browser, data); }, @@ -120,21 +118,16 @@ var TabStateInternal = { // be read from the tab/browser every time we collect data. // Store the tab icon. if (!("image" in tabData)) { let tabbrowser = tab.ownerGlobal.gBrowser; tabData.image = tabbrowser.getIcon(tab); } - // Store the serialized contentPrincipal of this tab to use for the icon. - if (!("iconLoadingPrincipal" in tabData)) { - tabData.iconLoadingPrincipal = Utils.serializePrincipal(browser.mIconLoadingPrincipal); - } - // If there is a userTypedValue set, then either the user has typed something // in the URL bar, or a new tab was opened with a URI to load. // If so, we also track whether we were still in the process of loading something. if (!("userTypedValue" in tabData) && browser.userTypedValue) { tabData.userTypedValue = browser.userTypedValue; // We always used to keep track of the loading state as an integer, where // '0' indicated the user had typed since the last load (or no load was // ongoing), and any positive value indicated we had started a load since
--- a/browser/components/sessionstore/test/browser.ini +++ b/browser/components/sessionstore/test/browser.ini @@ -52,16 +52,17 @@ support-files = browser_911547_sample.html^headers^ restore_redirect_http.html restore_redirect_http.html^headers^ restore_redirect_js.html restore_redirect_target.html browser_1234021_page.html browser_1284886_suspend_tab.html browser_1284886_suspend_tab_2.html + empty.html #NB: the following are disabled # browser_464620_a.html # browser_464620_b.html # browser_464620_xd.html #disabled-for-intermittent-failures--bug-766044, browser_459906_empty.html @@ -102,16 +103,17 @@ skip-if = (verify && debug) [browser_formdata_xpath.js] [browser_frametree.js] [browser_frame_history.js] skip-if = (verify && (os == 'win' || os == 'mac')) [browser_global_store.js] [browser_history_persist.js] [browser_label_and_icon.js] [browser_merge_closed_tabs.js] +[browser_old_favicon.js] [browser_page_title.js] [browser_pending_tabs.js] [browser_privatetabs.js] [browser_purge_shistory.js] skip-if = e10s # Bug 1271024 [browser_replace_load.js] [browser_restore_redirect.js] [browser_restore_cookies_noOriginAttributes.js]
new file mode 100644 --- /dev/null +++ b/browser/components/sessionstore/test/browser_old_favicon.js @@ -0,0 +1,44 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Ensure that we can restore old style favicon and principals. + */ +add_task(async function test_label_and_icon() { + let helper = Cc["@mozilla.org/network/serialization-helper;1"].getService(Ci.nsISerializationHelper); + + // Make sure that tabs are restored on demand as otherwise the tab will start + // loading immediately and override the icon. + await SpecialPowers.pushPrefEnv({ + set: [["browser.sessionstore.restore_on_demand", true]], + }); + + // Create a new tab. + let tab = BrowserTestUtils.addTab(gBrowser, "http://www.example.com/browser/browser/components/sessionstore/test/empty.html"); + let browser = tab.linkedBrowser; + await promiseBrowserLoaded(browser); + + let contentPrincipal = browser.contentPrincipal; + let serializedPrincipal = helper.serializeToString(contentPrincipal); + + // Retrieve the tab state. + await TabStateFlusher.flush(browser); + let state = JSON.parse(ss.getTabState(tab)); + state.image = "http://www.example.com/favicon.ico"; + state.iconLoadingPrincipal = serializedPrincipal; + + BrowserTestUtils.removeTab(tab); + + // Open a new tab to restore into. + tab = BrowserTestUtils.addTab(gBrowser, "about:blank"); + ss.setTabState(tab, state); + await promiseTabRestoring(tab); + + // Check that label and icon are set for the restoring tab. + is(gBrowser.getIcon(tab), "http://www.example.com/favicon.ico", "icon is set"); + is(tab.getAttribute("image"), "http://www.example.com/favicon.ico", "tab image is set"); + is(tab.getAttribute("iconloadingprincipal"), serializedPrincipal, "tab image loading principal is set"); + + // Cleanup. + BrowserTestUtils.removeTab(tab); +});
new file mode 100644 --- /dev/null +++ b/browser/components/sessionstore/test/empty.html @@ -0,0 +1,6 @@ +<!DOCTYPE html> + +<html> + <body> + </body> +</html>
--- a/browser/config/mozconfigs/linux64/plain-opt +++ b/browser/config/mozconfigs/linux64/plain-opt @@ -4,13 +4,13 @@ export LLVM_CONFIG="${TOOLTOOL_DIR}/clan CARGO="${TOOLTOOL_DIR}/rustc/bin/cargo" RUSTC="${TOOLTOOL_DIR}/rustc/bin/rustc" RUSTDOC="${TOOLTOOL_DIR}/rustc/bin/rustdoc" RUSTFMT="${TOOLTOOL_DIR}/rustc/bin/rustfmt" CBINDGEN="${TOOLTOOL_DIR}/cbindgen/cbindgen" export NODEJS="${TOOLTOOL_DIR}/node/bin/node" -CC="${TOOLTOOL_DIR}/gcc/bin/gcc" -CXX="${TOOLTOOL_DIR}/gcc/bin/g++" +CC="${TOOLTOOL_DIR}/clang/bin/clang" +CXX="${TOOLTOOL_DIR}/clang/bin/clang++" mk_add_options "export PATH=${TOOLTOOL_DIR}/gcc/bin:${PATH}" mk_add_options "export LD_LIBRARY_PATH=${TOOLTOOL_DIR}/gcc/lib64:${TOOLTOOL_DIR}/gcc/lib32:${TOOLTOOL_DIR}/gcc/lib"
--- a/browser/extensions/formautofill/FormAutofillUtils.jsm +++ b/browser/extensions/formautofill/FormAutofillUtils.jsm @@ -241,51 +241,56 @@ this.FormAutofillUtils = { getAddressSeparator() { // The separator should be based on the L10N address format, and using a // white space is a temporary solution. return " "; }, /** - * Get address display label. It should display up to two pieces of - * information, separated by a comma. + * Get address display label. It should display information separated + * by a comma. * * @param {object} address + * @param {string?} addressFields Override the fields which can be displayed, but not the order. * @returns {string} */ - getAddressLabel(address) { + getAddressLabel(address, addressFields = null) { // TODO: Implement a smarter way for deciding what to display // as option text. Possibly improve the algorithm in // ProfileAutoCompleteResult.jsm and reuse it here. - const fieldOrder = [ + let fieldOrder = [ "name", "-moz-street-address-one-line", // Street address "address-level2", // City/Town "organization", // Company or organization name "address-level1", // Province/State (Standardized code if possible) "country-name", // Country name "postal-code", // Postal code "tel", // Phone number "email", // Email address ]; address = {...address}; let parts = []; + if (addressFields) { + let requiredFields = addressFields.trim().split(/\s+/); + fieldOrder = fieldOrder.filter(name => requiredFields.includes(name)); + } if (address["street-address"]) { address["-moz-street-address-one-line"] = this.toOneLineAddress( address["street-address"] ); } for (const fieldName of fieldOrder) { let string = address[fieldName]; if (string) { parts.push(string); } - if (parts.length == 2) { + if (parts.length == 2 && !addressFields) { break; } } return parts.join(", "); }, toOneLineAddress(address, delimiter = "\n") { let array = typeof address == "string" ? address.split(delimiter) : address;
--- a/build/autoconf/nspr-build.m4 +++ b/build/autoconf/nspr-build.m4 @@ -151,18 +151,18 @@ if test -n "$MOZ_SYSTEM_NSPR" -o -n "$NS AC_MSG_ERROR([system NSPR does not support PR_STATIC_ASSERT or including prtypes.h does not provide it])) AC_TRY_COMPILE([#include "prtypes.h"], [#ifndef PR_UINT64 #error PR_UINT64 not defined or requires including prtypes.h #endif], , AC_MSG_ERROR([system NSPR does not support PR_UINT64 or including prtypes.h does not provide it])) CFLAGS=$_SAVE_CFLAGS - NSPR_INCLUDE_DIR=`echo ${NSPR_CFLAGS} | sed -e 's/.*-I\([^ ]*\).*/\1/'` - NSPR_LIB_DIR=`echo ${NSPR_LIBS} | sed -e 's/.*-L\([^ ]*\).*/\1/'` + NSPR_INCLUDE_DIR=`echo ${NSPR_CFLAGS} | sed -e 's/.*-I\([[^ ]]*\).*/\1/'` + NSPR_LIB_DIR=`echo ${NSPR_LIBS} | sed -e 's/.*-L\([[^ ]]*\).*/\1/'` elif test -z "$JS_POSIX_NSPR"; then NSPR_INCLUDE_DIR="${DIST}/include/nspr" NSPR_CFLAGS="-I${NSPR_INCLUDE_DIR}" if test -n "$GNU_CC"; then if test -n "$MOZ_FOLD_LIBS"; then NSPR_LIB_DIR=${DIST}/lib else NSPR_LIB_DIR=${DIST}/bin
--- a/build/autoconf/sanitize.m4 +++ b/build/autoconf/sanitize.m4 @@ -1,34 +1,18 @@ dnl This Source Code Form is subject to the terms of the Mozilla Public dnl License, v. 2.0. If a copy of the MPL was not distributed with this dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. AC_DEFUN([MOZ_CONFIG_SANITIZE], [ dnl ======================================================== -dnl = Link Time Optimization (LTO) -dnl ======================================================== -if test -n "$MOZ_LTO"; then - MOZ_LLVM_HACKS=1 - AC_DEFINE(MOZ_LTO) - MOZ_PATH_PROG(LLVM_SYMBOLIZER, llvm-symbolizer) - - CFLAGS="$CFLAGS $MOZ_LTO_CFLAGS" - CPPFLAGS="$CPPFLAGS $MOZ_LTO_CFLAGS" - CXXFLAGS="$CXXFLAGS $MOZ_LTO_CFLAGS" - LDFLAGS="$LDFLAGS $MOZ_LTO_LDFLAGS" -fi -AC_SUBST(MOZ_LTO) - -dnl ======================================================== dnl = Use Address Sanitizer dnl ======================================================== if test -n "$MOZ_ASAN"; then - MOZ_LLVM_HACKS=1 if test -n "$CLANG_CL"; then # Look for the ASan runtime binary if test "$CPU_ARCH" = "x86_64"; then MOZ_CLANG_RT_ASAN_LIB=clang_rt.asan_dynamic-x86_64.dll else MOZ_CLANG_RT_ASAN_LIB=clang_rt.asan_dynamic-i386.dll fi # We use MOZ_PATH_PROG in order to get a Windows style path. @@ -56,17 +40,16 @@ AC_SUBST(MOZ_ASAN) dnl ======================================================== dnl = Use Memory Sanitizer dnl ======================================================== MOZ_ARG_ENABLE_BOOL(memory-sanitizer, [ --enable-memory-sanitizer Enable Memory Sanitizer (default=no)], MOZ_MSAN=1, MOZ_MSAN= ) if test -n "$MOZ_MSAN"; then - MOZ_LLVM_HACKS=1 CFLAGS="-fsanitize=memory -fsanitize-memory-track-origins $CFLAGS" CXXFLAGS="-fsanitize=memory -fsanitize-memory-track-origins $CXXFLAGS" if test -z "$CLANG_CL"; then LDFLAGS="-fsanitize=memory -fsanitize-memory-track-origins $LDFLAGS" fi AC_DEFINE(MOZ_MSAN) MOZ_PATH_PROG(LLVM_SYMBOLIZER, llvm-symbolizer) fi @@ -75,17 +58,16 @@ AC_SUBST(MOZ_MSAN) dnl ======================================================== dnl = Use Thread Sanitizer dnl ======================================================== MOZ_ARG_ENABLE_BOOL(thread-sanitizer, [ --enable-thread-sanitizer Enable Thread Sanitizer (default=no)], MOZ_TSAN=1, MOZ_TSAN= ) if test -n "$MOZ_TSAN"; then - MOZ_LLVM_HACKS=1 CFLAGS="-fsanitize=thread $CFLAGS" CXXFLAGS="-fsanitize=thread $CXXFLAGS" if test -z "$CLANG_CL"; then LDFLAGS="-fsanitize=thread $LDFLAGS" fi AC_DEFINE(MOZ_TSAN) MOZ_PATH_PROG(LLVM_SYMBOLIZER, llvm-symbolizer) fi @@ -100,17 +82,16 @@ MOZ_ARG_ENABLE_BOOL(signed-overflow-sani MOZ_SIGNED_OVERFLOW_SANITIZE=1, MOZ_SIGNED_OVERFLOW_SANITIZE= ) MOZ_ARG_ENABLE_BOOL(unsigned-overflow-sanitizer, [ --enable-unsigned-overflow-sanitizer Enable UndefinedBehavior Sanitizer (Unsigned Integer Overflow Parts, default=no)], MOZ_UNSIGNED_OVERFLOW_SANITIZE=1, MOZ_UNSIGNED_OVERFLOW_SANITIZE= ) if test -n "$MOZ_SIGNED_OVERFLOW_SANITIZE$MOZ_UNSIGNED_OVERFLOW_SANITIZE"; then - MOZ_LLVM_HACKS=1 MOZ_UBSAN=1 SANITIZER_BLACKLISTS="" if test -n "$MOZ_SIGNED_OVERFLOW_SANITIZE"; then SANITIZER_BLACKLISTS="-fsanitize-blacklist=$_topsrcdir/build/sanitizers/ubsan_signed_overflow_blacklist.txt $SANITIZER_BLACKLISTS" CFLAGS="-fsanitize=signed-integer-overflow $CFLAGS" CXXFLAGS="-fsanitize=signed-integer-overflow $CXXFLAGS" if test -z "$CLANG_CL"; then LDFLAGS="-fsanitize=signed-integer-overflow $LDFLAGS" @@ -134,28 +115,14 @@ fi AC_SUBST(MOZ_SIGNED_OVERFLOW_SANITIZE) AC_SUBST(MOZ_UNSIGNED_OVERFLOW_SANITIZE) AC_SUBST(MOZ_UBSAN) # The LLVM symbolizer is used by all sanitizers AC_SUBST(LLVM_SYMBOLIZER) dnl ======================================================== -dnl = Enable hacks required for LLVM instrumentations -dnl ======================================================== -MOZ_ARG_ENABLE_BOOL(llvm-hacks, -[ --enable-llvm-hacks Enable workarounds required for several LLVM instrumentations (default=no)], - MOZ_LLVM_HACKS=1, - MOZ_LLVM_HACKS= ) -if test -n "$MOZ_LLVM_HACKS"; then - MOZ_NO_WLZDEFS=1 - MOZ_CFLAGS_NSS=1 -fi -AC_SUBST(MOZ_NO_WLZDEFS) -AC_SUBST(MOZ_CFLAGS_NSS) - -dnl ======================================================== dnl = Test for whether the compiler is compatible with the dnl = given sanitize options. dnl ======================================================== AC_TRY_LINK(,,,AC_MSG_ERROR([compiler is incompatible with sanitize options])) ])
--- a/build/moz.configure/toolchain.configure +++ b/build/moz.configure/toolchain.configure @@ -928,29 +928,29 @@ def compiler(language, host_or_target, c raise FatalCheckError( 'Only clang/llvm 3.6 or newer is supported.') if info.type == 'msvc': if info.version < '19.13.26128': raise FatalCheckError( 'This version (%s) of the MSVC compiler is not ' 'supported.\n' - 'You must install Visual C++ 2017 Update 6 in ' - 'order to build.\n' + 'You must install Visual C++ 2017 Update 6 or ' + 'Update 8 or later in order to build.\n' 'See https://developer.mozilla.org/en/' 'Windows_Build_Prerequisites' % info.version) # MSVC version 15.7 and the previews for 15.8, at least, # can't build Firefox. - if info.version >= '19.14.0': + if info.version >= '19.14.0' and info.version < '19.15.0': raise FatalCheckError( 'This version (%s) of the MSVC compiler is not ' 'supported due to compiler bugs.\n' - 'You must install Visual C++ 2017 Update 6 in ' - 'order to build.\n' + 'You must install Visual C++ 2017 Update 6 or ' + 'Update 8 or later in order to build.\n' 'See https://developer.mozilla.org/en/' 'Windows_Build_Prerequisites' % info.version) if info.flags: raise FatalCheckError( 'Unknown compiler or compiler not supported.') return namespace( @@ -1398,18 +1398,20 @@ def lto(value, pgo, c_compiler): return namespace( enabled=enabled, cflags=cflags, ldflags=ldflags ) add_old_configure_assignment('MOZ_LTO', lto.enabled) -add_old_configure_assignment('MOZ_LTO_CFLAGS', lto.cflags) -add_old_configure_assignment('MOZ_LTO_LDFLAGS', lto.ldflags) +set_config('MOZ_LTO', lto.enabled) +set_define('MOZ_LTO', lto.enabled) +set_config('MOZ_LTO_CFLAGS', lto.cflags) +set_config('MOZ_LTO_LDFLAGS', lto.ldflags) # ASAN # ============================================================== js_option('--enable-address-sanitizer', help='Enable Address Sanitizer') @depends_if('--enable-address-sanitizer')
--- a/build/unix/elfhack/inject/moz.build +++ b/build/unix/elfhack/inject/moz.build @@ -33,12 +33,12 @@ for v in ('OS_CPPFLAGS', 'OS_CFLAGS', 'D for flag in COMPILE_FLAGS[v]: if flag == '-isystem': flags.append(''.join(COMPILE_FLAGS[v][idx:idx + 2])) elif flag.startswith(('-m', '-I', '-isystem')): flags.append(flag) idx += 1 COMPILE_FLAGS[v] = flags -COMPILE_FLAGS['OS_CFLAGS'] += ['-O2', '-fno-stack-protector'] +COMPILE_FLAGS['OS_CFLAGS'] += ['-O2', '-fno-stack-protector', '-fno-lto'] AllowCompilerWarnings() NoVisibilityFlags()
--- a/build/unix/elfhack/moz.build +++ b/build/unix/elfhack/moz.build @@ -9,21 +9,19 @@ DIRS += ['inject'] if not CONFIG['CROSS_COMPILE']: SOURCES += [ 'dummy.c', 'test-array.c', 'test-ctors.c', ] - for f in CONFIG['OS_CFLAGS']: - if f.startswith('-flto'): - SOURCES['dummy.c'].flags += ['-fno-lto'] - SOURCES['test-array.c'].flags += ['-fno-lto'] - SOURCES['test-ctors.c'].flags += ['-fno-lto'] + SOURCES['dummy.c'].flags += ['-fno-lto'] + SOURCES['test-array.c'].flags += ['-fno-lto'] + SOURCES['test-ctors.c'].flags += ['-fno-lto'] HOST_SOURCES += [ 'elf.cpp', 'elfhack.cpp', ] HostProgram('elfhack')
--- a/build/unix/mozconfig.linux +++ b/build/unix/mozconfig.linux @@ -2,17 +2,17 @@ if [ "x$IS_NIGHTLY" = "xyes" ]; then # Some nightlies (eg: Mulet) don't want these set. MOZ_AUTOMATION_UPDATE_PACKAGING=${MOZ_AUTOMATION_UPDATE_PACKAGING-1} fi . "$topsrcdir/build/mozconfig.common" TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir} -if [ -n "$FORCE_GCC" ]; then +if [ -n "$FORCE_GCC" -o -n "$MOZ_PGO" ]; then CC="$TOOLTOOL_DIR/gcc/bin/gcc" CXX="$TOOLTOOL_DIR/gcc/bin/g++" else CC="$TOOLTOOL_DIR/clang/bin/clang" CXX="$TOOLTOOL_DIR/clang/bin/clang++" fi # We want to make sure we use binutils and other binaries in the tooltool
--- a/config/config.mk +++ b/config/config.mk @@ -194,22 +194,22 @@ INCLUDES = \ -I$(srcdir) \ -I$(CURDIR) \ $(LOCAL_INCLUDES) \ -I$(ABS_DIST)/include \ $(NULL) include $(MOZILLA_DIR)/config/static-checking-config.mk -LDFLAGS = $(COMPUTED_LDFLAGS) $(PGO_LDFLAGS) $(MK_LDFLAGS) +LDFLAGS = $(MOZ_LTO_LDFLAGS) $(COMPUTED_LDFLAGS) $(PGO_LDFLAGS) $(MK_LDFLAGS) -COMPILE_CFLAGS = $(COMPUTED_CFLAGS) $(PGO_CFLAGS) $(_DEPEND_CFLAGS) $(MK_COMPILE_DEFINES) -COMPILE_CXXFLAGS = $(COMPUTED_CXXFLAGS) $(PGO_CFLAGS) $(_DEPEND_CFLAGS) $(MK_COMPILE_DEFINES) -COMPILE_CMFLAGS = $(OS_COMPILE_CMFLAGS) $(MOZBUILD_CMFLAGS) -COMPILE_CMMFLAGS = $(OS_COMPILE_CMMFLAGS) $(MOZBUILD_CMMFLAGS) +COMPILE_CFLAGS = $(MOZ_LTO_CFLAGS) $(COMPUTED_CFLAGS) $(PGO_CFLAGS) $(_DEPEND_CFLAGS) $(MK_COMPILE_DEFINES) +COMPILE_CXXFLAGS = $(MOZ_LTO_CFLAGS) $(COMPUTED_CXXFLAGS) $(PGO_CFLAGS) $(_DEPEND_CFLAGS) $(MK_COMPILE_DEFINES) +COMPILE_CMFLAGS = $(MOZ_LTO_CFLAGS) $(OS_COMPILE_CMFLAGS) $(MOZBUILD_CMFLAGS) +COMPILE_CMMFLAGS = $(MOZ_LTO_CFLAGS) $(OS_COMPILE_CMMFLAGS) $(MOZBUILD_CMMFLAGS) ASFLAGS = $(COMPUTED_ASFLAGS) SFLAGS = $(COMPUTED_SFLAGS) HOST_CFLAGS = $(COMPUTED_HOST_CFLAGS) $(_DEPEND_CFLAGS) HOST_CXXFLAGS = $(COMPUTED_HOST_CXXFLAGS) $(_DEPEND_CFLAGS) HOST_C_LDFLAGS = $(COMPUTED_HOST_C_LDFLAGS) HOST_CXX_LDFLAGS = $(COMPUTED_HOST_CXX_LDFLAGS)
--- a/config/rules.mk +++ b/config/rules.mk @@ -383,18 +383,27 @@ ifneq (,$(filter ml%,$(AS))) ASOUTOPTION = -Fo# eol else ASOUTOPTION = -o # eol endif ifeq (,$(CROSS_COMPILE)) HOST_OUTOPTION = $(OUTOPTION) else +# Windows-to-Windows cross compiles should always use MSVC-style options for +# host compiles. +ifeq (WINNT_WINNT,$(HOST_OS_ARCH)_$(OS_ARCH)) +ifneq (,$(filter-out msvc clang-cl,$(HOST_CC_TYPE))) +$(error MSVC-style compilers should be used for host compilations!) +endif +HOST_OUTOPTION = -Fo# eol +else HOST_OUTOPTION = -o # eol endif +endif ################################################################################ # Ensure the build config is up to date. This is done automatically when builds # are performed through |mach build|. The check here is to catch people not # using mach. If we ever enforce builds through mach, this code can be removed. ifndef MOZBUILD_BACKEND_CHECKED ifndef MACH ifndef TOPLEVEL_BUILD
--- a/config/tests/unit-nsinstall.py +++ b/config/tests/unit-nsinstall.py @@ -163,17 +163,17 @@ class TestNsinstall(unittest.TestCase): testdir = self.mkdirs(u"\u4241\u1D04\u1414") # We don't use subprocess because it can't handle Unicode on # Windows <http://bugs.python.org/issue1759845>. mozprocess calls # CreateProcessW directly so it's perfect. p = processhandler.ProcessHandlerMixin([sys.executable, NSINSTALL_PATH, testfile, testdir]) p.run() - rv = p.waitForFinish() + rv = p.wait() self.assertEqual(rv, 0) destfile = os.path.join(testdir, filename) self.assert_(os.path.isfile(destfile)) # TODO: implement -R, -l, -L and test them!
--- a/devtools/client/inspector/markup/test/browser_markup_events-windowed-host.js +++ b/devtools/client/inspector/markup/test/browser_markup_events-windowed-host.js @@ -46,18 +46,18 @@ async function runTests(inspector) { await onInspectorUpdated; ok(tooltip.isVisible(), "EventTooltip visible."); onInspectorUpdated = inspector.once("inspector-updated"); const onTooltipHidden = tooltip.once("hidden"); info("Click on another tag to hide the event tooltip"); - const h1 = await getContainerForSelector("h1", inspector); - const tag = h1.elt.querySelector(".tag"); + const script = await getContainerForSelector("script", inspector); + const tag = script.elt.querySelector(".tag"); EventUtils.synthesizeMouseAtCenter(tag, {}, inspector.markup.doc.defaultView); await onTooltipHidden; // New node is selected, wait for inspector-updated. await onInspectorUpdated; ok(!tooltip.isVisible(), "EventTooltip hidden."); }
--- a/devtools/client/themes/markup.css +++ b/devtools/client/themes/markup.css @@ -389,29 +389,35 @@ ul.children + .tag-line::before { /* Applicable to the DOCTYPE */ .doctype { font-style: italic; } /* Markup Badges */ .markup-badge { display: inline-block; - font-size: 9px; + font-size: 10px; font-weight: normal; line-height: 11px; vertical-align: 1px; border: 1px solid var(--markup-badge-border-color); border-radius: 3px; padding: 0px 2px; margin-inline-start: 5px; -moz-user-select: none; background-color: var(--markup-badge-background-color); color: var(--markup-badge-color); } +@media (min-resolution: 1.1dppx) { + .markup-badge { + font-size: 9px; + } +} + .markup-badge.active { background-color: var(--markup-badge-active-background-color); border-color: var(--theme-selection-color); color: var(--theme-selection-color); } .markup-badge[data-custom], .markup-badge[data-display="flex"].interactive,
--- a/devtools/shared/heapsnapshot/tests/gtest/DeserializedStackFrameUbiStackFrames.cpp +++ b/devtools/shared/heapsnapshot/tests/gtest/DeserializedStackFrameUbiStackFrames.cpp @@ -3,16 +3,17 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ // Test that the `JS::ubi::StackFrame`s we create from // `mozilla::devtools::DeserializedStackFrame` instances look and behave as we would // like. #include "DevTools.h" +#include "js/SavedFrameAPI.h" #include "js/TypeDecls.h" #include "mozilla/devtools/DeserializedNode.h" using testing::Field; using testing::ReturnRef; // A mock DeserializedStackFrame for testing. struct MockDeserializedStackFrame : public DeserializedStackFrame
--- a/dom/base/BodyUtil.cpp +++ b/dom/base/BodyUtil.cpp @@ -13,16 +13,17 @@ #include "nsCharSeparatedTokenizer.h" #include "nsDOMString.h" #include "nsNetUtil.h" #include "nsReadableUtils.h" #include "nsStreamUtils.h" #include "nsStringStream.h" +#include "js/JSON.h" #include "mozilla/ErrorResult.h" #include "mozilla/dom/Exceptions.h" #include "mozilla/dom/FetchUtil.h" #include "mozilla/dom/File.h" #include "mozilla/dom/FormData.h" #include "mozilla/dom/Headers.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/URLSearchParams.h"
--- a/dom/base/ChromeUtils.cpp +++ b/dom/base/ChromeUtils.cpp @@ -1,16 +1,18 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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/. */ #include "ChromeUtils.h" +#include "js/AutoByteString.h" +#include "js/SavedFrameAPI.h" #include "jsfriendapi.h" #include "WrapperFactory.h" #include "mozilla/Base64.h" #include "mozilla/BasePrincipal.h" #include "mozilla/CycleCollectedJSRuntime.h" #include "mozilla/PerformanceMetricsCollector.h" #include "mozilla/Preferences.h"
new file mode 100644 --- /dev/null +++ b/dom/base/VisualViewport.cpp @@ -0,0 +1,140 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "VisualViewport.h" +#include "nsIScrollableFrame.h" +#include "nsIDocShell.h" + +using namespace mozilla; +using namespace mozilla::dom; + +VisualViewport::VisualViewport(nsPIDOMWindowInner* aWindow) + : DOMEventTargetHelper(aWindow) +{ +} + +VisualViewport::~VisualViewport() +{ +} + +/* virtual */ +JSObject* +VisualViewport::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return VisualViewport_Binding::Wrap(aCx, this, aGivenProto); +} + +CSSSize +VisualViewport::VisualViewportSize() const +{ + CSSSize size = CSSSize(0,0); + + nsIPresShell* presShell = GetPresShell(); + if (presShell) { + if (presShell->IsVisualViewportSizeSet()) { + size = CSSRect::FromAppUnits( + presShell->GetVisualViewportSize()); + } else { + nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable(); + if (sf) { + size = CSSRect::FromAppUnits(sf->GetScrollPortRect().Size()); + } + } + } + return size; +} + +double +VisualViewport::Width() const +{ + CSSSize size = VisualViewportSize(); + return size.width; +} + +double +VisualViewport::Height() const +{ + CSSSize size = VisualViewportSize(); + return size.height; +} + +double +VisualViewport::Scale() const +{ + double scale = 1; + nsIPresShell* presShell = GetPresShell(); + if (presShell) { + scale = presShell->GetResolution(); + } + return scale; +} + +CSSPoint +VisualViewport::VisualViewportOffset() const +{ + CSSPoint offset = CSSPoint(0,0); + + nsIPresShell* presShell = GetPresShell(); + if (presShell) { + offset = CSSPoint::FromAppUnits(presShell->GetVisualViewportOffset()); + } + return offset; +} + +CSSPoint +VisualViewport::LayoutViewportOffset() const +{ + CSSPoint offset = CSSPoint(0,0); + + nsIPresShell* presShell = GetPresShell(); + if (presShell) { + nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable(); + if (sf) { + offset = CSSPoint::FromAppUnits(sf->GetScrollPosition()); + } + } + return offset; +} + +double +VisualViewport::PageLeft() const +{ + return VisualViewportOffset().X(); +} + +double +VisualViewport::PageTop() const +{ + return VisualViewportOffset().Y(); +} + +double +VisualViewport::OffsetLeft() const +{ + return PageLeft() - LayoutViewportOffset().X(); +} + +double +VisualViewport::OffsetTop() const +{ + return PageTop() - LayoutViewportOffset().Y(); +} + +nsIPresShell* +VisualViewport::GetPresShell() const +{ + nsCOMPtr<nsPIDOMWindowInner> window = GetOwner(); + if (!window) { + return nullptr; + } + + nsIDocShell* docShell = window->GetDocShell(); + if (!docShell) { + return nullptr; + } + + return docShell->GetPresShell(); +}
new file mode 100644 --- /dev/null +++ b/dom/base/VisualViewport.h @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + + +#ifndef mozilla_dom_VisualViewport_h +#define mozilla_dom_VisualViewport_h + +#include "mozilla/DOMEventTargetHelper.h" +#include "mozilla/dom/VisualViewportBinding.h" +#include "Units.h" +#include "nsIPresShell.h" + +namespace mozilla { +namespace dom { + +/* Visual Viewport API spec: https://wicg.github.io/visual-viewport/#the-visualviewport-interface */ +class VisualViewport final: public mozilla::DOMEventTargetHelper +{ + +public: + explicit VisualViewport(nsPIDOMWindowInner* aWindow); + + double OffsetLeft() const; + double OffsetTop() const; + double PageLeft() const; + double PageTop() const; + double Width() const; + double Height() const; + double Scale() const; + + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + +private: + virtual ~VisualViewport(); + + CSSSize VisualViewportSize() const; + CSSPoint VisualViewportOffset() const; + CSSPoint LayoutViewportOffset() const; + nsIPresShell* GetPresShell() const; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_VisualViewport_h
--- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -222,16 +222,17 @@ EXPORTS.mozilla.dom += [ 'SyncMessageSender.h', 'TabGroup.h', 'Text.h', 'Timeout.h', 'TimeoutHandler.h', 'TimeoutManager.h', 'TreeIterator.h', 'TreeWalker.h', + 'VisualViewport.h', 'WebKitCSSMatrix.h', 'WindowOrientationObserver.h', ] if CONFIG['FUZZING']: EXPORTS.mozilla.dom += [ 'FuzzingFunctions.h', ] @@ -378,16 +379,17 @@ UNIFIED_SOURCES += [ 'TextInputProcessor.cpp', 'ThirdPartyUtil.cpp', 'Timeout.cpp', 'TimeoutBudgetManager.cpp', 'TimeoutExecutor.cpp', 'TimeoutHandler.cpp', 'TimeoutManager.cpp', 'TreeWalker.cpp', + 'VisualViewport.cpp', 'WebKitCSSMatrix.cpp', 'WindowDestroyedEvent.cpp', 'WindowNamedPropertiesHandler.cpp', 'WindowOrientationObserver.cpp', 'XPathGenerator.cpp', ] if CONFIG['MOZ_WEBRTC']:
--- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -15,16 +15,17 @@ #include "harfbuzz/hb.h" #include "imgICache.h" #include "imgIContainer.h" #include "imgINotificationObserver.h" #include "imgLoader.h" #include "imgRequestProxy.h" #include "jsapi.h" #include "jsfriendapi.h" +#include "js/JSON.h" #include "js/Value.h" #include "Layers.h" #include "nsAppRunner.h" // nsNPAPIPluginInstance must be included before nsIDocument.h, which is included in mozAutoDocUpdate.h. #include "nsNPAPIPluginInstance.h" #include "gfxDrawable.h" #include "gfxPrefs.h" #include "ImageOps.h"
--- a/dom/base/nsFrameMessageManager.cpp +++ b/dom/base/nsFrameMessageManager.cpp @@ -24,16 +24,17 @@ #include "nsIInputStream.h" #include "nsIXULRuntime.h" #include "nsIScriptError.h" #include "nsIConsoleService.h" #include "nsIMemoryReporter.h" #include "nsIProtocolHandler.h" #include "nsIScriptSecurityManager.h" #include "xpcpublic.h" +#include "js/JSON.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/CycleCollectedJSContext.h" #include "mozilla/Preferences.h" #include "mozilla/ScriptPreloader.h" #include "mozilla/Telemetry.h" #include "mozilla/dom/ChildProcessMessageManager.h" #include "mozilla/dom/ChromeMessageBroadcaster.h" #include "mozilla/dom/File.h"
--- a/dom/base/nsGlobalWindowInner.cpp +++ b/dom/base/nsGlobalWindowInner.cpp @@ -28,16 +28,17 @@ #include "mozilla/dom/Performance.h" #include "mozilla/dom/StorageEvent.h" #include "mozilla/dom/StorageEventBinding.h" #include "mozilla/dom/StorageNotifierService.h" #include "mozilla/dom/StorageUtils.h" #include "mozilla/dom/Timeout.h" #include "mozilla/dom/TimeoutHandler.h" #include "mozilla/dom/TimeoutManager.h" +#include "mozilla/dom/VisualViewport.h" #include "mozilla/IntegerPrintfMacros.h" #if defined(MOZ_WIDGET_ANDROID) #include "mozilla/dom/WindowOrientationObserver.h" #endif #include "nsDOMOfflineResourceList.h" #include "nsError.h" #include "nsIIdleService.h" #include "nsISizeOfEventTarget.h" @@ -2212,16 +2213,25 @@ nsPIDOMWindowInner::Navigator() { if (!mNavigator) { mNavigator = new mozilla::dom::Navigator(this); } return mNavigator; } +VisualViewport* nsGlobalWindowInner::VisualViewport() +{ + if (!mVisualViewport) { + mVisualViewport = new mozilla::dom::VisualViewport(this); + } + + return mVisualViewport; +} + nsScreen* nsGlobalWindowInner::GetScreen(ErrorResult& aError) { if (!mScreen) { mScreen = nsScreen::Create(this); if (!mScreen) { aError.Throw(NS_ERROR_UNEXPECTED); return nullptr;
--- a/dom/base/nsGlobalWindowInner.h +++ b/dom/base/nsGlobalWindowInner.h @@ -121,16 +121,17 @@ class Promise; class PostMessageEvent; struct RequestInit; class RequestOrUSVString; class Selection; class SpeechSynthesis; class TabGroup; class Timeout; class U2F; +class VisualViewport; class VRDisplay; enum class VRDisplayEventReason : uint8_t; class VREventObserver; class WakeLock; #if defined(MOZ_WIDGET_ANDROID) class WindowOrientationObserver; #endif class Worklet; @@ -759,16 +760,17 @@ public: mozilla::dom::Storage* GetSessionStorage(mozilla::ErrorResult& aError); mozilla::dom::Storage* GetLocalStorage(mozilla::ErrorResult& aError); mozilla::dom::Selection* GetSelection(mozilla::ErrorResult& aError); mozilla::dom::IDBFactory* GetIndexedDB(mozilla::ErrorResult& aError); already_AddRefed<nsICSSDeclaration> GetComputedStyle(mozilla::dom::Element& aElt, const nsAString& aPseudoElt, mozilla::ErrorResult& aError) override; + mozilla::dom::VisualViewport* VisualViewport(); already_AddRefed<mozilla::dom::MediaQueryList> MatchMedia( const nsAString& aQuery, mozilla::dom::CallerType aCallerType, mozilla::ErrorResult& aError); nsScreen* GetScreen(mozilla::ErrorResult& aError); void MoveTo(int32_t aXPos, int32_t aYPos, mozilla::dom::CallerType aCallerType, mozilla::ErrorResult& aError); @@ -1388,16 +1390,18 @@ protected: RefPtr<mozilla::dom::Storage> mLocalStorage; RefPtr<mozilla::dom::Storage> mSessionStorage; RefPtr<mozilla::EventListenerManager> mListenerManager; RefPtr<mozilla::dom::Location> mLocation; RefPtr<nsHistory> mHistory; RefPtr<mozilla::dom::CustomElementRegistry> mCustomElements; + RefPtr<mozilla::dom::VisualViewport> mVisualViewport; + nsCOMPtr<nsIPrincipal> mDocumentPrincipal; // mTabChild is only ever populated in the content process. nsCOMPtr<nsITabChild> mTabChild; uint32_t mSuspendDepth; uint32_t mFreezeDepth; // the method that was used to focus mFocusedNode
--- a/dom/base/nsINode.cpp +++ b/dom/base/nsINode.cpp @@ -7,16 +7,17 @@ /* * Base class for all DOM nodes. */ #include "nsINode.h" #include "AccessCheck.h" #include "jsapi.h" +#include "js/JSON.h" #include "mozAutoDocUpdate.h" #include "mozilla/AsyncEventDispatcher.h" #include "mozilla/CORSMode.h" #include "mozilla/EventDispatcher.h" #include "mozilla/EventListenerManager.h" #include "mozilla/HTMLEditor.h" #include "mozilla/InternalMutationEvent.h" #include "mozilla/Likely.h"
--- a/dom/base/nsJSUtils.h +++ b/dom/base/nsJSUtils.h @@ -14,17 +14,19 @@ * the generated code itself. */ #include "mozilla/Assertions.h" #include "GeckoProfiler.h" #include "jsapi.h" #include "jsfriendapi.h" +#include "js/AutoByteString.h" #include "js/Conversions.h" +#include "js/StableStringChars.h" #include "nsString.h" class nsIScriptContext; class nsIScriptElement; class nsIScriptGlobalObject; class nsXBLPrototypeBinding; namespace mozilla { @@ -220,17 +222,17 @@ public: static void ResetTimeZone(); }; template<typename T> inline bool AssignJSString(JSContext *cx, T &dest, JSString *s) { - size_t len = js::GetStringLength(s); + size_t len = JS::GetStringLength(s); static_assert(js::MaxStringLength < (1 << 28), "Shouldn't overflow here or in SetCapacity"); if (MOZ_UNLIKELY(!dest.SetLength(len, mozilla::fallible))) { JS_ReportOutOfMemory(cx); return false; } return js::CopyStringChars(cx, dest.BeginWriting(), s, len); }
--- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -12,16 +12,18 @@ #include "mozilla/Assertions.h" #include "mozilla/DebugOnly.h" #include "mozilla/FloatingPoint.h" #include "mozilla/Preferences.h" #include "mozilla/Unused.h" #include "mozilla/UseCounter.h" #include "AccessCheck.h" +#include "js/JSON.h" +#include "js/StableStringChars.h" #include "jsfriendapi.h" #include "nsContentCreatorFunctions.h" #include "nsContentUtils.h" #include "nsGlobalWindow.h" #include "nsHTMLTags.h" #include "nsIDocShell.h" #include "nsIDOMGlobalPropertyInitializer.h" #include "nsINode.h" @@ -2853,17 +2855,17 @@ ConvertJSValueToByteString(JSContext* cx // terminator. char badCharArray[6]; static_assert(sizeof(char16_t) <= 2, "badCharArray too small"); SprintfLiteral(badCharArray, "%d", badChar); ThrowErrorMessage(cx, MSG_INVALID_BYTESTRING, index, badCharArray); return false; } } else { - length = js::GetStringLength(s); + length = JS::GetStringLength(s); } static_assert(js::MaxStringLength < UINT32_MAX, "length+1 shouldn't overflow"); if (!result.SetLength(length, fallible)) { return false; }
--- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -3,16 +3,17 @@ /* 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/. */ #ifndef mozilla_dom_BindingUtils_h__ #define mozilla_dom_BindingUtils_h__ #include "jsfriendapi.h" +#include "js/AutoByteString.h" #include "js/Wrapper.h" #include "js/Conversions.h" #include "mozilla/ArrayUtils.h" #include "mozilla/Alignment.h" #include "mozilla/Array.h" #include "mozilla/Assertions.h" #include "mozilla/DeferredFinalize.h" #include "mozilla/dom/BindingDeclarations.h"
--- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -1059,16 +1059,20 @@ DOMInterfaces = { 'TreeContentView': { 'nativeType': 'nsTreeContentView', }, 'TreeWalker': { 'wrapperCache': False, }, +'VisualViewport': { + 'nativeType': 'mozilla::dom::VisualViewport', +}, + 'VTTCue': { 'nativeType': 'mozilla::dom::TextTrackCue' }, 'VTTRegion': { 'nativeType': 'mozilla::dom::TextTrackRegion', },
--- a/dom/bindings/DOMJSClass.h +++ b/dom/bindings/DOMJSClass.h @@ -2,16 +2,17 @@ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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/. */ #ifndef mozilla_dom_DOMJSClass_h #define mozilla_dom_DOMJSClass_h +#include "jsapi.h" #include "jsfriendapi.h" #include "js/Wrapper.h" #include "mozilla/Assertions.h" #include "mozilla/Attributes.h" #include "mozilla/Likely.h" #include "mozilla/dom/PrototypeList.h" // auto-generated
--- a/dom/bindings/Exceptions.cpp +++ b/dom/bindings/Exceptions.cpp @@ -4,16 +4,17 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/dom/Exceptions.h" #include "js/RootingAPI.h" #include "js/TypeDecls.h" #include "jsapi.h" +#include "js/SavedFrameAPI.h" #include "mozilla/CycleCollectedJSContext.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/DOMException.h" #include "mozilla/dom/ScriptSettings.h" #include "nsPIDOMWindow.h" #include "nsServiceManagerUtils.h" #include "nsThreadUtils.h" #include "XPCWrapper.h"
--- a/dom/console/ConsoleAPIStorage.js +++ b/dom/console/ConsoleAPIStorage.js @@ -41,22 +41,16 @@ const CONSOLEAPISTORAGE_CID = Components function ConsoleAPIStorageService() { this.init(); } ConsoleAPIStorageService.prototype = { classID : CONSOLEAPISTORAGE_CID, QueryInterface: ChromeUtils.generateQI([Ci.nsIConsoleAPIStorage, Ci.nsIObserver]), - classInfo: XPCOMUtils.generateCI({ - classID: CONSOLEAPISTORAGE_CID, - contractID: '@mozilla.org/consoleAPI-storage;1', - interfaces: [Ci.nsIConsoleAPIStorage, Ci.nsIObserver], - flags: Ci.nsIClassInfo.SINGLETON - }), observe: function CS_observe(aSubject, aTopic, aData) { if (aTopic == "xpcom-shutdown") { Services.obs.removeObserver(this, "xpcom-shutdown"); Services.obs.removeObserver(this, "inner-window-destroyed"); Services.obs.removeObserver(this, "memory-pressure"); }
--- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -10,16 +10,17 @@ #include "gfxPrefs.h" #ifdef ACCESSIBILITY #include "mozilla/a11y/DocAccessibleChild.h" #endif #include "Layers.h" #include "ContentChild.h" #include "TabParent.h" +#include "js/JSON.h" #include "mozilla/Preferences.h" #include "mozilla/BrowserElementParent.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/EventListenerManager.h" #include "mozilla/dom/DataTransfer.h" #include "mozilla/dom/Event.h" #include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestChild.h" #include "mozilla/dom/MessageManagerBinding.h"
--- a/dom/media/webvtt/WebVTTParserWrapper.js +++ b/dom/media/webvtt/WebVTTParserWrapper.js @@ -1,17 +1,16 @@ /* 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/. */ ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); ChromeUtils.import("resource://gre/modules/vtt.jsm"); var WEBVTTPARSERWRAPPER_CID = "{acf6e493-0092-4b26-b172-241e375c57ab}"; -var WEBVTTPARSERWRAPPER_CONTRACTID = "@mozilla.org/webvttParserWrapper;1"; function WebVTTParserWrapper() { // Nothing } WebVTTParserWrapper.prototype = { @@ -55,16 +54,11 @@ WebVTTParserWrapper.prototype = processCues: function(window, cues, overlay, controls) { WebVTT.processCues(window, cues, overlay, controls); }, classDescription: "Wrapper for the JS WebVTT implementation (vtt.js)", classID: Components.ID(WEBVTTPARSERWRAPPER_CID), QueryInterface: ChromeUtils.generateQI([Ci.nsIWebVTTParserWrapper]), - classInfo: XPCOMUtils.generateCI({ - classID: WEBVTTPARSERWRAPPER_CID, - contractID: WEBVTTPARSERWRAPPER_CONTRACTID, - interfaces: [Ci.nsIWebVTTParserWrapper] - }) }; this.NSGetFactory = XPCOMUtils.generateNSGetFactory([WebVTTParserWrapper]);
--- a/dom/payments/PaymentRequestUtils.cpp +++ b/dom/payments/PaymentRequestUtils.cpp @@ -1,14 +1,15 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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/. */ +#include "js/JSON.h" #include "nsArrayUtils.h" #include "PaymentRequestUtils.h" #include "nsIMutableArray.h" #include "nsISupportsPrimitives.h" namespace mozilla { namespace dom {
--- a/dom/plugins/base/nsJSNPRuntime.cpp +++ b/dom/plugins/base/nsJSNPRuntime.cpp @@ -1130,17 +1130,17 @@ nsJSObjWrapper::GetNewOrUsed(NPP npp, JS if (!sJSObjWrappers) { // No hash yet (or any more), initialize it. if (!CreateJSObjWrapperTable()) return nullptr; } MOZ_ASSERT(sJSObjWrappersAccessible); - JSObjWrapperTable::Ptr p = sJSObjWrappers->lookupForAdd(nsJSObjWrapperKey(obj, npp)); + JSObjWrapperTable::Ptr p = sJSObjWrappers->lookup(nsJSObjWrapperKey(obj, npp)); if (p) { MOZ_ASSERT(p->value()); // Found a live nsJSObjWrapper, return it. return _retainobject(p->value()); } // No existing nsJSObjWrapper, create one.
--- a/dom/script/ScriptSettings.cpp +++ b/dom/script/ScriptSettings.cpp @@ -6,16 +6,17 @@ #include "mozilla/dom/ScriptSettings.h" #include "mozilla/ThreadLocal.h" #include "mozilla/Assertions.h" #include "mozilla/CycleCollectedJSContext.h" #include "mozilla/dom/WorkerPrivate.h" #include "jsapi.h" +#include "js/StableStringChars.h" #include "xpcpublic.h" #include "nsIGlobalObject.h" #include "nsIDocShell.h" #include "nsIScriptGlobalObject.h" #include "nsIScriptContext.h" #include "nsContentUtils.h" #include "nsGlobalWindow.h" #include "nsPIDOMWindow.h" @@ -705,17 +706,17 @@ AutoEntryScript::DocshellEntryMonitor::E !window->GetDocShell()->GetRecordProfileTimelineMarkers()) { return; } nsCOMPtr<nsIDocShell> docShellForJSRunToCompletion = window->GetDocShell(); nsString filename; uint32_t lineNumber = 0; - js::AutoStableStringChars functionName(aCx); + JS::AutoStableStringChars functionName(aCx); if (rootedFunction) { JS::Rooted<JSString*> displayId(aCx, JS_GetFunctionDisplayId(rootedFunction)); if (displayId) { if (!functionName.initTwoByte(aCx, displayId)) { JS_ClearPendingException(aCx); return; } }
--- a/dom/tests/browser/browser_ConsoleAPITests.js +++ b/dom/tests/browser/browser_ConsoleAPITests.js @@ -47,17 +47,17 @@ function spawnWithObserver(browser, obse // before exiting the test " let resolve = () => {", " Services.obs.removeObserver(ConsoleObserver, 'console-api-log-event');", " _resolve();", " };", // This is the observer itself, it calls the passed-in function whenever // it encounters an event " let ConsoleObserver = {", - " QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),", + " QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),", " observe: function(aSubject, aTopic, aData) {", " try {", " (" + observerFunc.toString() + ")(aSubject.wrappedJSObject);", " } catch (ex) {", " ok(false, 'Exception thrown in observe: ' + ex);", " }", " }", " };",
--- a/dom/tests/mochitest/general/test_interfaces.js +++ b/dom/tests/mochitest/general/test_interfaces.js @@ -1157,16 +1157,18 @@ var interfaceNamesInGlobalScope = {name: "UserProximityEvent", insecureContext: true, disabled: true}, // IMPORTANT: Do not change this list without review from a DOM peer! {name: "ValidityState", insecureContext: true}, // IMPORTANT: Do not change this list without review from a DOM peer! {name: "VideoPlaybackQuality", insecureContext: true}, // IMPORTANT: Do not change this list without review from a DOM peer! {name: "VideoStreamTrack", insecureContext: true}, // IMPORTANT: Do not change this list without review from a DOM peer! + {name: "VisualViewport", insecureContext: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! {name: "VRDisplay", insecureContext: true, releaseNonWindows: false}, // IMPORTANT: Do not change this list without review from a DOM peer! {name: "VRDisplayCapabilities", insecureContext: true, releaseNonWindows: false}, // IMPORTANT: Do not change this list without review from a DOM peer! {name: "VRDisplayEvent", insecureContext: true, releaseNonWindows: false}, // IMPORTANT: Do not change this list without review from a DOM peer! {name: "VREyeParameters", insecureContext: true, releaseNonWindows: false}, // IMPORTANT: Do not change this list without review from a DOM peer!
new file mode 100644 --- /dev/null +++ b/dom/webidl/VisualViewport.webidl @@ -0,0 +1,21 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. + * + * The origin of this IDL file is: + * https://wicg.github.io/visual-viewport/#the-visualviewport-interface + */ + +interface VisualViewport : EventTarget { + readonly attribute double offsetLeft; + readonly attribute double offsetTop; + + readonly attribute double pageLeft; + readonly attribute double pageTop; + + readonly attribute double width; + readonly attribute double height; + + readonly attribute double scale; +};
--- a/dom/webidl/Window.webidl +++ b/dom/webidl/Window.webidl @@ -11,16 +11,17 @@ * http://dev.w3.org/csswg/cssom-view/ * https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/RequestAnimationFrame/Overview.html * https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html * https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html * http://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi.html * https://w3c.github.io/webappsec-secure-contexts/#monkey-patching-global-object * https://w3c.github.io/requestidlecallback/ * https://drafts.css-houdini.org/css-paint-api-1/#dom-window-paintworklet + * https://wicg.github.io/visual-viewport/#the-visualviewport-interface */ interface IID; interface nsIBrowserDOMWindow; interface XULControllers; interface nsIDOMWindowUtils; typedef OfflineResourceList ApplicationCache; @@ -560,8 +561,14 @@ partial interface Window { * Getter funcion for IntlUtils, which provides helper functions for * localization. */ [Throws, Func="IsChromeOrXBL"] readonly attribute IntlUtils intlUtils; }; Window implements WebGPUProvider; + +partial interface Window { + [SameObject, Pref="dom.visualviewport.enabled", Replaceable] + readonly attribute VisualViewport visualViewport; + +};
--- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -913,16 +913,17 @@ WEBIDL_FILES = [ 'UIEvent.webidl', 'URL.webidl', 'URLSearchParams.webidl', 'ValidityState.webidl', 'VideoPlaybackQuality.webidl', 'VideoStreamTrack.webidl', 'VideoTrack.webidl', 'VideoTrackList.webidl', + 'VisualViewport.webidl', 'VRDisplay.webidl', 'VRDisplayEvent.webidl', 'VRServiceTest.webidl', 'VTTCue.webidl', 'VTTRegion.webidl', 'WaveShaperNode.webidl', 'WebAuthentication.webidl', 'WebComponents.webidl',
--- a/dom/xhr/XMLHttpRequestMainThread.cpp +++ b/dom/xhr/XMLHttpRequestMainThread.cpp @@ -69,16 +69,17 @@ #include "nsIPromptFactory.h" #include "nsIWindowWatcher.h" #include "nsIConsoleService.h" #include "nsIContentSecurityPolicy.h" #include "nsAsyncRedirectVerifyHelper.h" #include "nsStringBuffer.h" #include "nsIFileChannel.h" #include "mozilla/Telemetry.h" +#include "js/JSON.h" #include "jsfriendapi.h" #include "GeckoProfiler.h" #include "mozilla/dom/XMLHttpRequestBinding.h" #include "mozilla/Attributes.h" #include "MultipartBlobImpl.h" #include "nsIPermissionManager.h" #include "nsMimeTypes.h" #include "nsIHttpChannelInternal.h"
--- a/gfx/ipc/GPUChild.cpp +++ b/gfx/ipc/GPUChild.cpp @@ -3,16 +3,17 @@ /* 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/. */ #include "GPUChild.h" #include "gfxConfig.h" #include "gfxPrefs.h" #include "GPUProcessHost.h" #include "GPUProcessManager.h" +#include "VRProcessManager.h" #include "mozilla/Telemetry.h" #include "mozilla/TelemetryIPC.h" #include "mozilla/dom/CheckerboardReportService.h" #include "mozilla/dom/MemoryReportRequest.h" #include "mozilla/gfx/gfxVars.h" #if defined(XP_WIN) # include "mozilla/gfx/DeviceManagerDx.h" #endif @@ -105,16 +106,22 @@ GPUChild::EnsureGPUReady() } gfxPlatform::GetPlatform()->ImportGPUDeviceData(data); Telemetry::AccumulateTimeDelta(Telemetry::GPU_PROCESS_LAUNCH_TIME_MS_2, mHost->GetLaunchTime()); mGPUReady = true; return true; } +base::ProcessHandle +GPUChild::GetChildProcessHandle() +{ + return mHost->GetChildProcessHandle(); +} + PAPZInputBridgeChild* GPUChild::AllocPAPZInputBridgeChild(const LayersId& aLayersId) { APZInputBridgeChild* child = new APZInputBridgeChild(); child->AddRef(); return child; } @@ -166,16 +173,34 @@ GPUChild::RecvInitCrashReporter(Shmem&& GeckoProcessType_GPU, aShmem, aThreadId); return IPC_OK(); } mozilla::ipc::IPCResult +GPUChild::RecvCreateVRProcess() +{ + // Make sure create VR process at the main process + MOZ_ASSERT(XRE_IsParentProcess()); + if (gfxPrefs::VRProcessEnabled()) { + VRProcessManager::Initialize(); + VRProcessManager* vr = VRProcessManager::Get(); + MOZ_ASSERT(vr, "VRProcessManager must be initialized first."); + + if (vr) { + vr->LaunchVRProcess(); + } + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvNotifyUiObservers(const nsCString& aTopic) { nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService(); MOZ_ASSERT(obsSvc); if (obsSvc) { obsSvc->NotifyObservers(nullptr, aTopic.get(), nullptr); } return IPC_OK();
--- a/gfx/ipc/GPUChild.h +++ b/gfx/ipc/GPUChild.h @@ -31,27 +31,29 @@ class GPUChild final public: explicit GPUChild(GPUProcessHost* aHost); ~GPUChild(); void Init(); bool EnsureGPUReady(); + base::ProcessHandle GetChildProcessHandle(); PAPZInputBridgeChild* AllocPAPZInputBridgeChild(const LayersId& aLayersId) override; bool DeallocPAPZInputBridgeChild(PAPZInputBridgeChild* aActor) override; // gfxVarReceiver overrides. void OnVarChanged(const GfxVarUpdate& aVar) override; // PGPUChild overrides. mozilla::ipc::IPCResult RecvInitComplete(const GPUDeviceData& aData) override; mozilla::ipc::IPCResult RecvReportCheckerboard(const uint32_t& aSeverity, const nsCString& aLog) override; mozilla::ipc::IPCResult RecvInitCrashReporter(Shmem&& shmem, const NativeThreadId& aThreadId) override; + mozilla::ipc::IPCResult RecvCreateVRProcess() override; mozilla::ipc::IPCResult RecvAccumulateChildHistograms(InfallibleTArray<HistogramAccumulation>&& aAccumulations) override; mozilla::ipc::IPCResult RecvAccumulateChildKeyedHistograms(InfallibleTArray<KeyedHistogramAccumulation>&& aAccumulations) override; mozilla::ipc::IPCResult RecvUpdateChildScalars(InfallibleTArray<ScalarAction>&& aScalarActions) override; mozilla::ipc::IPCResult RecvUpdateChildKeyedScalars(InfallibleTArray<KeyedScalarAction>&& aScalarActions) override; mozilla::ipc::IPCResult RecvRecordChildEvents(nsTArray<ChildEventData>&& events) override; mozilla::ipc::IPCResult RecvRecordDiscardedData(const DiscardedData& aDiscardedData) override;
--- a/gfx/ipc/GPUParent.cpp +++ b/gfx/ipc/GPUParent.cpp @@ -36,16 +36,17 @@ #include "mozilla/webrender/RenderThread.h" #include "mozilla/webrender/WebRenderAPI.h" #include "mozilla/HangDetails.h" #include "nsDebugImpl.h" #include "nsIGfxInfo.h" #include "nsThreadManager.h" #include "prenv.h" #include "ProcessUtils.h" +#include "VRGPUChild.h" #include "VRManager.h" #include "VRManagerParent.h" #include "VRThread.h" #include "VsyncBridgeParent.h" #if defined(XP_WIN) # include "mozilla/gfx/DeviceManagerDx.h" # include <process.h> # include <dwrite.h> @@ -302,16 +303,23 @@ GPUParent::RecvInitImageBridge(Endpoint< mozilla::ipc::IPCResult GPUParent::RecvInitVRManager(Endpoint<PVRManagerParent>&& aEndpoint) { VRManagerParent::CreateForGPUProcess(std::move(aEndpoint)); return IPC_OK(); } mozilla::ipc::IPCResult +GPUParent::RecvInitVR(Endpoint<PVRGPUChild>&& aEndpoint) +{ + gfx::VRGPUChild::InitForGPUProcess(std::move(aEndpoint)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvInitUiCompositorController(const LayersId& aRootLayerTreeId, Endpoint<PUiCompositorControllerParent>&& aEndpoint) { UiCompositorControllerParent::Start(aRootLayerTreeId, std::move(aEndpoint)); return IPC_OK(); } mozilla::ipc::IPCResult GPUParent::RecvInitProfiler(Endpoint<PProfilerChild>&& aEndpoint) @@ -460,16 +468,25 @@ GPUParent::RecvRequestMemoryReport(const { nsPrintfCString processName("GPU (pid %u)", (unsigned)getpid()); mozilla::dom::MemoryReportRequestClient::Start( aGeneration, aAnonymize, aMinimizeMemoryUsage, aDMDFile, processName); return IPC_OK(); } +mozilla::ipc::IPCResult +GPUParent::RecvShutdownVR() +{ + if (gfxPrefs::VRProcessEnabled()) { + VRGPUChild::ShutDown(); + } + return IPC_OK(); +} + void GPUParent::ActorDestroy(ActorDestroyReason aWhy) { if (AbnormalShutdown == aWhy) { NS_WARNING("Shutting down GPU process early due to a crash!"); ProcessChild::QuickExit(); }
--- a/gfx/ipc/GPUParent.h +++ b/gfx/ipc/GPUParent.h @@ -38,16 +38,17 @@ public: mozilla::ipc::IPCResult RecvInit(nsTArray<GfxPrefSetting>&& prefs, nsTArray<GfxVarUpdate>&& vars, const DevicePrefs& devicePrefs, nsTArray<LayerTreeIdMapping>&& mappings) override; mozilla::ipc::IPCResult RecvInitCompositorManager(Endpoint<PCompositorManagerParent>&& aEndpoint) override; mozilla::ipc::IPCResult RecvInitVsyncBridge(Endpoint<PVsyncBridgeParent>&& aVsyncEndpoint) override; mozilla::ipc::IPCResult RecvInitImageBridge(Endpoint<PImageBridgeParent>&& aEndpoint) override; mozilla::ipc::IPCResult RecvInitVRManager(Endpoint<PVRManagerParent>&& aEndpoint) override; + mozilla::ipc::IPCResult RecvInitVR(Endpoint<PVRGPUChild>&& aVRGPUChild) override; mozilla::ipc::IPCResult RecvInitUiCompositorController(const LayersId& aRootLayerTreeId, Endpoint<PUiCompositorControllerParent>&& aEndpoint) override; mozilla::ipc::IPCResult RecvInitProfiler(Endpoint<PProfilerChild>&& aEndpoint) override; mozilla::ipc::IPCResult RecvUpdatePref(const GfxPrefSetting& pref) override; mozilla::ipc::IPCResult RecvUpdateVar(const GfxVarUpdate& pref) override; mozilla::ipc::IPCResult RecvNewContentCompositorManager(Endpoint<PCompositorManagerParent>&& aEndpoint) override; mozilla::ipc::IPCResult RecvNewContentImageBridge(Endpoint<PImageBridgeParent>&& aEndpoint) override; mozilla::ipc::IPCResult RecvNewContentVRManager(Endpoint<PVRManagerParent>&& aEndpoint) override; mozilla::ipc::IPCResult RecvNewContentVideoDecoderManager(Endpoint<PVideoDecoderManagerParent>&& aEndpoint) override; @@ -56,16 +57,17 @@ public: mozilla::ipc::IPCResult RecvAddLayerTreeIdMapping(const LayerTreeIdMapping& aMapping) override; mozilla::ipc::IPCResult RecvRemoveLayerTreeIdMapping(const LayerTreeIdMapping& aMapping) override; mozilla::ipc::IPCResult RecvNotifyGpuObservers(const nsCString& aTopic) override; mozilla::ipc::IPCResult RecvRequestMemoryReport( const uint32_t& generation, const bool& anonymize, const bool& minimizeMemoryUsage, const MaybeFileDesc& DMDFile) override; + mozilla::ipc::IPCResult RecvShutdownVR() override; void ActorDestroy(ActorDestroyReason aWhy) override; private: const TimeStamp mLaunchTime; RefPtr<VsyncBridgeParent> mVsyncBridge; #ifdef MOZ_GECKO_PROFILER RefPtr<ChildProfilerController> mProfilerController;
--- a/gfx/ipc/GPUProcessHost.cpp +++ b/gfx/ipc/GPUProcessHost.cpp @@ -155,16 +155,18 @@ GPUProcessHost::InitAfterConnect(bool aS void GPUProcessHost::Shutdown() { MOZ_ASSERT(!mShutdownRequested); mListener = nullptr; if (mGPUChild) { + mGPUChild->SendShutdownVR(); + // OnChannelClosed uses this to check if the shutdown was expected or // unexpected. mShutdownRequested = true; // The channel might already be closed if we got here unexpectedly. if (!mChannelClosed) { mGPUChild->Close(); }
--- a/gfx/ipc/GraphicsMessages.ipdlh +++ b/gfx/ipc/GraphicsMessages.ipdlh @@ -10,16 +10,29 @@ using struct mozilla::null_t from "ipc/I using mozilla::gfx::FeatureStatus from "gfxTelemetry.h"; using mozilla::gfx::BackendType from "mozilla/gfx/Types.h"; using mozilla::gfx::IntSize from "mozilla/gfx/Point.h"; using gfxImageFormat from "mozilla/gfx/Types.h"; namespace mozilla { namespace gfx { +union GfxPrefValue { + bool; + int32_t; + uint32_t; + float; + nsCString; +}; + +struct GfxPrefSetting { + int32_t index; + GfxPrefValue value; +}; + struct D3D11DeviceStatus { bool isWARP; bool textureSharingWorks; uint32_t featureLevel; DxgiAdapterDesc adapter; int32_t sequenceNumber; bool useNV12;
--- a/gfx/ipc/PGPU.ipdl +++ b/gfx/ipc/PGPU.ipdl @@ -5,16 +5,17 @@ include GraphicsMessages; include MemoryReportTypes; include HangTypes; include protocol PAPZInputBridge; include protocol PCompositorManager; include protocol PImageBridge; include protocol PProfiler; +include protocol PVRGPU; include protocol PVRManager; include protocol PVsyncBridge; include protocol PUiCompositorController; include protocol PVideoDecoderManager; using base::ProcessId from "base/process.h"; using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h"; using mozilla::Telemetry::HistogramAccumulation from "mozilla/TelemetryComms.h"; @@ -25,29 +26,16 @@ using mozilla::Telemetry::ChildEventData using mozilla::Telemetry::DiscardedData from "mozilla/TelemetryComms.h"; using mozilla::gfx::Feature from "gfxFeature.h"; using mozilla::gfx::Fallback from "gfxFallback.h"; using mozilla::layers::LayersId from "mozilla/layers/LayersTypes.h"; namespace mozilla { namespace gfx { -union GfxPrefValue { - bool; - int32_t; - uint32_t; - float; - nsCString; -}; - -struct GfxPrefSetting { - int32_t index; - GfxPrefValue value; -}; - struct LayerTreeIdMapping { LayersId layersId; ProcessId ownerId; }; // This protocol allows the UI process to talk to the GPU process. There is one // instance of this protocol, with the GPUParent living on the main thread of // the GPU process and the GPUChild living on the main thread of the UI process. @@ -67,17 +55,18 @@ parent: LayerTreeIdMapping[] mapping); async InitCompositorManager(Endpoint<PCompositorManagerParent> endpoint); async InitVsyncBridge(Endpoint<PVsyncBridgeParent> endpoint); async InitImageBridge(Endpoint<PImageBridgeParent> endpoint); async InitVRManager(Endpoint<PVRManagerParent> endpoint); async InitUiCompositorController(LayersId rootLayerTreeId, Endpoint<PUiCompositorControllerParent> endpoint); async InitProfiler(Endpoint<PProfilerChild> endpoint); - + // Forward GPU process its endpoints to the VR process. + async InitVR(Endpoint<PVRGPUChild> endpoint); // Called to update a gfx preference or variable. async UpdatePref(GfxPrefSetting pref); async UpdateVar(GfxVarUpdate var); // Create a new content-process compositor bridge. async NewContentCompositorManager(Endpoint<PCompositorManagerParent> endpoint); async NewContentImageBridge(Endpoint<PImageBridgeParent> endpoint); async NewContentVRManager(Endpoint<PVRManagerParent> endpoint); @@ -98,30 +87,33 @@ parent: // Have a message be broadcasted to the GPU process by the GPU process // observer service. async NotifyGpuObservers(nsCString aTopic); async RequestMemoryReport(uint32_t generation, bool anonymize, bool minimizeMemoryUsage, MaybeFileDesc DMDFile); + async ShutdownVR(); child: // Sent when the GPU process has initialized devices. This occurs once, after // Init(). async InitComplete(GPUDeviceData data); // Sent when APZ detects checkerboarding and apz checkerboard reporting is enabled. async ReportCheckerboard(uint32_t severity, nsCString log); // Graphics errors, analogous to PContent::GraphicsError async GraphicsError(nsCString aError); async InitCrashReporter(Shmem shmem, NativeThreadId threadId); + async CreateVRProcess(); + // Have a message be broadcasted to the UI process by the UI process // observer service. async NotifyUiObservers(nsCString aTopic); // Messages for reporting telemetry to the UI process. async AccumulateChildHistograms(HistogramAccumulation[] accumulations); async AccumulateChildKeyedHistograms(KeyedHistogramAccumulation[] accumulations); async UpdateChildScalars(ScalarAction[] actions);
--- a/gfx/thebes/DeviceManagerDx.cpp +++ b/gfx/thebes/DeviceManagerDx.cpp @@ -149,16 +149,17 @@ DeviceManagerDx::ReleaseD3D11() mD3D11Module.reset(); sD3D11CreateDeviceFn = nullptr; } static inline bool ProcessOwnsCompositor() { return XRE_GetProcessType() == GeckoProcessType_GPU || + XRE_GetProcessType() == GeckoProcessType_VR || (XRE_IsParentProcess() && !gfxConfig::IsEnabled(Feature::GPU_PROCESS)); } bool DeviceManagerDx::CreateCompositorDevices() { MOZ_ASSERT(ProcessOwnsCompositor());
--- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -26,16 +26,17 @@ #include "gfxCrashReporterUtils.h" #include "gfxPlatform.h" #include "gfxPrefs.h" #include "gfxEnv.h" #include "gfxTextRun.h" #include "gfxUserFontSet.h" #include "gfxConfig.h" +#include "VRProcessManager.h" #include "VRThread.h" #ifdef XP_WIN #include <process.h> #define getpid _getpid #else #include <unistd.h> #endif @@ -998,16 +999,17 @@ gfxPlatform::Shutdown() // We should only support the default GL provider on Windows; then, this // could go away. Unfortunately, we currently support WGL (the default) for // WebGL on Optimus. GLContextProviderEGL::Shutdown(); #endif if (XRE_IsParentProcess()) { GPUProcessManager::Shutdown(); + VRProcessManager::Shutdown(); } gfx::Factory::ShutDown(); delete gGfxPlatformPrefsLock; gfxVars::Shutdown(); gfxPrefs::DestroySingleton();
--- a/gfx/thebes/gfxPrefs.cpp +++ b/gfx/thebes/gfxPrefs.cpp @@ -8,16 +8,18 @@ #include "MainThreadUtils.h" #include "nsXULAppAPI.h" #include "mozilla/Preferences.h" #include "mozilla/Unused.h" #include "mozilla/gfx/gfxVars.h" #include "mozilla/gfx/Logging.h" #include "mozilla/gfx/GPUChild.h" #include "mozilla/gfx/GPUProcessManager.h" +#include "VRProcessManager.h" +#include "VRChild.h" using namespace mozilla; nsTArray<gfxPrefs::Pref*>* gfxPrefs::sGfxPrefList = nullptr; gfxPrefs* gfxPrefs::sInstance = nullptr; bool gfxPrefs::sInstanceHasBeenDestroyed = false; gfxPrefs& @@ -87,16 +89,23 @@ gfxPrefs::Pref::OnChange() { if (auto gpm = gfx::GPUProcessManager::Get()) { if (gfx::GPUChild* gpu = gpm->GetGPUChild()) { GfxPrefValue value; GetLiveValue(&value); Unused << gpu->SendUpdatePref(gfx::GfxPrefSetting(mIndex, value)); } } + if (auto vpm = gfx::VRProcessManager::Get()) { + if (gfx::VRChild* vr = vpm->GetVRChild()) { + GfxPrefValue value; + GetLiveValue(&value); + Unused << vr->SendUpdatePref(gfx::GfxPrefSetting(mIndex, value)); + } + } FireChangeCallback(); } void gfxPrefs::Pref::FireChangeCallback() { if (mChangeCallback) { GfxPrefValue value;
--- a/gfx/thebes/gfxPrefs.h +++ b/gfx/thebes/gfxPrefs.h @@ -367,16 +367,17 @@ private: DECL_GFX_PREF(Live, "apz.scale_repaint_delay_ms", APZScaleRepaintDelay, int32_t, 500); DECL_GFX_PREF(Live, "browser.ui.scroll-toolbar-threshold", ToolbarScrollThreshold, int32_t, 10); DECL_GFX_PREF(Live, "browser.ui.zoom.force-user-scalable", ForceUserScalable, bool, false); DECL_GFX_PREF(Live, "browser.viewport.desktopWidth", DesktopViewportWidth, int32_t, 980); DECL_GFX_PREF(Live, "dom.ipc.plugins.asyncdrawing.enabled", PluginAsyncDrawingEnabled, bool, false); DECL_GFX_PREF(Live, "dom.meta-viewport.enabled", MetaViewportEnabled, bool, false); + DECL_GFX_PREF(Live, "dom.visualviewport.enabled", VisualViewportEnabled, bool, false); DECL_GFX_PREF(Once, "dom.vr.enabled", VREnabled, bool, false); DECL_GFX_PREF(Live, "dom.vr.autoactivate.enabled", VRAutoActivateEnabled, bool, false); DECL_GFX_PREF(Live, "dom.vr.controller_trigger_threshold", VRControllerTriggerThreshold, float, 0.1f); DECL_GFX_PREF(Once, "dom.vr.external.enabled", VRExternalEnabled, bool, true); DECL_GFX_PREF(Live, "dom.vr.navigation.timeout", VRNavigationTimeout, int32_t, 1000); DECL_GFX_PREF(Once, "dom.vr.oculus.enabled", VROculusEnabled, bool, true); DECL_GFX_PREF(Live, "dom.vr.oculus.invisible.enabled", VROculusInvisibleEnabled, bool, true); DECL_GFX_PREF(Live, "dom.vr.oculus.present.timeout", VROculusPresentTimeout, int32_t, 500); @@ -386,16 +387,17 @@ private: DECL_GFX_PREF(Live, "dom.vr.controller.enumerate.interval", VRControllerEnumerateInterval, int32_t, 1000); DECL_GFX_PREF(Live, "dom.vr.display.enumerate.interval", VRDisplayEnumerateInterval, int32_t, 5000); DECL_GFX_PREF(Live, "dom.vr.inactive.timeout", VRInactiveTimeout, int32_t, 5000); DECL_GFX_PREF(Live, "dom.vr.poseprediction.enabled", VRPosePredictionEnabled, bool, true); DECL_GFX_PREF(Live, "dom.vr.require-gesture", VRRequireGesture, bool, true); DECL_GFX_PREF(Live, "dom.vr.puppet.enabled", VRPuppetEnabled, bool, false); DECL_GFX_PREF(Live, "dom.vr.puppet.submitframe", VRPuppetSubmitFrame, uint32_t, 0); DECL_GFX_PREF(Live, "dom.vr.display.rafMaxDuration", VRDisplayRafMaxDuration, uint32_t, 50); + DECL_GFX_PREF(Once, "dom.vr.process.enabled", VRProcessEnabled, bool, false); DECL_GFX_PREF(Once, "dom.vr.service.enabled", VRServiceEnabled, bool, false); DECL_GFX_PREF(Live, "dom.w3c_pointer_events.enabled", PointerEventsEnabled, bool, false); DECL_GFX_PREF(Live, "general.smoothScroll", SmoothScrollEnabled, bool, true); DECL_GFX_PREF(Live, "general.smoothScroll.currentVelocityWeighting", SmoothScrollCurrentVelocityWeighting, float, 0.25); DECL_GFX_PREF(Live, "general.smoothScroll.durationToIntervalRatio", SmoothScrollDurationToIntervalRatio, int32_t, 200);
--- a/gfx/vr/VRManager.cpp +++ b/gfx/vr/VRManager.cpp @@ -2,24 +2,26 @@ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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/. */ #include "VRManager.h" #include "VRManagerParent.h" +#include "VRGPUChild.h" #include "VRThread.h" #include "gfxVR.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/dom/VRDisplay.h" #include "mozilla/dom/GamepadEventTypes.h" #include "mozilla/layers/TextureHost.h" #include "mozilla/layers/CompositorThread.h" #include "mozilla/Unused.h" +#include "mozilla/gfx/GPUParent.h" #include "gfxPrefs.h" #include "gfxVR.h" #include "gfxVRExternal.h" #if defined(XP_WIN) #include "gfxVROculus.h" #endif #if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID)) @@ -43,26 +45,29 @@ namespace gfx { static StaticRefPtr<VRManager> sVRManagerSingleton; /*static*/ void VRManager::ManagerInit() { MOZ_ASSERT(NS_IsMainThread()); + // TODO: We should make VRManager::ManagerInit + // be called when entering VR content pages. if (sVRManagerSingleton == nullptr) { sVRManagerSingleton = new VRManager(); ClearOnShutdown(&sVRManagerSingleton); } } VRManager::VRManager() : mInitialized(false) , mVRDisplaysRequested(false) , mVRControllersRequested(false) + , mVRServiceStarted(false) { MOZ_COUNT_CTOR(VRManager); MOZ_ASSERT(sVRManagerSingleton == nullptr); RefPtr<VRSystemManager> mgr; /** * We must add the VRDisplayManager's to mManagers in a careful order to @@ -76,17 +81,23 @@ VRManager::VRManager() * * OSVR will be used if Oculus SDK and OpenVR don't detect any HMDS, * to support everyone else. */ #if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID)) // The VR Service accesses all hardware from a separate process // and replaces the other VRSystemManager when enabled. - mVRService = VRService::Create(); + if (!gfxPrefs::VRProcessEnabled()) { + mVRService = VRService::Create(); + } else if (gfxPrefs::VRProcessEnabled() && XRE_IsGPUProcess()) { + gfx::GPUParent* gpu = GPUParent::GetSingleton(); + MOZ_ASSERT(gpu); + Unused << gpu->SendCreateVRProcess(); + } if (mVRService) { mExternalManager = VRSystemManagerExternal::Create(mVRService->GetAPIShmem()); } if (mExternalManager) { mManagers.AppendElement(mExternalManager); } #endif @@ -140,33 +151,49 @@ VRManager::~VRManager() void VRManager::Destroy() { mVRDisplays.Clear(); mVRControllers.Clear(); for (uint32_t i = 0; i < mManagers.Length(); ++i) { mManagers[i]->Destroy(); } - +#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID)) + if (mVRService) { + mVRService->Stop(); + mVRService = nullptr; + } +#endif mInitialized = false; } void VRManager::Shutdown() { mVRDisplays.Clear(); mVRControllers.Clear(); for (uint32_t i = 0; i < mManagers.Length(); ++i) { mManagers[i]->Shutdown(); } #if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID)) if (mVRService) { mVRService->Stop(); } + if (gfxPrefs::VRProcessEnabled()) { + RefPtr<Runnable> task = NS_NewRunnableFunction( + "VRGPUChild::SendStopVRService", + [] () -> void { + VRGPUChild* vrGPUChild = VRGPUChild::Get(); + vrGPUChild->SendStopVRService(); + }); + + NS_DispatchToMainThread(task.forget()); + } #endif + mVRServiceStarted = false; } void VRManager::Init() { mInitialized = true; } @@ -344,18 +371,32 @@ VRManager::RefreshVRDisplays(bool aMustD { /** * If we aren't viewing WebVR content, don't enumerate * new hardware, as it will cause some devices to power on * or interrupt other VR activities. */ if (mVRDisplaysRequested || aMustDispatch) { #if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID)) - if (mVRService) { - mVRService->Start(); + // Tell VR process to start VR service. + if (gfxPrefs::VRProcessEnabled() && !mVRServiceStarted) { + RefPtr<Runnable> task = NS_NewRunnableFunction( + "VRGPUChild::SendStartVRService", + [] () -> void { + VRGPUChild* vrGPUChild = VRGPUChild::Get(); + vrGPUChild->SendStartVRService(); + }); + + NS_DispatchToMainThread(task.forget()); + mVRServiceStarted = true; + } else if (!gfxPrefs::VRProcessEnabled()){ + if (mVRService) { + mVRService->Start(); + mVRServiceStarted = true; + } } #endif EnumerateVRDisplays(); } /** * VRSystemManager::GetHMDs will not activate new hardware * or result in interruption of other VR activities.
--- a/gfx/vr/VRManager.h +++ b/gfx/vr/VRManager.h @@ -97,14 +97,15 @@ private: TimeStamp mLastActiveTime; RefPtr<VRSystemManagerPuppet> mPuppetManager; RefPtr<VRSystemManagerExternal> mExternalManager; #if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID)) RefPtr<VRService> mVRService; #endif bool mVRDisplaysRequested; bool mVRControllersRequested; + bool mVRServiceStarted; }; } // namespace gfx } // namespace mozilla #endif // GFX_VR_MANAGER_H
--- a/gfx/vr/gfxVRExternal.cpp +++ b/gfx/vr/gfxVRExternal.cpp @@ -349,16 +349,20 @@ VRSystemManagerExternal::VRSystemManager mShmemFD = 0; #elif defined(XP_WIN) mShmemFile = NULL; #elif defined(MOZ_WIDGET_ANDROID) mDoShutdown = false; mExternalStructFailed = false; mEnumerationCompleted = false; #endif + + if (!aAPIShmem) { + OpenShmem(); + } } VRSystemManagerExternal::~VRSystemManagerExternal() { CloseShmem(); } void @@ -395,17 +399,24 @@ VRSystemManagerExternal::OpenShmem() // TODO - Implement logging mExternalShmem = NULL; CloseShmem(); return; } #elif defined(XP_WIN) if (mShmemFile == NULL) { - mShmemFile = OpenFileMappingA(FILE_MAP_ALL_ACCESS, FALSE, kShmemName); + if (gfxPrefs::VRProcessEnabled()) { + mShmemFile = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, + sizeof(VRExternalShmem), kShmemName); + MOZ_ASSERT(GetLastError() == 0); + } else { + mShmemFile = OpenFileMappingA(FILE_MAP_ALL_ACCESS, FALSE, kShmemName); + } + if (mShmemFile == NULL) { // TODO - Implement logging CloseShmem(); return; } } LARGE_INTEGER length; length.QuadPart = sizeof(VRExternalShmem); @@ -499,17 +510,18 @@ VRSystemManagerExternal::CloseShmem() VRSystemManagerExternal::Create(VRExternalShmem* aAPIShmem /* = nullptr*/) { MOZ_ASSERT(NS_IsMainThread()); if (!gfxPrefs::VREnabled()) { return nullptr; } - if (!gfxPrefs::VRExternalEnabled() && aAPIShmem == nullptr) { + if ((!gfxPrefs::VRExternalEnabled() && aAPIShmem == nullptr) || + !XRE_IsGPUProcess()) { return nullptr; } RefPtr<VRSystemManagerExternal> manager = new VRSystemManagerExternal(aAPIShmem); return manager.forget(); } void
--- a/gfx/vr/gfxVRExternal.h +++ b/gfx/vr/gfxVRExternal.h @@ -114,17 +114,17 @@ protected: virtual ~VRSystemManagerExternal(); private: // there can only be one RefPtr<impl::VRDisplayExternal> mDisplay; #if defined(XP_MACOSX) int mShmemFD; #elif defined(XP_WIN) - HANDLE mShmemFile; + base::ProcessHandle mShmemFile; #elif defined(MOZ_WIDGET_ANDROID) bool mDoShutdown; bool mExternalStructFailed; bool mEnumerationCompleted; #endif volatile VRExternalShmem* mExternalShmem; #if !defined(MOZ_WIDGET_ANDROID)
--- a/gfx/vr/gfxVRPuppet.h +++ b/gfx/vr/gfxVRPuppet.h @@ -9,20 +9,28 @@ #include "nsTArray.h" #include "mozilla/RefPtr.h" #include "nsRefPtrHashtable.h" #include "gfxVR.h" #include "VRDisplayLocal.h" +#if defined(XP_WIN) +#include "CompositorD3D11.h" +#endif + #if defined(XP_MACOSX) class MacIOSurface; #endif namespace mozilla { +namespace layers { +struct VertexShaderConstants; +struct PixelShaderConstants; +} namespace gfx { namespace impl { class VRDisplayPuppet : public VRDisplayLocal { public: void SetDisplayInfo(const VRDisplayInfo& aDisplayInfo); void SetSensorState(const VRHMDSensorState& aSensorState);
new file mode 100644 --- /dev/null +++ b/gfx/vr/ipc/PVR.ipdl @@ -0,0 +1,26 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +using mozilla::TimeStamp from "mozilla/TimeStamp.h"; + +include GraphicsMessages; +include protocol PVRGPU; + +namespace mozilla { +namespace gfx { + +async protocol PVR +{ +parent: + async NewGPUVRManager(Endpoint<PVRGPUParent> endpoint); + async Init(GfxPrefSetting[] prefs, GfxVarUpdate[] vars, DevicePrefs devicePrefs); + async NotifyVsync(TimeStamp vsyncTimestamp); + + async UpdatePref(GfxPrefSetting pref); + async UpdateVar(GfxVarUpdate var); +}; + +} // namespace gfx +} // namespace mozilla \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/gfx/vr/ipc/PVRGPU.ipdl @@ -0,0 +1,18 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +namespace mozilla { +namespace gfx { + +// IPC for VR-Content process +async protocol PVRGPU +{ +parent: + async StartVRService(); + async StopVRService(); +}; + +} // gfx +} // mozilla \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/gfx/vr/ipc/VRChild.cpp @@ -0,0 +1,94 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "VRChild.h" +#include "VRProcessParent.h" +#include "gfxConfig.h" + +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/SystemGroup.h" +#include "mozilla/VsyncDispatcher.h" + +namespace mozilla { +namespace gfx { + +VRChild::VRChild(VRProcessParent* aHost) + : mHost(aHost) +{ + MOZ_ASSERT(XRE_IsParentProcess()); +} + +void +VRChild::ActorDestroy(ActorDestroyReason aWhy) +{ + gfxVars::RemoveReceiver(this); + mHost->OnChannelClosed(); + XRE_ShutdownChildProcess(); +} + +void +VRChild::Init() +{ + // Build a list of prefs the VR process will need. Note that because we + // limit the VR process to prefs contained in gfxPrefs, we can simplify + // the message in two ways: one, we only need to send its index in gfxPrefs + // rather than its name, and two, we only need to send prefs that don't + // have their default value. + // Todo: Consider to make our own vrPrefs that we are interested in VR process. + nsTArray<GfxPrefSetting> prefs; + for (auto pref : gfxPrefs::all()) { + if (pref->HasDefaultValue()) { + continue; + } + + GfxPrefValue value; + pref->GetCachedValue(&value); + prefs.AppendElement(GfxPrefSetting(pref->Index(), value)); + } + nsTArray<GfxVarUpdate> updates = gfxVars::FetchNonDefaultVars(); + + DevicePrefs devicePrefs; + devicePrefs.hwCompositing() = gfxConfig::GetValue(Feature::HW_COMPOSITING); + devicePrefs.d3d11Compositing() = gfxConfig::GetValue(Feature::D3D11_COMPOSITING); + devicePrefs.oglCompositing() = gfxConfig::GetValue(Feature::OPENGL_COMPOSITING); + devicePrefs.advancedLayers() = gfxConfig::GetValue(Feature::ADVANCED_LAYERS); + devicePrefs.useD2D1() = gfxConfig::GetValue(Feature::DIRECT2D); + + SendInit(prefs, updates, devicePrefs); + gfxVars::AddReceiver(this); +} + +void +VRChild::OnVarChanged(const GfxVarUpdate& aVar) +{ + SendUpdateVar(aVar); +} + +class DeferredDeleteVRChild : public Runnable +{ +public: + explicit DeferredDeleteVRChild(UniquePtr<VRChild>&& aChild) + : Runnable("gfx::DeferredDeleteVRChild") + , mChild(std::move(aChild)) + { + } + + NS_IMETHODIMP Run() override { + return NS_OK; + } + +private: + UniquePtr<VRChild> mChild; +}; + +/* static */ void +VRChild::Destroy(UniquePtr<VRChild>&& aChild) +{ + NS_DispatchToMainThread(new DeferredDeleteVRChild(std::move(aChild))); +} + +} // namespace gfx +} // namespace mozilla \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/gfx/vr/ipc/VRChild.h @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef GFX_VR_CHILD_H +#define GFX_VR_CHILD_H + +#include "mozilla/gfx/PVRChild.h" +#include "mozilla/gfx/gfxVarReceiver.h" +#include "mozilla/VsyncDispatcher.h" + +namespace mozilla { +namespace gfx { + +class VRProcessParent; +class VRChild; + +class VRChild final + : public PVRChild, + public gfxVarReceiver { + +public: + explicit VRChild(VRProcessParent* aHost); + ~VRChild() = default; + + static void Destroy(UniquePtr<VRChild>&& aChild); + void Init(); + virtual void OnVarChanged(const GfxVarUpdate& aVar) override; + +protected: + virtual void ActorDestroy(ActorDestroyReason aWhy) override; + +private: + VRProcessParent* mHost; +}; + +} // namespace gfx +} // namespace mozilla + +#endif // GFX_VR_CHILD_H \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/gfx/vr/ipc/VRGPUChild.cpp @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "VRGPUChild.h" + + +namespace mozilla { +namespace gfx { + +static StaticRefPtr<VRGPUChild> sVRGPUChildSingleton; + +/* static */ bool +VRGPUChild::InitForGPUProcess(Endpoint<PVRGPUChild>&& aEndpoint) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!sVRGPUChildSingleton); + + RefPtr<VRGPUChild> child(new VRGPUChild()); + if (!aEndpoint.Bind(child)) { + return false; + } + sVRGPUChildSingleton = child; + return true; +} + +/* static */ bool +VRGPUChild::IsCreated() +{ + return !!sVRGPUChildSingleton; +} + +/* static */ VRGPUChild* +VRGPUChild::Get() +{ + MOZ_ASSERT(IsCreated(), "VRGPUChild haven't initialized yet."); + return sVRGPUChildSingleton; +} + +/*static*/ void +VRGPUChild::ShutDown() +{ + MOZ_ASSERT(NS_IsMainThread()); + if (sVRGPUChildSingleton) { + sVRGPUChildSingleton->Destroy(); + sVRGPUChildSingleton = nullptr; + } +} + +class DeferredDeleteVRGPUChild : public Runnable +{ +public: + explicit DeferredDeleteVRGPUChild(RefPtr<VRGPUChild> aChild) + : Runnable("gfx::DeferredDeleteVRGPUChild") + , mChild(std::move(aChild)) + { + } + + NS_IMETHODIMP Run() override { + mChild->Close(); + return NS_OK; + } + +private: + RefPtr<VRGPUChild> mChild; +}; + +void +VRGPUChild::Destroy() +{ + // Keep ourselves alive until everything has been shut down + RefPtr<VRGPUChild> selfRef = this; + NS_DispatchToMainThread(new DeferredDeleteVRGPUChild(this)); +} + +} // namespace gfx +} // namespace mozilla
new file mode 100644 --- /dev/null +++ b/gfx/vr/ipc/VRGPUChild.h @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef GFX_VR_GPU_CHILD_H +#define GFX_VR_GPU_CHILD_H + +#include "mozilla/gfx/PVRGPUChild.h" + +namespace mozilla { +namespace gfx { + +class VRGPUChild final : public PVRGPUChild +{ +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRGPUChild); + + static VRGPUChild* Get(); + static bool InitForGPUProcess(Endpoint<PVRGPUChild>&& aEndpoint); + static bool IsCreated(); + static void ShutDown(); + +protected: + explicit VRGPUChild() {} + ~VRGPUChild() {} + + void Destroy(); + +private: + DISALLOW_COPY_AND_ASSIGN(VRGPUChild); +}; + +} // namespace gfx +} // namespace mozilla + +#endif // GFX_VR_GPU_CHILD_H \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/gfx/vr/ipc/VRGPUParent.cpp @@ -0,0 +1,99 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "VRGPUParent.h" + +#include "mozilla/ipc/ProcessChild.h" + + +namespace mozilla { +namespace gfx { + +using namespace ipc; + + +VRGPUParent::VRGPUParent(ProcessId aChildProcessId) +{ + MOZ_COUNT_CTOR(VRGPUParent); + MOZ_ASSERT(NS_IsMainThread()); + + SetOtherProcessId(aChildProcessId); +} + +void +VRGPUParent::ActorDestroy(ActorDestroyReason aWhy) +{ +#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID)) + if (mVRService) { + mVRService->Stop(); + mVRService = nullptr; + } +#endif + + MessageLoop::current()->PostTask( + NewRunnableMethod("gfx::VRGPUParent::DeferredDestroy", + this, + &VRGPUParent::DeferredDestroy)); +} + +void +VRGPUParent::DeferredDestroy() +{ + mSelfRef = nullptr; +} + +/* static */ RefPtr<VRGPUParent> +VRGPUParent::CreateForGPU(Endpoint<PVRGPUParent>&& aEndpoint) +{ + RefPtr<VRGPUParent> vcp = new VRGPUParent(aEndpoint.OtherPid()); + MessageLoop::current()->PostTask( + NewRunnableMethod<Endpoint<PVRGPUParent>&&>( + "gfx::VRGPUParent::Bind", + vcp, + &VRGPUParent::Bind, + std::move(aEndpoint))); + + return vcp; +} + +void +VRGPUParent::Bind(Endpoint<PVRGPUParent>&& aEndpoint) +{ + if (!aEndpoint.Bind(this)) { + return; + } + + mSelfRef = this; +} + +mozilla::ipc::IPCResult +VRGPUParent::RecvStartVRService() +{ +#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID)) + mVRService = VRService::Create(); + MOZ_ASSERT(mVRService); + + mVRService->Start(); +#endif + + return IPC_OK(); +} + +mozilla::ipc::IPCResult +VRGPUParent::RecvStopVRService() +{ +#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID)) + if (mVRService) { + mVRService->Stop(); + mVRService = nullptr; + } +#endif + + return IPC_OK(); +} + +} // namespace gfx +} // namespace mozilla \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/gfx/vr/ipc/VRGPUParent.h @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef GFX_VR_GPU_PARENT_H +#define GFX_VR_GPU_PARENT_H + +#include "mozilla/gfx/PVRGPUParent.h" + +namespace mozilla { +namespace gfx { + +class VRGPUParent final : public PVRGPUParent { + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRGPUParent) + +public: + explicit VRGPUParent(ProcessId aChildProcessId); + + virtual void ActorDestroy(ActorDestroyReason aWhy) override; + static RefPtr<VRGPUParent> CreateForGPU(Endpoint<PVRGPUParent>&& aEndpoint); + +protected: + ~VRGPUParent() {} + + void Bind(Endpoint<PVRGPUParent>&& aEndpoint); + virtual mozilla::ipc::IPCResult RecvStartVRService() override; + virtual mozilla::ipc::IPCResult RecvStopVRService() override; + +private: + void DeferredDestroy(); + + RefPtr<VRGPUParent> mSelfRef; +#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID)) + RefPtr<VRService> mVRService; +#endif +}; + +} // namespace gfx +} // namespace mozilla + +#endif // GFX_VR_CONTENT_PARENT_H \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/gfx/vr/ipc/VRParent.cpp @@ -0,0 +1,143 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "VRParent.h" +#include "VRGPUParent.h" +#include "VRManager.h" +#include "gfxConfig.h" + +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/ipc/ProcessChild.h" + +#if defined(XP_WIN) +#include "mozilla/gfx/DeviceManagerDx.h" +#endif + +namespace mozilla { +namespace gfx { + +using mozilla::ipc::IPCResult; + +VRParent::VRParent() + : mVRGPUParent(nullptr) +{ +} + +IPCResult +VRParent::RecvNewGPUVRManager(Endpoint<PVRGPUParent>&& aEndpoint) +{ + RefPtr<VRGPUParent> vrGPUParent = VRGPUParent::CreateForGPU(std::move(aEndpoint)); + if (!vrGPUParent) { + return IPC_FAIL_NO_REASON(this); + } + + mVRGPUParent = std::move(vrGPUParent); + return IPC_OK(); +} + +IPCResult +VRParent::RecvInit(nsTArray<GfxPrefSetting>&& prefs, + nsTArray<GfxVarUpdate>&& vars, + const DevicePrefs& devicePrefs) +{ + const nsTArray<gfxPrefs::Pref*>& globalPrefs = gfxPrefs::all(); + for (auto& setting : prefs) { + gfxPrefs::Pref* pref = globalPrefs[setting.index()]; + pref->SetCachedValue(setting.value()); + } + for (const auto& var : vars) { + gfxVars::ApplyUpdate(var); + } + + // Inherit device preferences. + gfxConfig::Inherit(Feature::HW_COMPOSITING, devicePrefs.hwCompositing()); + gfxConfig::Inherit(Feature::D3D11_COMPOSITING, devicePrefs.d3d11Compositing()); + gfxConfig::Inherit(Feature::OPENGL_COMPOSITING, devicePrefs.oglCompositing()); + gfxConfig::Inherit(Feature::ADVANCED_LAYERS, devicePrefs.advancedLayers()); + gfxConfig::Inherit(Feature::DIRECT2D, devicePrefs.useD2D1()); + +#if defined(XP_WIN) + if (gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) { + DeviceManagerDx::Get()->CreateCompositorDevices(); + } +#endif + return IPC_OK(); +} + +IPCResult +VRParent::RecvNotifyVsync(const TimeStamp& vsyncTimestamp) +{ + VRManager* vm = VRManager::Get(); + vm->NotifyVsync(vsyncTimestamp); + return IPC_OK(); +} + +IPCResult +VRParent::RecvUpdatePref(const GfxPrefSetting& setting) +{ + gfxPrefs::Pref* pref = gfxPrefs::all()[setting.index()]; + pref->SetCachedValue(setting.value()); + return IPC_OK(); +} + +IPCResult +VRParent::RecvUpdateVar(const GfxVarUpdate& aUpdate) +{ + gfxVars::ApplyUpdate(aUpdate); + return IPC_OK(); +} + +void +VRParent::ActorDestroy(ActorDestroyReason aWhy) +{ + if (AbnormalShutdown == aWhy) { + NS_WARNING("Shutting down VR process early due to a crash!"); + ProcessChild::QuickExit(); + } + + mVRGPUParent->Close(); +#if defined(XP_WIN) + DeviceManagerDx::Shutdown(); +#endif + gfxVars::Shutdown(); + gfxConfig::Shutdown(); + gfxPrefs::DestroySingleton(); + XRE_ShutdownChildProcess(); +} + +bool +VRParent::Init(base::ProcessId aParentPid, + const char* aParentBuildID, + MessageLoop* aIOLoop, + IPC::Channel* aChannel) +{ + // Now it's safe to start IPC. + if (NS_WARN_IF(!Open(aChannel, aParentPid, aIOLoop))) { + return false; + } + + // This must be checked before any IPDL message, which may hit sentinel + // errors due to parent and content processes having different + // versions. + MessageChannel* channel = GetIPCChannel(); + if (channel && !channel->SendBuildIDsMatchMessage(aParentBuildID)) { + // We need to quit this process if the buildID doesn't match the parent's. + // This can occur when an update occurred in the background. + ProcessChild::QuickExit(); + } + + // Ensure gfxPrefs are initialized. + gfxPrefs::GetSingleton(); + gfxConfig::Init(); + gfxVars::Initialize(); +#if defined(XP_WIN) + DeviceManagerDx::Init(); +#endif + return true; +} + +} // namespace gfx +} // namespace mozilla \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/gfx/vr/ipc/VRParent.h @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef GFX_VR_PARENT_H +#define GFX_VR_PARENT_H + +#include "mozilla/gfx/PVRParent.h" + +namespace mozilla { +namespace gfx { + +class VRGPUParent; +class VRService; +class VRSystemManagerExternal; + +class VRParent final : public PVRParent { + +public: + VRParent(); + bool Init(base::ProcessId aParentPid, + const char* aParentBuildID, + MessageLoop* aIOLoop, + IPC::Channel* aChannel); + virtual void ActorDestroy(ActorDestroyReason aWhy) override; + +protected: + virtual mozilla::ipc::IPCResult RecvNewGPUVRManager(Endpoint<PVRGPUParent>&& aEndpoint) override; + virtual mozilla::ipc::IPCResult RecvInit(nsTArray<GfxPrefSetting>&& prefs, + nsTArray<GfxVarUpdate>&& vars, + const DevicePrefs& devicePrefs) override; + virtual mozilla::ipc::IPCResult RecvNotifyVsync(const TimeStamp& vsyncTimestamp) override; + virtual mozilla::ipc::IPCResult RecvUpdatePref(const GfxPrefSetting& setting) override; + virtual mozilla::ipc::IPCResult RecvUpdateVar(const GfxVarUpdate& pref) override; + +private: + RefPtr<VRGPUParent> mVRGPUParent; +}; + +} // namespace gfx +} // namespace mozilla + +#endif // GFX_VR_PARENT_H \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/gfx/vr/ipc/VRProcessChild.cpp @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "VRProcessChild.h" + +#include "mozilla/BackgroundHangMonitor.h" +#include "mozilla/ipc/IOThreadChild.h" + + +using namespace mozilla; +using namespace mozilla::gfx; +using mozilla::ipc::IOThreadChild; + + +VRProcessChild::VRProcessChild(ProcessId aParentPid) + : ProcessChild(aParentPid) +#if defined(aParentPid) + , mVR(nullptr) +#endif +{ +} + +VRProcessChild::~VRProcessChild() +{ +} + +bool +VRProcessChild::Init(int aArgc, char* aArgv[]) +{ + BackgroundHangMonitor::Startup(); + + char* parentBuildID = nullptr; + for (int i = 1; i < aArgc; i++) { + if (strcmp(aArgv[i], "-parentBuildID") == 0) { + parentBuildID = aArgv[i + 1]; + } + } + + mVR.Init(ParentPid(), parentBuildID, + IOThreadChild::message_loop(), + IOThreadChild::channel()); + + return true; +} + +void +VRProcessChild::CleanUp() +{ + NS_ShutdownXPCOM(nullptr); +} \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/gfx/vr/ipc/VRProcessChild.h @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef GFX_VR_PROCESS_CHILD_H +#define GFX_VR_PROCESS_CHILD_H + +#include "mozilla/ipc/ProcessChild.h" +#include "VRParent.h" + + +namespace mozilla { +namespace gfx { + +/** + * Contains the VRChild object that facilitates IPC communication to/from + * the instance of the VR library that is run in this process. + */ +class VRProcessChild final : public mozilla::ipc::ProcessChild +{ +protected: + typedef mozilla::ipc::ProcessChild ProcessChild; + +public: + explicit VRProcessChild(ProcessId aParentPid); + ~VRProcessChild(); + + // ProcessChild functions. + virtual bool Init(int aArgc, char* aArgv[]) override; + virtual void CleanUp() override; + +private: + DISALLOW_COPY_AND_ASSIGN(VRProcessChild); + + VRParent mVR; +}; + +} // namespace gfx +} // namespace mozilla + +#endif /* GFX_VR_PROCESS_CHILD_H */ \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/gfx/vr/ipc/VRProcessManager.cpp @@ -0,0 +1,171 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "VRProcessManager.h" + +#include "VRProcessParent.h" +#include "VRChild.h" +#include "VRGPUChild.h" +#include "VRGPUParent.h" + + +namespace mozilla { +namespace gfx { + +static StaticAutoPtr<VRProcessManager> sSingleton; + +/* static */ VRProcessManager* +VRProcessManager::Get() +{ + return sSingleton; +} + +/* static */ void +VRProcessManager::Initialize() +{ + MOZ_ASSERT(XRE_IsParentProcess()); + sSingleton = new VRProcessManager(); +} + +/* static */ void +VRProcessManager::Shutdown() +{ + sSingleton = nullptr; +} + +VRProcessManager::VRProcessManager() + : mProcess(nullptr) +{ + MOZ_COUNT_CTOR(VRProcessManager); + + mObserver = new Observer(this); + nsContentUtils::RegisterShutdownObserver(mObserver); +} + +VRProcessManager::~VRProcessManager() +{ + MOZ_COUNT_DTOR(VRProcessManager); + + DestroyProcess(); + // The VR process should have already been shut down. + MOZ_ASSERT(!mProcess); +} + +void +VRProcessManager::LaunchVRProcess() +{ + if (mProcess) { + return; + } + + // The subprocess is launched asynchronously, so we wait for a callback to + // acquire the IPDL actor. + mProcess = new VRProcessParent(); + if (!mProcess->Launch()) { + DisableVRProcess("Failed to launch VR process"); + } +} + +void +VRProcessManager::DisableVRProcess(const char* aMessage) +{ + if (!gfxPrefs::VRProcessEnabled()) { + return; + } + + DestroyProcess(); +} + +void +VRProcessManager::DestroyProcess() +{ + if (!mProcess) { + return; + } + + mProcess->Shutdown(); + mProcess = nullptr; +} + +bool +VRProcessManager::CreateGPUBridges(base::ProcessId aOtherProcess, + mozilla::ipc::Endpoint<PVRGPUChild>* aOutVRBridge) +{ + if (!CreateGPUVRManager(aOtherProcess, aOutVRBridge)) { + return false; + } + return true; +} + +bool +VRProcessManager::CreateGPUVRManager(base::ProcessId aOtherProcess, + mozilla::ipc::Endpoint<PVRGPUChild>* aOutEndpoint) +{ + base::ProcessId vrparentPid = mProcess + ? mProcess->OtherPid() // VR process id. + : base::GetCurrentProcId(); + + ipc::Endpoint<PVRGPUParent> vrparentPipe; + ipc::Endpoint<PVRGPUChild> vrchildPipe; + nsresult rv = PVRGPU::CreateEndpoints(vrparentPid, // vr process id + aOtherProcess, // gpu process id + &vrparentPipe, + &vrchildPipe); + + if (NS_FAILED(rv)) { + gfxCriticalNote << "Could not create gpu-vr bridge: " << hexa(int(rv)); + return false; + } + + // Bind vr-gpu pipe to VRParent and make a PVRGPU connection. + VRChild* vrChild = mProcess->GetActor(); + vrChild->SendNewGPUVRManager(std::move(vrparentPipe)); + + *aOutEndpoint = std::move(vrchildPipe); + return true; +} + +NS_IMPL_ISUPPORTS(VRProcessManager::Observer, nsIObserver); + +VRProcessManager::Observer::Observer(VRProcessManager* aManager) + : mManager(aManager) +{ +} + +NS_IMETHODIMP +VRProcessManager::Observer::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) +{ + if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { + mManager->OnXPCOMShutdown(); + } + return NS_OK; +} + +void +VRProcessManager::CleanShutdown() +{ + DestroyProcess(); +} + +void +VRProcessManager::OnXPCOMShutdown() +{ + if (mObserver) { + nsContentUtils::UnregisterShutdownObserver(mObserver); + mObserver = nullptr; + } + + CleanShutdown(); +} + +VRChild* +VRProcessManager::GetVRChild() +{ + return mProcess->GetActor(); +} + +} // namespace gfx +} // namespace mozilla \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/gfx/vr/ipc/VRProcessManager.h @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ +#ifndef GFX_VR_PROCESS_MANAGER_H +#define GFX_VR_PROCESS_MANAGER_H + + +namespace mozilla { +namespace gfx { + +class VRProcessParent; +class VRManagerChild; +class PVRGPUChild; +class VRChild; + +// The VRProcessManager is a singleton responsible for creating VR-bound +// objects that may live in another process. +class VRProcessManager final +{ +public: + static VRProcessManager* Get(); + static void Initialize(); + static void Shutdown(); + + ~VRProcessManager(); + + // If not using a VR process, launch a new VR process asynchronously. + void LaunchVRProcess(); + void DestroyProcess(); + + bool CreateGPUBridges(base::ProcessId aOtherProcess, + mozilla::ipc::Endpoint<PVRGPUChild>* aOutVRBridge); + + VRChild* GetVRChild(); + +private: + VRProcessManager(); + + DISALLOW_COPY_AND_ASSIGN(VRProcessManager); + + bool CreateGPUVRManager(base::ProcessId aOtherProcess, + mozilla::ipc::Endpoint<PVRGPUChild>* aOutEndpoint); + void OnXPCOMShutdown(); + void CleanShutdown(); + + // Permanently disable the VR process and record a message why. + void DisableVRProcess(const char* aMessage); + + class Observer final : public nsIObserver { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + explicit Observer(VRProcessManager* aManager); + + protected: + ~Observer() {} + + VRProcessManager* mManager; + }; + friend class Observer; + + RefPtr<Observer> mObserver; + VRProcessParent* mProcess; +}; + +} // namespace gfx +} // namespace mozilla + +#endif // GFX_VR_PROCESS_MANAGER_H
new file mode 100644 --- /dev/null +++ b/gfx/vr/ipc/VRProcessParent.cpp @@ -0,0 +1,197 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "VRProcessParent.h" +#include "VRGPUChild.h" +#include "VRProcessManager.h" +#include "mozilla/gfx/GPUProcessManager.h" +#include "mozilla/gfx/GPUChild.h" +#include "mozilla/ipc/ProtocolTypes.h" +#include "mozilla/ipc/ProtocolUtils.h" // for IToplevelProtocol +#include "mozilla/TimeStamp.h" // for TimeStamp +#include "mozilla/Unused.h" +#include "VRChild.h" +#include "VRManager.h" +#include "VRThread.h" +#include "gfxVRPuppet.h" + +#include "nsAppRunner.h" // for IToplevelProtocol +#include "mozilla/ipc/ProtocolUtils.h" + +using std::vector; +using std::string; + +using namespace mozilla::ipc; + +namespace mozilla { +namespace gfx { + +VRProcessParent::VRProcessParent() + : GeckoChildProcessHost(GeckoProcessType_VR), + mTaskFactory(this), + mChannelClosed(false) +{ + MOZ_COUNT_CTOR(VRProcessParent); +} + +VRProcessParent::~VRProcessParent() +{ + // Cancel all tasks. We don't want anything triggering after our caller + // expects this to go away. + { + MonitorAutoLock lock(mMonitor); + mTaskFactory.RevokeAll(); + } + MOZ_COUNT_DTOR(VRProcessParent); +} + +bool +VRProcessParent::Launch() +{ + mLaunchThread = NS_GetCurrentThread(); + + std::vector<std::string> extraArgs; + nsCString parentBuildID(mozilla::PlatformBuildID()); + extraArgs.push_back("-parentBuildID"); + extraArgs.push_back(parentBuildID.get()); + + if (!GeckoChildProcessHost::AsyncLaunch(extraArgs)) { + return false; + } + return true; +} + +void +VRProcessParent::Shutdown() +{ + if (mVRChild) { + // The channel might already be closed if we got here unexpectedly. + if (!mChannelClosed) { + mVRChild->Close(); + } + +#ifndef NS_FREE_PERMANENT_DATA + // No need to communicate shutdown, the VR process doesn't need to + // communicate anything back. + KillHard("NormalShutdown"); +#endif + + // If we're shutting down unexpectedly, we're in the middle of handling an + // ActorDestroy for PGPUChild, which is still on the stack. We'll return + // back to OnChannelClosed. + // + // Otherwise, we'll wait for OnChannelClose to be called whenever PGPUChild + // acknowledges shutdown. + return; + } + + DestroyProcess(); +} + +static void +DelayedDeleteSubprocess(GeckoChildProcessHost* aSubprocess) +{ + XRE_GetIOMessageLoop()-> + PostTask(mozilla::MakeAndAddRef<DeleteTask<GeckoChildProcessHost>>(aSubprocess)); +} + + +void +VRProcessParent::DestroyProcess() +{ + mLaunchThread->Dispatch(NewRunnableFunction("DestroyProcessRunnable", DelayedDeleteSubprocess, this)); +} + +void +VRProcessParent::InitAfterConnect(bool aSucceeded) +{ + if (aSucceeded) { + mVRChild = MakeUnique<VRChild>(this); + + DebugOnly<bool> rv = + mVRChild->Open(GetChannel(), base::GetProcId(GetChildProcessHandle())); + MOZ_ASSERT(rv); + + mVRChild->Init(); + + // Make vr-gpu process connection + GPUChild* gpuChild = GPUProcessManager::Get()->GetGPUChild(); + MOZ_ASSERT(gpuChild); + + Endpoint<PVRGPUChild> vrGPUBridge; + VRProcessManager* vpm = VRProcessManager::Get(); + DebugOnly<bool> opened = vpm->CreateGPUBridges(gpuChild->OtherPid(), &vrGPUBridge); + MOZ_ASSERT(opened); + + Unused << gpuChild->SendInitVR(std::move(vrGPUBridge)); + } +} + +void +VRProcessParent::KillHard(const char* aReason) +{ + ProcessHandle handle = GetChildProcessHandle(); + if (!base::KillProcess(handle, base::PROCESS_END_KILLED_BY_USER, false)) { + NS_WARNING("failed to kill subprocess!"); + } + + SetAlreadyDead(); +} + +void +VRProcessParent::OnChannelError() +{ + MOZ_ASSERT(false, "VR process channel error."); +} + +void +VRProcessParent::OnChannelConnected(int32_t peer_pid) +{ + MOZ_ASSERT(!NS_IsMainThread()); + + GeckoChildProcessHost::OnChannelConnected(peer_pid); + + // Post a task to the main thread. Take the lock because mTaskFactory is not + // thread-safe. + RefPtr<Runnable> runnable; + { + MonitorAutoLock lock(mMonitor); + runnable = mTaskFactory.NewRunnableMethod(&VRProcessParent::OnChannelConnectedTask); + } + NS_DispatchToMainThread(runnable); +} + +void +VRProcessParent::OnChannelConnectedTask() +{ + InitAfterConnect(true); +} + +void +VRProcessParent::OnChannelErrorTask() +{ + MOZ_ASSERT(false, "VR process channel error."); +} + +void +VRProcessParent::OnChannelClosed() +{ + mChannelClosed = true; + DestroyProcess(); + + // Release the actor. + VRChild::Destroy(std::move(mVRChild)); + MOZ_ASSERT(!mVRChild); +} + +base::ProcessId +VRProcessParent::OtherPid() +{ + return mVRChild->OtherPid(); +} + +} // namespace gfx +} // namespace mozilla \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/gfx/vr/ipc/VRProcessParent.h @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef GFX_VR_PROCESS_PARENT_H +#define GFX_VR_PROCESS_PARENT_H + +#include "mozilla/UniquePtr.h" + +#include "mozilla/ipc/GeckoChildProcessHost.h" +#include "mozilla/ipc/TaskFactory.h" + +namespace mozilla { +namespace gfx { + +class VRChild; + +class VRProcessParent final : public mozilla::ipc::GeckoChildProcessHost +{ +public: + explicit VRProcessParent(); + ~VRProcessParent(); + + bool Launch(); + void Shutdown(); + void DestroyProcess(); + bool CanShutdown() override { return true; } + + void OnChannelError() override; + void OnChannelConnected(int32_t peer_pid) override; + void OnChannelConnectedTask(); + void OnChannelErrorTask(); + void OnChannelClosed(); + + base::ProcessId OtherPid(); + VRChild* GetActor() const { + return mVRChild.get(); + } + +private: + DISALLOW_COPY_AND_ASSIGN(VRProcessParent); + + void InitAfterConnect(bool aSucceeded); + void KillHard(const char* aReason); + + UniquePtr<VRChild> mVRChild; + mozilla::ipc::TaskFactory<VRProcessParent> mTaskFactory; + nsCOMPtr<nsIThread> mLaunchThread; + bool mChannelClosed; +}; + +} // namespace gfx +} // namespace mozilla + +#endif // ifndef GFX_VR_PROCESS_PARENT_H \ No newline at end of file
--- a/gfx/vr/moz.build +++ b/gfx/vr/moz.build @@ -2,38 +2,54 @@ # vim: set filetype=python: # 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/. EXPORTS += [ 'external_api/moz_external_vr.h', 'gfxVR.h', + 'gfxVRExternal.h', + 'ipc/VRChild.h', + 'ipc/VRGPUChild.h', + 'ipc/VRGPUParent.h', 'ipc/VRLayerChild.h', 'ipc/VRManagerChild.h', 'ipc/VRManagerParent.h', 'ipc/VRMessageUtils.h', + 'ipc/VRParent.h', + 'ipc/VRProcessChild.h', + 'ipc/VRProcessManager.h', + 'ipc/VRProcessParent.h', 'VRDisplayClient.h', + 'VRDisplayHost.h', 'VRDisplayPresentation.h', 'VRManager.h', 'VRThread.h', ] LOCAL_INCLUDES += [ '/dom/base', '/gfx/layers/d3d11', '/gfx/thebes', ] UNIFIED_SOURCES += [ 'gfxVR.cpp', + 'ipc/VRChild.cpp', + 'ipc/VRGPUChild.cpp', + 'ipc/VRGPUParent.cpp', 'ipc/VRLayerChild.cpp', 'ipc/VRLayerParent.cpp', 'ipc/VRManagerChild.cpp', 'ipc/VRManagerParent.cpp', + 'ipc/VRParent.cpp', + 'ipc/VRProcessChild.cpp', + 'ipc/VRProcessManager.cpp', + 'ipc/VRProcessParent.cpp', 'VRDisplayClient.cpp', 'VRDisplayPresentation.cpp', 'VRManager.cpp', 'VRThread.cpp', ] if CONFIG['OS_TARGET'] != 'Android': UNIFIED_SOURCES += [ @@ -64,16 +80,18 @@ if CONFIG['OS_TARGET'] == 'WINNT': SOURCES += [ 'gfxVROculus.cpp', ] if CONFIG['OS_TARGET'] == 'Android': LOCAL_INCLUDES += ['/widget/android'] IPDL_SOURCES = [ + 'ipc/PVR.ipdl', + 'ipc/PVRGPU.ipdl', 'ipc/PVRLayer.ipdl', 'ipc/PVRManager.ipdl', ] # For building with the real SDK instead of our local hack #SOURCES += [ # 'OVR_CAPI_Util.cpp', # 'OVR_CAPIShim.c',
--- a/gfx/vr/service/VRService.cpp +++ b/gfx/vr/service/VRService.cpp @@ -54,23 +54,37 @@ VRService::Create() return service.forget(); } VRService::VRService() : mSystemState{} , mBrowserState{} , mServiceThread(nullptr) , mShutdownRequested(false) + , mAPIShmem(nullptr) + , mTargetShmemFile(0) { - memset(&mAPIShmem, 0, sizeof(mAPIShmem)); + // When we have the VR process, we map the memory + // of mAPIShmem from GPU process. + // If we don't have the VR process, we will instantiate + // mAPIShmem in VRService. + if (!gfxPrefs::VRProcessEnabled()) { + mAPIShmem = new VRExternalShmem(); + memset(mAPIShmem, 0, sizeof(VRExternalShmem)); + } } VRService::~VRService() { Stop(); + + if (!gfxPrefs::VRProcessEnabled() && mAPIShmem) { + delete mAPIShmem; + mAPIShmem = nullptr; + } } void VRService::Start() { if (!mServiceThread) { /** * We must ensure that any time the service is re-started, that @@ -105,51 +119,98 @@ VRService::Start() )); } } void VRService::Stop() { if (mServiceThread) { - mServiceThread->message_loop()->PostTask(NewRunnableMethod( - "gfx::VRService::RequestShutdown", - this, &VRService::RequestShutdown - )); + mShutdownRequested = true; delete mServiceThread; mServiceThread = nullptr; } + if (mTargetShmemFile) { +#if defined(XP_WIN) + CloseHandle(mTargetShmemFile); +#endif + mTargetShmemFile = 0; + } + if (gfxPrefs::VRProcessEnabled() && mAPIShmem) { +#if defined(XP_WIN) + UnmapViewOfFile((void *)mAPIShmem); +#endif + mAPIShmem = nullptr; + } + mSession = nullptr; +} + +bool +VRService::InitShmem() +{ + if (!gfxPrefs::VRProcessEnabled()) { + return true; + } + +#if defined(XP_WIN) + const char* kShmemName = "moz.gecko.vr_ext.0.0.1"; + base::ProcessHandle targetHandle = 0; + + // Opening a file-mapping object by name + targetHandle = OpenFileMappingA( + FILE_MAP_ALL_ACCESS, // read/write access + FALSE, // do not inherit the name + kShmemName); // name of mapping object + + MOZ_ASSERT(GetLastError() == 0); + + LARGE_INTEGER length; + length.QuadPart = sizeof(VRExternalShmem); + mAPIShmem = (VRExternalShmem *)MapViewOfFile(reinterpret_cast<base::ProcessHandle>(targetHandle), // handle to map object + FILE_MAP_ALL_ACCESS, // read/write permission + 0, + 0, + length.QuadPart); + MOZ_ASSERT(GetLastError() == 0); + // TODO - Implement logging + mTargetShmemFile = targetHandle; + if (!mAPIShmem) { + MOZ_ASSERT(mAPIShmem); + return false; + } +#else + // TODO: Implement shmem for other platforms. +#endif + + return true; } bool VRService::IsInServiceThread() { return mServiceThread && mServiceThread->thread_id() == PlatformThread::CurrentId(); } void -VRService::RequestShutdown() -{ - MOZ_ASSERT(IsInServiceThread()); - mShutdownRequested = true; -} - -void VRService::ServiceInitialize() { MOZ_ASSERT(IsInServiceThread()); + if (!InitShmem()) { + return; + } + mShutdownRequested = false; memset(&mBrowserState, 0, sizeof(mBrowserState)); // Try to start a VRSession - unique_ptr<VRSession> session; + UniquePtr<VRSession> session; // Try OpenVR - session = make_unique<OpenVRSession>(); + session = MakeUnique<OpenVRSession>(); if (!session->Initialize(mSystemState)) { session = nullptr; } if (session) { mSession = std::move(session); // Setting enumerationCompleted to true indicates to the browser // that it should resolve any promises in the WebVR/WebXR API // waiting for hardware detection. @@ -277,56 +338,62 @@ VRService::ServiceImmersiveMode() "gfx::VRService::ServiceImmersiveMode", this, &VRService::ServiceImmersiveMode )); } void VRService::PushState(const mozilla::gfx::VRSystemState& aState) { + if (!mAPIShmem) { + return; + } // Copying the VR service state to the shmem is atomic, infallable, // and non-blocking on x86/x64 architectures. Arm requires a mutex // that is locked for the duration of the memcpy to and from shmem on // both sides. #if defined(MOZ_WIDGET_ANDROID) if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->systemMutex)) == 0) { - memcpy((void *)&mAPIShmem.state, &aState, sizeof(VRSystemState)); + memcpy((void *)&mAPIShmem->state, &aState, sizeof(VRSystemState)); pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->systemMutex)); } #else - mAPIShmem.generationA++; - memcpy((void *)&mAPIShmem.state, &aState, sizeof(VRSystemState)); - mAPIShmem.generationB++; + mAPIShmem->generationA++; + memcpy((void *)&mAPIShmem->state, &aState, sizeof(VRSystemState)); + mAPIShmem->generationB++; #endif } void VRService::PullState(mozilla::gfx::VRBrowserState& aState) { + if (!mAPIShmem) { + return; + } // Copying the browser state from the shmem is non-blocking // on x86/x64 architectures. Arm requires a mutex that is // locked for the duration of the memcpy to and from shmem on // both sides. // On x86/x64 It is fallable -- If a dirty copy is detected by // a mismatch of browserGenerationA and browserGenerationB, // the copy is discarded and will not replace the last known // browser state. #if defined(MOZ_WIDGET_ANDROID) if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->browserMutex)) == 0) { memcpy(&aState, &tmp.browserState, sizeof(VRBrowserState)); pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->browserMutex)); } #else VRExternalShmem tmp; - memcpy(&tmp, &mAPIShmem, sizeof(VRExternalShmem)); + memcpy(&tmp, mAPIShmem, sizeof(VRExternalShmem)); if (tmp.browserGenerationA == tmp.browserGenerationB && tmp.browserGenerationA != 0 && tmp.browserGenerationA != -1) { memcpy(&aState, &tmp.browserState, sizeof(VRBrowserState)); } #endif } VRExternalShmem* VRService::GetAPIShmem() { - return &mAPIShmem; + return mAPIShmem; }
--- a/gfx/vr/service/VRService.h +++ b/gfx/vr/service/VRService.h @@ -6,17 +6,16 @@ #ifndef GFX_VR_SERVICE_VRSERVICE_H #define GFX_VR_SERVICE_VRSERVICE_H #include "mozilla/Atomics.h" #include "moz_external_vr.h" -#include <thread> namespace base { class Thread; } // namespace base namespace mozilla { namespace gfx { class VRSession; @@ -28,16 +27,18 @@ public: void Start(); void Stop(); VRExternalShmem* GetAPIShmem(); private: VRService(); ~VRService(); + + bool InitShmem(); void PushState(const mozilla::gfx::VRSystemState& aState); void PullState(mozilla::gfx::VRBrowserState& aState); /** * VRSystemState contains the most recent state of the VR * system, to be shared with the browser by Shmem. * mSystemState is the VR Service copy of this data, which * is memcpy'ed atomically to the Shmem. @@ -46,24 +47,24 @@ private: */ VRSystemState mSystemState; /** * VRBrowserState contains the most recent state of the browser. * mBrowserState is memcpy'ed from the Shmem atomically */ VRBrowserState mBrowserState; - std::unique_ptr<VRSession> mSession; + UniquePtr<VRSession> mSession; base::Thread* mServiceThread; bool mShutdownRequested; - VRExternalShmem mAPIShmem; + VRExternalShmem* MOZ_OWNING_REF mAPIShmem; + base::ProcessHandle mTargetShmemFile; bool IsInServiceThread(); - void RequestShutdown(); /** * The VR Service thread is a state machine that always has one * task queued depending on the state. * * VR Service thread state task functions: */ void ServiceInitialize();
--- a/gfx/webrender/Cargo.toml +++ b/gfx/webrender/Cargo.toml @@ -1,14 +1,15 @@ [package] name = "webrender" version = "0.57.2" authors = ["Glenn Watson <gw@intuitionlibrary.com>"] license = "MPL-2.0" repository = "https://github.com/servo/webrender" +description = "A GPU accelerated 2D renderer for web content" build = "build.rs" [features] default = ["freetype-lib"] freetype-lib = ["freetype/servo-freetype-sys"] profiler = ["thread_profiler/thread_profiler", "debug_renderer"] debugger = ["ws", "serde_json", "serde", "image", "base64", "debug_renderer"] capture = ["webrender_api/serialize", "ron", "serde", "debug_renderer"] @@ -35,17 +36,17 @@ plane-split = "0.12.1" png = { optional = true, version = "0.12" } rayon = "1" ron = { optional = true, version = "0.1.7" } serde = { optional = true, version = "1.0", features = ["serde_derive"] } serde_json = { optional = true, version = "1.0" } smallvec = "0.6" thread_profiler = "0.1.1" time = "0.1" -webrender_api = {path = "../webrender_api"} +webrender_api = { version = "0.57.2", path = "../webrender_api" } ws = { optional = true, version = "0.7.3" } [dependencies.pathfinder_font_renderer] git = "https://github.com/pcwalton/pathfinder" optional = true # Uncomment to test FreeType on macOS: # features = ["freetype"]
--- a/gfx/webrender/res/brush_image.glsl +++ b/gfx/webrender/res/brush_image.glsl @@ -170,16 +170,17 @@ void brush_vs( switch (color_mode) { case COLOR_MODE_ALPHA: case COLOR_MODE_BITMAP: vMaskSwizzle = vec2(0.0, 1.0); vColor = image_data.color; break; case COLOR_MODE_SUBPX_BG_PASS2: case COLOR_MODE_SUBPX_DUAL_SOURCE: + case COLOR_MODE_IMAGE: vMaskSwizzle = vec2(1.0, 0.0); vColor = image_data.color; break; case COLOR_MODE_SUBPX_CONST_COLOR: case COLOR_MODE_SUBPX_BG_PASS0: case COLOR_MODE_COLOR_BITMAP: vMaskSwizzle = vec2(1.0, 0.0); vColor = vec4(image_data.color.a);
--- a/gfx/webrender/res/prim_shared.glsl +++ b/gfx/webrender/res/prim_shared.glsl @@ -37,16 +37,17 @@ varying vec4 vClipMaskUv; #define COLOR_MODE_ALPHA 1 #define COLOR_MODE_SUBPX_CONST_COLOR 2 #define COLOR_MODE_SUBPX_BG_PASS0 3 #define COLOR_MODE_SUBPX_BG_PASS1 4 #define COLOR_MODE_SUBPX_BG_PASS2 5 #define COLOR_MODE_SUBPX_DUAL_SOURCE 6 #define COLOR_MODE_BITMAP 7 #define COLOR_MODE_COLOR_BITMAP 8 +#define COLOR_MODE_IMAGE 9 uniform HIGHP_SAMPLER_FLOAT sampler2D sPrimitiveHeadersF; uniform HIGHP_SAMPLER_FLOAT isampler2D sPrimitiveHeadersI; // Instanced attributes in ivec4 aData; #define VECS_PER_PRIM_HEADER_F 2
--- a/gfx/webrender/src/batch.rs +++ b/gfx/webrender/src/batch.rs @@ -258,46 +258,35 @@ impl OpaqueBatchList { batch.instances.reverse(); } } } pub struct BatchList { pub alpha_batch_list: AlphaBatchList, pub opaque_batch_list: OpaqueBatchList, - pub combined_bounding_rect: DeviceIntRect, } impl BatchList { pub fn new(screen_size: DeviceIntSize) -> Self { // The threshold for creating a new batch is // one quarter the screen size. let batch_area_threshold = (screen_size.width * screen_size.height) as f32 / 4.0; BatchList { alpha_batch_list: AlphaBatchList::new(), opaque_batch_list: OpaqueBatchList::new(batch_area_threshold), - combined_bounding_rect: DeviceIntRect::zero(), } } - fn add_bounding_rect( - &mut self, - task_relative_bounding_rect: &DeviceIntRect, - ) { - self.combined_bounding_rect = self.combined_bounding_rect.union(task_relative_bounding_rect); - } - pub fn get_suitable_batch( &mut self, key: BatchKey, task_relative_bounding_rect: &DeviceIntRect, ) -> &mut Vec<PrimitiveInstance> { - self.add_bounding_rect(task_relative_bounding_rect); - match key.blend_mode { BlendMode::None => { self.opaque_batch_list .get_suitable_batch(key, task_relative_bounding_rect) } BlendMode::Alpha | BlendMode::PremultipliedAlpha | BlendMode::PremultipliedDestOut | @@ -404,41 +393,37 @@ impl AlphaBatchContainer { } } /// Encapsulates the logic of building batches for items that are blended. pub struct AlphaBatchBuilder { pub batch_list: BatchList, glyph_fetch_buffer: Vec<GlyphFetchResult>, target_rect: DeviceIntRect, + can_merge: bool, } impl AlphaBatchBuilder { pub fn new( screen_size: DeviceIntSize, target_rect: DeviceIntRect, + can_merge: bool, ) -> Self { AlphaBatchBuilder { batch_list: BatchList::new(screen_size), glyph_fetch_buffer: Vec::new(), target_rect, + can_merge, } } pub fn build(mut self, merged_batches: &mut AlphaBatchContainer) -> Option<AlphaBatchContainer> { self.batch_list.finalize(); - let task_relative_target_rect = DeviceIntRect::new( - DeviceIntPoint::zero(), - self.target_rect.size, - ); - - let can_merge = task_relative_target_rect.contains_rect(&self.batch_list.combined_bounding_rect); - - if can_merge { + if self.can_merge { merged_batches.merge(self); None } else { Some(AlphaBatchContainer { alpha_batches: self.batch_list.alpha_batch_list.batches, opaque_batches: self.batch_list.opaque_batch_list.batches, target_rect: Some(self.target_rect), }) @@ -468,22 +453,18 @@ impl AlphaBatchBuilder { }; // Even though most of the time a splitter isn't used or needed, // they are cheap to construct so we will always pass one down. let mut splitter = BspSplitter::new(); // Add each run in this picture to the batch. for run in &pic.runs { - let transform_id = ctx - .transforms - .get_id(run.spatial_node_index); self.add_run_to_batch( run, - transform_id, ctx, gpu_cache, render_tasks, task_id, task_address, deferred_resolves, &mut splitter, content_origin, @@ -535,32 +516,35 @@ impl AlphaBatchBuilder { } // Helper to add an entire primitive run to a batch list. // TODO(gw): Restructure this so the param list isn't quite // so daunting! fn add_run_to_batch( &mut self, run: &PrimitiveRun, - transform_id: TransformPaletteId, ctx: &RenderTargetContext, gpu_cache: &mut GpuCache, render_tasks: &RenderTaskTree, task_id: RenderTaskId, task_address: RenderTaskAddress, deferred_resolves: &mut Vec<DeferredResolve>, splitter: &mut BspSplitter<f64, WorldPixel>, content_origin: DeviceIntPoint, prim_headers: &mut PrimitiveHeaders, ) { for i in 0 .. run.count { let prim_index = PrimitiveIndex(run.base_prim_index.0 + i); let metadata = &ctx.prim_store.primitives[prim_index.0].metadata; if metadata.screen_rect.is_some() { + let transform_id = ctx + .transforms + .get_id(metadata.spatial_node_index); + self.add_prim_to_batch( transform_id, prim_index, ctx, gpu_cache, render_tasks, task_id, task_address, @@ -665,33 +649,33 @@ impl AlphaBatchBuilder { BrushKind::Picture(ref picture) => { // If this picture is participating in a 3D rendering context, // then don't add it to any batches here. Instead, create a polygon // for it and add it to the current plane splitter. if picture.is_in_3d_context { // Push into parent plane splitter. debug_assert!(picture.surface.is_some()); let transform = &ctx.transforms - .get_transform(picture.original_spatial_node_index); + .get_transform_by_id(transform_id); match transform.transform_kind { TransformedRectKind::AxisAligned => { let polygon = Polygon::from_transformed_rect( - picture.real_local_rect.cast(), + prim_metadata.local_rect.cast(), transform.m.cast(), prim_index.0, ).unwrap(); splitter.add(polygon); } TransformedRectKind::Complex => { let mut clipper = Clipper::new(); let bounds = (screen_rect.clipped.to_f32() / ctx.device_pixel_scale).to_f64(); let matrix = transform.m.cast(); let results = clipper.clip_transformed( - Polygon::from_rect(picture.real_local_rect.cast(), prim_index.0), + Polygon::from_rect(prim_metadata.local_rect.cast(), prim_index.0), &matrix, Some(bounds), ); for poly in results { splitter.add(poly); } } } @@ -718,17 +702,17 @@ impl AlphaBatchBuilder { let key = BatchKey::new( kind, non_segmented_blend_mode, textures, ); let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect); let prim_header_index = prim_headers.push(&prim_header, [ uv_rect_address.as_int(), - (ShaderColorMode::ColorBitmap as i32) << 16 | + (ShaderColorMode::Image as i32) << 16 | RasterizationSpace::Screen as i32, 0, ]); let instance = BrushInstance { prim_header_index, segment_index: 0, edge_flags: EdgeAaSegmentMask::empty(), @@ -781,17 +765,17 @@ impl AlphaBatchBuilder { .get_texture_address(gpu_cache) .as_int(); // Get the GPU cache address of the extra data handle. let shadow_prim_address = gpu_cache.get_address(&picture.extra_gpu_data_handle); let content_prim_header_index = prim_headers.push(&prim_header, [ content_uv_rect_address, - (ShaderColorMode::ColorBitmap as i32) << 16 | + (ShaderColorMode::Image as i32) << 16 | RasterizationSpace::Screen as i32, 0, ]); let shadow_rect = prim_metadata.local_rect.translate(&offset); let shadow_clip_rect = prim_metadata.local_clip_rect.translate(&offset); let shadow_prim_header = PrimitiveHeader { @@ -966,17 +950,17 @@ impl AlphaBatchBuilder { &task_relative_bounding_rect ); let uv_rect_address = render_tasks[cache_task_id] .get_texture_address(gpu_cache) .as_int(); let prim_header_index = prim_headers.push(&prim_header, [ uv_rect_address, - (ShaderColorMode::ColorBitmap as i32) << 16 | + (ShaderColorMode::Image as i32) << 16 | RasterizationSpace::Screen as i32, 0, ]); let instance = BrushInstance { prim_header_index, clip_task_address, segment_index: 0, @@ -1198,18 +1182,16 @@ impl AlphaBatchBuilder { let base_instance = BrushInstance { prim_header_index, clip_task_address, segment_index: 0, edge_flags, brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION, }; - self.batch_list.add_bounding_rect(task_relative_bounding_rect); - let batch_key = BatchKey { blend_mode, kind: BatchKind::Brush(batch_kind), textures, }; let batch = self.batch_list.get_suitable_batch(batch_key, task_relative_bounding_rect); batch.push(PrimitiveInstance::from(base_instance)); } @@ -1231,18 +1213,16 @@ impl AlphaBatchBuilder { let base_instance = BrushInstance { prim_header_index, clip_task_address, segment_index: 0, edge_flags: EdgeAaSegmentMask::all(), brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION, }; - self.batch_list.add_bounding_rect(task_relative_bounding_rect); - match brush.segment_desc { Some(ref segment_desc) => { let alpha_batch_key = BatchKey { blend_mode: alpha_blend_mode, kind: BatchKind::Brush(batch_kind), textures, }; @@ -1312,17 +1292,16 @@ fn add_gradient_tiles( blend_mode: BlendMode, task_relative_bounding_rect: &DeviceIntRect, clip_task_address: RenderTaskAddress, gpu_cache: &GpuCache, batch_list: &mut BatchList, base_prim_header: &PrimitiveHeader, prim_headers: &mut PrimitiveHeaders, ) { - batch_list.add_bounding_rect(task_relative_bounding_rect); let batch = batch_list.get_suitable_batch( BatchKey { blend_mode: blend_mode, kind: BatchKind::Brush(kind), textures: BatchTextures::no_texture(), }, task_relative_bounding_rect ); @@ -1368,17 +1347,17 @@ fn get_image_tile_params( None } else { let textures = BatchTextures::color(cache_item.texture_id); Some(( BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)), textures, [ cache_item.uv_rect_handle.as_int(gpu_cache), - (ShaderColorMode::ColorBitmap as i32) << 16 | + (ShaderColorMode::Image as i32) << 16 | RasterizationSpace::Local as i32, 0, ], )) } } impl BrushPrimitive { @@ -1418,17 +1397,17 @@ impl BrushPrimitive { } else { let textures = BatchTextures::color(cache_item.texture_id); Some(( BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)), textures, [ cache_item.uv_rect_handle.as_int(gpu_cache), - (ShaderColorMode::ColorBitmap as i32) << 16| + (ShaderColorMode::Image as i32) << 16| RasterizationSpace::Local as i32, 0, ], )) } } BrushKind::Border { ref source, .. } => { let cache_item = match *source { @@ -1456,17 +1435,17 @@ impl BrushPrimitive { } else { let textures = BatchTextures::color(cache_item.texture_id); Some(( BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)), textures, [ cache_item.uv_rect_handle.as_int(gpu_cache), - (ShaderColorMode::ColorBitmap as i32) << 16| + (ShaderColorMode::Image as i32) << 16| RasterizationSpace::Local as i32, 0, ], )) } } BrushKind::Picture { .. } => { panic!("bug: get_batch_key is handled at higher level for pictures");
--- a/gfx/webrender/src/clip.rs +++ b/gfx/webrender/src/clip.rs @@ -6,17 +6,17 @@ use api::{BorderRadius, ClipMode, Comple use api::{ImageRendering, LayoutRect, LayoutSize, LayoutPoint, LayoutVector2D, LocalClip}; use api::{BoxShadowClipMode, LayoutToWorldScale, LineOrientation, LineStyle, LayoutTransform}; use border::{ensure_no_corner_overlap}; use box_shadow::{BLUR_SAMPLE_SCALE, BoxShadowClipSource, BoxShadowCacheKey}; use clip_scroll_tree::{CoordinateSystemId, SpatialNodeIndex}; use ellipse::Ellipse; use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks}; use gpu_types::BoxShadowStretchMode; -use prim_store::{ClipData, ImageMaskData}; +use prim_store::{BrushClipMaskKind, ClipData, ImageMaskData}; use render_task::to_cache_size; use resource_cache::{ImageRequest, ResourceCache}; use spatial_node::SpatialNode; use std::u32; use util::{extract_inner_rect_safe, pack_as_float, recycle_vec, MatrixHelpers}; /* @@ -347,22 +347,23 @@ pub struct ClipStore { pub clip_nodes: Vec<ClipNode>, pub clip_chain_nodes: Vec<ClipChainNode>, clip_node_indices: Vec<ClipNodeInstance>, clip_node_info: Vec<ClipNodeInfo>, } // A clip chain instance is what gets built for a given clip // chain id + local primitive region + positioning node. +#[derive(Debug)] pub struct ClipChainInstance { pub clips_range: ClipNodeRange, pub local_clip_rect: LayoutRect, - pub has_clips_from_other_coordinate_systems: bool, pub has_non_root_coord_system: bool, pub local_bounding_rect: LayoutRect, + pub clip_mask_kind: BrushClipMaskKind, } impl ClipStore { pub fn new() -> Self { ClipStore { clip_nodes: Vec::new(), clip_chain_nodes: Vec::new(), clip_node_indices: Vec::new(), @@ -544,22 +545,29 @@ impl ClipStore { // Now, we've collected all the clip nodes that *potentially* affect this // primitive region, and reduced the size of the prim region as much as possible. // Run through the clip nodes, and see which ones affect this prim region. let first_clip_node_index = self.clip_node_indices.len() as u32; let mut has_non_root_coord_system = false; - let mut has_clips_from_other_coordinate_systems = false; + let mut clip_mask_kind = BrushClipMaskKind::Individual; // For each potential clip node for node_info in self.clip_node_info.drain(..) { let node = &mut self.clip_nodes[node_info.node_index.0 as usize]; + // TODO(gw): We can easily extend the segment builder to support these clip sources in + // the future, but they are rarely used. + // We must do this check here in case we continue early below. + if node.item.is_image_or_line_decoration_clip() { + clip_mask_kind = BrushClipMaskKind::Global; + } + // Convert the prim rect into the clip nodes local space let prim_rect = node_info .conversion .transform_from_prim_space(¤t_local_clip_rect); // See how this clip affects the prim region. let clip_result = match prim_rect { Some(prim_rect) => { @@ -595,20 +603,25 @@ impl ClipStore { // Calculate some flags that are required for the segment // building logic. let flags = match node_info.conversion { ClipSpaceConversion::Local => { ClipNodeFlags::SAME_SPATIAL_NODE | ClipNodeFlags::SAME_COORD_SYSTEM } ClipSpaceConversion::Offset(..) => { + if !node.item.is_rect() { + clip_mask_kind = BrushClipMaskKind::Global; + } ClipNodeFlags::SAME_COORD_SYSTEM } ClipSpaceConversion::Transform(..) => { - has_clips_from_other_coordinate_systems = true; + // If this primitive is clipped by clips from a different coordinate system, then we + // need to apply a clip mask for the entire primitive. + clip_mask_kind = BrushClipMaskKind::Global; ClipNodeFlags::empty() } }; // Store this in the index buffer for this clip chain instance. self.clip_node_indices .push(ClipNodeInstance::new(node_info.node_index, flags)); @@ -621,20 +634,20 @@ impl ClipStore { let clips_range = ClipNodeRange { first: first_clip_node_index, count: self.clip_node_indices.len() as u32 - first_clip_node_index, }; // Return a valid clip chain instance Some(ClipChainInstance { clips_range, - has_clips_from_other_coordinate_systems, has_non_root_coord_system, local_clip_rect: current_local_clip_rect, local_bounding_rect, + clip_mask_kind, }) } } #[derive(Debug)] pub struct LineDecorationClipSource { rect: LayoutRect, style: LineStyle,
--- a/gfx/webrender/src/clip_scroll_tree.rs +++ b/gfx/webrender/src/clip_scroll_tree.rs @@ -1,13 +1,13 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{ExternalScrollId, LayoutPoint, LayoutRect, LayoutVector2D}; +use api::{ExternalScrollId, LayoutPoint, LayoutRect, LayoutVector2D, LayoutVector3D}; use api::{PipelineId, ScrollClamping, ScrollNodeState, ScrollLocation}; use api::{LayoutSize, LayoutTransform, PropertyBinding, ScrollSensitivity, WorldPoint}; use clip::{ClipStore}; use gpu_types::TransformPalette; use internal_types::{FastHashMap, FastHashSet}; use print_tree::{PrintTree, PrintTreePrinter}; use scene::SceneProperties; use spatial_node::{ScrollFrameInfo, SpatialNode, SpatialNodeType, StickyFrameInfo}; @@ -19,44 +19,59 @@ pub type ScrollStates = FastHashMap<Exte /// coordinate system has an id and those ids will be shared when the coordinates /// system are the same or are in the same axis-aligned space. This allows /// for optimizing mask generation. #[derive(Debug, Copy, Clone, PartialEq)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct CoordinateSystemId(pub u32); +/// A node in the hierarchy of coordinate system +/// transforms. +#[derive(Debug)] +pub struct CoordinateSystem { + pub offset: LayoutVector3D, + pub transform: LayoutTransform, + pub parent: Option<CoordinateSystemId>, +} + +impl CoordinateSystem { + fn root() -> Self { + CoordinateSystem { + offset: LayoutVector3D::zero(), + transform: LayoutTransform::identity(), + parent: None, + } + } +} + #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct SpatialNodeIndex(pub usize); const ROOT_REFERENCE_FRAME_INDEX: SpatialNodeIndex = SpatialNodeIndex(0); const TOPMOST_SCROLL_NODE_INDEX: SpatialNodeIndex = SpatialNodeIndex(1); impl CoordinateSystemId { pub fn root() -> Self { CoordinateSystemId(0) } - - pub fn next(&self) -> Self { - let CoordinateSystemId(id) = *self; - CoordinateSystemId(id + 1) - } - - pub fn advance(&mut self) { - self.0 += 1; - } } pub struct ClipScrollTree { /// Nodes which determine the positions (offsets and transforms) for primitives /// and clips. pub spatial_nodes: Vec<SpatialNode>, + /// A list of transforms that establish new coordinate systems. + /// Spatial nodes only establish a new coordinate system when + /// they have a transform that is not a simple 2d translation. + pub coord_systems: Vec<CoordinateSystem>, + pub pending_scroll_offsets: FastHashMap<ExternalScrollId, (LayoutPoint, ScrollClamping)>, /// A set of pipelines which should be discarded the next time this /// tree is drained. pub pipelines_to_discard: FastHashSet<PipelineId>, } #[derive(Clone)] @@ -80,21 +95,59 @@ pub struct TransformUpdateState { /// node will not be clipped by clips that are transformed by this node. pub invertible: bool, } impl ClipScrollTree { pub fn new() -> Self { ClipScrollTree { spatial_nodes: Vec::new(), + coord_systems: Vec::new(), pending_scroll_offsets: FastHashMap::default(), pipelines_to_discard: FastHashSet::default(), } } + /// Calculate the relative transform from `ref_node_index` + /// to `target_node_index`. It's assumed that `ref_node_index` + /// is a parent of `target_node_index`. This method will + /// panic if that invariant isn't true! + pub fn get_relative_transform( + &self, + ref_node_index: SpatialNodeIndex, + target_node_index: SpatialNodeIndex, + ) -> LayoutTransform { + let ref_node = &self.spatial_nodes[ref_node_index.0]; + let target_node = &self.spatial_nodes[target_node_index.0]; + + let mut offset = LayoutVector3D::new( + target_node.coordinate_system_relative_offset.x, + target_node.coordinate_system_relative_offset.y, + 0.0, + ); + let mut transform = LayoutTransform::identity(); + + // Walk up the tree of coordinate systems, accumulating each + // relative transform. + let mut current_coordinate_system_id = target_node.coordinate_system_id; + while current_coordinate_system_id != ref_node.coordinate_system_id { + let coord_system = &self.coord_systems[current_coordinate_system_id.0 as usize]; + + let relative_transform = coord_system + .transform + .post_translate(offset); + transform = transform.pre_mul(&relative_transform); + + offset = coord_system.offset; + current_coordinate_system_id = coord_system.parent.expect("invalid parent!"); + } + + transform + } + /// The root reference frame, which is the true root of the ClipScrollTree. Initially /// this ID is not valid, which is indicated by ```spatial_nodes``` being empty. pub fn root_reference_frame_index(&self) -> SpatialNodeIndex { // TODO(mrobinson): We should eventually make this impossible to misuse. debug_assert!(!self.spatial_nodes.is_empty()); ROOT_REFERENCE_FRAME_INDEX } @@ -128,16 +181,17 @@ impl ClipScrollTree { match old_node.node_type { SpatialNodeType::ScrollFrame(info) if info.external_id.is_some() => { scroll_states.insert(info.external_id.unwrap(), info); } _ => {} } } + self.coord_systems.clear(); self.pipelines_to_discard.clear(); scroll_states } pub fn scroll_node( &mut self, origin: LayoutPoint, id: ExternalScrollId, @@ -186,72 +240,71 @@ impl ClipScrollTree { pan: WorldPoint, scene_properties: &SceneProperties, ) -> TransformPalette { let mut transform_palette = TransformPalette::new(self.spatial_nodes.len()); if self.spatial_nodes.is_empty() { return transform_palette; } + self.coord_systems.clear(); + self.coord_systems.push(CoordinateSystem::root()); + let root_reference_frame_index = self.root_reference_frame_index(); let mut state = TransformUpdateState { parent_reference_frame_transform: LayoutVector2D::new(pan.x, pan.y).into(), parent_accumulated_scroll_offset: LayoutVector2D::zero(), nearest_scrolling_ancestor_offset: LayoutVector2D::zero(), nearest_scrolling_ancestor_viewport: LayoutRect::zero(), current_coordinate_system_id: CoordinateSystemId::root(), coordinate_system_relative_offset: LayoutVector2D::zero(), invertible: true, }; - let mut next_coordinate_system_id = state.current_coordinate_system_id.next(); self.update_node( root_reference_frame_index, &mut state, - &mut next_coordinate_system_id, &mut transform_palette, scene_properties, ); transform_palette } fn update_node( &mut self, node_index: SpatialNodeIndex, state: &mut TransformUpdateState, - next_coordinate_system_id: &mut CoordinateSystemId, transform_palette: &mut TransformPalette, scene_properties: &SceneProperties, ) { // TODO(gw): This is an ugly borrow check workaround to clone these. // Restructure this to avoid the clones! let mut state = state.clone(); let node_children = { let node = match self.spatial_nodes.get_mut(node_index.0) { Some(node) => node, None => return, }; - node.update(&mut state, next_coordinate_system_id, scene_properties); + node.update(&mut state, &mut self.coord_systems, scene_properties); node.push_gpu_data(transform_palette, node_index); if node.children.is_empty() { return; } node.prepare_state_for_children(&mut state); node.children.clone() }; for child_node_index in node_children { self.update_node( child_node_index, &mut state, - next_coordinate_system_id, transform_palette, scene_properties, ); } } pub fn finalize_and_apply_pending_scroll_offsets(&mut self, old_states: ScrollStates) { for node in &mut self.spatial_nodes {
--- a/gfx/webrender/src/display_list_flattener.rs +++ b/gfx/webrender/src/display_list_flattener.rs @@ -508,16 +508,17 @@ impl<'a> DisplayListFlattener<'a> { clip_and_scroll, &prim_info, info.stretch_size, info.tile_spacing, None, info.image_key, info.image_rendering, info.alpha_type, + info.color, ); } SpecificDisplayItem::YuvImage(ref info) => { self.add_yuv_image( clip_and_scroll, &prim_info, info.yuv_data, info.color_space, @@ -771,25 +772,27 @@ impl<'a> DisplayListFlattener<'a> { /// Create a primitive and add it to the prim store. This method doesn't /// add the primitive to the draw list, so can be used for creating /// sub-primitives. pub fn create_primitive( &mut self, info: &LayoutPrimitiveInfo, clip_chain_id: ClipChainId, + spatial_node_index: SpatialNodeIndex, container: PrimitiveContainer, ) -> PrimitiveIndex { let stacking_context = self.sc_stack.last().expect("bug: no stacking context!"); self.prim_store.add_primitive( &info.rect, &info.clip_rect, info.is_backface_visible && stacking_context.is_backface_visible, clip_chain_id, + spatial_node_index, info.tag, container, ) } pub fn add_primitive_to_hit_testing_list( &mut self, info: &LayoutPrimitiveInfo, @@ -812,22 +815,21 @@ impl<'a> DisplayListFlattener<'a> { self.hit_testing_runs.push(HitTestingRun(vec![new_item], clip_and_scroll)); } /// Add an already created primitive to the draw lists. pub fn add_primitive_to_draw_list( &mut self, prim_index: PrimitiveIndex, - spatial_node_index: SpatialNodeIndex, ) { // Add primitive to the top-most Picture on the stack. let pic_prim_index = *self.picture_stack.last().unwrap(); let pic = self.prim_store.get_pic_mut(pic_prim_index); - pic.add_primitive(prim_index, spatial_node_index); + pic.add_primitive(prim_index); } /// Convenience interface that creates a primitive entry and adds it /// to the draw list. pub fn add_primitive( &mut self, clip_and_scroll: ScrollNodeAndClipChain, info: &LayoutPrimitiveInfo, @@ -854,44 +856,46 @@ impl<'a> DisplayListFlattener<'a> { clip_and_scroll.spatial_node_index, clip_and_scroll.clip_chain_id, ); // Construct and add a primitive for the given shadow. let shadow_prim_index = self.create_primitive( &info, clip_chain_id, + clip_and_scroll.spatial_node_index, container.create_shadow(shadow), ); // Add the new primitive to the shadow picture. let shadow_pic = self.prim_store.get_pic_mut(shadow_pic_prim_index); - shadow_pic.add_primitive( - shadow_prim_index, - clip_and_scroll.spatial_node_index, - ); + shadow_pic.add_primitive(shadow_prim_index); } self.shadow_stack = shadow_stack; } if container.is_visible() { let clip_chain_id = self.build_clip_chain( clip_items, clip_and_scroll.spatial_node_index, clip_and_scroll.clip_chain_id, ); - let prim_index = self.create_primitive(info, clip_chain_id, container); + let prim_index = self.create_primitive( + info, + clip_chain_id, + clip_and_scroll.spatial_node_index, + container, + ); if cfg!(debug_assertions) && ChasePrimitive::LocalRect(info.rect) == self.config.chase_primitive { println!("Chasing {:?}", prim_index); self.prim_store.chase_id = Some(prim_index); } self.add_primitive_to_hit_testing_list(info, clip_and_scroll); self.add_primitive_to_draw_list( prim_index, - clip_and_scroll.spatial_node_index, ); } } fn get_next_picture_id(&mut self) -> PictureId { let id = PictureId(self.next_picture_id); self.next_picture_id += 1; id @@ -930,26 +934,26 @@ impl<'a> DisplayListFlattener<'a> { // This picture stores primitive runs for items on the // main framebuffer. let picture = PicturePrimitive::new_image( self.get_next_picture_id(), None, false, pipeline_id, - spatial_node_index, None, true, ); let prim_index = self.prim_store.add_primitive( &LayoutRect::zero(), &max_clip, true, ClipChainId::NONE, + spatial_node_index, None, PrimitiveContainer::Brush(BrushPrimitive::new_picture(picture)), ); self.picture_stack.push(prim_index); } else if composite_ops.mix_blend_mode.is_some() && self.sc_stack.len() > 2 { // If we have a mix-blend-mode, and we aren't the primary framebuffer, // the stacking context needs to be isolated to blend correctly as per @@ -996,36 +1000,36 @@ impl<'a> DisplayListFlattener<'a> { // If establishing a 3d context, we need to add a picture // that will be the container for all the planes and any // un-transformed content. let picture = PicturePrimitive::new_image( self.get_next_picture_id(), None, false, pipeline_id, - spatial_node_index, None, true, ); let prim = BrushPrimitive::new_picture(picture); let prim_index = self.prim_store.add_primitive( &LayoutRect::zero(), &max_clip, is_backface_visible, clip_chain_id, + spatial_node_index, None, PrimitiveContainer::Brush(prim), ); let parent_prim_index = *self.picture_stack.last().unwrap(); let pic = self.prim_store.get_pic_mut(parent_prim_index); - pic.add_primitive(prim_index, spatial_node_index); + pic.add_primitive(prim_index); self.picture_stack.push(prim_index); Some(prim_index) } else { None }; @@ -1047,65 +1051,65 @@ impl<'a> DisplayListFlattener<'a> { // For each filter, create a new image with that composite mode. for filter in composite_ops.filters.iter().rev() { let picture = PicturePrimitive::new_image( self.get_next_picture_id(), Some(PictureCompositeMode::Filter(*filter)), false, pipeline_id, - spatial_node_index, None, true, ); let src_prim = BrushPrimitive::new_picture(picture); let src_prim_index = self.prim_store.add_primitive( &LayoutRect::zero(), &max_clip, is_backface_visible, clip_chain_id, + spatial_node_index, None, PrimitiveContainer::Brush(src_prim), ); let parent_pic = self.prim_store.get_pic_mut(parent_prim_index); parent_prim_index = src_prim_index; - parent_pic.add_primitive(src_prim_index, spatial_node_index); + parent_pic.add_primitive(src_prim_index); self.picture_stack.push(src_prim_index); } // Same for mix-blend-mode. if let Some(mix_blend_mode) = composite_ops.mix_blend_mode { let picture = PicturePrimitive::new_image( self.get_next_picture_id(), Some(PictureCompositeMode::MixBlend(mix_blend_mode)), false, pipeline_id, - spatial_node_index, None, true, ); let src_prim = BrushPrimitive::new_picture(picture); let src_prim_index = self.prim_store.add_primitive( &LayoutRect::zero(), &max_clip, is_backface_visible, clip_chain_id, + spatial_node_index, None, PrimitiveContainer::Brush(src_prim), ); let parent_pic = self.prim_store.get_pic_mut(parent_prim_index); parent_prim_index = src_prim_index; - parent_pic.add_primitive(src_prim_index, spatial_node_index); + parent_pic.add_primitive(src_prim_index); self.picture_stack.push(src_prim_index); } // By default, this picture will be collapsed into // the owning target. let mut composite_mode = None; let mut frame_output_pipeline_id = None; @@ -1133,36 +1137,36 @@ impl<'a> DisplayListFlattener<'a> { } // Add picture for this actual stacking context contents to render into. let picture = PicturePrimitive::new_image( self.get_next_picture_id(), composite_mode, participating_in_3d_context, pipeline_id, - spatial_node_index, frame_output_pipeline_id, true, ); // Create a brush primitive that draws this picture. let sc_prim = BrushPrimitive::new_picture(picture); // Add the brush to the parent picture. let sc_prim_index = self.prim_store.add_primitive( &LayoutRect::zero(), &max_clip, is_backface_visible, clip_chain_id, + spatial_node_index, None, PrimitiveContainer::Brush(sc_prim), ); let parent_pic = self.prim_store.get_pic_mut(parent_prim_index); - parent_pic.add_primitive(sc_prim_index, spatial_node_index); + parent_pic.add_primitive(sc_prim_index); // Add this as the top-most picture for primitives to be added to. self.picture_stack.push(sc_prim_index); // Push the SC onto the stack, so we know how to handle things in // pop_stacking_context. let sc = FlattenedStackingContext { composite_ops, @@ -1379,37 +1383,36 @@ impl<'a> DisplayListFlattener<'a> { // blur radius is 0, the code in Picture::prepare_for_render will // detect this and mark the picture to be drawn directly into the // parent picture, which avoids an intermediate surface and blur. let shadow_pic = PicturePrimitive::new_image( self.get_next_picture_id(), Some(PictureCompositeMode::Filter(FilterOp::Blur(std_deviation))), false, pipeline_id, - clip_and_scroll.spatial_node_index, None, apply_local_clip_rect, ); // Create the primitive to draw the shadow picture into the scene. let shadow_prim = BrushPrimitive::new_picture(shadow_pic); let shadow_prim_index = self.prim_store.add_primitive( &LayoutRect::zero(), &max_clip, info.is_backface_visible, clip_and_scroll.clip_chain_id, + clip_and_scroll.spatial_node_index, None, PrimitiveContainer::Brush(shadow_prim), ); // Add the shadow primitive. This must be done before pushing this // picture on to the shadow stack, to avoid infinite recursion! self.add_primitive_to_draw_list( shadow_prim_index, - clip_and_scroll.spatial_node_index, ); self.shadow_stack.push((shadow, shadow_prim_index)); } pub fn pop_all_shadows(&mut self) { assert!(self.shadow_stack.len() > 0, "popped shadows, but none were present"); self.shadow_stack.clear(); } @@ -1474,22 +1477,22 @@ impl<'a> DisplayListFlattener<'a> { let prim = BrushPrimitive::new( BrushKind::new_solid(color), None, ); let prim_index = self.create_primitive( info, ClipChainId::NONE, + spatial_node_index, PrimitiveContainer::Brush(prim), ); self.add_primitive_to_draw_list( prim_index, - spatial_node_index, ); self.scrollbar_prims.push(ScrollbarPrimitive { prim_index, scroll_frame_index: scrollbar_info.0, frame_rect: scrollbar_info.1, }); } @@ -1903,16 +1906,17 @@ impl<'a> DisplayListFlattener<'a> { clip_and_scroll: ScrollNodeAndClipChain, info: &LayoutPrimitiveInfo, stretch_size: LayoutSize, mut tile_spacing: LayoutSize, sub_rect: Option<TexelRect>, image_key: ImageKey, image_rendering: ImageRendering, alpha_type: AlphaType, + color: ColorF, ) { let mut prim_rect = info.rect; simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect); let info = LayoutPrimitiveInfo { rect: prim_rect, .. *info }; @@ -1934,16 +1938,17 @@ impl<'a> DisplayListFlattener<'a> { request: ImageRequest { key: image_key, rendering: image_rendering, tile: None, }, alpha_type, stretch_size, tile_spacing, + color, source: ImageSource::Default, sub_rect, visible_tiles: Vec::new(), opacity_binding: OpacityBinding::new(), }, None, );
--- a/gfx/webrender/src/frame_builder.rs +++ b/gfx/webrender/src/frame_builder.rs @@ -8,24 +8,24 @@ use api::{LayoutPoint, LayoutRect, Layou use clip::{ClipStore}; use clip_scroll_tree::{ClipScrollTree, SpatialNodeIndex}; use display_list_flattener::{DisplayListFlattener}; use gpu_cache::GpuCache; use gpu_types::{PrimitiveHeaders, TransformPalette, UvRectKind}; use hit_test::{HitTester, HitTestingRun}; use internal_types::{FastHashMap}; use picture::PictureSurface; -use prim_store::{PrimitiveIndex, PrimitiveRun, PrimitiveStore, Transform}; +use prim_store::{PrimitiveIndex, PrimitiveRun, LocalRectBuilder, PrimitiveStore, Transform}; use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters}; use render_backend::FrameId; use render_task::{RenderTask, RenderTaskId, RenderTaskLocation, RenderTaskTree}; use resource_cache::{ResourceCache}; use scene::{ScenePipeline, SceneProperties}; use spatial_node::SpatialNode; -use std::{mem, f32}; +use std::f32; use std::sync::Arc; use tiling::{Frame, RenderPass, RenderPassKind, RenderTargetContext}; use tiling::{ScrollbarPrimitive, SpecialRenderPasses}; use util; #[derive(Clone, Copy, Debug, PartialEq)] #[cfg_attr(feature = "capture", derive(Serialize))] @@ -84,18 +84,16 @@ pub struct FrameBuildingState<'a> { pub resource_cache: &'a mut ResourceCache, pub gpu_cache: &'a mut GpuCache, pub special_render_passes: &'a mut SpecialRenderPasses, } pub struct PictureContext { pub pipeline_id: PipelineId, pub prim_runs: Vec<PrimitiveRun>, - pub spatial_node_index: SpatialNodeIndex, - pub original_spatial_node_index: SpatialNodeIndex, pub apply_local_clip_rect: bool, pub inflation_factor: f32, pub allow_subpixel_aa: bool, } pub struct PictureState { pub tasks: Vec<RenderTaskId>, pub has_non_root_coord_system: bool, @@ -107,30 +105,30 @@ impl PictureState { PictureState { tasks: Vec::new(), has_non_root_coord_system: false, local_rect_changed: false, } } } -pub struct PrimitiveRunContext<'a> { - pub scroll_node: &'a SpatialNode, +pub struct PrimitiveContext<'a> { + pub spatial_node: &'a SpatialNode, pub spatial_node_index: SpatialNodeIndex, pub transform: Transform<'a>, } -impl<'a> PrimitiveRunContext<'a> { +impl<'a> PrimitiveContext<'a> { pub fn new( - scroll_node: &'a SpatialNode, + spatial_node: &'a SpatialNode, spatial_node_index: SpatialNodeIndex, transform: Transform<'a>, ) -> Self { - PrimitiveRunContext { - scroll_node, + PrimitiveContext { + spatial_node, spatial_node_index, transform, } } } impl FrameBuilder { pub fn empty() -> Self { @@ -190,21 +188,21 @@ impl FrameBuilder { scene_properties: &SceneProperties, transform_palette: &TransformPalette, ) -> Option<RenderTaskId> { profile_scope!("cull"); if self.prim_store.primitives.is_empty() { return None } + self.prim_store.reset_prim_visibility(); // The root picture is always the first one added. let root_prim_index = PrimitiveIndex(0); let root_spatial_node_index = clip_scroll_tree.root_reference_frame_index(); - let root_spatial_node = &clip_scroll_tree.spatial_nodes[root_spatial_node_index.0]; const MAX_CLIP_COORD: f32 = 1.0e9; let frame_context = FrameBuildingContext { scene_id: self.scene_id, device_pixel_scale, scene_properties, pipelines, @@ -221,44 +219,43 @@ impl FrameBuilder { render_tasks, profile_counters, clip_store: &mut self.clip_store, resource_cache, gpu_cache, special_render_passes, }; - let pic_context = PictureContext { - pipeline_id: root_spatial_node.pipeline_id, - prim_runs: mem::replace( - &mut self.prim_store.get_pic_mut(root_prim_index).runs, - Vec::new(), - ), - spatial_node_index: root_spatial_node_index, - original_spatial_node_index: root_spatial_node_index, - apply_local_clip_rect: true, - inflation_factor: 0.0, - allow_subpixel_aa: true, - }; - let mut pic_state = PictureState::new(); - self.prim_store.reset_prim_visibility(); + let pic_context = self + .prim_store + .get_pic_mut(root_prim_index) + .take_context(true); + + let mut local_rect_builder = LocalRectBuilder::new( + root_spatial_node_index, + ); + self.prim_store.prepare_prim_runs( &pic_context, &mut pic_state, &frame_context, &mut frame_state, + &mut local_rect_builder, ); - let pic = self.prim_store.get_pic_mut(root_prim_index); - pic.runs = pic_context.prim_runs; + let pic = self + .prim_store + .get_pic_mut(root_prim_index); + pic.restore_context(pic_context, local_rect_builder); let root_render_task = RenderTask::new_picture( RenderTaskLocation::Fixed(frame_context.screen_rect), + frame_context.screen_rect.size, root_prim_index, DeviceIntPoint::zero(), pic_state.tasks, UvRectKind::Rect, ); let render_task_id = frame_state.render_tasks.add(root_render_task); pic.surface = Some(PictureSurface::RenderTask(render_task_id));
--- a/gfx/webrender/src/glyph_rasterizer/pathfinder.rs +++ b/gfx/webrender/src/glyph_rasterizer/pathfinder.rs @@ -296,17 +296,17 @@ fn request_render_task_from_pathfinder(g // FIXME(pcwalton): Support vertical subpixel offsets. // FIXME(pcwalton): Embolden amount should be 0 on macOS if "Use LCD font // smoothing" is unchecked in System Preferences. let subpixel_offset = TypedPoint2D::new(glyph_subpixel_offset as f32, 0.0); let embolden_amount = compute_embolden_amount(size.to_f32_px()); - let location = RenderTaskLocation::Dynamic(None, Some(*glyph_size)); + let location = RenderTaskLocation::Dynamic(None, *glyph_size); let glyph_render_task = RenderTask::new_glyph(location, mesh, &glyph_origin, &subpixel_offset, font.render_mode, &embolden_amount); let root_task_id = render_tasks.add(glyph_render_task);
--- a/gfx/webrender/src/gpu_types.rs +++ b/gfx/webrender/src/gpu_types.rs @@ -349,17 +349,17 @@ impl From<BrushInstance> for PrimitiveIn #[repr(C)] pub struct TransformPaletteId(pub u32); impl TransformPaletteId { /// Identity transform ID. pub const IDENTITY: Self = TransformPaletteId(0); /// Extract the spatial node index from the id. - pub fn _spatial_node_index(&self) -> SpatialNodeIndex { + pub fn spatial_node_index(&self) -> SpatialNodeIndex { SpatialNodeIndex(self.0 as usize & 0xFFFFFF) } /// Extract the transform kind from the id. pub fn transform_kind(&self) -> TransformedRectKind { if (self.0 >> 24) == 0 { TransformedRectKind::AxisAligned } else { @@ -475,16 +475,23 @@ impl TransformPalette { Transform { m: &data.transform, transform_kind: metadata.transform_kind, backface_is_visible: data.transform.is_backface_visible(), } } + pub fn get_transform_by_id( + &self, + id: TransformPaletteId, + ) -> Transform { + self.get_transform(id.spatial_node_index()) + } + // Get a transform palette id for the given spatial node. // TODO(gw): In the future, it will be possible to specify // a coordinate system id here, to allow retrieving // transforms in the local space of a given spatial node. pub fn get_id( &self, index: SpatialNodeIndex, ) -> TransformPaletteId {
--- a/gfx/webrender/src/picture.rs +++ b/gfx/webrender/src/picture.rs @@ -1,21 +1,21 @@ /* 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/. */ use api::{DeviceRect, FilterOp, MixBlendMode, PipelineId, PremultipliedColorF}; use api::{DeviceIntRect, DeviceIntSize, DevicePoint, LayoutPoint, LayoutRect}; use api::{DevicePixelScale, PictureIntPoint, PictureIntRect, PictureIntSize}; use box_shadow::{BLUR_SAMPLE_SCALE}; -use clip_scroll_tree::SpatialNodeIndex; -use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureState, PrimitiveRunContext}; +use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureState}; +use frame_builder::{PictureContext, PrimitiveContext}; use gpu_cache::{GpuCacheHandle}; use gpu_types::UvRectKind; -use prim_store::{PrimitiveIndex, PrimitiveRun, PrimitiveRunLocalRect}; +use prim_store::{PrimitiveIndex, PrimitiveRun, LocalRectBuilder}; use prim_store::{PrimitiveMetadata, Transform}; use render_task::{ClearMode, RenderTask, RenderTaskCacheEntryHandle}; use render_task::{RenderTaskCacheKey, RenderTaskCacheKeyKind, RenderTaskId, RenderTaskLocation}; use scene::{FilterOpHelpers, SceneProperties}; use std::mem; use tiling::RenderTargetKind; use util::TransformedRectKind; @@ -141,21 +141,16 @@ pub struct PicturePrimitive { /// If None, don't composite - just draw directly on parent surface. pub composite_mode: Option<PictureCompositeMode>, // If true, this picture is part of a 3D context. pub is_in_3d_context: bool, // If requested as a frame output (for rendering // pages to a texture), this is the pipeline this // picture is the root of. pub frame_output_pipeline_id: Option<PipelineId>, - // The original reference spatial node for this picture. - // It is only different if this is part of a 3D - // rendering context. - pub original_spatial_node_index: SpatialNodeIndex, - pub real_local_rect: LayoutRect, // An optional cache handle for storing extra data // in the GPU cache, depending on the type of // picture. pub extra_gpu_data_handle: GpuCacheHandle, // Unique identifier for this picture. pub id: PictureId, } @@ -177,69 +172,85 @@ impl PicturePrimitive { } } pub fn new_image( id: PictureId, composite_mode: Option<PictureCompositeMode>, is_in_3d_context: bool, pipeline_id: PipelineId, - original_spatial_node_index: SpatialNodeIndex, frame_output_pipeline_id: Option<PipelineId>, apply_local_clip_rect: bool, ) -> Self { PicturePrimitive { runs: Vec::new(), surface: None, secondary_render_task_id: None, composite_mode, is_in_3d_context, frame_output_pipeline_id, - original_spatial_node_index, - real_local_rect: LayoutRect::zero(), extra_gpu_data_handle: GpuCacheHandle::new(), apply_local_clip_rect, pipeline_id, id, } } + pub fn take_context( + &mut self, + allow_subpixel_aa: bool, + ) -> PictureContext { + // TODO(lsalzman): allow overriding parent if intermediate surface is opaque + let allow_subpixel_aa = allow_subpixel_aa && self.allow_subpixel_aa(); + + let inflation_factor = match self.composite_mode { + Some(PictureCompositeMode::Filter(FilterOp::Blur(blur_radius))) => { + // The amount of extra space needed for primitives inside + // this picture to ensure the visibility check is correct. + BLUR_SAMPLE_SCALE * blur_radius + } + _ => { + 0.0 + } + }; + + PictureContext { + pipeline_id: self.pipeline_id, + prim_runs: mem::replace(&mut self.runs, Vec::new()), + apply_local_clip_rect: self.apply_local_clip_rect, + inflation_factor, + allow_subpixel_aa, + } + } + pub fn add_primitive( &mut self, prim_index: PrimitiveIndex, - spatial_node_index: SpatialNodeIndex, ) { if let Some(ref mut run) = self.runs.last_mut() { - if run.spatial_node_index == spatial_node_index && - run.base_prim_index.0 + run.count == prim_index.0 { + if run.base_prim_index.0 + run.count == prim_index.0 { run.count += 1; return; } } self.runs.push(PrimitiveRun { base_prim_index: prim_index, count: 1, - spatial_node_index, }); } - pub fn update_local_rect_and_set_runs( + pub fn restore_context( &mut self, - prim_run_rect: PrimitiveRunLocalRect, - prim_runs: Vec<PrimitiveRun>, + context: PictureContext, + local_rect_builder: LocalRectBuilder, ) -> LayoutRect { - self.runs = prim_runs; + self.runs = context.prim_runs; - let local_content_rect = prim_run_rect.mapping.local_rect; - - self.real_local_rect = match prim_run_rect.original_mapping { - Some(mapping) => mapping.local_rect, - None => local_content_rect, - }; + let local_content_rect = local_rect_builder.local_rect; match self.composite_mode { Some(PictureCompositeMode::Filter(FilterOp::Blur(blur_radius))) => { let inflate_size = (blur_radius * BLUR_SAMPLE_SCALE).ceil(); local_content_rect.inflate(inflate_size, inflate_size) } Some(PictureCompositeMode::Filter(FilterOp::DropShadow(_, blur_radius, _))) => { let inflate_size = (blur_radius * BLUR_SAMPLE_SCALE).ceil(); @@ -272,25 +283,25 @@ impl PicturePrimitive { } None => { true } } } // Disallow subpixel AA if an intermediate surface is needed. - pub fn allow_subpixel_aa(&self) -> bool { + fn allow_subpixel_aa(&self) -> bool { self.can_draw_directly_to_parent_surface() } pub fn prepare_for_render( &mut self, prim_index: PrimitiveIndex, prim_metadata: &mut PrimitiveMetadata, - prim_run_context: &PrimitiveRunContext, + prim_context: &PrimitiveContext, mut pic_state_for_children: PictureState, pic_state: &mut PictureState, frame_context: &FrameBuildingContext, frame_state: &mut FrameBuildingState, ) { let prim_screen_rect = prim_metadata .screen_rect .as_ref() @@ -323,30 +334,31 @@ impl PicturePrimitive { let device_rect = prim_screen_rect .clipped .inflate(blur_range, blur_range) .intersection(&prim_screen_rect.unclipped) .unwrap(); let uv_rect_kind = calculate_uv_rect_kind( &prim_metadata.local_rect, - &prim_run_context.transform, + &prim_context.transform, &device_rect, frame_context.device_pixel_scale, ); // If we are drawing a blur that has primitives or clips that contain // a complex coordinate system, don't bother caching them (for now). // It's likely that they are animating and caching may not help here // anyway. In the future we should relax this a bit, so that we can // cache tasks with complex coordinate systems if we detect the // relevant transforms haven't changed from frame to frame. let surface = if pic_state_for_children.has_non_root_coord_system { let picture_task = RenderTask::new_picture( - RenderTaskLocation::Dynamic(None, Some(device_rect.size)), + RenderTaskLocation::Dynamic(None, device_rect.size), + prim_screen_rect.unclipped.size, prim_index, device_rect.origin, pic_state_for_children.tasks, uv_rect_kind, ); let picture_task_id = frame_state.render_tasks.add(picture_task); @@ -392,17 +404,18 @@ impl PicturePrimitive { frame_state.gpu_cache, frame_state.render_tasks, None, false, |render_tasks| { let child_tasks = mem::replace(&mut pic_state_for_children.tasks, Vec::new()); let picture_task = RenderTask::new_picture( - RenderTaskLocation::Dynamic(None, Some(device_rect.size)), + RenderTaskLocation::Dynamic(None, device_rect.size), + prim_screen_rect.unclipped.size, prim_index, device_rect.origin, child_tasks, uv_rect_kind, ); let picture_task_id = render_tasks.add(picture_task); @@ -442,23 +455,24 @@ impl PicturePrimitive { let device_rect = prim_screen_rect .clipped .inflate(blur_range, blur_range) .intersection(&prim_screen_rect.unclipped) .unwrap(); let uv_rect_kind = calculate_uv_rect_kind( &prim_metadata.local_rect, - &prim_run_context.transform, + &prim_context.transform, &device_rect, frame_context.device_pixel_scale, ); let mut picture_task = RenderTask::new_picture( - RenderTaskLocation::Dynamic(None, Some(device_rect.size)), + RenderTaskLocation::Dynamic(None, device_rect.size), + prim_screen_rect.unclipped.size, prim_index, device_rect.origin, pic_state_for_children.tasks, uv_rect_kind, ); picture_task.mark_for_saving(); let picture_task_id = frame_state.render_tasks.add(picture_task); @@ -511,23 +525,24 @@ impl PicturePrimitive { // segment rect / extra data request.push(shadow_rect); request.push([0.0, 0.0, 0.0, 0.0]); } } Some(PictureCompositeMode::MixBlend(..)) => { let uv_rect_kind = calculate_uv_rect_kind( &prim_metadata.local_rect, - &prim_run_context.transform, + &prim_context.transform, &prim_screen_rect.clipped, frame_context.device_pixel_scale, ); let picture_task = RenderTask::new_picture( - RenderTaskLocation::Dynamic(None, Some(prim_screen_rect.clipped.size)), + RenderTaskLocation::Dynamic(None, prim_screen_rect.clipped.size), + prim_screen_rect.unclipped.size, prim_index, prim_screen_rect.clipped.origin, pic_state_for_children.tasks, uv_rect_kind, ); let readback_task_id = frame_state.render_tasks.add( RenderTask::new_readback(prim_screen_rect.clipped) @@ -546,43 +561,45 @@ impl PicturePrimitive { for i in 0..5 { request.push([m[i*4], m[i*4+1], m[i*4+2], m[i*4+3]]); } } } let uv_rect_kind = calculate_uv_rect_kind( &prim_metadata.local_rect, - &prim_run_context.transform, + &prim_context.transform, &prim_screen_rect.clipped, frame_context.device_pixel_scale, ); let picture_task = RenderTask::new_picture( - RenderTaskLocation::Dynamic(None, Some(prim_screen_rect.clipped.size)), + RenderTaskLocation::Dynamic(None, prim_screen_rect.clipped.size), + prim_screen_rect.unclipped.size, prim_index, prim_screen_rect.clipped.origin, pic_state_for_children.tasks, uv_rect_kind, ); let render_task_id = frame_state.render_tasks.add(picture_task); pic_state.tasks.push(render_task_id); self.surface = Some(PictureSurface::RenderTask(render_task_id)); } Some(PictureCompositeMode::Blit) | None => { let uv_rect_kind = calculate_uv_rect_kind( &prim_metadata.local_rect, - &prim_run_context.transform, + &prim_context.transform, &prim_screen_rect.clipped, frame_context.device_pixel_scale, ); let picture_task = RenderTask::new_picture( - RenderTaskLocation::Dynamic(None, Some(prim_screen_rect.clipped.size)), + RenderTaskLocation::Dynamic(None, prim_screen_rect.clipped.size), + prim_screen_rect.unclipped.size, prim_index, prim_screen_rect.clipped.origin, pic_state_for_children.tasks, uv_rect_kind, ); let render_task_id = frame_state.render_tasks.add(picture_task); pic_state.tasks.push(render_task_id);
--- a/gfx/webrender/src/prim_store.rs +++ b/gfx/webrender/src/prim_store.rs @@ -5,36 +5,34 @@ use api::{AlphaType, BorderRadius, BuiltDisplayList, ClipMode, ColorF}; use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, ExtendMode, LayoutTransform}; use api::{FilterOp, GlyphInstance, GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag, TileOffset}; use api::{GlyphRasterSpace, LayoutPoint, LayoutRect, LayoutSize, LayoutToWorldTransform, LayoutVector2D}; use api::{PremultipliedColorF, PropertyBinding, Shadow, YuvColorSpace, YuvFormat, DeviceIntSideOffsets}; use api::{BorderWidths, BoxShadowClipMode, LayoutToWorldScale, NormalBorder}; use app_units::Au; use border::{BorderCacheKey, BorderRenderTaskInfo}; -use box_shadow::BLUR_SAMPLE_SCALE; -use clip_scroll_tree::{CoordinateSystemId, SpatialNodeIndex}; +use clip_scroll_tree::{ClipScrollTree, CoordinateSystemId, SpatialNodeIndex}; use clip::{ClipNodeFlags, ClipChainId, ClipChainInstance, ClipItem}; use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState}; -use frame_builder::PrimitiveRunContext; +use frame_builder::PrimitiveContext; use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey, FONT_SIZE_LIMIT}; use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest, ToGpuBlocks}; use gpu_types::BrushFlags; use image::{for_each_tile, for_each_repetition}; use picture::{PictureCompositeMode, PicturePrimitive}; #[cfg(debug_assertions)] use render_backend::FrameId; use render_task::{BlitSource, RenderTask, RenderTaskCacheKey}; use render_task::{RenderTaskCacheKeyKind, RenderTaskId, RenderTaskCacheEntryHandle}; use renderer::{MAX_VERTEX_TEXTURE_WIDTH}; use resource_cache::{ImageProperties, ImageRequest, ResourceCache}; use scene::SceneProperties; use segment::SegmentBuilder; -use spatial_node::SpatialNode; use std::{mem, usize}; use util::{MatrixHelpers, calculate_screen_bounding_rect}; use util::{pack_as_float, recycle_vec, TransformedRectKind}; const MIN_BRUSH_SPLIT_AREA: f32 = 256.0 * 256.0; pub const VECS_PER_SEGMENT: usize = 2; @@ -66,17 +64,16 @@ pub struct Transform<'a> { pub backface_is_visible: bool, pub transform_kind: TransformedRectKind, } #[derive(Debug)] pub struct PrimitiveRun { pub base_prim_index: PrimitiveIndex, pub count: usize, - pub spatial_node_index: SpatialNodeIndex, } impl PrimitiveRun { pub fn is_chasing(&self, index: Option<PrimitiveIndex>) -> bool { match index { Some(id) if cfg!(debug_assertions) => { self.base_prim_index <= id && id.0 < self.base_prim_index.0 + self.count } @@ -102,91 +99,20 @@ impl PrimitiveOpacity { pub fn from_alpha(alpha: f32) -> PrimitiveOpacity { PrimitiveOpacity { is_opaque: alpha == 1.0, } } } #[derive(Debug)] -pub enum CoordinateSpaceMappingKind { +pub enum CoordinateSpaceMapping { Local, Offset(LayoutVector2D), - Transform(Option<LayoutTransform>), -} - -#[derive(Debug)] -pub struct CoordinateSpaceMapping { - kind: CoordinateSpaceMappingKind, - pub local_rect: LayoutRect, - ref_spatial_node_index: SpatialNodeIndex, -} - -impl CoordinateSpaceMapping { - fn new( - ref_spatial_node_index: SpatialNodeIndex, - ) -> Self { - CoordinateSpaceMapping { - kind: CoordinateSpaceMappingKind::Local, - local_rect: LayoutRect::zero(), - ref_spatial_node_index, - } - } - - pub fn set_target_spatial_node( - &mut self, - target_node_index: SpatialNodeIndex, - spatial_nodes: &[SpatialNode], - ) { - let ref_spatial_node = &spatial_nodes[self.ref_spatial_node_index.0]; - let target_spatial_node = &spatial_nodes[target_node_index.0]; - - self.kind = if self.ref_spatial_node_index == target_node_index { - CoordinateSpaceMappingKind::Local - } else if ref_spatial_node.coordinate_system_id == target_spatial_node.coordinate_system_id { - let offset = target_spatial_node.coordinate_system_relative_offset - - ref_spatial_node.coordinate_system_relative_offset; - CoordinateSpaceMappingKind::Offset(offset) - } else { - let relative_transform = ref_spatial_node - .world_content_transform - .inverse() - .map(|inv_parent| { - inv_parent.pre_mul(&target_spatial_node.world_content_transform) - }) - .map(|transform| { - *transform.to_transform() - }); - CoordinateSpaceMappingKind::Transform(relative_transform) - } - } - - pub fn accumulate(&mut self, rect: &LayoutRect) { - match self.kind { - CoordinateSpaceMappingKind::Local => { - self.local_rect = self.local_rect.union(rect); - } - CoordinateSpaceMappingKind::Offset(ref offset) => { - let rect = rect.translate(offset); - self.local_rect = self.local_rect.union(&rect); - } - CoordinateSpaceMappingKind::Transform(ref transform) => { - if let Some(ref matrix) = transform { - match matrix.transform_rect(rect) { - Some(bounds) => { - self.local_rect = self.local_rect.union(&bounds); - } - None => { - warn!("parent relative transform can't transform the primitive rect for {:?}", rect); - } - } - } - } - } - } + Transform(LayoutTransform), } // Represents the local space rect of a list of // primitive runs. For most primitive runs, the // primitive runs are attached to the parent they // are declared in. However, when a primitive run // is part of a 3d rendering context, it may get // hoisted to a higher level in the picture tree. @@ -195,58 +121,79 @@ impl CoordinateSpaceMapping { // allows constructing the true world space polygons // for the primitive, to enable the plane splitting // logic to work correctly. // TODO(gw) In the future, we can probably simplify // this - perhaps calculate the world space // polygons directly and store internally // in the picture structure. #[derive(Debug)] -pub struct PrimitiveRunLocalRect { - pub mapping: CoordinateSpaceMapping, - pub original_mapping: Option<CoordinateSpaceMapping>, +pub struct LocalRectBuilder { + kind: CoordinateSpaceMapping, + ref_spatial_node_index: SpatialNodeIndex, + current_target_spatial_node_index: SpatialNodeIndex, + pub local_rect: LayoutRect, } -impl PrimitiveRunLocalRect { - pub fn new( - spatial_node_index: SpatialNodeIndex, - original_spatial_node_index: SpatialNodeIndex, - ) -> Self { - let mapping = CoordinateSpaceMapping::new(spatial_node_index); - - let original_mapping = if spatial_node_index == original_spatial_node_index { - None - } else { - Some(CoordinateSpaceMapping::new(original_spatial_node_index)) - }; - - PrimitiveRunLocalRect { - mapping, - original_mapping, +impl LocalRectBuilder { + pub fn new(ref_spatial_node_index: SpatialNodeIndex) -> Self { + LocalRectBuilder { + kind: CoordinateSpaceMapping::Local, + local_rect: LayoutRect::zero(), + ref_spatial_node_index, + current_target_spatial_node_index: ref_spatial_node_index, } } pub fn set_target_spatial_node( &mut self, target_node_index: SpatialNodeIndex, - spatial_nodes: &[SpatialNode], + clip_scroll_tree: &ClipScrollTree, ) { - self.mapping - .set_target_spatial_node(target_node_index, spatial_nodes); + if target_node_index != self.current_target_spatial_node_index { + let spatial_nodes = &clip_scroll_tree.spatial_nodes; + let ref_spatial_node = &spatial_nodes[self.ref_spatial_node_index.0]; + let target_spatial_node = &spatial_nodes[target_node_index.0]; + self.current_target_spatial_node_index = target_node_index; - if let Some(ref mut mapping) = self.original_mapping { - mapping.set_target_spatial_node(target_node_index, spatial_nodes); + self.kind = if self.ref_spatial_node_index == target_node_index { + CoordinateSpaceMapping::Local + } else if ref_spatial_node.coordinate_system_id == target_spatial_node.coordinate_system_id { + let offset = target_spatial_node.coordinate_system_relative_offset - + ref_spatial_node.coordinate_system_relative_offset; + CoordinateSpaceMapping::Offset(offset) + } else { + let transform = clip_scroll_tree.get_relative_transform( + self.ref_spatial_node_index, + target_node_index, + ); + CoordinateSpaceMapping::Transform(transform) + }; } } pub fn accumulate(&mut self, rect: &LayoutRect) { - self.mapping.accumulate(rect); - - if let Some(ref mut mapping) = self.original_mapping { - mapping.accumulate(rect); + match self.kind { + CoordinateSpaceMapping::Local => { + self.local_rect = self.local_rect.union(rect); + } + CoordinateSpaceMapping::Offset(ref offset) => { + let rect = rect.translate(offset); + self.local_rect = self.local_rect.union(&rect); + } + CoordinateSpaceMapping::Transform(ref transform) => { + match transform.transform_rect(rect) { + Some(bounds) => { + self.local_rect = self.local_rect.union(&bounds); + } + None => { + warn!("parent relative transform can't transform the primitive rect for {:?}", rect); + } + } + } } } } /// For external images, it's not possible to know the /// UV coords of the image (or the image data itself) /// until the render thread receives the frame and issues /// callbacks to the client application. For external @@ -287,16 +234,17 @@ pub struct ScreenRect { pub unclipped: DeviceIntRect, } // TODO(gw): Pack the fields here better! #[derive(Debug)] pub struct PrimitiveMetadata { pub opacity: PrimitiveOpacity, pub clip_chain_id: ClipChainId, + pub spatial_node_index: SpatialNodeIndex, pub gpu_location: GpuCacheHandle, pub clip_task_id: Option<RenderTaskId>, // TODO(gw): In the future, we should just pull these // directly from the DL item, instead of // storing them here. pub local_rect: LayoutRect, pub local_clip_rect: LayoutRect, @@ -395,16 +343,17 @@ pub enum BrushKind { }, Clear, Picture(PicturePrimitive), Image { request: ImageRequest, alpha_type: AlphaType, stretch_size: LayoutSize, tile_spacing: LayoutSize, + color: ColorF, source: ImageSource, sub_rect: Option<DeviceIntRect>, opacity_binding: OpacityBinding, visible_tiles: Vec<VisibleImageTile>, }, YuvImage { yuv_key: [ImageKey; 3], format: YuvFormat, @@ -598,18 +547,18 @@ impl BrushPrimitive { local_rect.size.width, local_rect.size.height, 0.0, 0.0, ]); } // Images are drawn as a white color, modulated by the total // opacity coming from any collapsed property bindings. - BrushKind::Image { stretch_size, tile_spacing, ref opacity_binding, .. } => { - request.push(ColorF::new(1.0, 1.0, 1.0, opacity_binding.current).premultiplied()); + BrushKind::Image { stretch_size, tile_spacing, color, ref opacity_binding, .. } => { + request.push(color.scale_alpha(opacity_binding.current).premultiplied()); request.push(PremultipliedColorF::WHITE); request.push([ stretch_size.width + tile_spacing.width, stretch_size.height + tile_spacing.height, 0.0, 0.0, ]); } @@ -1369,25 +1318,27 @@ impl PrimitiveStore { } pub fn add_primitive( &mut self, local_rect: &LayoutRect, local_clip_rect: &LayoutRect, is_backface_visible: bool, clip_chain_id: ClipChainId, + spatial_node_index: SpatialNodeIndex, tag: Option<ItemTag>, container: PrimitiveContainer, ) -> PrimitiveIndex { let prim_index = self.primitives.len(); let base_metadata = PrimitiveMetadata { clip_chain_id, gpu_location: GpuCacheHandle::new(), clip_task_id: None, + spatial_node_index, local_rect: *local_rect, local_clip_rect: *local_clip_rect, combined_local_clip_rect: *local_clip_rect, is_backface_visible, screen_rect: None, tag, opacity: PrimitiveOpacity::translucent(), #[cfg(debug_assertions)] @@ -1551,120 +1502,93 @@ impl PrimitiveStore { pub fn prim_count(&self) -> usize { self.primitives.len() } pub fn prepare_prim_for_render( &mut self, prim_index: PrimitiveIndex, - prim_run_context: &PrimitiveRunContext, + prim_context: &PrimitiveContext, pic_context: &PictureContext, pic_state: &mut PictureState, frame_context: &FrameBuildingContext, frame_state: &mut FrameBuildingState, display_list: &BuiltDisplayList, + is_chased: bool, ) -> Option<LayoutRect> { let mut may_need_clip_mask = true; let mut pic_state_for_children = PictureState::new(); - let is_chased = Some(prim_index) == self.chase_id; // If we have dependencies, we need to prepare them first, in order // to know the actual rect of this primitive. // For example, scrolling may affect the location of an item in // local space, which may force us to render this item on a larger // picture target, if being composited. let pic_context_for_children = { - let prim = &mut self.primitives[prim_index.0]; - - // Do some basic checks first, that can early out - // without even knowing the local rect. - if !prim.metadata.is_backface_visible && prim_run_context.transform.backface_is_visible { - if cfg!(debug_assertions) && is_chased { - println!("\tculled for not having visible back faces"); - } - return None; - } - - match prim.details { + match self.primitives[prim_index.0].details { PrimitiveDetails::Brush(ref mut brush) => { match brush.kind { BrushKind::Picture(ref mut pic) => { if !pic.resolve_scene_properties(frame_context.scene_properties) { if cfg!(debug_assertions) && is_chased { println!("\tculled for carrying an invisible composite filter"); } return None; } may_need_clip_mask = pic.composite_mode.is_some(); - let inflation_factor = match pic.composite_mode { - Some(PictureCompositeMode::Filter(FilterOp::Blur(blur_radius))) => { - // The amount of extra space needed for primitives inside - // this picture to ensure the visibility check is correct. - BLUR_SAMPLE_SCALE * blur_radius - } - _ => { - 0.0 - } - }; - // Mark whether this picture has a complex coordinate system. pic_state_for_children.has_non_root_coord_system |= - prim_run_context.scroll_node.coordinate_system_id != CoordinateSystemId::root(); + prim_context.spatial_node.coordinate_system_id != CoordinateSystemId::root(); - Some(PictureContext { - pipeline_id: pic.pipeline_id, - prim_runs: mem::replace(&mut pic.runs, Vec::new()), - spatial_node_index: prim_run_context.spatial_node_index, - original_spatial_node_index: pic.original_spatial_node_index, - apply_local_clip_rect: pic.apply_local_clip_rect, - inflation_factor, - // TODO(lsalzman): allow overriding parent if intermediate surface is opaque - allow_subpixel_aa: pic_context.allow_subpixel_aa && pic.allow_subpixel_aa(), - }) + Some(pic.take_context(pic_context.allow_subpixel_aa)) } _ => { None } } } PrimitiveDetails::TextRun(..) => { None } } }; if let Some(pic_context_for_children) = pic_context_for_children { - let result = self.prepare_prim_runs( + let mut local_rect_builder = LocalRectBuilder::new( + prim_context.spatial_node_index, + ); + + self.prepare_prim_runs( &pic_context_for_children, &mut pic_state_for_children, frame_context, frame_state, + &mut local_rect_builder, ); // Restore the dependencies (borrow check dance) let prim = &mut self.primitives[prim_index.0]; let new_local_rect = prim .as_pic_mut() - .update_local_rect_and_set_runs( - result, - pic_context_for_children.prim_runs + .restore_context( + pic_context_for_children, + local_rect_builder, ); if new_local_rect != prim.metadata.local_rect { prim.metadata.local_rect = new_local_rect; frame_state.gpu_cache.invalidate(&mut prim.metadata.gpu_location); pic_state.local_rect_changed = true; } } let prim = &mut self.primitives[prim_index.0]; - prim.metadata.screen_rect = None; if prim.metadata.local_rect.size.width <= 0.0 || prim.metadata.local_rect.size.height <= 0.0 { if cfg!(debug_assertions) && is_chased { println!("\tculled for zero local rectangle"); } return None; } @@ -1690,17 +1614,17 @@ impl PrimitiveStore { }; let clip_chain = frame_state .clip_store .build_clip_chain_instance( prim.metadata.clip_chain_id, local_rect, prim.metadata.local_clip_rect, - prim_run_context.spatial_node_index, + prim_context.spatial_node_index, &frame_context.clip_scroll_tree.spatial_nodes, frame_state.gpu_cache, frame_state.resource_cache, frame_context.device_pixel_scale, ); let clip_chain = match clip_chain { Some(clip_chain) => clip_chain, @@ -1715,42 +1639,42 @@ impl PrimitiveStore { clip_chain.clips_range, if pic_context.apply_local_clip_rect { "(applied)" } else { "" }, ); } pic_state.has_non_root_coord_system |= clip_chain.has_non_root_coord_system; let unclipped_device_rect = match calculate_screen_bounding_rect( - &prim_run_context.scroll_node.world_content_transform, + &prim_context.spatial_node.world_content_transform, &local_rect, frame_context.device_pixel_scale, None, //TODO: inflate `frame_context.screen_rect` appropriately ) { Some(rect) => rect, None => { if cfg!(debug_assertions) && is_chased { println!("\tculled for being behind the near plane of transform: {:?}", - prim_run_context.scroll_node.world_content_transform); + prim_context.spatial_node.world_content_transform); } return None } }; let clipped_device_rect = match calculate_screen_bounding_rect( - &prim_run_context.scroll_node.world_content_transform, + &prim_context.spatial_node.world_content_transform, &clip_chain.local_bounding_rect, frame_context.device_pixel_scale, None, ) { Some(rect) => rect, None => { if cfg!(debug_assertions) && is_chased { println!("\tculled for being behind the near plane of transform: {:?}", - prim_run_context.scroll_node.world_content_transform); + prim_context.spatial_node.world_content_transform); } return None } }; let clipped_device_rect = match clipped_device_rect.intersection(&frame_context.screen_rect) { Some(clipped_device_rect) => clipped_device_rect, None => return None, @@ -1774,34 +1698,34 @@ impl PrimitiveStore { prim.build_prim_segments_if_needed( pic_state, frame_state, frame_context, ); if may_need_clip_mask && !prim.update_clip_task( - prim_run_context, + prim_context, &clipped_device_rect, &clip_chain, pic_state, frame_context, frame_state, is_chased, ) { return None; } if cfg!(debug_assertions) && is_chased { println!("\tconsidered visible and ready with local rect {:?}", local_rect); } prim.prepare_prim_for_render_inner( prim_index, - prim_run_context, + prim_context, pic_state_for_children, pic_context, pic_state, frame_context, frame_state, display_list, is_chased, ); @@ -1818,89 +1742,100 @@ impl PrimitiveStore { } pub fn prepare_prim_runs( &mut self, pic_context: &PictureContext, pic_state: &mut PictureState, frame_context: &FrameBuildingContext, frame_state: &mut FrameBuildingState, - ) -> PrimitiveRunLocalRect { - let mut result = PrimitiveRunLocalRect::new( - pic_context.spatial_node_index, - pic_context.original_spatial_node_index, - ); - + local_rect_builder: &mut LocalRectBuilder, + ) { let display_list = &frame_context .pipelines .get(&pic_context.pipeline_id) .expect("No display list?") .display_list; for run in &pic_context.prim_runs { - // TODO(gw): Perhaps we can restructure this to not need to create - // a new primitive context for every run (if the hash - // lookups ever show up in a profile). - let scroll_node = &frame_context - .clip_scroll_tree - .spatial_nodes[run.spatial_node_index.0]; - if run.is_chasing(self.chase_id) { println!("\tpreparing a run of length {} in pipeline {:?}", run.count, pic_context.pipeline_id); - println!("\trun {:?}", run.spatial_node_index); - println!("\ttransform {:?}", scroll_node.world_content_transform.to_transform()); } - // Mark whether this picture contains any complex coordinate - // systems, due to either the scroll node or the clip-chain. - pic_state.has_non_root_coord_system |= - scroll_node.coordinate_system_id != CoordinateSystemId::root(); - - if !scroll_node.invertible { - if run.is_chasing(self.chase_id) { - println!("\tculled for the scroll node transform being invertible"); - } - continue; - } - - result.set_target_spatial_node( - run.spatial_node_index, - &frame_context.clip_scroll_tree.spatial_nodes, - ); - - let transform = frame_context - .transforms - .get_transform(run.spatial_node_index); - - let child_prim_run_context = PrimitiveRunContext::new( - scroll_node, - run.spatial_node_index, - transform, - ); - for i in 0 .. run.count { let prim_index = PrimitiveIndex(run.base_prim_index.0 + i); + let is_chased = Some(prim_index) == self.chase_id; + + // TODO(gw): These workarounds for borrowck are unfortunate. We + // should see if we can re-structure these to avoid so + // many special borrow blocks. + let (spatial_node_index, is_backface_visible) = { + let prim = &self.primitives[prim_index.0]; + (prim.metadata.spatial_node_index, prim.metadata.is_backface_visible) + }; + + let spatial_node = &frame_context + .clip_scroll_tree + .spatial_nodes[spatial_node_index.0]; + + let transform = frame_context + .transforms + .get_transform(spatial_node_index); + + // TODO(gw): Although constructing these is cheap, they are often + // the same for many consecutive primitives, so it may + // be worth caching the most recent context. + let prim_context = PrimitiveContext::new( + spatial_node, + spatial_node_index, + transform, + ); + + // Do some basic checks first, that can early out + // without even knowing the local rect. + if !is_backface_visible && prim_context.transform.backface_is_visible { + if cfg!(debug_assertions) && is_chased { + println!("\tculled for not having visible back faces"); + } + continue; + } + + if !spatial_node.invertible { + if cfg!(debug_assertions) && is_chased { + println!("\tculled for the scroll node transform being invertible"); + } + continue; + } + + // Mark whether this picture contains any complex coordinate + // systems, due to either the scroll node or the clip-chain. + pic_state.has_non_root_coord_system |= + spatial_node.coordinate_system_id != CoordinateSystemId::root(); + + local_rect_builder.set_target_spatial_node( + spatial_node_index, + &frame_context.clip_scroll_tree, + ); if let Some(prim_local_rect) = self.prepare_prim_for_render( prim_index, - &child_prim_run_context, + &prim_context, pic_context, pic_state, frame_context, frame_state, display_list, + is_chased, ) { frame_state.profile_counters.visible_primitives.inc(); - result.accumulate(&prim_local_rect); + local_rect_builder.accumulate(&prim_local_rect); } } } - - result } } fn build_gradient_stops_request( stops_handle: &mut GpuCacheHandle, stops_range: ItemRange<GradientStop>, reverse_stops: bool, frame_state: &mut FrameBuildingState, @@ -1918,17 +1853,17 @@ fn build_gradient_stops_request( } } fn decompose_repeated_primitive( visible_tiles: &mut Vec<VisibleGradientTile>, metadata: &mut PrimitiveMetadata, stretch_size: &LayoutSize, tile_spacing: &LayoutSize, - prim_run_context: &PrimitiveRunContext, + prim_context: &PrimitiveContext, frame_context: &FrameBuildingContext, frame_state: &mut FrameBuildingState, callback: &mut FnMut(&LayoutRect, GpuDataRequest), ) { visible_tiles.clear(); // Tighten the clip rect because decomposing the repeated image can // produce primitives that are partially covering the original image @@ -1938,17 +1873,17 @@ fn decompose_repeated_primitive( .intersection(&metadata.local_rect).unwrap(); let unclipped_device_rect = &metadata .screen_rect .unwrap() .unclipped; let visible_rect = compute_conservative_visible_rect( - prim_run_context, + prim_context, frame_context, unclipped_device_rect, &tight_clip_rect ); let stride = *stretch_size + *tile_spacing; for_each_repetition( &metadata.local_rect, @@ -1979,26 +1914,26 @@ fn decompose_repeated_primitive( // Clearing the screen rect has the effect of "culling out" the primitive // from the point of view of the batch builder, and ensures we don't hit // assertions later on because we didn't request any image. metadata.screen_rect = None; } } fn compute_conservative_visible_rect( - prim_run_context: &PrimitiveRunContext, + prim_context: &PrimitiveContext, frame_context: &FrameBuildingContext, clipped_device_rect: &DeviceIntRect, local_clip_rect: &LayoutRect, ) -> LayoutRect { let world_screen_rect = clipped_device_rect .to_f32() / frame_context.device_pixel_scale; - if let Some(layer_screen_rect) = prim_run_context - .scroll_node + if let Some(layer_screen_rect) = prim_context + .spatial_node .world_content_transform .unapply(&world_screen_rect) { return local_clip_rect.intersection(&layer_screen_rect).unwrap_or(LayoutRect::zero()); } *local_clip_rect } @@ -2034,17 +1969,17 @@ fn write_brush_segment_description( metadata: &PrimitiveMetadata, clip_chain: &ClipChainInstance, frame_state: &mut FrameBuildingState, ) { match brush.segment_desc { Some(ref segment_desc) => { // If we already have a segment descriptor, only run through the // clips list if we haven't already determined the mask kind. - if segment_desc.clip_mask_kind != BrushClipMaskKind::Unknown { + if segment_desc.clip_mask_kind == clip_chain.clip_mask_kind { return; } } None => { // If no segment descriptor built yet, see if it is a brush // type that wants to be segmented. if !brush.kind.supports_segments(frame_state.resource_cache) { return; @@ -2065,51 +2000,26 @@ fn write_brush_segment_description( let mut rect_clips_only = true; let mut segment_builder = SegmentBuilder::new( metadata.local_rect, None, metadata.local_clip_rect ); - // If this primitive is clipped by clips from a different coordinate system, then we - // need to apply a clip mask for the entire primitive. - let mut clip_mask_kind = if clip_chain.has_clips_from_other_coordinate_systems { - BrushClipMaskKind::Global - } else { - BrushClipMaskKind::Individual - }; - // Segment the primitive on all the local-space clip sources that we can. for i in 0 .. clip_chain.clips_range.count { let (clip_node, flags) = frame_state.clip_store.get_node_from_range(&clip_chain.clips_range, i); - if !flags.contains(ClipNodeFlags::SAME_COORD_SYSTEM) { - continue; - } - - // TODO(gw): We can easily extend the segment builder to support these clip sources in - // the future, but they are rarely used. - // We must do this check here in case we continue early below. - if clip_node.item.is_image_or_line_decoration_clip() { - clip_mask_kind = BrushClipMaskKind::Global; - } - // If this clip item is positioned by another positioning node, its relative position // could change during scrolling. This means that we would need to resegment. Instead // of doing that, only segment with clips that have the same positioning node. // TODO(mrobinson, #2858): It may make sense to include these nodes, resegmenting only // when necessary while scrolling. if !flags.contains(ClipNodeFlags::SAME_SPATIAL_NODE) { - // We don't need to generate a global clip mask for rectangle clips because we are - // in the same coordinate system and rectangular clips are handled by the local - // clip chain rectangle. - if !clip_node.item.is_rect() { - clip_mask_kind = BrushClipMaskKind::Global; - } continue; } let (local_clip_rect, radius, mode) = match clip_node.item { ClipItem::RoundedRectangle(rect, radii, clip_mode) => { rect_clips_only = false; (rect, Some(radii), clip_mode) } @@ -2150,17 +2060,17 @@ fn write_brush_segment_description( }; segment_builder.push_clip_rect(local_clip_rect, radius, mode); } if is_large || rect_clips_only { match brush.segment_desc { Some(ref mut segment_desc) => { - segment_desc.clip_mask_kind = clip_mask_kind; + segment_desc.clip_mask_kind = clip_chain.clip_mask_kind; } None => { // TODO(gw): We can probably make the allocation // patterns of this and the segment // builder significantly better, by // retaining it across primitives. let mut segments = Vec::new(); @@ -2173,27 +2083,27 @@ fn write_brush_segment_description( [0.0; 4], BrushFlags::empty(), ), ); }); brush.segment_desc = Some(BrushSegmentDescriptor { segments, - clip_mask_kind, + clip_mask_kind: clip_chain.clip_mask_kind, }); } } } } impl Primitive { fn update_clip_task_for_brush( &mut self, - prim_run_context: &PrimitiveRunContext, + prim_context: &PrimitiveContext, clip_chain: &ClipChainInstance, combined_outer_rect: &DeviceIntRect, pic_state: &mut PictureState, frame_context: &FrameBuildingContext, frame_state: &mut FrameBuildingState, ) -> bool { debug_assert!(frame_context.screen_rect.contains_rect(combined_outer_rect)); @@ -2217,17 +2127,17 @@ impl Primitive { for segment in &mut segment_desc.segments { if !segment.may_need_clip_mask && clip_mask_kind != BrushClipMaskKind::Global { segment.clip_task_id = BrushSegmentTaskId::Opaque; continue; } let intersected_rect = calculate_screen_bounding_rect( - &prim_run_context.scroll_node.world_content_transform, + &prim_context.spatial_node.world_content_transform, &segment.local_rect, frame_context.device_pixel_scale, Some(&combined_outer_rect), ); let bounds = match intersected_rect { Some(bounds) => bounds, None => { @@ -2264,17 +2174,17 @@ impl Primitive { } } } } fn prepare_prim_for_render_inner( &mut self, prim_index: PrimitiveIndex, - prim_run_context: &PrimitiveRunContext, + prim_context: &PrimitiveContext, pic_state_for_children: PictureState, pic_context: &PictureContext, pic_state: &mut PictureState, frame_context: &FrameBuildingContext, frame_state: &mut FrameBuildingState, display_list: &BuiltDisplayList, is_chased: bool, ) { @@ -2283,31 +2193,32 @@ impl Primitive { #[cfg(debug_assertions)] { metadata.prepared_frame_id = frame_state.render_tasks.frame_id(); } match self.details { PrimitiveDetails::TextRun(ref mut text) => { // The transform only makes sense for screen space rasterization - let transform = prim_run_context.scroll_node.world_content_transform.to_transform(); + let transform = prim_context.spatial_node.world_content_transform.to_transform(); text.prepare_for_render( frame_context.device_pixel_scale, &transform, pic_context.allow_subpixel_aa, display_list, frame_state, ); } PrimitiveDetails::Brush(ref mut brush) => { match brush.kind { BrushKind::Image { request, sub_rect, stretch_size, + color, ref mut tile_spacing, ref mut source, ref mut opacity_binding, ref mut visible_tiles, .. } => { let image_properties = frame_state .resource_cache @@ -2323,17 +2234,18 @@ impl Primitive { if opacity_binding.update(frame_context.scene_properties) { frame_state.gpu_cache.invalidate(&mut metadata.gpu_location); } // Update opacity for this primitive to ensure the correct // batching parameters are used. metadata.opacity.is_opaque = image_properties.descriptor.is_opaque && - opacity_binding.current == 1.0; + opacity_binding.current == 1.0 && + color.a == 1.0; if *tile_spacing != LayoutSize::zero() && !is_tiled { *source = ImageSource::Cache { // Size in device-pixels we need to allocate in render task cache. size: image_properties.descriptor.size.to_i32(), handle: None, }; } @@ -2436,17 +2348,17 @@ impl Primitive { // Tighten the clip rect because decomposing the repeated image can // produce primitives that are partially covering the original image // rect and we want to clip these extra parts out. let tight_clip_rect = metadata .combined_local_clip_rect .intersection(&metadata.local_rect).unwrap(); let visible_rect = compute_conservative_visible_rect( - prim_run_context, + prim_context, frame_context, &metadata.screen_rect.unwrap().clipped, &tight_clip_rect ); let base_edge_flags = edge_flags_for_tile_spacing(tile_spacing); let stride = stretch_size + *tile_spacing; @@ -2576,17 +2488,17 @@ impl Primitive { if tile_spacing != LayoutSize::zero() { is_tiled = true; decompose_repeated_primitive( visible_tiles, metadata, &stretch_size, &tile_spacing, - prim_run_context, + prim_context, frame_context, frame_state, &mut |rect, mut request| { request.push([ center.x, center.y, start_radius, end_radius, @@ -2626,17 +2538,17 @@ impl Primitive { if tile_spacing != LayoutSize::zero() { is_tiled = true; decompose_repeated_primitive( visible_tiles, metadata, &stretch_size, &tile_spacing, - prim_run_context, + prim_context, frame_context, frame_state, &mut |rect, mut request| { request.push([ start_point.x, start_point.y, end_point.x, end_point.y, @@ -2651,17 +2563,17 @@ impl Primitive { } ); } } BrushKind::Picture(ref mut pic) => { pic.prepare_for_render( prim_index, metadata, - prim_run_context, + prim_context, pic_state_for_children, pic_state, frame_context, frame_state, ); } BrushKind::Solid { ref color, ref mut opacity_binding, .. } => { // If the opacity changed, invalidate the GPU cache so that @@ -2714,33 +2626,33 @@ impl Primitive { } } } } } fn update_clip_task( &mut self, - prim_run_context: &PrimitiveRunContext, + prim_context: &PrimitiveContext, prim_screen_rect: &DeviceIntRect, clip_chain: &ClipChainInstance, pic_state: &mut PictureState, frame_context: &FrameBuildingContext, frame_state: &mut FrameBuildingState, is_chased: bool, ) -> bool { if cfg!(debug_assertions) && is_chased { println!("\tupdating clip task with screen rect {:?}", prim_screen_rect); } // Reset clips from previous frames since we may clip differently each frame. self.reset_clip_task(); // First try to render this primitive's mask using optimized brush rendering. if self.update_clip_task_for_brush( - prim_run_context, + prim_context, &clip_chain, prim_screen_rect, pic_state, frame_context, frame_state, ) { if cfg!(debug_assertions) && is_chased { println!("\tsegment tasks have been created for clipping");
--- a/gfx/webrender/src/render_task.rs +++ b/gfx/webrender/src/render_task.rs @@ -173,17 +173,17 @@ impl ops::IndexMut<RenderTaskId> for Ren } } #[derive(Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub enum RenderTaskLocation { Fixed(DeviceIntRect), - Dynamic(Option<(DeviceIntPoint, RenderTargetIndex)>, Option<DeviceIntSize>), + Dynamic(Option<(DeviceIntPoint, RenderTargetIndex)>, DeviceIntSize), TextureCache(SourceTexture, i32, DeviceIntRect), } #[derive(Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct CacheMaskTask { actual_rect: DeviceIntRect, @@ -197,16 +197,17 @@ pub struct ClipRegionTask { pub clip_data_address: GpuCacheAddress, } #[derive(Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct PictureTask { pub prim_index: PrimitiveIndex, + pub can_merge: bool, pub content_origin: DeviceIntPoint, pub uv_rect_handle: GpuCacheHandle, uv_rect_kind: UvRectKind, } #[derive(Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] @@ -325,48 +326,50 @@ impl RenderTask { size: DeviceIntSize, children: Vec<RenderTaskId>, kind: RenderTaskKind, clear_mode: ClearMode, ) -> Self { render_task_sanity_check(&size); RenderTask { - location: RenderTaskLocation::Dynamic(None, Some(size)), + location: RenderTaskLocation::Dynamic(None, size), children, kind, clear_mode, saved_index: None, } } pub fn new_picture( location: RenderTaskLocation, + unclipped_size: DeviceIntSize, prim_index: PrimitiveIndex, content_origin: DeviceIntPoint, children: Vec<RenderTaskId>, uv_rect_kind: UvRectKind, ) -> Self { let size = match location { - RenderTaskLocation::Dynamic(_, Some(size)) => Some(size), - RenderTaskLocation::Fixed(rect) => Some(rect.size), - RenderTaskLocation::TextureCache(_, _, rect) => Some(rect.size), - _ => None, + RenderTaskLocation::Dynamic(_, size) => size, + RenderTaskLocation::Fixed(rect) => rect.size, + RenderTaskLocation::TextureCache(_, _, rect) => rect.size, }; - if let Some(size) = size { - render_task_sanity_check(&size); - } + render_task_sanity_check(&size); + + let can_merge = size.width >= unclipped_size.width && + size.height >= unclipped_size.height; RenderTask { location, children, kind: RenderTaskKind::Picture(PictureTask { prim_index, content_origin, + can_merge, uv_rect_handle: GpuCacheHandle::new(), uv_rect_kind, }), clear_mode: ClearMode::Transparent, saved_index: None, } } @@ -765,20 +768,17 @@ impl RenderTask { panic!("texture handle not supported for this task kind"); } } } pub fn get_dynamic_size(&self) -> DeviceIntSize { match self.location { RenderTaskLocation::Fixed(..) => DeviceIntSize::zero(), - RenderTaskLocation::Dynamic(_, Some(size)) => size, - RenderTaskLocation::Dynamic(_, None) => { - panic!("bug: render task must have assigned size by now"); - } + RenderTaskLocation::Dynamic(_, size) => size, RenderTaskLocation::TextureCache(_, _, rect) => rect.size, } } pub fn get_target_rect(&self) -> (DeviceIntRect, RenderTargetIndex) { match self.location { RenderTaskLocation::Fixed(rect) => { (rect, RenderTargetIndex(0)) @@ -793,17 +793,16 @@ impl RenderTask { // Render tasks that are created but not assigned to // passes consume a row in the render task texture, but // don't allocate any space in render targets nor // draw any pixels. // TODO(gw): Consider some kind of tag or other method // to mark a task as unused explicitly. This // would allow us to restore this debug check. RenderTaskLocation::Dynamic(Some((origin, target_index)), size) => { - let size = size.expect("bug: must be assigned a size by now"); (DeviceIntRect::new(origin, size), target_index) } RenderTaskLocation::Dynamic(None, _) => { (DeviceIntRect::zero(), RenderTargetIndex(0)) } RenderTaskLocation::TextureCache(_, layer, rect) => { (rect, RenderTargetIndex(layer as usize)) } @@ -1091,20 +1090,17 @@ impl RenderTaskCache { let target_kind = render_task.target_kind(); // Find out what size to alloc in the texture cache. let size = match render_task.location { RenderTaskLocation::Fixed(..) | RenderTaskLocation::TextureCache(..) => { panic!("BUG: dynamic task was expected"); } - RenderTaskLocation::Dynamic(_, None) => { - panic!("BUG: must have assigned size by now"); - } - RenderTaskLocation::Dynamic(_, Some(size)) => size, + RenderTaskLocation::Dynamic(_, size) => size, }; // Select the right texture page to allocate from. let image_format = match target_kind { RenderTargetKind::Color => ImageFormat::BGRA8, RenderTargetKind::Alpha => ImageFormat::R8, };
--- a/gfx/webrender/src/renderer.rs +++ b/gfx/webrender/src/renderer.rs @@ -243,16 +243,17 @@ pub enum ShaderColorMode { Alpha = 1, SubpixelConstantTextColor = 2, SubpixelWithBgColorPass0 = 3, SubpixelWithBgColorPass1 = 4, SubpixelWithBgColorPass2 = 5, SubpixelDualSource = 6, Bitmap = 7, ColorBitmap = 8, + Image = 9, } impl From<GlyphFormat> for ShaderColorMode { fn from(format: GlyphFormat) -> ShaderColorMode { match format { GlyphFormat::Alpha | GlyphFormat::TransformedAlpha => ShaderColorMode::Alpha, GlyphFormat::Subpixel | GlyphFormat::TransformedSubpixel => { panic!("Subpixel glyph formats must be handled separately.");
--- a/gfx/webrender/src/spatial_node.rs +++ b/gfx/webrender/src/spatial_node.rs @@ -1,17 +1,17 @@ /* 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/. */ use api::{ExternalScrollId, LayoutPixel, LayoutPoint, LayoutRect, LayoutSize, LayoutTransform}; use api::{LayoutVector2D, PipelineId, PropertyBinding, ScrollClamping, ScrollLocation}; -use api::{ScrollSensitivity, StickyOffsetBounds}; -use clip_scroll_tree::{CoordinateSystemId, SpatialNodeIndex, TransformUpdateState}; +use api::{ScrollSensitivity, StickyOffsetBounds, LayoutVector3D}; +use clip_scroll_tree::{CoordinateSystem, CoordinateSystemId, SpatialNodeIndex, TransformUpdateState}; use euclid::SideOffsets2D; use gpu_types::TransformPalette; use scene::SceneProperties; use util::{LayoutFastTransform, LayoutToWorldFastTransform, MatrixHelpers, TransformedRectKind}; #[derive(Clone, Debug)] pub enum SpatialNodeType { /// A special kind of node that adjusts its position based on the position @@ -209,27 +209,27 @@ impl SpatialNode { } transform_palette.set(node_index, &self.world_content_transform); } pub fn update( &mut self, state: &mut TransformUpdateState, - next_coordinate_system_id: &mut CoordinateSystemId, + coord_systems: &mut Vec<CoordinateSystem>, scene_properties: &SceneProperties, ) { // If any of our parents was not rendered, we are not rendered either and can just // quit here. if !state.invertible { self.mark_uninvertible(); return; } - self.update_transform(state, next_coordinate_system_id, scene_properties); + self.update_transform(state, coord_systems, scene_properties); self.transform_kind = self.world_content_transform.kind(); // If this node is a reference frame, we check if it has a non-invertible matrix. // For non-reference-frames we assume that they will produce only additional // translations which should be invertible. match self.node_type { SpatialNodeType::ReferenceFrame(info) if !info.invertible => { self.mark_uninvertible(); @@ -237,17 +237,17 @@ impl SpatialNode { } _ => self.invertible = true, } } pub fn update_transform( &mut self, state: &mut TransformUpdateState, - next_coordinate_system_id: &mut CoordinateSystemId, + coord_systems: &mut Vec<CoordinateSystem>, scene_properties: &SceneProperties, ) { match self.node_type { SpatialNodeType::ReferenceFrame(ref mut info) => { // Resolve the transform against any property bindings. let source_transform = scene_properties.resolve_layout_transform(&info.source_transform); info.resolved_transform = LayoutFastTransform::with_vector(info.origin_in_parent_reference_frame) @@ -276,18 +276,29 @@ impl SpatialNode { if relative_transform.is_simple_2d_translation() { self.coordinate_system_relative_offset = state.coordinate_system_relative_offset + LayoutVector2D::new(relative_transform.m41, relative_transform.m42); } else { // If we break 2D axis alignment or have a perspective component, we need to start a // new incompatible coordinate system with which we cannot share clips without masking. self.coordinate_system_relative_offset = LayoutVector2D::zero(); - state.current_coordinate_system_id = *next_coordinate_system_id; - next_coordinate_system_id.advance(); + + // Push that new coordinate system and record the new id. + let coord_system = CoordinateSystem { + offset: LayoutVector3D::new( + state.coordinate_system_relative_offset.x, + state.coordinate_system_relative_offset.y, + 0.0, + ), + transform: *info.resolved_transform.to_transform(), + parent: Some(state.current_coordinate_system_id), + }; + state.current_coordinate_system_id = CoordinateSystemId(coord_systems.len() as u32); + coord_systems.push(coord_system); } self.coordinate_system_id = state.current_coordinate_system_id; } _ => { // We calculate this here to avoid a double-borrow later. let sticky_offset = self.calculate_sticky_offset( &state.nearest_scrolling_ancestor_offset,
--- a/gfx/webrender/src/tiling.rs +++ b/gfx/webrender/src/tiling.rs @@ -346,17 +346,21 @@ impl RenderTarget for ColorRenderTarget let task = &render_tasks[*task_id]; match task.kind { RenderTaskKind::Picture(ref pic_task) => { let pic = ctx.prim_store.get_pic(pic_task.prim_index); let (target_rect, _) = task.get_target_rect(); - let mut batch_builder = AlphaBatchBuilder::new(self.screen_size, target_rect); + let mut batch_builder = AlphaBatchBuilder::new( + self.screen_size, + target_rect, + pic_task.can_merge, + ); batch_builder.add_pic_to_batch( pic, *task_id, ctx, gpu_cache, render_tasks, deferred_resolves, @@ -840,17 +844,16 @@ impl RenderPass { let texture_target = match task.location { RenderTaskLocation::TextureCache(texture_id, layer, _) => { Some((texture_id, layer)) } RenderTaskLocation::Fixed(..) => { None } RenderTaskLocation::Dynamic(ref mut origin, size) => { - let size = size.expect("bug: size must be assigned by now"); let alloc_size = DeviceUintSize::new(size.width as u32, size.height as u32); let (alloc_origin, target_index) = match target_kind { RenderTargetKind::Color => color.allocate(alloc_size), RenderTargetKind::Alpha => alpha.allocate(alloc_size), }; *origin = Some((alloc_origin.to_i32(), target_index)); None }
--- a/gfx/webrender_api/Cargo.toml +++ b/gfx/webrender_api/Cargo.toml @@ -1,14 +1,15 @@ [package] name = "webrender_api" version = "0.57.2" authors = ["Glenn Watson <gw@intuitionlibrary.com>"] license = "MPL-2.0" repository = "https://github.com/servo/webrender" +description = "Public API for WebRender" [features] nightly = ["euclid/unstable", "serde/unstable"] ipc = ["ipc-channel"] serialize = [] deserialize = [] [dependencies]
--- a/gfx/webrender_api/src/color.rs +++ b/gfx/webrender_api/src/color.rs @@ -43,16 +43,23 @@ impl PremultipliedColorF { pub struct ColorF { pub r: f32, pub g: f32, pub b: f32, pub a: f32, } impl ColorF { + /// + pub const BLACK: Self = ColorF { r: 0.0, g: 0.0, b: 0.0, a: 1.0 }; + /// + pub const TRANSPARENT: Self = ColorF { r: 0.0, g: 0.0, b: 0.0, a: 0.0 }; + /// + pub const WHITE: Self = ColorF { r: 1.0, g: 1.0, b: 1.0, a: 1.0 }; + /// Constructs a new `ColorF` from its components. pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self { ColorF { r, g, b, a } } /// Multiply the RGB channels (but not alpha) with a given factor. pub fn scale_rgb(&self, scale: f32) -> Self { ColorF {
--- a/gfx/webrender_api/src/display_item.rs +++ b/gfx/webrender_api/src/display_item.rs @@ -553,16 +553,17 @@ pub struct IframeDisplayItem { #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] pub struct ImageDisplayItem { pub image_key: ImageKey, pub stretch_size: LayoutSize, pub tile_spacing: LayoutSize, pub image_rendering: ImageRendering, pub alpha_type: AlphaType, + pub color: ColorF, } #[repr(u32)] #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub enum ImageRendering { Auto = 0, CrispEdges = 1, Pixelated = 2,
--- a/gfx/webrender_api/src/display_list.rs +++ b/gfx/webrender_api/src/display_list.rs @@ -1057,23 +1057,25 @@ impl DisplayListBuilder { pub fn push_image( &mut self, info: &LayoutPrimitiveInfo, stretch_size: LayoutSize, tile_spacing: LayoutSize, image_rendering: ImageRendering, alpha_type: AlphaType, key: ImageKey, + color: ColorF, ) { let item = SpecificDisplayItem::Image(ImageDisplayItem { image_key: key, stretch_size, tile_spacing, image_rendering, alpha_type, + color, }); self.push_item(item, info); } /// Push a yuv image. All planar data in yuv image should use the same buffer type. pub fn push_yuv_image( &mut self,
--- a/gfx/webrender_bindings/revision.txt +++ b/gfx/webrender_bindings/revision.txt @@ -1,1 +1,1 @@ -890f3746b798538d201e54b66d2711a70447b781 +e70bae07664def86aefd11c86dac818ab7ea64ea
--- a/gfx/webrender_bindings/src/bindings.rs +++ b/gfx/webrender_bindings/src/bindings.rs @@ -1973,17 +1973,18 @@ pub extern "C" fn wr_dp_push_image(state }; state.frame_builder .dl_builder .push_image(&prim_info, stretch_size, tile_spacing, image_rendering, alpha_type, - key); + key, + ColorF::WHITE); } /// Push a 3 planar yuv image. #[no_mangle] pub extern "C" fn wr_dp_push_yuv_planar_image(state: &mut WrState, bounds: LayoutRect, clip: LayoutRect, is_backface_visible: bool,
--- a/gfx/wrench/src/main.rs +++ b/gfx/wrench/src/main.rs @@ -81,18 +81,16 @@ use webrender::DebugFlags; use webrender::api::*; use winit::dpi::{LogicalPosition, LogicalSize}; use winit::VirtualKeyCode; use wrench::{Wrench, WrenchThing}; use yaml_frame_reader::YamlFrameReader; lazy_static! { static ref PLATFORM_DEFAULT_FACE_NAME: String = String::from("Arial"); - static ref WHITE_COLOR: ColorF = ColorF::new(1.0, 1.0, 1.0, 1.0); - static ref BLACK_COLOR: ColorF = ColorF::new(0.0, 0.0, 0.0, 1.0); } pub static mut CURRENT_FRAME_NUMBER: u32 = 0; #[cfg(feature = "headless")] pub struct HeadlessContext { width: u32, height: u32,
--- a/gfx/wrench/src/rawtest.rs +++ b/gfx/wrench/src/rawtest.rs @@ -109,16 +109,17 @@ impl<'a> RawtestHarness<'a> { // setup some malicious image size parameters builder.push_image( &info, size(151., 56.0), size(151.0, 56.0), ImageRendering::Auto, AlphaType::PremultipliedAlpha, blob_img, + ColorF::WHITE, ); let mut epoch = Epoch(0); self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates); self.rx.recv().unwrap(); self.wrench.render(); @@ -175,16 +176,17 @@ impl<'a> RawtestHarness<'a> { // setup some malicious image size parameters builder.push_image( &info, image_size * 2., image_size, ImageRendering::Auto, AlphaType::PremultipliedAlpha, blob_img, + ColorF::WHITE, ); txn.set_image_visible_area( blob_img, NormalizedRect { origin: point2(0.0, 0.03), size: size2(1.0, 0.03), } ); @@ -279,16 +281,17 @@ impl<'a> RawtestHarness<'a> { builder.push_image( &info, image_size, image_size, ImageRendering::Auto, AlphaType::PremultipliedAlpha, blob_img1, + ColorF::WHITE, ); self.submit_dl(&mut Epoch(0), layout_size, builder, &txn.resource_updates); let pixels1 = self.render_and_get_pixels(window_rect); let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); let mut txn = Transaction::new(); @@ -314,16 +317,17 @@ impl<'a> RawtestHarness<'a> { builder.push_image( &info, image_size, image_size, ImageRendering::Auto, AlphaType::PremultipliedAlpha, blob_img2, + ColorF::WHITE, ); self.submit_dl(&mut Epoch(1), layout_size, builder, &txn.resource_updates); let pixels2 = self.render_and_get_pixels(window_rect); assert!(pixels1 == pixels2); txn = Transaction::new(); @@ -367,16 +371,17 @@ impl<'a> RawtestHarness<'a> { // setup some malicious image size parameters builder.push_image( &info, image_size, image_size, ImageRendering::Auto, AlphaType::PremultipliedAlpha, blob_img, + ColorF::WHITE, ); let mut epoch = Epoch(0); self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates); let original_pixels = self.render_and_get_pixels(window_rect); @@ -391,16 +396,17 @@ impl<'a> RawtestHarness<'a> { // setup some malicious image size parameters builder.push_image( &info, image_size, image_size, ImageRendering::Auto, AlphaType::PremultipliedAlpha, blob_img, + ColorF::WHITE, ); self.submit_dl(&mut epoch, layout_size, builder, &[]); let _offscreen_pixels = self.render_and_get_pixels(window_rect); let mut txn = Transaction::new(); @@ -420,16 +426,17 @@ impl<'a> RawtestHarness<'a> { // setup some malicious image size parameters builder.push_image( &info, image_size, image_size, ImageRendering::Auto, AlphaType::PremultipliedAlpha, blob_img, + ColorF::WHITE, ); let mut epoch = Epoch(2); self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates); let pixels = self.render_and_get_pixels(window_rect); @@ -480,16 +487,17 @@ impl<'a> RawtestHarness<'a> { builder.push_image( &info, size(200.0, 200.0), size(0.0, 0.0), ImageRendering::Auto, AlphaType::PremultipliedAlpha, blob_img, + ColorF::WHITE, ); let mut epoch = Epoch(0); self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates); let pixels_first = self.render_and_get_pixels(window_rect); @@ -502,16 +510,17 @@ impl<'a> RawtestHarness<'a> { let info = LayoutPrimitiveInfo::new(rect(1.0, 60.0, 200.0, 200.0)); builder.push_image( &info, size(200.0, 200.0), size(0.0, 0.0), ImageRendering::Auto, AlphaType::PremultipliedAlpha, blob_img, + ColorF::WHITE, ); txn.resource_updates.clear(); self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates); let pixels_second = self.render_and_get_pixels(window_rect); @@ -586,24 +595,26 @@ impl<'a> RawtestHarness<'a> { let push_images = |builder: &mut DisplayListBuilder| { builder.push_image( &info, size(200.0, 200.0), size(0.0, 0.0), ImageRendering::Auto, AlphaType::PremultipliedAlpha, blob_img, + ColorF::WHITE, ); builder.push_image( &info2, size(200.0, 200.0), size(0.0, 0.0), ImageRendering::Auto, AlphaType::PremultipliedAlpha, blob_img2, + ColorF::WHITE, ); }; push_images(&mut builder); let mut epoch = Epoch(0); self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates); @@ -684,16 +695,17 @@ impl<'a> RawtestHarness<'a> { builder.push_image( &info, size(200.0, 200.0), size(0.0, 0.0), ImageRendering::Auto, AlphaType::PremultipliedAlpha, blob_img, + ColorF::WHITE, ); let mut epoch = Epoch(0); self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates); let pixels_first = self.render_and_get_pixels(window_rect); // draw the blob image a second time after updating it with the same color @@ -710,16 +722,17 @@ impl<'a> RawtestHarness<'a> { let info = LayoutPrimitiveInfo::new(rect(0.0, 60.0, 200.0, 200.0)); builder.push_image( &info, size(200.0, 200.0), size(0.0, 0.0), ImageRendering::Auto, AlphaType::PremultipliedAlpha, blob_img, + ColorF::WHITE, ); self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates); let pixels_second = self.render_and_get_pixels(window_rect); // draw the blob image a third time after updating it with a different color let mut txn = Transaction::new(); txn.update_image( @@ -734,16 +747,17 @@ impl<'a> RawtestHarness<'a> { let info = LayoutPrimitiveInfo::new(rect(0.0, 60.0, 200.0, 200.0)); builder.push_image( &info, size(200.0, 200.0), size(0.0, 0.0), ImageRendering::Auto, AlphaType::PremultipliedAlpha, blob_img, + ColorF::WHITE, ); self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates); let pixels_third = self.render_and_get_pixels(window_rect); assert!(pixels_first == pixels_second); assert!(pixels_first != pixels_third); } @@ -895,16 +909,17 @@ impl<'a> RawtestHarness<'a> { builder.push_image( &LayoutPrimitiveInfo::new(rect(300.0, 70.0, 150.0, 50.0)), size(150.0, 50.0), size(0.0, 0.0), ImageRendering::Auto, AlphaType::PremultipliedAlpha, image, + ColorF::WHITE, ); let mut txn = Transaction::new(); txn.set_display_list( Epoch(0), Some(ColorF::new(1.0, 1.0, 1.0, 1.0)), layout_size,
--- a/gfx/wrench/src/wrench.rs +++ b/gfx/wrench/src/wrench.rs @@ -17,17 +17,17 @@ use std::collections::HashMap; use std::path::PathBuf; use std::sync::{Arc, Mutex}; use std::sync::mpsc::Receiver; use time; use webrender; use webrender::api::*; use webrender::{DebugFlags, RendererStats}; use yaml_frame_writer::YamlFrameWriterReceiver; -use {WindowWrapper, NotifierEvent, BLACK_COLOR, WHITE_COLOR}; +use {WindowWrapper, NotifierEvent}; // TODO(gw): This descriptor matches what we currently support for fonts // but is quite a mess. We should at least document and // use better types for things like the style and stretch. #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub enum FontDescriptor { Path { path: PathBuf, font_index: u32 }, Family { name: String }, @@ -556,17 +556,17 @@ impl Wrench { "S - Toggle compact profiler", "Q - Toggle GPU queries for time and samples", "M - Trigger memory pressure event", "T - Save CPU profile to a file", "C - Save a capture to captures/wrench/", "X - Do a hit test at the current cursor position", ]; - let color_and_offset = [(*BLACK_COLOR, 2.0), (*WHITE_COLOR, 0.0)]; + let color_and_offset = [(ColorF::BLACK, 2.0), (ColorF::WHITE, 0.0)]; let dr = self.renderer.debug_renderer().unwrap(); for ref co in &color_and_offset { let x = self.device_pixel_ratio * (15.0 + co.1); let mut y = self.device_pixel_ratio * (15.0 + co.1 + dr.line_height()); for ref line in &help_lines { dr.add_text(x, y, line, co.0.into()); y += self.device_pixel_ratio * dr.line_height();
--- a/gfx/wrench/src/yaml_frame_reader.rs +++ b/gfx/wrench/src/yaml_frame_reader.rs @@ -12,17 +12,17 @@ use premultiply::premultiply; use std::collections::HashMap; use std::fs::File; use std::io::Read; use std::path::{Path, PathBuf}; use webrender::api::*; use wrench::{FontDescriptor, Wrench, WrenchThing}; use yaml_helper::{StringEnum, YamlHelper, make_perspective}; use yaml_rust::{Yaml, YamlLoader}; -use {BLACK_COLOR, PLATFORM_DEFAULT_FACE_NAME, WHITE_COLOR}; +use PLATFORM_DEFAULT_FACE_NAME; fn rsrc_path(item: &Yaml, aux_dir: &PathBuf) -> PathBuf { let filename = item.as_str().unwrap(); let mut file = aux_dir.clone(); file.push(filename); file } @@ -675,17 +675,17 @@ impl YamlFrameReader { let bounds_key = if item["type"].is_badvalue() { "rect" } else { "bounds" }; info.rect = item[bounds_key] .as_rect() .expect("rect type must have bounds"); - let color = item["color"].as_colorf().unwrap_or(*WHITE_COLOR); + let color = item["color"].as_colorf().unwrap_or(ColorF::WHITE); dl.push_rect(&info, color); } fn handle_clear_rect( &mut self, dl: &mut DisplayListBuilder, item: &Yaml, info: &mut LayoutPrimitiveInfo, @@ -697,17 +697,17 @@ impl YamlFrameReader { } fn handle_line( &mut self, dl: &mut DisplayListBuilder, item: &Yaml, info: &mut LayoutPrimitiveInfo, ) { - let color = item["color"].as_colorf().unwrap_or(*BLACK_COLOR); + let color = item["color"].as_colorf().unwrap_or(ColorF::BLACK); let orientation = item["orientation"] .as_str() .and_then(LineOrientation::from_str) .expect("line must have orientation"); let style = item["style"] .as_str() .and_then(LineStyle::from_str) .expect("line must have style"); @@ -1118,28 +1118,28 @@ impl YamlFrameReader { let alpha_type = match item["alpha-type"].as_str() { Some("premultiplied-alpha") | None => AlphaType::PremultipliedAlpha, Some("alpha") => AlphaType::Alpha, Some(_) => panic!( "AlphaType can be premultiplied-alpha or alpha -- got {:?}", item ), }; - dl.push_image(&info, stretch_size, tile_spacing, rendering, alpha_type, image_key); + dl.push_image(&info, stretch_size, tile_spacing, rendering, alpha_type, image_key, ColorF::WHITE); } fn handle_text( &mut self, dl: &mut DisplayListBuilder, wrench: &mut Wrench, item: &Yaml, info: &mut LayoutPrimitiveInfo, ) { let size = item["size"].as_pt_to_au().unwrap_or(Au::from_f32_px(16.0)); - let color = item["color"].as_colorf().unwrap_or(*BLACK_COLOR); + let color = item["color"].as_colorf().unwrap_or(ColorF::BLACK); let bg_color = item["bg-color"].as_colorf().map(|c| c.into()); let synthetic_italics = if let Some(angle) = item["synthetic-italics"].as_f32() { SyntheticItalics::from_degrees(angle) } else if item["synthetic-italics"].as_bool().unwrap_or(false) { SyntheticItalics::enabled() } else { SyntheticItalics::disabled() }; @@ -1447,17 +1447,17 @@ impl YamlFrameReader { pub fn handle_push_shadow( &mut self, dl: &mut DisplayListBuilder, yaml: &Yaml, info: &mut LayoutPrimitiveInfo, ) { let blur_radius = yaml["blur-radius"].as_f32().unwrap_or(0.0); let offset = yaml["offset"].as_vector().unwrap_or(LayoutVector2D::zero()); - let color = yaml["color"].as_colorf().unwrap_or(*BLACK_COLOR); + let color = yaml["color"].as_colorf().unwrap_or(ColorF::BLACK); dl.push_shadow( &info, Shadow { blur_radius, offset, color, },
--- a/ipc/glue/GeckoChildProcessHost.cpp +++ b/ipc/glue/GeckoChildProcessHost.cpp @@ -138,17 +138,18 @@ GeckoChildProcessHost::~GeckoChildProces } } //static auto GeckoChildProcessHost::GetPathToBinary(FilePath& exePath, GeckoProcessType processType) -> BinaryPathType { if (sRunSelfAsContentProc && - (processType == GeckoProcessType_Content || processType == GeckoProcessType_GPU)) { + (processType == GeckoProcessType_Content || processType == GeckoProcessType_GPU || + processType == GeckoProcessType_VR)) { #if defined(OS_WIN) wchar_t exePathBuf[MAXPATHLEN]; if (!::GetModuleFileNameW(nullptr, exePathBuf, MAXPATHLEN)) { MOZ_CRASH("GetModuleFileNameW failed (FIXME)"); } #if defined(MOZ_SANDBOX) // We need to start the child process using the real path, so that the // sandbox policy rules will match for DLLs loaded from the bin dir after @@ -758,17 +759,17 @@ GeckoChildProcessHost::PerformAsyncLaunc } } // Add the application directory path (-appdir path) AddAppDirToCommandLine(childArgv); // Tmp dir that the GPU process should use for crash reports. This arg is // always populated (but possibly with an empty value) for a GPU child process. - if (mProcessType == GeckoProcessType_GPU) { + if (mProcessType == GeckoProcessType_GPU || mProcessType == GeckoProcessType_VR) { nsCOMPtr<nsIFile> file; CrashReporter::GetChildProcessTmpDir(getter_AddRefs(file)); nsAutoCString path; if (file) { file->GetNativePath(path); } childArgv.push_back(path.get()); } @@ -995,16 +996,21 @@ GeckoChildProcessHost::PerformAsyncLaunc if (mSandboxLevel > 0 && !PR_GetEnv("MOZ_DISABLE_GPU_SANDBOX")) { // For now we treat every failure as fatal in SetSecurityLevelForGPUProcess // and just crash there right away. Should this change in the future then we // should also handle the error here. mSandboxBroker.SetSecurityLevelForGPUProcess(mSandboxLevel); shouldSandboxCurrentProcess = true; } break; + case GeckoProcessType_VR: + if (mSandboxLevel > 0 && !PR_GetEnv("MOZ_DISABLE_VR_SANDBOX")) { + // TODO: Implement sandbox for VR process, Bug 1430043. + } + break; case GeckoProcessType_Default: default: MOZ_CRASH("Bad process type in GeckoChildProcessHost"); break; }; if (shouldSandboxCurrentProcess) { for (auto it = mAllowedFilesRead.begin(); @@ -1077,16 +1083,17 @@ GeckoChildProcessHost::PerformAsyncLaunc { base::LaunchApp(cmdLine, *mLaunchOptions, &process); # ifdef MOZ_SANDBOX // We need to be able to duplicate handles to some types of non-sandboxed // child processes. if (mProcessType == GeckoProcessType_Content || mProcessType == GeckoProcessType_GPU || + mProcessType == GeckoProcessType_VR || mProcessType == GeckoProcessType_GMPlugin) { if (!mSandboxBroker.AddTargetPeer(process)) { NS_WARNING("Failed to add content process as target peer."); } } # endif // MOZ_SANDBOX }
--- a/ipc/mscom/MainThreadRuntime.cpp +++ b/ipc/mscom/MainThreadRuntime.cpp @@ -8,16 +8,17 @@ #if defined(ACCESSIBILITY) #include "mozilla/a11y/Compatibility.h" #endif #include "mozilla/ArrayUtils.h" #include "mozilla/Assertions.h" #include "mozilla/RefPtr.h" #include "mozilla/UniquePtr.h" +#include "mozilla/WindowsVersion.h" #if defined(ACCESSIBILITY) #include "nsExceptionHandler.h" #endif // defined(ACCESSIBILITY) #include "nsWindowsHelpers.h" #include "nsXULAppAPI.h" #include <accctrl.h> #include <aclapi.h> @@ -191,32 +192,51 @@ MainThreadRuntime::InitializeSecurity() BYTE adminSid[SECURITY_MAX_SID_SIZE]; DWORD adminSidSize = sizeof(adminSid); if (!::CreateWellKnownSid(WinBuiltinAdministratorsSid, nullptr, adminSid, &adminSidSize)) { return HRESULT_FROM_WIN32(::GetLastError()); } - // Grant access to SYSTEM, Administrators, and the user. + BYTE appContainersSid[SECURITY_MAX_SID_SIZE]; + DWORD appContainersSidSize = sizeof(appContainersSid); + if (XRE_IsParentProcess() && IsWin8OrLater()) { + if (!::CreateWellKnownSid(WinBuiltinAnyPackageSid, nullptr, + appContainersSid, &appContainersSidSize)) { + return HRESULT_FROM_WIN32(::GetLastError()); + } + } + + // Grant access to SYSTEM, Administrators, the user, and when running as the + // browser process on Windows 8+, all app containers. EXPLICIT_ACCESS entries[] = { {COM_RIGHTS_EXECUTE, GRANT_ACCESS, NO_INHERITANCE, {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_USER, reinterpret_cast<LPWSTR>(systemSid)}}, {COM_RIGHTS_EXECUTE, GRANT_ACCESS, NO_INHERITANCE, {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_WELL_KNOWN_GROUP, reinterpret_cast<LPWSTR>(adminSid)}}, {COM_RIGHTS_EXECUTE, GRANT_ACCESS, NO_INHERITANCE, {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_USER, - reinterpret_cast<LPWSTR>(tokenUser.User.Sid)}} + reinterpret_cast<LPWSTR>(tokenUser.User.Sid)}}, + // appContainersSid must be the last entry in this array! + {COM_RIGHTS_EXECUTE, GRANT_ACCESS, NO_INHERITANCE, + {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_WELL_KNOWN_GROUP, + reinterpret_cast<LPWSTR>(appContainersSid)}} }; + ULONG numEntries = ArrayLength(entries); + if (!XRE_IsParentProcess() || !IsWin8OrLater()) { + // Exclude appContainersSid on Windows 7 and non-parent processes. + --numEntries; + } + PACL rawDacl = nullptr; - win32Error = ::SetEntriesInAcl(ArrayLength(entries), entries, nullptr, - &rawDacl); + win32Error = ::SetEntriesInAcl(numEntries, entries, nullptr, &rawDacl); if (win32Error != ERROR_SUCCESS) { return HRESULT_FROM_WIN32(win32Error); } UniquePtr<ACL, LocalFreeDeleter> dacl(rawDacl); if (!::SetSecurityDescriptorDacl(&sd, TRUE, dacl.get(), FALSE)) { return HRESULT_FROM_WIN32(::GetLastError());
--- a/ipc/testshell/XPCShellEnvironment.cpp +++ b/ipc/testshell/XPCShellEnvironment.cpp @@ -11,16 +11,17 @@ #endif #ifdef HAVE_UNISTD_H #include <unistd.h> /* for isatty() */ #endif #include "base/basictypes.h" #include "jsapi.h" +#include "js/AutoByteString.h" #include "xpcpublic.h" #include "XPCShellEnvironment.h" #include "mozilla/XPCOM.h" #include "nsIChannel.h"
new file mode 100644 --- /dev/null +++ b/js/public/AutoByteString.h @@ -0,0 +1,140 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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/. */ + +/* + * DEPRECATED functions and classes for heap-allocating copies of a JSString's + * data. + */ + +#ifndef js_AutoByteString_h +#define js_AutoByteString_h + +#include "mozilla/Assertions.h" // MOZ_ASSERT +#include "mozilla/Attributes.h" // MOZ_RAII, MOZ_GUARD* + +#include <string.h> // strlen + +#include "jstypes.h" // JS_PUBLIC_API + +#include "js/MemoryFunctions.h" // JS_free +#include "js/RootingAPI.h" // JS::Handle +#include "js/TypeDecls.h" // JSContext, JSString +#include "js/Utility.h" // js_free, JS::UniqueChars + +/** + * DEPRECATED + * + * Allocate memory sufficient to contain the characters of |str| truncated to + * Latin-1 and a trailing null terminator, fill the memory with the characters + * interpreted in that manner plus the null terminator, and return a pointer to + * the memory. The memory must be freed using JS_free to avoid leaking. + * + * This function *loses information* when it copies the characters of |str| if + * |str| contains code units greater than 0xFF. Additionally, users that + * depend on null-termination will misinterpret the copied characters if |str| + * contains any nulls. Avoid using this function if possible, because it will + * eventually be removed. + */ +extern JS_PUBLIC_API(char*) +JS_EncodeString(JSContext* cx, JSString* str); + +/** + * DEPRECATED + * + * Same behavior as JS_EncodeString(), but encode into a UTF-8 string. + * + * This function *loses information* when it copies the characters of |str| if + * |str| contains invalid UTF-16: U+FFFD REPLACEMENT CHARACTER will be copied + * instead. + * + * The returned string is also subject to misinterpretation if |str| contains + * any nulls (which are faithfully transcribed into the returned string, but + * which will implicitly truncate the string if it's passed to functions that + * expect null-terminated strings). + * + * Avoid using this function if possible, because we'll remove it once we can + * devise a better API for the task. + */ +extern JS_PUBLIC_API(char*) +JS_EncodeStringToUTF8(JSContext* cx, JS::Handle<JSString*> str); + +/** + * DEPRECATED + * + * A lightweight RAII helper class around the various JS_Encode* functions + * above, subject to the same pitfalls noted above. Avoid using this class if + * possible, because as with the functions above, it too needs to be replaced + * with a better, safer API. + */ +class MOZ_RAII JSAutoByteString final +{ + private: + char* mBytes; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER + + private: + JSAutoByteString(const JSAutoByteString& another) = delete; + void operator=(const JSAutoByteString& another) = delete; + + public: + JSAutoByteString(JSContext* cx, JSString* str + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : mBytes(JS_EncodeString(cx, str)) + { + MOZ_ASSERT(cx); + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + } + + explicit JSAutoByteString(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) + : mBytes(nullptr) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + } + + ~JSAutoByteString() { + JS_free(nullptr, mBytes); + } + + /* Take ownership of the given byte array. */ + void initBytes(JS::UniqueChars&& bytes) { + MOZ_ASSERT(!mBytes); + mBytes = bytes.release(); + } + + char* encodeLatin1(JSContext* cx, JSString* str) { + MOZ_ASSERT(!mBytes); + MOZ_ASSERT(cx); + mBytes = JS_EncodeString(cx, str); + return mBytes; + } + + char* encodeUtf8(JSContext* cx, JS::Handle<JSString*> str) { + MOZ_ASSERT(!mBytes); + MOZ_ASSERT(cx); + mBytes = JS_EncodeStringToUTF8(cx, str); + return mBytes; + } + + void clear() { + js_free(mBytes); + mBytes = nullptr; + } + + char* ptr() const { + return mBytes; + } + + bool operator!() const { + return !mBytes; + } + + size_t length() const { + if (!mBytes) + return 0; + return strlen(mBytes); + } +}; + +#endif /* js_AutoByteString_h */
new file mode 100644 --- /dev/null +++ b/js/public/ErrorReport.h @@ -0,0 +1,294 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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/. */ + +/* + * Error-reporting types and structures. + * + * Despite these types and structures existing in js/public, significant parts + * of their heritage date back to distant SpiderMonkey past, and they are not + * all universally well-thought-out as ideal, intended-to-be-permanent API. + * We may eventually replace this with something more consistent with + * ECMAScript the language and less consistent with '90s-era JSAPI inventions, + * but it's doubtful this will happen any time soon. + */ + +#ifndef js_ErrorReport_h +#define js_ErrorReport_h + +#include "mozilla/Assertions.h" // MOZ_ASSERT + +#include <iterator> // std::input_iterator_tag, std::iterator +#include <stddef.h> // size_t +#include <stdint.h> // int16_t, uint16_t +#include <string.h> // strlen + +#include "jstypes.h" // JS_PUBLIC_API + +#include "js/AllocPolicy.h" // js::SystemAllocPolicy +#include "js/CharacterEncoding.h" // JS::ConstUTF8CharsZ +#include "js/UniquePtr.h" // js::UniquePtr +#include "js/Vector.h" // js::Vector + +struct JSContext; +class JSString; + +/** + * Possible exception types. These types are part of a JSErrorFormatString + * structure. They define which error to throw in case of a runtime error. + * + * JSEXN_WARN is used for warnings in js.msg files (for instance because we + * don't want to prepend 'Error:' to warning messages). This value can go away + * if we ever decide to use an entirely separate mechanism for warnings. + */ +enum JSExnType +{ + JSEXN_ERR, + JSEXN_FIRST = JSEXN_ERR, + JSEXN_INTERNALERR, + JSEXN_EVALERR, + JSEXN_RANGEERR, + JSEXN_REFERENCEERR, + JSEXN_SYNTAXERR, + JSEXN_TYPEERR, + JSEXN_URIERR, + JSEXN_DEBUGGEEWOULDRUN, + JSEXN_WASMCOMPILEERROR, + JSEXN_WASMLINKERROR, + JSEXN_WASMRUNTIMEERROR, + JSEXN_ERROR_LIMIT, + JSEXN_WARN = JSEXN_ERROR_LIMIT, + JSEXN_NOTE, + JSEXN_LIMIT +}; + +struct JSErrorFormatString +{ + /** The error message name in ASCII. */ + const char* name; + + /** The error format string in ASCII. */ + const char* format; + + /** The number of arguments to expand in the formatted error message. */ + uint16_t argCount; + + /** One of the JSExnType constants above. */ + int16_t exnType; +}; + +using JSErrorCallback = + const JSErrorFormatString* (*)(void* userRef, const unsigned errorNumber); + +/** + * Base class that implements parts shared by JSErrorReport and + * JSErrorNotes::Note. + */ +class JSErrorBase +{ + private: + // The (default) error message. + // If ownsMessage_ is true, the it is freed in destructor. + JS::ConstUTF8CharsZ message_; + + public: + // Source file name, URL, etc., or null. + const char* filename; + + // Source line number. + unsigned lineno; + + // Zero-based column index in line. + unsigned column; + + // the error number, e.g. see js.msg. + unsigned errorNumber; + + private: + bool ownsMessage_ : 1; + + public: + JSErrorBase() + : filename(nullptr), lineno(0), column(0), + errorNumber(0), + ownsMessage_(false) + {} + + ~JSErrorBase() { + freeMessage(); + } + + public: + const JS::ConstUTF8CharsZ message() const { + return message_; + } + + void initOwnedMessage(const char* messageArg) { + initBorrowedMessage(messageArg); + ownsMessage_ = true; + } + void initBorrowedMessage(const char* messageArg) { + MOZ_ASSERT(!message_); + message_ = JS::ConstUTF8CharsZ(messageArg, strlen(messageArg)); + } + + JSString* newMessageString(JSContext* cx); + + private: + void freeMessage(); +}; + +/** + * Notes associated with JSErrorReport. + */ +class JSErrorNotes +{ + public: + class Note final : public JSErrorBase + {}; + + private: + // Stores pointers to each note. + js::Vector<js::UniquePtr<Note>, 1, js::SystemAllocPolicy> notes_; + + public: + JSErrorNotes(); + ~JSErrorNotes(); + + // Add an note to the given position. + bool addNoteASCII(JSContext* cx, + const char* filename, unsigned lineno, unsigned column, + JSErrorCallback errorCallback, void* userRef, + const unsigned errorNumber, ...); + bool addNoteLatin1(JSContext* cx, + const char* filename, unsigned lineno, unsigned column, + JSErrorCallback errorCallback, void* userRef, + const unsigned errorNumber, ...); + bool addNoteUTF8(JSContext* cx, + const char* filename, unsigned lineno, unsigned column, + JSErrorCallback errorCallback, void* userRef, + const unsigned errorNumber, ...); + + JS_PUBLIC_API(size_t) length(); + + // Create a deep copy of notes. + js::UniquePtr<JSErrorNotes> copy(JSContext* cx); + + class iterator final + : public std::iterator<std::input_iterator_tag, js::UniquePtr<Note>> + { + private: + js::UniquePtr<Note>* note_; + + public: + explicit iterator(js::UniquePtr<Note>* note = nullptr) : note_(note) + {} + + bool operator==(iterator other) const { + return note_ == other.note_; + } + bool operator!=(iterator other) const { + return !(*this == other); + } + iterator& operator++() { + note_++; + return *this; + } + reference operator*() { + return *note_; + } + }; + + JS_PUBLIC_API(iterator) begin(); + JS_PUBLIC_API(iterator) end(); +}; + +/** + * Describes a single error or warning that occurs in the execution of script. + */ +class JSErrorReport : public JSErrorBase +{ + private: + // Offending source line without final '\n'. + // If ownsLinebuf_ is true, the buffer is freed in destructor. + const char16_t* linebuf_; + + // Number of chars in linebuf_. Does not include trailing '\0'. + size_t linebufLength_; + + // The 0-based offset of error token in linebuf_. + size_t tokenOffset_; + + public: + // Associated notes, or nullptr if there's no note. + js::UniquePtr<JSErrorNotes> notes; + + // error/warning, etc. + unsigned flags; + + // One of the JSExnType constants. + int16_t exnType; + + // See the comment in TransitiveCompileOptions. + bool isMuted : 1; + + private: + bool ownsLinebuf_ : 1; + + public: + JSErrorReport() + : linebuf_(nullptr), linebufLength_(0), tokenOffset_(0), + notes(nullptr), + flags(0), exnType(0), isMuted(false), + ownsLinebuf_(false) + {} + + ~JSErrorReport() { + freeLinebuf(); + } + + public: + const char16_t* linebuf() const { + return linebuf_; + } + size_t linebufLength() const { + return linebufLength_; + } + size_t tokenOffset() const { + return tokenOffset_; + } + void initOwnedLinebuf(const char16_t* linebufArg, size_t linebufLengthArg, + size_t tokenOffsetArg) { + initBorrowedLinebuf(linebufArg, linebufLengthArg, tokenOffsetArg); + ownsLinebuf_ = true; + } + void initBorrowedLinebuf(const char16_t* linebufArg, size_t linebufLengthArg, + size_t tokenOffsetArg); + + private: + void freeLinebuf(); +}; + +/* + * JSErrorReport flag values. These may be freely composed. + */ +#define JSREPORT_ERROR 0x0 /* pseudo-flag for default case */ +#define JSREPORT_WARNING 0x1 /* reported via JS_ReportWarning */ +#define JSREPORT_EXCEPTION 0x2 /* exception was thrown */ +#define JSREPORT_STRICT 0x4 /* error or warning due to strict option */ + +#define JSREPORT_USER_1 0x8 /* user-defined flag */ + +/* + * If JSREPORT_EXCEPTION is set, then a JavaScript-catchable exception + * has been thrown for this runtime error, and the host should ignore it. + * Exception-aware hosts should also check for JS_IsExceptionPending if + * JS_ExecuteScript returns failure, and signal or propagate the exception, as + * appropriate. + */ +#define JSREPORT_IS_WARNING(flags) (((flags) & JSREPORT_WARNING) != 0) +#define JSREPORT_IS_EXCEPTION(flags) (((flags) & JSREPORT_EXCEPTION) != 0) +#define JSREPORT_IS_STRICT(flags) (((flags) & JSREPORT_STRICT) != 0) + +#endif /* js_ErrorReport_h */
new file mode 100644 --- /dev/null +++ b/js/public/JSON.h @@ -0,0 +1,91 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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/. */ + +/* + * JSON serialization and deserialization operations. + */ + +#ifndef js_JSON_h +#define js_JSON_h + +#include <stdint.h> // uint32_t + +#include "jstypes.h" // JS_PUBLIC_API + +#include "js/RootingAPI.h" // JS::Handle, JS::MutableHandle + +struct JSContext; +class JSObject; +class JSString; + +namespace JS { union Value; } + +using JSONWriteCallback = bool (*)(const char16_t* buf, uint32_t len, void* data); + +/** + * Performs the JSON.stringify operation, as specified by ECMAScript, except + * writing stringified data by repeated calls of |callback|, with each such + * call passed |data| as argument. + */ +extern JS_PUBLIC_API(bool) +JS_Stringify(JSContext* cx, JS::MutableHandle<JS::Value> value, JS::Handle<JSObject*> replacer, + JS::Handle<JS::Value> space, JSONWriteCallback callback, void* data); + +namespace JS { + +/** + * An API akin to JS_Stringify but with the goal of not having observable + * side-effects when the stringification is performed. This means it does not + * allow a replacer or a custom space and has the following constraints on its + * input: + * + * 1) The input must be a plain object or array, not an abitrary value. + * 2) Every value in the graph reached by the algorithm starting with this + * object must be one of the following: null, undefined, a string (NOT a + * string object!), a boolean, a finite number (i.e. no NaN or Infinity or + * -Infinity), a plain object with no accessor properties, or an Array with + * no holes. + * + * The actual behavior differs from JS_Stringify only in asserting the above and + * NOT attempting to get the "toJSON" property from things, since that could + * clearly have side-effects. + */ +extern JS_PUBLIC_API(bool) +ToJSONMaybeSafely(JSContext* cx, JS::Handle<JSObject*> input, + JSONWriteCallback callback, void* data); + +} /* namespace JS */ + +/** + * Performs the JSON.parse operation as specified by ECMAScript. + */ +extern JS_PUBLIC_API(bool) +JS_ParseJSON(JSContext* cx, const char16_t* chars, uint32_t len, JS::MutableHandle<JS::Value> vp); + +/** + * Performs the JSON.parse operation as specified by ECMAScript. + */ +extern JS_PUBLIC_API(bool) +JS_ParseJSON(JSContext* cx, JS::Handle<JSString*> str, JS::MutableHandle<JS::Value> vp); + +/** + * Performs the JSON.parse operation as specified by ECMAScript, using the + * given |reviver| argument as the corresponding optional argument to that + * function. + */ +extern JS_PUBLIC_API(bool) +JS_ParseJSONWithReviver(JSContext* cx, const char16_t* chars, uint32_t len, + JS::Handle<JS::Value> reviver, JS::MutableHandle<JS::Value> vp); + +/** + * Performs the JSON.parse operation as specified by ECMAScript, using the + * given |reviver| argument as the corresponding optional argument to that + * function. + */ +extern JS_PUBLIC_API(bool) +JS_ParseJSONWithReviver(JSContext* cx, JS::Handle<JSString*> str, JS::Handle<JS::Value> reviver, + JS::MutableHandle<JS::Value> vp); + +#endif /* js_JSON_h */
new file mode 100644 --- /dev/null +++ b/js/public/MemoryFunctions.h @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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/. */ + +/* Low-level memory-allocation functions. */ + +#ifndef js_MemoryFunctions_h +#define js_MemoryFunctions_h + +#include "mozilla/Attributes.h" // MOZ_MUST_USE + +#include <stddef.h> // size_t + +#include "jstypes.h" // JS_PUBLIC_API + +struct JSContext; + +extern JS_PUBLIC_API(void*) +JS_malloc(JSContext* cx, size_t nbytes); + +extern JS_PUBLIC_API(void*) +JS_realloc(JSContext* cx, void* p, size_t oldBytes, size_t newBytes); + +/** + * A wrapper for |js_free(p)| that may delay |js_free(p)| invocation as a + * performance optimization. |cx| may be nullptr. + */ +extern JS_PUBLIC_API(void) +JS_free(JSContext* cx, void* p); + +/** + * A wrapper for |js_free(p)| that may delay |js_free(p)| invocation as a + * performance optimization as specified by the given JSFreeOp instance. + */ +extern JS_PUBLIC_API(void) +JS_freeop(JSFreeOp* fop, void* p); + +extern JS_PUBLIC_API(void) +JS_updateMallocCounter(JSContext* cx, size_t nbytes); + +#endif /* js_MemoryFunctions_h */
new file mode 100644 --- /dev/null +++ b/js/public/SavedFrameAPI.h @@ -0,0 +1,145 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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/. */ + +/* + * Functions and types related to SavedFrame objects created by the Debugger + * API. + */ + +#ifndef js_SavedFrameAPI_h +#define js_SavedFrameAPI_h + +#include "jstypes.h" // JS_FRIEND_API, JS_PUBLIC_API + +#include "js/RootingAPI.h" // JS::Handle, JS::MutableHandle + +struct JSContext; +class JSObject; +struct JSPrincipals; + +namespace JS { + +/* + * Accessors for working with SavedFrame JSObjects + * + * Each of these functions assert that if their `HandleObject savedFrame` + * argument is non-null, its JSClass is the SavedFrame class (or it is a + * cross-compartment or Xray wrapper around an object with the SavedFrame class) + * and the object is not the SavedFrame.prototype object. + * + * Each of these functions will find the first SavedFrame object in the chain + * whose underlying stack frame principals are subsumed by the given + * |principals|, and operate on that SavedFrame object. This prevents leaking + * information about privileged frames to un-privileged callers + * + * The SavedFrame in parameters do _NOT_ need to be in the same compartment as + * the cx, and the various out parameters are _NOT_ guaranteed to be in the same + * compartment as cx. + * + * You may consider or skip over self-hosted frames by passing + * `SavedFrameSelfHosted::Include` or `SavedFrameSelfHosted::Exclude` + * respectively. + * + * Additionally, it may be the case that there is no such SavedFrame object + * whose captured frame's principals are subsumed by |principals|! If the + * `HandleObject savedFrame` argument is null, or the |principals| do not + * subsume any of the chained SavedFrame object's principals, + * `SavedFrameResult::AccessDenied` is returned and a (hopefully) sane default + * value is chosen for the out param. + * + * See also `js/src/doc/SavedFrame/SavedFrame.md`. + */ + +enum class SavedFrameResult { + Ok, + AccessDenied +}; + +enum class SavedFrameSelfHosted { + Include, + Exclude +}; + +/** + * Given a SavedFrame JSObject, get its source property. Defaults to the empty + * string. + */ +extern JS_PUBLIC_API(SavedFrameResult) +GetSavedFrameSource(JSContext* cx, JSPrincipals* principals, Handle<JSObject*> savedFrame, + MutableHandle<JSString*> sourcep, + SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include); + +/** + * Given a SavedFrame JSObject, get its line property. Defaults to 0. + */ +extern JS_PUBLIC_API(SavedFrameResult) +GetSavedFrameLine(JSContext* cx, JSPrincipals* principals, Handle<JSObject*> savedFrame, + uint32_t* linep, + SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include); + +/** + * Given a SavedFrame JSObject, get its column property. Defaults to 0. + */ +extern JS_PUBLIC_API(SavedFrameResult) +GetSavedFrameColumn(JSContext* cx, JSPrincipals* principals, Handle<JSObject*> savedFrame, + uint32_t* columnp, + SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include); + +/** + * Given a SavedFrame JSObject, get its functionDisplayName string, or nullptr + * if SpiderMonkey was unable to infer a name for the captured frame's + * function. Defaults to nullptr. + */ +extern JS_PUBLIC_API(SavedFrameResult) +GetSavedFrameFunctionDisplayName(JSContext* cx, JSPrincipals* principals, + Handle<JSObject*> savedFrame, + MutableHandle<JSString*> namep, + SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include); + +/** + * Given a SavedFrame JSObject, get its asyncCause string. Defaults to nullptr. + */ +extern JS_PUBLIC_API(SavedFrameResult) +GetSavedFrameAsyncCause(JSContext* cx, JSPrincipals* principals, Handle<JSObject*> savedFrame, + MutableHandle<JSString*> asyncCausep, + SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include); + +/** + * Given a SavedFrame JSObject, get its asyncParent SavedFrame object or nullptr + * if there is no asyncParent. The `asyncParentp` out parameter is _NOT_ + * guaranteed to be in the cx's compartment. Defaults to nullptr. + */ +extern JS_PUBLIC_API(SavedFrameResult) +GetSavedFrameAsyncParent(JSContext* cx, JSPrincipals* principals, Handle<JSObject*> savedFrame, + MutableHandle<JSObject*> asyncParentp, + SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include); + +/** + * Given a SavedFrame JSObject, get its parent SavedFrame object or nullptr if + * it is the oldest frame in the stack. The `parentp` out parameter is _NOT_ + * guaranteed to be in the cx's compartment. Defaults to nullptr. + */ +extern JS_PUBLIC_API(SavedFrameResult) +GetSavedFrameParent(JSContext* cx, JSPrincipals* principals, Handle<JSObject*> savedFrame, + MutableHandle<JSObject*> parentp, + SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include); + +} // namespace JS + +namespace js { + +/** + * Get the first SavedFrame object in this SavedFrame stack whose principals are + * subsumed by the given |principals|. If there is no such frame, return nullptr. + * + * Do NOT pass a non-SavedFrame object here. + */ +extern JS_FRIEND_API(JSObject*) +GetFirstSubsumedSavedFrame(JSContext* cx, JSPrincipals* principals, + JS::Handle<JSObject*> savedFrame, JS::SavedFrameSelfHosted selfHosted); + +} // namespace js + +#endif /* js_SavedFrameAPI_h */
new file mode 100644 --- /dev/null +++ b/js/public/StableStringChars.h @@ -0,0 +1,123 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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/. */ + +/* + * Safely access the contents of a string even as GC can cause the string's + * contents to move around in memory. + */ + +#ifndef js_StableStringChars_h +#define js_StableStringChars_h + +#include "mozilla/Assertions.h" // MOZ_ASSERT +#include "mozilla/Attributes.h" // MOZ_INIT_OUTSIDE_CTOR, MOZ_STACK_CLASS, MOZ_MUST_USE +#include "mozilla/Maybe.h" // mozilla::Maybe +#include "mozilla/Range.h" // mozilla::Range + +#include <stddef.h> // size_t +#include <stdint.h> // uint8_t + +#include "jstypes.h" // JS_FRIEND_API + +#include "js/HeapAPI.h" // JS::shadow::String +#include "js/RootingAPI.h" // JS::Handle, JS::Rooted +#include "js/TypeDecls.h" // JSContext, JS::Latin1Char, JSString +#include "js/Vector.h" // js::Vector + +class JSLinearString; + +namespace JS { + +MOZ_ALWAYS_INLINE size_t +GetStringLength(JSString* s) +{ + return reinterpret_cast<shadow::String*>(s)->length(); +} + +/** + * This class provides safe access to a string's chars across a GC. Once + * we allocate strings and chars in the nursery (bug 903519), this class + * will have to make a copy of the string's chars if they are allocated + * in the nursery, so it's best to avoid using this class unless you really + * need it. It's usually more efficient to use the latin1Chars/twoByteChars + * JSString methods and often the code can be rewritten so that only indexes + * instead of char pointers are used in parts of the code that can GC. + */ +class MOZ_STACK_CLASS JS_FRIEND_API(AutoStableStringChars) final +{ + /* + * When copying string char, use this many bytes of inline storage. This is + * chosen to allow the inline string types to be copied without allocating. + * This is asserted in AutoStableStringChars::allocOwnChars. + */ + static const size_t InlineCapacity = 24; + + /* Ensure the string is kept alive while we're using its chars. */ + Rooted<JSString*> s_; + MOZ_INIT_OUTSIDE_CTOR union { + const char16_t* twoByteChars_; + const Latin1Char* latin1Chars_; + }; + mozilla::Maybe<js::Vector<uint8_t, InlineCapacity>> ownChars_; + enum State { Uninitialized, Latin1, TwoByte }; + State state_; + + public: + explicit AutoStableStringChars(JSContext* cx) + : s_(cx), state_(Uninitialized) + {} + + MOZ_MUST_USE bool init(JSContext* cx, JSString* s); + + /* Like init(), but Latin1 chars are inflated to TwoByte. */ + MOZ_MUST_USE bool initTwoByte(JSContext* cx, JSString* s); + + bool isLatin1() const { return state_ == Latin1; } + bool isTwoByte() const { return state_ == TwoByte; } + + const Latin1Char* latin1Chars() const { + MOZ_ASSERT(state_ == Latin1); + return latin1Chars_; + } + const char16_t* twoByteChars() const { + MOZ_ASSERT(state_ == TwoByte); + return twoByteChars_; + } + + mozilla::Range<const Latin1Char> latin1Range() const { + MOZ_ASSERT(state_ == Latin1); + return mozilla::Range<const Latin1Char>(latin1Chars_, GetStringLength(s_)); + } + + mozilla::Range<const char16_t> twoByteRange() const { + MOZ_ASSERT(state_ == TwoByte); + return mozilla::Range<const char16_t>(twoByteChars_, + GetStringLength(s_)); + } + + /* If we own the chars, transfer ownership to the caller. */ + bool maybeGiveOwnershipToCaller() { + MOZ_ASSERT(state_ != Uninitialized); + if (!ownChars_.isSome() || !ownChars_->extractRawBuffer()) + return false; + state_ = Uninitialized; + ownChars_.reset(); + return true; + } + + private: + AutoStableStringChars(const AutoStableStringChars& other) = delete; + void operator=(const AutoStableStringChars& other) = delete; + + bool baseIsInline(Handle<JSLinearString*> linearString); + template <typename T> T* allocOwnChars(JSContext* cx, size_t count); + bool copyLatin1Chars(JSContext* cx, Handle<JSLinearString*> linearString); + bool copyTwoByteChars(JSContext* cx, Handle<JSLinearString*> linearString); + bool copyAndInflateLatin1Chars(JSContext*, Handle<JSLinearString*> linearString); +}; + +} // namespace JS + +#endif /* js_StableStringChars_h */
--- a/js/src/builtin/Eval.cpp +++ b/js/src/builtin/Eval.cpp @@ -6,30 +6,32 @@ #include "builtin/Eval.h" #include "mozilla/HashFunctions.h" #include "mozilla/Range.h" #include "frontend/BytecodeCompiler.h" #include "gc/HashUtil.h" +#include "js/StableStringChars.h" #include "vm/Debugger.h" #include "vm/GlobalObject.h" #include "vm/JSContext.h" #include "vm/JSONParser.h" #include "vm/Interpreter-inl.h" using namespace js; using mozilla::AddToHash; using mozilla::HashString; using mozilla::RangedPtr; using JS::AutoCheckCannotGC; +using JS::AutoStableStringChars; // We should be able to assert this for *any* fp->environmentChain(). static void AssertInnerizedEnvironmentChain(JSContext* cx, JSObject& env) { #ifdef DEBUG RootedObject obj(cx); for (obj = &env; obj; obj = obj->enclosingEnvironment())
--- a/js/src/builtin/JSON.cpp +++ b/js/src/builtin/JSON.cpp @@ -14,16 +14,17 @@ #include "jstypes.h" #include "jsutil.h" #include "builtin/Array.h" #ifdef ENABLE_BIGINT #include "builtin/BigInt.h" #endif #include "builtin/String.h" +#include "js/StableStringChars.h" #include "util/StringBuffer.h" #include "vm/Interpreter.h" #include "vm/JSAtom.h" #include "vm/JSContext.h" #include "vm/JSObject.h" #include "vm/JSONParser.h" #include "builtin/Array-inl.h" @@ -33,16 +34,18 @@ using namespace js; using namespace js::gc; using mozilla::IsFinite; using mozilla::Maybe; using mozilla::RangedPtr; +using JS::AutoStableStringChars; + const Class js::JSONClass = { js_JSON_str, JSCLASS_HAS_CACHED_PROTO(JSProto_JSON) }; /* ES5 15.12.3 Quote. * Requires that the destination has enough space allocated for src after escaping * (that is, `2 + 6 * (srcEnd - srcBegin)` characters).
--- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -1960,16 +1960,17 @@ static const JSFunctionSpec object_stati JS_FN("getOwnPropertyNames", obj_getOwnPropertyNames, 1, 0), JS_FN("getOwnPropertySymbols", obj_getOwnPropertySymbols, 1, 0), JS_SELF_HOSTED_FN("isExtensible", "ObjectIsExtensible", 1, 0), JS_FN("preventExtensions", obj_preventExtensions, 1, 0), JS_FN("freeze", obj_freeze, 1, 0), JS_FN("isFrozen", obj_isFrozen, 1, 0), JS_FN("seal", obj_seal, 1, 0), JS_FN("isSealed", obj_isSealed, 1, 0), + JS_SELF_HOSTED_FN("fromEntries", "ObjectFromEntries", 1, 0), JS_FS_END }; static JSObject* CreateObjectConstructor(JSContext* cx, JSProtoKey key) { Rooted<GlobalObject*> self(cx, cx->global()); if (!GlobalObject::ensureConstructor(cx, self, JSProto_Function))
--- a/js/src/builtin/Object.js +++ b/js/src/builtin/Object.js @@ -281,8 +281,28 @@ function ObjectOrReflectDefineProperty(o // 19.1.2.4 Object.defineProperty ( O, P, Attributes ) function ObjectDefineProperty(obj, propertyKey, attributes) { // Steps 1-4. ObjectOrReflectDefineProperty(obj, propertyKey, attributes, true); // Step 5. return obj; } + +// Proposal https://tc39.github.io/proposal-object-from-entries/ +// 1. Object.fromEntries ( iterable ) +function ObjectFromEntries(iter) { + // We omit the usual step number comments here because they don't help. + // This implementation inlines AddEntriesFromIterator and + // CreateDataPropertyOnObject, so it looks more like the polyfill + // <https://github.com/tc39/proposal-object-from-entries/blob/master/polyfill.js> + // than the spec algorithm. + const obj = {}; + + for (const pair of allowContentIter(iter)) { + if (!IsObject(pair)) + ThrowTypeError(JSMSG_INVALID_MAP_ITERABLE, "Object.fromEntries"); + _DefineDataProperty(obj, pair[0], pair[1]); + } + + return obj; +} +
--- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -14,26 +14,28 @@ #include "jspubtd.h" #include "builtin/Array.h" #include "builtin/Reflect.h" #include "frontend/Parser.h" #include "frontend/TokenStream.h" #include "js/CharacterEncoding.h" +#include "js/StableStringChars.h" #include "vm/JSAtom.h" #include "vm/JSObject.h" #include "vm/RegExpObject.h" #include "frontend/ParseNode-inl.h" #include "vm/JSObject-inl.h" using namespace js; using namespace js::frontend; +using JS::AutoStableStringChars; using JS::AutoValueArray; using mozilla::DebugOnly; enum ASTType { AST_ERROR = -1, #define ASTDEF(ast, str, method) ast, #include "jsast.tbl" #undef ASTDEF
--- a/js/src/builtin/String.cpp +++ b/js/src/builtin/String.cpp @@ -26,16 +26,17 @@ #include "builtin/Array.h" #include "builtin/Boolean.h" #include "builtin/intl/CommonFunctions.h" #include "builtin/intl/ICUStubs.h" #include "builtin/RegExp.h" #include "jit/InlinableNatives.h" #include "js/Conversions.h" +#include "js/StableStringChars.h" #include "js/UniquePtr.h" #if ENABLE_INTL_API # include "unicode/uchar.h" # include "unicode/unorm2.h" #endif #include "util/StringBuffer.h" #include "util/Unicode.h" #include "vm/BytecodeUtil.h" @@ -63,16 +64,17 @@ using JS::SymbolCode; using mozilla::CheckedInt; using mozilla::IsNaN; using mozilla::IsSame; using mozilla::PodCopy; using mozilla::RangedPtr; using JS::AutoCheckCannotGC; +using JS::AutoStableStringChars; static JSLinearString* ArgToLinearString(JSContext* cx, const CallArgs& args, unsigned argno) { if (argno >= args.length()) return cx->names().undefined; JSString* str = ToString<CanGC>(cx, args[argno]);
--- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -36,18 +36,20 @@ #include "irregexp/RegExpAST.h" #include "irregexp/RegExpEngine.h" #include "irregexp/RegExpParser.h" #endif #include "gc/Heap.h" #include "jit/BaselineJIT.h" #include "jit/InlinableNatives.h" #include "jit/JitRealm.h" +#include "js/AutoByteString.h" #include "js/Debug.h" #include "js/HashTable.h" +#include "js/StableStringChars.h" #include "js/StructuredClone.h" #include "js/UbiNode.h" #include "js/UbiNodeBreadthFirst.h" #include "js/UbiNodeShortestPaths.h" #include "js/UniquePtr.h" #include "js/Vector.h" #include "js/Wrapper.h" #include "util/StringBuffer.h" @@ -80,16 +82,18 @@ #include "vm/NativeObject-inl.h" #include "vm/StringType-inl.h" using namespace js; using mozilla::ArrayLength; using mozilla::Maybe; +using JS::AutoStableStringChars; + // If fuzzingSafe is set, remove functionality that could cause problems with // fuzzers. Set this via the environment variable MOZ_FUZZING_SAFE. mozilla::Atomic<bool> fuzzingSafe(false); // If disableOOMFunctions is set, disable functionality that causes artificial // OOM conditions. static mozilla::Atomic<bool> disableOOMFunctions(false);
--- a/js/src/builtin/intl/Collator.cpp +++ b/js/src/builtin/intl/Collator.cpp @@ -12,26 +12,30 @@ #include "jsapi.h" #include "builtin/intl/CommonFunctions.h" #include "builtin/intl/ICUStubs.h" #include "builtin/intl/ScopedICUObject.h" #include "builtin/intl/SharedIntlData.h" #include "gc/FreeOp.h" +#include "js/AutoByteString.h" +#include "js/StableStringChars.h" #include "js/TypeDecls.h" #include "vm/GlobalObject.h" #include "vm/JSContext.h" #include "vm/Runtime.h" #include "vm/StringType.h" #include "vm/JSObject-inl.h" using namespace js; +using JS::AutoStableStringChars; + using js::intl::GetAvailableLocales; using js::intl::IcuLocale; using js::intl::ReportInternalError; using js::intl::SharedIntlData; using js::intl::StringsAreEqual; const ClassOps CollatorObject::classOps_ = { nullptr, /* addProperty */
--- a/js/src/builtin/intl/DateTimeFormat.cpp +++ b/js/src/builtin/intl/DateTimeFormat.cpp @@ -14,26 +14,29 @@ #include "jsfriendapi.h" #include "builtin/intl/CommonFunctions.h" #include "builtin/intl/ICUStubs.h" #include "builtin/intl/ScopedICUObject.h" #include "builtin/intl/SharedIntlData.h" #include "builtin/intl/TimeZoneDataGenerated.h" #include "gc/FreeOp.h" +#include "js/AutoByteString.h" +#include "js/StableStringChars.h" #include "vm/DateTime.h" #include "vm/GlobalObject.h" #include "vm/JSContext.h" #include "vm/Runtime.h" #include "vm/JSObject-inl.h" #include "vm/NativeObject-inl.h" using namespace js; +using JS::AutoStableStringChars; using JS::ClippedTime; using JS::TimeClip; using js::intl::CallICU; using js::intl::DateTimeFormatOptions; using js::intl::GetAvailableLocales; using js::intl::IcuLocale; using js::intl::INITIAL_CHAR_BUFFER_SIZE;
--- a/js/src/builtin/intl/IntlObject.cpp +++ b/js/src/builtin/intl/IntlObject.cpp @@ -16,29 +16,33 @@ #include "builtin/intl/Collator.h" #include "builtin/intl/CommonFunctions.h" #include "builtin/intl/DateTimeFormat.h" #include "builtin/intl/ICUStubs.h" #include "builtin/intl/NumberFormat.h" #include "builtin/intl/PluralRules.h" #include "builtin/intl/ScopedICUObject.h" +#include "js/AutoByteString.h" #include "js/Class.h" +#include "js/StableStringChars.h" #include "vm/GlobalObject.h" #include "vm/JSContext.h" #include "vm/JSObject.h" #include "vm/StringType.h" #include "vm/JSObject-inl.h" using namespace js; using mozilla::Range; using mozilla::RangedPtr; +using JS::AutoStableStringChars; + using js::intl::CallICU; using js::intl::DateTimeFormatOptions; using js::intl::IcuLocale; /******************** Intl ********************/ bool js::intl_GetCalendarInfo(JSContext* cx, unsigned argc, Value* vp)
--- a/js/src/builtin/intl/NumberFormat.cpp +++ b/js/src/builtin/intl/NumberFormat.cpp @@ -16,35 +16,38 @@ #include <stdint.h> #include "builtin/intl/CommonFunctions.h" #include "builtin/intl/ICUStubs.h" #include "builtin/intl/ScopedICUObject.h" #include "ds/Sort.h" #include "gc/FreeOp.h" #include "js/RootingAPI.h" +#include "js/StableStringChars.h" #include "js/TypeDecls.h" #include "vm/JSContext.h" #include "vm/SelfHosting.h" #include "vm/Stack.h" #include "vm/JSObject-inl.h" using namespace js; using mozilla::AssertedCast; using mozilla::IsFinite; using mozilla::IsNaN; -using mozilla::IsNegativeZero; +using mozilla::IsNegative; using js::intl::CallICU; using js::intl::DateTimeFormatOptions; using js::intl::GetAvailableLocales; using js::intl::IcuLocale; +using JS::AutoStableStringChars; + const ClassOps NumberFormatObject::classOps_ = { nullptr, /* addProperty */ nullptr, /* delProperty */ nullptr, /* enumerate */ nullptr, /* newEnumerate */ nullptr, /* resolve */ nullptr, /* mayResolve */ NumberFormatObject::finalize @@ -368,34 +371,30 @@ NewUNumberFormat(JSContext* cx, Handle<N } unum_setAttribute(nf, UNUM_GROUPING_USED, uUseGrouping); unum_setAttribute(nf, UNUM_ROUNDING_MODE, UNUM_ROUND_HALFUP); return toClose.forget(); } static JSString* -PartitionNumberPattern(JSContext* cx, UNumberFormat* nf, double* x, +PartitionNumberPattern(JSContext* cx, UNumberFormat* nf, double x, UFieldPositionIterator* fpositer) { - // PartitionNumberPattern doesn't consider -0.0 to be negative. - if (IsNegativeZero(*x)) - *x = 0.0; - return CallICU(cx, [nf, x, fpositer](UChar* chars, int32_t size, UErrorCode* status) { - return unum_formatDoubleForFields(nf, *x, chars, size, fpositer, status); + return unum_formatDoubleForFields(nf, x, chars, size, fpositer, status); }); } static bool intl_FormatNumber(JSContext* cx, UNumberFormat* nf, double x, MutableHandleValue result) { // Passing null for |fpositer| will just not compute partition information, // letting us common up all ICU number-formatting code. - JSString* str = PartitionNumberPattern(cx, nf, &x, nullptr); + JSString* str = PartitionNumberPattern(cx, nf, x, nullptr); if (!str) return false; result.setString(str); return true; } using FieldType = ImmutablePropertyNamePtr JSAtomState::*; @@ -421,23 +420,20 @@ GetFieldTypeForNumberField(UNumberFormat case UNUM_DECIMAL_SEPARATOR_FIELD: return &JSAtomState::decimal; case UNUM_FRACTION_FIELD: return &JSAtomState::fraction; case UNUM_SIGN_FIELD: { - MOZ_ASSERT(!IsNegativeZero(d), - "-0 should have been excluded by PartitionNumberPattern"); - // Manual trawling through the ICU call graph appears to indicate that // the basic formatting we request will never include a positive sign. // But this analysis may be mistaken, so don't absolutely trust it. - return d < 0 ? &JSAtomState::minusSign : &JSAtomState::plusSign; + return IsNegative(d) ? &JSAtomState::minusSign : &JSAtomState::plusSign; } case UNUM_PERCENT_FIELD: return &JSAtomState::percentSign; case UNUM_CURRENCY_FIELD: return &JSAtomState::currency; @@ -477,17 +473,17 @@ intl_FormatNumberToParts(JSContext* cx, if (U_FAILURE(status)) { intl::ReportInternalError(cx); return false; } MOZ_ASSERT(fpositer); ScopedICUObject<UFieldPositionIterator, ufieldpositer_close> toClose(fpositer); - RootedString overallResult(cx, PartitionNumberPattern(cx, nf, &x, fpositer)); + RootedString overallResult(cx, PartitionNumberPattern(cx, nf, x, fpositer)); if (!overallResult) return false; RootedArrayObject partsArray(cx, NewDenseEmptyArray(cx)); if (!partsArray) return false; // First, vacuum up fields in the overall formatted string.
--- a/js/src/builtin/intl/PluralRules.cpp +++ b/js/src/builtin/intl/PluralRules.cpp @@ -10,16 +10,17 @@ #include "mozilla/Assertions.h" #include "mozilla/Casting.h" #include "builtin/intl/CommonFunctions.h" #include "builtin/intl/ICUStubs.h" #include "builtin/intl/ScopedICUObject.h" #include "gc/FreeOp.h" +#include "js/AutoByteString.h" #include "vm/GlobalObject.h" #include "vm/JSContext.h" #include "vm/StringType.h" #include "vm/JSObject-inl.h" #include "vm/NativeObject-inl.h" using namespace js;
--- a/js/src/builtin/intl/RelativeTimeFormat.cpp +++ b/js/src/builtin/intl/RelativeTimeFormat.cpp @@ -10,16 +10,17 @@ #include "mozilla/Assertions.h" #include "mozilla/FloatingPoint.h" #include "builtin/intl/CommonFunctions.h" #include "builtin/intl/ICUStubs.h" #include "builtin/intl/ScopedICUObject.h" #include "gc/FreeOp.h" +#include "js/AutoByteString.h" #include "vm/GlobalObject.h" #include "vm/JSContext.h" #include "vm/NativeObject-inl.h" using namespace js; using mozilla::IsNegativeZero;
--- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -35,30 +35,33 @@ #include "jsnum.h" #include "builtin/TypedObject.h" #include "ctypes/Library.h" #include "gc/FreeOp.h" #include "gc/Policy.h" #include "gc/Zone.h" #include "jit/AtomicOperations.h" +#include "js/AutoByteString.h" +#include "js/StableStringChars.h" #include "js/UniquePtr.h" #include "js/Vector.h" #include "util/Windows.h" #include "vm/JSContext.h" #include "vm/JSFunction.h" #include "vm/JSObject-inl.h" using namespace std; using mozilla::IsAsciiAlpha; using mozilla::IsAsciiDigit; using JS::AutoCheckCannotGC; +using JS::AutoStableStringChars; namespace js { namespace ctypes { template <typename CharT> size_t GetDeflatedUTF8StringLength(JSContext* maybecx, const CharT* chars, size_t nchars)
--- a/js/src/ctypes/Library.cpp +++ b/js/src/ctypes/Library.cpp @@ -5,16 +5,20 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ctypes/Library.h" #include "prerror.h" #include "prlink.h" #include "ctypes/CTypes.h" +#include "js/AutoByteString.h" +#include "js/StableStringChars.h" + +using JS::AutoStableStringChars; namespace js { namespace ctypes { /******************************************************************************* ** JSAPI function prototypes *******************************************************************************/
--- a/js/src/frontend/EmitterScope.cpp +++ b/js/src/frontend/EmitterScope.cpp @@ -3,16 +3,17 @@ * 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/. */ #include "frontend/EmitterScope.h" #include "frontend/BytecodeEmitter.h" #include "frontend/TDZCheckCache.h" +#include "js/AutoByteString.h" using namespace js; using namespace js::frontend; using mozilla::DebugOnly; using mozilla::Maybe; using mozilla::Nothing; using mozilla::Some;
--- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -32,16 +32,17 @@ #include "jstypes.h" #include "builtin/ModuleObject.h" #include "builtin/SelfHostingDefines.h" #include "frontend/BytecodeCompiler.h" #include "frontend/FoldConstants.h" #include "frontend/TokenStream.h" #include "irregexp/RegExpParser.h" +#include "js/AutoByteString.h" #include "vm/BytecodeUtil.h" #include "vm/JSAtom.h" #include "vm/JSContext.h" #include "vm/JSFunction.h" #include "vm/JSScript.h" #include "vm/RegExpObject.h" #include "vm/StringType.h" #include "wasm/AsmJS.h" @@ -420,17 +421,17 @@ ParseContext::init() return true; } bool UsedNameTracker::noteUse(JSContext* cx, JSAtom* name, uint32_t scriptId, uint32_t scopeId) { if (UsedNameMap::AddPtr p = map_.lookupForAdd(name)) { - if (!p->value().noteUsedInScope(scriptId, scopeId)) + if (!p || !p->value().noteUsedInScope(scriptId, scopeId)) return false; } else { UsedNameInfo info(cx); if (!info.noteUsedInScope(scriptId, scopeId)) return false; if (!map_.add(p, name, std::move(info))) return false; }
--- a/js/src/gc/HashUtil.h +++ b/js/src/gc/HashUtil.h @@ -42,17 +42,18 @@ struct DependentAddPtr return false; } return true; } template <class KeyInput> void remove(JSContext* cx, T& table, const KeyInput& key) { refreshAddPtr(cx, table, key); - table.remove(addPtr); + if (addPtr) + table.remove(addPtr); } bool found() const { return addPtr.found(); } explicit operator bool() const { return found(); } const Entry& operator*() const { return *addPtr; } const Entry* operator->() const { return &*addPtr; } private:
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/structured-clone/roundtrip.js @@ -0,0 +1,30 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +load(libdir + "asserts.js"); + +const objects = [ + {}, + {a: 1, b: 2}, + {0: 1, 1: 2}, + {0: 1, 1: 2, a: 1}, + {0: 1, 1: 2, a: 1, b: 2}, + {1000000: 0, 1000001: 1}, + {0: 0, 1: 0, 1000000: 0, 1000001: 1}, + + [], + [0, 1, 2], + [0, 15, 16], + [{a: 0, b: 0}, {b: 0, a: 0}], + [0, , , 1, 2], + [, 1], + [0,,], + [,,], +] + +for (const obj of objects) { + assertDeepEq(deserialize(serialize(obj)), obj); + assertDeepEq(deserialize(serialize(wrapWithProto(obj, null))), obj); +}
--- a/js/src/jsapi-tests/testParseJSON.cpp +++ b/js/src/jsapi-tests/testParseJSON.cpp @@ -5,16 +5,17 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include <limits> #include <string.h> #include "builtin/String.h" +#include "js/JSON.h" #include "js/Printf.h" #include "jsapi-tests/tests.h" using namespace js; class AutoInflatedString { JSContext * const cx; char16_t* chars_;
--- a/js/src/jsapi-tests/testSavedStacks.cpp +++ b/js/src/jsapi-tests/testSavedStacks.cpp @@ -3,16 +3,17 @@ * 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/. */ #include "jsfriendapi.h" #include "builtin/String.h" #include "builtin/TestingFunctions.h" +#include "js/SavedFrameAPI.h" #include "jsapi-tests/tests.h" #include "vm/ArrayObject.h" #include "vm/Realm.h" #include "vm/SavedStacks.h" BEGIN_TEST(testSavedStacks_withNoStack) { JS::Realm* realm = cx->realm();
--- a/js/src/jsapi-tests/tests.h +++ b/js/src/jsapi-tests/tests.h @@ -12,16 +12,17 @@ #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "gc/GC.h" #include "js/AllocPolicy.h" +#include "js/AutoByteString.h" #include "js/Vector.h" #include "vm/JSContext.h" /* Note: Aborts on OOM. */ class JSAPITestString { js::Vector<char, 0, js::SystemAllocPolicy> chars; public:
--- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -50,22 +50,25 @@ #include "frontend/Parser.h" // for JS_BufferIsCompileableUnit #include "gc/FreeOp.h" #include "gc/Marking.h" #include "gc/Policy.h" #include "gc/PublicIterators.h" #include "gc/WeakMap.h" #include "jit/JitCommon.h" #include "jit/JitSpewer.h" +#include "js/AutoByteString.h" #include "js/CharacterEncoding.h" #include "js/Conversions.h" #include "js/Date.h" #include "js/Initialization.h" +#include "js/JSON.h" #include "js/Proxy.h" #include "js/SliceBudget.h" +#include "js/StableStringChars.h" #include "js/StructuredClone.h" #include "js/Utility.h" #include "js/Wrapper.h" #include "util/StringBuffer.h" #include "util/Text.h" #include "vm/AsyncFunction.h" #include "vm/AsyncIteration.h" #include "vm/DateObject.h" @@ -103,16 +106,18 @@ using namespace js; using namespace js::gc; using mozilla::Maybe; using mozilla::PodCopy; using mozilla::Some; +using JS::AutoStableStringChars; + #ifdef HAVE_VA_LIST_AS_ARRAY #define JS_ADDRESSOF_VA_LIST(ap) ((va_list*)(ap)) #else #define JS_ADDRESSOF_VA_LIST(ap) (&(ap)) #endif JS_PUBLIC_API(bool) JS::CallArgs::requireAtLeast(JSContext* cx, const char* fnname, unsigned required) const
--- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -12,31 +12,32 @@ #include "mozilla/AlreadyAddRefed.h" #include "mozilla/FloatingPoint.h" #include "mozilla/MemoryReporting.h" #include "mozilla/Range.h" #include "mozilla/RangedPtr.h" #include "mozilla/RefPtr.h" #include "mozilla/Variant.h" -#include <iterator> #include <stdarg.h> #include <stddef.h> #include <stdint.h> #include <stdio.h> #include "jspubtd.h" #include "js/AllocPolicy.h" #include "js/CallArgs.h" #include "js/CharacterEncoding.h" #include "js/Class.h" +#include "js/ErrorReport.h" #include "js/GCVector.h" #include "js/HashTable.h" #include "js/Id.h" +#include "js/MemoryFunctions.h" #include "js/Principals.h" #include "js/Realm.h" #include "js/RefCounted.h" #include "js/RootingAPI.h" #include "js/Stream.h" #include "js/TracingAPI.h" #include "js/UniquePtr.h" #include "js/Utility.h" @@ -217,61 +218,16 @@ enum class PromiseRejectionHandlingState } /* namespace JS */ typedef void (* JSPromiseRejectionTrackerCallback)(JSContext* cx, JS::HandleObject promise, JS::PromiseRejectionHandlingState state, void* data); -/** - * Possible exception types. These types are part of a JSErrorFormatString - * structure. They define which error to throw in case of a runtime error. - * - * JSEXN_WARN is used for warnings in js.msg files (for instance because we - * don't want to prepend 'Error:' to warning messages). This value can go away - * if we ever decide to use an entirely separate mechanism for warnings. - */ -typedef enum JSExnType { - JSEXN_ERR, - JSEXN_FIRST = JSEXN_ERR, - JSEXN_INTERNALERR, - JSEXN_EVALERR, - JSEXN_RANGEERR, - JSEXN_REFERENCEERR, - JSEXN_SYNTAXERR, - JSEXN_TYPEERR, - JSEXN_URIERR, - JSEXN_DEBUGGEEWOULDRUN, - JSEXN_WASMCOMPILEERROR, - JSEXN_WASMLINKERROR, - JSEXN_WASMRUNTIMEERROR, - JSEXN_ERROR_LIMIT, - JSEXN_WARN = JSEXN_ERROR_LIMIT, - JSEXN_NOTE, - JSEXN_LIMIT -} JSExnType; - -struct JSErrorFormatString { - /** The error message name in ASCII. */ - const char* name; - - /** The error format string in ASCII. */ - const char* format; - - /** The number of arguments to expand in the formatted error message. */ - uint16_t argCount; - - /** One of the JSExnType constants above. */ - int16_t exnType; -}; - -typedef const JSErrorFormatString* -(* JSErrorCallback)(void* userRef, const unsigned errorNumber); - typedef bool (* JSLocaleToUpperCase)(JSContext* cx, JS::HandleString src, JS::MutableHandleValue rval); typedef bool (* JSLocaleToLowerCase)(JSContext* cx, JS::HandleString src, JS::MutableHandleValue rval); typedef bool (* JSLocaleCompare)(JSContext* cx, JS::HandleString src1, JS::HandleString src2, @@ -1306,40 +1262,16 @@ struct JSCTypesCallbacks { * pointer to static data that exists for the lifetime of 'ctypesObj', but it * may safely be altered after calling this function and without having * to call this function again. */ extern JS_PUBLIC_API(void) JS_SetCTypesCallbacks(JSObject* ctypesObj, const JSCTypesCallbacks* callbacks); #endif -extern JS_PUBLIC_API(void*) -JS_malloc(JSContext* cx, size_t nbytes); - -extern JS_PUBLIC_API(void*) -JS_realloc(JSContext* cx, void* p, size_t oldBytes, size_t newBytes); - -/** - * A wrapper for js_free(p) that may delay js_free(p) invocation as a - * performance optimization. - * cx may be nullptr. - */ -extern JS_PUBLIC_API(void) -JS_free(JSContext* cx, void* p); - -/** - * A wrapper for js_free(p) that may delay js_free(p) invocation as a - * performance optimization as specified by the given JSFreeOp instance. - */ -extern JS_PUBLIC_API(void) -JS_freeop(JSFreeOp* fop, void* p); - -extern JS_PUBLIC_API(void) -JS_updateMallocCounter(JSContext* cx, size_t nbytes); - /* * A replacement for MallocAllocPolicy that allocates in the JS heap and adds no * extra behaviours. * * This is currently used for allocating source buffers for parsing. Since these * are temporary and will not be freed by GC, the memory is not tracked by the * usual accounting. */ @@ -4659,29 +4591,16 @@ JS_ConcatStrings(JSContext* cx, JS::Hand * NB: This function does not store an additional zero byte or char16_t after the * transcoded string. */ JS_PUBLIC_API(bool) JS_DecodeBytes(JSContext* cx, const char* src, size_t srclen, char16_t* dst, size_t* dstlenp); /** - * A variation on JS_EncodeCharacters where a null terminated string is - * returned that you are expected to call JS_free on when done. - */ -JS_PUBLIC_API(char*) -JS_EncodeString(JSContext* cx, JSString* str); - -/** - * Same behavior as JS_EncodeString(), but encode into UTF-8 string - */ -JS_PUBLIC_API(char*) -JS_EncodeStringToUTF8(JSContext* cx, JS::HandleString str); - -/** * Get number of bytes in the string encoding (without accounting for a * terminating zero bytes. The function returns (size_t) -1 if the string * can not be encoded into bytes and reports an error using cx accordingly. */ JS_PUBLIC_API(size_t) JS_GetStringEncodingLength(JSContext* cx, JSString* str); /** @@ -4690,85 +4609,16 @@ JS_GetStringEncodingLength(JSContext* cx * encoded into bytes with no error reported. Otherwise it returns the number * of bytes that are necessary to encode the string. If that exceeds the * length parameter, the string will be cut and only length bytes will be * written into the buffer. */ MOZ_MUST_USE JS_PUBLIC_API(bool) JS_EncodeStringToBuffer(JSContext* cx, JSString* str, char* buffer, size_t length); -class MOZ_RAII JSAutoByteString -{ - public: - JSAutoByteString(JSContext* cx, JSString* str - MOZ_GUARD_OBJECT_NOTIFIER_PARAM) - : mBytes(JS_EncodeString(cx, str)) - { - MOZ_ASSERT(cx); - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - } - - explicit JSAutoByteString(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) - : mBytes(nullptr) - { - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - } - - ~JSAutoByteString() { - JS_free(nullptr, mBytes); - } - - /* Take ownership of the given byte array. */ - void initBytes(JS::UniqueChars&& bytes) { - MOZ_ASSERT(!mBytes); - mBytes = bytes.release(); - } - - char* encodeLatin1(JSContext* cx, JSString* str) { - MOZ_ASSERT(!mBytes); - MOZ_ASSERT(cx); - mBytes = JS_EncodeString(cx, str); - return mBytes; - } - - char* encodeUtf8(JSContext* cx, JS::HandleString str) { - MOZ_ASSERT(!mBytes); - MOZ_ASSERT(cx); - mBytes = JS_EncodeStringToUTF8(cx, str); - return mBytes; - } - - void clear() { - js_free(mBytes); - mBytes = nullptr; - } - - char* ptr() const { - return mBytes; - } - - bool operator!() const { - return !mBytes; - } - - size_t length() const { - if (!mBytes) - return 0; - return strlen(mBytes); - } - - private: - char* mBytes; - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER - - /* Copy and assignment are not supported. */ - JSAutoByteString(const JSAutoByteString& another); - JSAutoByteString& operator=(const JSAutoByteString& another); -}; - /************************************************************************/ /* * Symbols */ namespace JS { /** @@ -4869,71 +4719,16 @@ PropertySpecNameEqualsId(const char* nam * during GC marking. */ JS_PUBLIC_API(bool) PropertySpecNameToPermanentId(JSContext* cx, const char* name, jsid* idp); } /* namespace JS */ /************************************************************************/ -/* - * JSON functions - */ -typedef bool (* JSONWriteCallback)(const char16_t* buf, uint32_t len, void* data); - -/** - * JSON.stringify as specified by ES5. - */ -JS_PUBLIC_API(bool) -JS_Stringify(JSContext* cx, JS::MutableHandleValue value, JS::HandleObject replacer, - JS::HandleValue space, JSONWriteCallback callback, void* data); - -namespace JS { - -/** - * An API akin to JS_Stringify but with the goal of not having observable - * side-effects when the stringification is performed. This means it does not - * allow a replacer or a custom space, and has the following constraints on its - * input: - * - * 1) The input must be a plain object or array, not an abitrary value. - * 2) Every value in the graph reached by the algorithm starting with this - * object must be one of the following: null, undefined, a string (NOT a - * string object!), a boolean, a finite number (i.e. no NaN or Infinity or - * -Infinity), a plain object with no accessor properties, or an Array with - * no holes. - * - * The actual behavior differs from JS_Stringify only in asserting the above and - * NOT attempting to get the "toJSON" property from things, since that could - * clearly have side-effects. - */ -JS_PUBLIC_API(bool) -ToJSONMaybeSafely(JSContext* cx, JS::HandleObject input, - JSONWriteCallback callback, void* data); - -} /* namespace JS */ - -/** - * JSON.parse as specified by ES5. - */ -JS_PUBLIC_API(bool) -JS_ParseJSON(JSContext* cx, const char16_t* chars, uint32_t len, JS::MutableHandleValue vp); - -JS_PUBLIC_API(bool) -JS_ParseJSON(JSContext* cx, JS::HandleString str, JS::MutableHandleValue vp); - -JS_PUBLIC_API(bool) -JS_ParseJSONWithReviver(JSContext* cx, const char16_t* chars, uint32_t len, JS::HandleValue reviver, - JS::MutableHandleValue vp); - -JS_PUBLIC_API(bool) -JS_ParseJSONWithReviver(JSContext* cx, JS::HandleString str, JS::HandleValue reviver, - JS::MutableHandleValue vp); - -/************************************************************************/ /** * The default locale for the ECMAScript Internationalization API * (Intl.Collator, Intl.NumberFormat, Intl.DateTimeFormat). * Note that the Internationalization API encourages clients to * specify their own locales. * The locale string remains owned by the caller. */ @@ -5113,218 +4908,16 @@ extern MOZ_COLD JS_PUBLIC_API(void) JS_ReportOutOfMemory(JSContext* cx); /** * Complain when an allocation size overflows the maximum supported limit. */ extern JS_PUBLIC_API(void) JS_ReportAllocationOverflow(JSContext* cx); -/** - * Base class that implements parts shared by JSErrorReport and - * JSErrorNotes::Note. - */ -class JSErrorBase -{ - // The (default) error message. - // If ownsMessage_ is true, the it is freed in destructor. - JS::ConstUTF8CharsZ message_; - - public: - JSErrorBase() - : filename(nullptr), lineno(0), column(0), - errorNumber(0), - ownsMessage_(false) - {} - - ~JSErrorBase() { - freeMessage(); - } - - // Source file name, URL, etc., or null. - const char* filename; - - // Source line number. - unsigned lineno; - - // Zero-based column index in line. - unsigned column; - - // the error number, e.g. see js.msg. - unsigned errorNumber; - - private: - bool ownsMessage_ : 1; - - public: - const JS::ConstUTF8CharsZ message() const { - return message_; - } - - void initOwnedMessage(const char* messageArg) { - initBorrowedMessage(messageArg); - ownsMessage_ = true; - } - void initBorrowedMessage(const char* messageArg) { - MOZ_ASSERT(!message_); - message_ = JS::ConstUTF8CharsZ(messageArg, strlen(messageArg)); - } - - JSString* newMessageString(JSContext* cx); - - private: - void freeMessage(); -}; - -/** - * Notes associated with JSErrorReport. - */ -class JSErrorNotes -{ - public: - class Note : public JSErrorBase - {}; - - private: - // Stores pointers to each note. - js::Vector<js::UniquePtr<Note>, 1, js::SystemAllocPolicy> notes_; - - public: - JSErrorNotes(); - ~JSErrorNotes(); - - // Add an note to the given position. - bool addNoteASCII(JSContext* cx, - const char* filename, unsigned lineno, unsigned column, - JSErrorCallback errorCallback, void* userRef, - const unsigned errorNumber, ...); - bool addNoteLatin1(JSContext* cx, - const char* filename, unsigned lineno, unsigned column, - JSErrorCallback errorCallback, void* userRef, - const unsigned errorNumber, ...); - bool addNoteUTF8(JSContext* cx, - const char* filename, unsigned lineno, unsigned column, - JSErrorCallback errorCallback, void* userRef, - const unsigned errorNumber, ...); - - JS_PUBLIC_API(size_t) length(); - - // Create a deep copy of notes. - js::UniquePtr<JSErrorNotes> copy(JSContext* cx); - - class iterator : public std::iterator<std::input_iterator_tag, js::UniquePtr<Note>> - { - js::UniquePtr<Note>* note_; - public: - explicit iterator(js::UniquePtr<Note>* note = nullptr) : note_(note) - {} - - bool operator==(iterator other) const { - return note_ == other.note_; - } - bool operator!=(iterator other) const { - return !(*this == other); - } - iterator& operator++() { - note_++; - return *this; - } - reference operator*() { - return *note_; - } - }; - JS_PUBLIC_API(iterator) begin(); - JS_PUBLIC_API(iterator) end(); -}; - -/** - * Describes a single error or warning that occurs in the execution of script. - */ -class JSErrorReport : public JSErrorBase -{ - // Offending source line without final '\n'. - // If ownsLinebuf_ is true, the buffer is freed in destructor. - const char16_t* linebuf_; - - // Number of chars in linebuf_. Does not include trailing '\0'. - size_t linebufLength_; - - // The 0-based offset of error token in linebuf_. - size_t tokenOffset_; - - public: - JSErrorReport() - : linebuf_(nullptr), linebufLength_(0), tokenOffset_(0), - notes(nullptr), - flags(0), exnType(0), isMuted(false), - ownsLinebuf_(false) - {} - - ~JSErrorReport() { - freeLinebuf(); - } - - // Associated notes, or nullptr if there's no note. - js::UniquePtr<JSErrorNotes> notes; - - // error/warning, etc. - unsigned flags; - - // One of the JSExnType constants. - int16_t exnType; - - // See the comment in TransitiveCompileOptions. - bool isMuted : 1; - - private: - bool ownsLinebuf_ : 1; - - public: - const char16_t* linebuf() const { - return linebuf_; - } - size_t linebufLength() const { - return linebufLength_; - } - size_t tokenOffset() const { - return tokenOffset_; - } - void initOwnedLinebuf(const char16_t* linebufArg, size_t linebufLengthArg, - size_t tokenOffsetArg) { - initBorrowedLinebuf(linebufArg, linebufLengthArg, tokenOffsetArg); - ownsLinebuf_ = true; - } - void initBorrowedLinebuf(const char16_t* linebufArg, size_t linebufLengthArg, - size_t tokenOffsetArg); - - private: - void freeLinebuf(); -}; - -/* - * JSErrorReport flag values. These may be freely composed. - */ -#define JSREPORT_ERROR 0x0 /* pseudo-flag for default case */ -#define JSREPORT_WARNING 0x1 /* reported via JS_ReportWarning */ -#define JSREPORT_EXCEPTION 0x2 /* exception was thrown */ -#define JSREPORT_STRICT 0x4 /* error or warning due to strict option */ - -#define JSREPORT_USER_1 0x8 /* user-defined flag */ - -/* - * If JSREPORT_EXCEPTION is set, then a JavaScript-catchable exception - * has been thrown for this runtime error, and the host should ignore it. - * Exception-aware hosts should also check for JS_IsExceptionPending if - * JS_ExecuteScript returns failure, and signal or propagate the exception, as - * appropriate. - */ -#define JSREPORT_IS_WARNING(flags) (((flags) & JSREPORT_WARNING) != 0) -#define JSREPORT_IS_EXCEPTION(flags) (((flags) & JSREPORT_EXCEPTION) != 0) -#define JSREPORT_IS_STRICT(flags) (((flags) & JSREPORT_STRICT) != 0) - namespace JS { using WarningReporter = void (*)(JSContext* cx, JSErrorReport* report); extern JS_PUBLIC_API(WarningReporter) SetWarningReporter(JSContext* cx, WarningReporter reporter); extern JS_PUBLIC_API(WarningReporter) @@ -6245,120 +5838,16 @@ CaptureCurrentStack(JSContext* cx, Mutab * new stack object is written to |stackp|. Returns true on success, * or sets an exception and returns |false| on error. */ extern JS_PUBLIC_API(bool) CopyAsyncStack(JSContext* cx, HandleObject asyncStack, HandleString asyncCause, MutableHandleObject stackp, const mozilla::Maybe<size_t>& maxFrameCount); -/* - * Accessors for working with SavedFrame JSObjects - * - * Each of these functions assert that if their `HandleObject savedFrame` - * argument is non-null, its JSClass is the SavedFrame class (or it is a - * cross-compartment or Xray wrapper around an object with the SavedFrame class) - * and the object is not the SavedFrame.prototype object. - * - * Each of these functions will find the first SavedFrame object in the chain - * whose underlying stack frame principals are subsumed by the given - * |principals|, and operate on that SavedFrame object. This prevents leaking - * information about privileged frames to un-privileged callers - * - * The SavedFrame in parameters do _NOT_ need to be in the same compartment as - * the cx, and the various out parameters are _NOT_ guaranteed to be in the same - * compartment as cx. - * - * You may consider or skip over self-hosted frames by passing - * `SavedFrameSelfHosted::Include` or `SavedFrameSelfHosted::Exclude` - * respectively. - * - * Additionally, it may be the case that there is no such SavedFrame object - * whose captured frame's principals are subsumed by |principals|! If the - * `HandleObject savedFrame` argument is null, or the |principals| do not - * subsume any of the chained SavedFrame object's principals, - * `SavedFrameResult::AccessDenied` is returned and a (hopefully) sane default - * value is chosen for the out param. - * - * See also `js/src/doc/SavedFrame/SavedFrame.md`. - */ - -enum class SavedFrameResult { - Ok, - AccessDenied -}; - -enum class SavedFrameSelfHosted { - Include, - Exclude -}; - -/** - * Given a SavedFrame JSObject, get its source property. Defaults to the empty - * string. - */ -extern JS_PUBLIC_API(SavedFrameResult) -GetSavedFrameSource(JSContext* cx, JSPrincipals* principals, HandleObject savedFrame, - MutableHandleString sourcep, - SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include); - -/** - * Given a SavedFrame JSObject, get its line property. Defaults to 0. - */ -extern JS_PUBLIC_API(SavedFrameResult) -GetSavedFrameLine(JSContext* cx, JSPrincipals* principals, HandleObject savedFrame, - uint32_t* linep, - SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include); - -/** - * Given a SavedFrame JSObject, get its column property. Defaults to 0. - */ -extern JS_PUBLIC_API(SavedFrameResult) -GetSavedFrameColumn(JSContext* cx, JSPrincipals* principals, HandleObject savedFrame, - uint32_t* columnp, - SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include); - -/** - * Given a SavedFrame JSObject, get its functionDisplayName string, or nullptr - * if SpiderMonkey was unable to infer a name for the captured frame's - * function. Defaults to nullptr. - */ -extern JS_PUBLIC_API(SavedFrameResult) -GetSavedFrameFunctionDisplayName(JSContext* cx, JSPrincipals* principals, HandleObject savedFrame, - MutableHandleString namep, - SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include); - -/** - * Given a SavedFrame JSObject, get its asyncCause string. Defaults to nullptr. - */ -extern JS_PUBLIC_API(SavedFrameResult) -GetSavedFrameAsyncCause(JSContext* cx, JSPrincipals* principals, HandleObject savedFrame, - MutableHandleString asyncCausep, - SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include); - -/** - * Given a SavedFrame JSObject, get its asyncParent SavedFrame object or nullptr - * if there is no asyncParent. The `asyncParentp` out parameter is _NOT_ - * guaranteed to be in the cx's compartment. Defaults to nullptr. - */ -extern JS_PUBLIC_API(SavedFrameResult) -GetSavedFrameAsyncParent(JSContext* cx, JSPrincipals* principals, HandleObject savedFrame, - MutableHandleObject asyncParentp, - SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include); - -/** - * Given a SavedFrame JSObject, get its parent SavedFrame object or nullptr if - * it is the oldest frame in the stack. The `parentp` out parameter is _NOT_ - * guaranteed to be in the cx's compartment. Defaults to nullptr. - */ -extern JS_PUBLIC_API(SavedFrameResult) -GetSavedFrameParent(JSContext* cx, JSPrincipals* principals, HandleObject savedFrame, - MutableHandleObject parentp, - SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include); - /** * Given a SavedFrame JSObject stack, stringify it in the same format as * Error.prototype.stack. The stringified stack out parameter is placed in the * cx's compartment. Defaults to the empty string. * * The same notes above about SavedFrame accessors applies here as well: cx * doesn't need to be in stack's compartment, and stack can be null, a * SavedFrame object, or a wrapper (CCW or Xray) around a SavedFrame object.
--- a/js/src/jsexn.cpp +++ b/js/src/jsexn.cpp @@ -18,16 +18,17 @@ #include "jsapi.h" #include "jsnum.h" #include "jstypes.h" #include "jsutil.h" #include "gc/FreeOp.h" #include "gc/Marking.h" +#include "js/AutoByteString.h" #include "js/CharacterEncoding.h" #include "js/UniquePtr.h" #include "js/Wrapper.h" #include "util/StringBuffer.h" #include "vm/ErrorObject.h" #include "vm/GlobalObject.h" #include "vm/JSContext.h" #include "vm/JSFunction.h"
--- a/js/src/jsexn.h +++ b/js/src/jsexn.h @@ -12,16 +12,18 @@ #define jsexn_h #include "jsapi.h" #include "NamespaceImports.h" #include "js/UniquePtr.h" #include "vm/JSContext.h" +class JSAutoByteString; + namespace js { class ErrorObject; UniquePtr<JSErrorNotes::Note> CopyErrorNote(JSContext* cx, JSErrorNotes::Note* note); UniquePtr<JSErrorReport> CopyErrorReport(JSContext* cx, JSErrorReport* report);
--- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -15,16 +15,17 @@ #ifdef ENABLE_BIGINT #include "builtin/BigInt.h" #endif #include "builtin/Promise.h" #include "builtin/TestingFunctions.h" #include "gc/GCInternals.h" #include "gc/PublicIterators.h" #include "gc/WeakMap.h" +#include "js/AutoByteString.h" #include "js/Printf.h" #include "js/Proxy.h" #include "js/Wrapper.h" #include "proxy/DeadObjectProxy.h" #include "vm/ArgumentsObject.h" #include "vm/JSContext.h" #include "vm/JSObject.h" #include "vm/Realm.h"
--- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -8,23 +8,26 @@ #define jsfriendapi_h #include "mozilla/Atomics.h" #include "mozilla/Casting.h" #include "mozilla/Maybe.h" #include "mozilla/MemoryReporting.h" #include "mozilla/UniquePtr.h" -#include "jsapi.h" // For JSAutoByteString. See bug 1033916. #include "jspubtd.h" +#include "js/AutoByteString.h" #include "js/CallArgs.h" #include "js/CallNonGenericMethod.h" +#include "js/CharacterEncoding.h" #include "js/Class.h" +#include "js/ErrorReport.h" #include "js/HeapAPI.h" +#include "js/StableStringChars.h" #include "js/TypeDecls.h" #include "js/Utility.h" #ifndef JS_STACK_GROWTH_DIRECTION # ifdef __hppa # define JS_STACK_GROWTH_DIRECTION (1) # else # define JS_STACK_GROWTH_DIRECTION (-1) @@ -33,19 +36,17 @@ #if JS_STACK_GROWTH_DIRECTION > 0 # define JS_CHECK_STACK_SIZE(limit, sp) (MOZ_LIKELY((uintptr_t)(sp) < (limit))) #else # define JS_CHECK_STACK_SIZE(limit, sp) (MOZ_LIKELY((uintptr_t)(sp) > (limit))) #endif struct JSErrorFormatString; -class JSLinearString; struct JSJitInfo; -class JSErrorReport; namespace JS { template <class T> class Heap; } /* namespace JS */ namespace js { class JS_FRIEND_API(BaseProxyHandler); @@ -796,22 +797,16 @@ MOZ_ALWAYS_INLINE size_t GetAtomLength(JSAtom* atom)