diff --git a/.eslintignore b/.eslintignore index 0a641b64e28c..c52c9c490948 100644 --- a/.eslintignore +++ b/.eslintignore @@ -36,6 +36,7 @@ uriloader/exthandler/** uriloader/exthandler/tests/mochitest/** widget/headless/tests/** widget/tests/** +xpfe/** # We currently have no js files in these directories, so we ignore them by # default to aid ESLint's performance. @@ -50,7 +51,6 @@ mozglue/** nsprpub/** other-licenses/** startupcache/** -xpfe/** # These directories only contain crashtests, but we still skip the whole # directory to aid performance. @@ -239,6 +239,7 @@ dom/xul/** # Third-party dom/canvas/test/webgl-conf/** dom/imptests/** +dom/media/webaudio/test/blink/** dom/media/webvtt/** # Third-party @@ -255,6 +256,9 @@ intl/locale/** intl/strres/** intl/uconv/** +# Third-party +layout/mathml/imptests/** + # Exclude everything but self-hosted JS js/ductwork/** js/examples/** diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index e7411d51e708..3abbb4735d3f 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1354,9 +1354,12 @@ pref("dom.debug.propagate_gesture_events_through_content", false); // All the Geolocation preferences are here. // +#ifndef EARLY_BETA_OR_EARLIER pref("geo.wifi.uri", "https://www.googleapis.com/geolocation/v1/geolocate?key=%GOOGLE_API_KEY%"); -// MLS URL: -// pref("geo.wifi.uri", "https://location.services.mozilla.com/v1/geolocate?key=%MOZILLA_API_KEY%"); +#else +// Use MLS on Nightly and early Beta. +pref("geo.wifi.uri", "https://location.services.mozilla.com/v1/geolocate?key=%MOZILLA_API_KEY%"); +#endif #ifdef XP_MACOSX pref("geo.provider.use_corelocation", true); diff --git a/browser/base/content/aboutNetError.js b/browser/base/content/aboutNetError.js index 1899d9c19b23..4a0c0de4e03b 100644 --- a/browser/base/content/aboutNetError.js +++ b/browser/base/content/aboutNetError.js @@ -231,22 +231,6 @@ function initPage() { var errContainer = document.getElementById("errorContainer"); errContainer.remove(); - if (className && className != "expertBadCert") { - // Associate a CSS class with the root of the page, if one was passed in, - // to allow custom styling. - // Not "expertBadCert" though, don't want to deal with the favicon - document.documentElement.className = className; - - // Also, if they specified a CSS class, they must supply their own - // favicon. In order to trigger the browser to repaint though, we - // need to remove/add the link element. - var favicon = document.getElementById("favicon"); - var faviconParent = favicon.parentNode; - faviconParent.removeChild(favicon); - favicon.setAttribute("href", "chrome://global/skin/icons/" + className + "_favicon.png"); - faviconParent.appendChild(favicon); - } - if (err == "remoteXUL") { // Remove the "Try again" button for remote XUL errors given that // it is useless. diff --git a/browser/base/content/urlbarBindings.xml b/browser/base/content/urlbarBindings.xml index 0c50a4fb720e..bd67c05c2d8b 100644 --- a/browser/base/content/urlbarBindings.xml +++ b/browser/base/content/urlbarBindings.xml @@ -2122,13 +2122,7 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. } // Set the direction of the popup based on the textbox (bug 649840). - // getComputedStyle causes a layout flush, so avoid calling it if a - // direction has already been set. - if (!this.style.direction) { - this.style.direction = - aElement.ownerGlobal.getComputedStyle(aElement).direction; - } - let popupDirection = this.style.direction; + let popupDirection = this.style.direction = (RTL_UI ? "rtl" : "ltr"); // Make the popup span the width of the window. First, set its width. let documentRect = diff --git a/browser/components/attribution/test/.eslintrc.js b/browser/components/attribution/test/xpcshell/.eslintrc.js similarity index 100% rename from browser/components/attribution/test/.eslintrc.js rename to browser/components/attribution/test/xpcshell/.eslintrc.js diff --git a/browser/components/newtab/lib/CFRPageActions.jsm b/browser/components/newtab/lib/CFRPageActions.jsm index a3bab97ec4d3..8d18aa5d9e3d 100644 --- a/browser/components/newtab/lib/CFRPageActions.jsm +++ b/browser/components/newtab/lib/CFRPageActions.jsm @@ -258,9 +258,7 @@ class PageAction { headerLabel.value = await this.getStrings(content.heading_text); headerLink.setAttribute("href", SUMO_BASE_URL + content.info_icon.sumo_path); - const isRTL = this.window.getComputedStyle(notification).direction === "rtl"; - const attribute = isRTL ? "left" : "right"; - headerLink.setAttribute(attribute, 0); + headerLink.setAttribute(this.window.RTL_UI ? "left" : "right", 0); headerImage.setAttribute("tooltiptext", await this.getStrings(content.info_icon.label, "tooltiptext")); headerLink.onclick = () => this._sendTelemetry({message_id: id, bucket_id: content.bucket_id, event: "RATIONALE"}); diff --git a/browser/components/payments/content/paymentDialogWrapper.js b/browser/components/payments/content/paymentDialogWrapper.js index de965d7f6277..6d77fb84c94e 100644 --- a/browser/components/payments/content/paymentDialogWrapper.js +++ b/browser/components/payments/content/paymentDialogWrapper.js @@ -627,6 +627,20 @@ var paymentDialogWrapper = { this.sendMessageToContent("responseSent"); }, + async onChangePayerAddress({payerAddressGUID}) { + if (payerAddressGUID) { + // If a payer address was de-selected e.g. the selected address was deleted, we'll + // just wait to send the address change when the payer address is eventually selected + // before clicking Pay since it's a required field. + let { + payerName, + payerEmail, + payerPhone, + } = await this._convertProfileAddressToPayerData(payerAddressGUID); + paymentSrv.changePayerDetail(this.request.requestId, payerName, payerEmail, payerPhone); + } + }, + async onChangeShippingAddress({shippingAddressGUID}) { if (shippingAddressGUID) { // If a shipping address was de-selected e.g. the selected address was deleted, we'll @@ -733,6 +747,10 @@ var paymentDialogWrapper = { this.initializeFrame(); break; } + case "changePayerAddress": { + this.onChangePayerAddress(data); + break; + } case "changeShippingAddress": { this.onChangeShippingAddress(data); break; diff --git a/browser/components/payments/res/containers/payment-dialog.js b/browser/components/payments/res/containers/payment-dialog.js index afcd8f3fc633..ae47de6ee8eb 100644 --- a/browser/components/payments/res/containers/payment-dialog.js +++ b/browser/components/payments/res/containers/payment-dialog.js @@ -132,7 +132,17 @@ export default class PaymentDialog extends PaymentStateSubscriberMixin(HTMLEleme paymentRequest.pay(data); } + /** + * Called when the selectedShippingAddress or its properties are changed. + * @param {string} shippingAddressGUID + */ changeShippingAddress(shippingAddressGUID) { + // Clear shipping address merchant errors when the shipping address changes. + let request = Object.assign({}, this.requestStore.getState().request); + request.paymentDetails = Object.assign({}, request.paymentDetails); + request.paymentDetails.shippingAddressErrors = {}; + this.requestStore.setState({request}); + paymentRequest.changeShippingAddress({ shippingAddressGUID, }); @@ -144,6 +154,36 @@ export default class PaymentDialog extends PaymentStateSubscriberMixin(HTMLEleme }); } + /** + * Called when the selectedPaymentCard or its relevant properties or billingAddress are changed. + * @param {string} selectedPaymentCardGUID + */ + changePaymentMethod(selectedPaymentCardGUID) { + // Clear paymentMethod merchant errors when the paymentMethod or billingAddress changes. + let request = Object.assign({}, this.requestStore.getState().request); + request.paymentDetails = Object.assign({}, request.paymentDetails); + request.paymentDetails.paymentMethodErrors = null; + this.requestStore.setState({request}); + + // TODO: Bug 1477113 - Dispatch paymentmethodchange + } + + /** + * Called when the selectedPayerAddress or its relevant properties are changed. + * @param {string} payerAddressGUID + */ + changePayerAddress(payerAddressGUID) { + // Clear payer address merchant errors when the payer address changes. + let request = Object.assign({}, this.requestStore.getState().request); + request.paymentDetails = Object.assign({}, request.paymentDetails); + request.paymentDetails.payerErrors = {}; + this.requestStore.setState({request}); + + paymentRequest.changePayerAddress({ + payerAddressGUID, + }); + } + _isPayerRequested(paymentOptions) { return paymentOptions.requestPayerName || paymentOptions.requestPayerEmail || @@ -188,8 +228,9 @@ export default class PaymentDialog extends PaymentStateSubscriberMixin(HTMLEleme * * @param {object} state - See `PaymentsStore.setState` */ - async setStateFromParent(state) { + async setStateFromParent(state) { // eslint-disable-line complexity let oldAddresses = paymentRequest.getAddresses(this.requestStore.getState()); + let oldBasicCards = paymentRequest.getBasicCards(this.requestStore.getState()); if (state.request) { state = this._updateCompleteStatus(state); } @@ -204,33 +245,67 @@ export default class PaymentDialog extends PaymentStateSubscriberMixin(HTMLEleme selectedShippingOption, } = state; let addresses = paymentRequest.getAddresses(state); - let shippingOptions = state.request.paymentDetails.shippingOptions; - let shippingAddress = selectedShippingAddress && addresses[selectedShippingAddress]; - let oldShippingAddress = selectedShippingAddress && - oldAddresses[selectedShippingAddress]; + let {paymentOptions} = state.request; - // Ensure `selectedShippingAddress` never refers to a deleted address. - // We also compare address timestamps to notify about changes - // made outside the payments UI. - if (shippingAddress) { - // invalidate the cached value if the address was modified - if (oldShippingAddress && - shippingAddress.guid == oldShippingAddress.guid && - shippingAddress.timeLastModified != oldShippingAddress.timeLastModified) { - delete this._cachedState.selectedShippingAddress; + if (paymentOptions.requestShipping) { + let shippingOptions = state.request.paymentDetails.shippingOptions; + let shippingAddress = selectedShippingAddress && addresses[selectedShippingAddress]; + let oldShippingAddress = selectedShippingAddress && + oldAddresses[selectedShippingAddress]; + + // Ensure `selectedShippingAddress` never refers to a deleted address. + // We also compare address timestamps to notify about changes + // made outside the payments UI. + if (shippingAddress) { + // invalidate the cached value if the address was modified + if (oldShippingAddress && + shippingAddress.guid == oldShippingAddress.guid && + shippingAddress.timeLastModified != oldShippingAddress.timeLastModified) { + delete this._cachedState.selectedShippingAddress; + } + } else if (selectedShippingAddress !== null) { + // null out the `selectedShippingAddress` property if it is undefined, + // or if the address it pointed to was removed from storage. + log.debug("resetting invalid/deleted shipping address"); + this.requestStore.setState({ + selectedShippingAddress: null, + }); + } + + // Ensure `selectedShippingOption` never refers to a deleted shipping option and + // matches the merchant's selected option if the user hasn't made a choice. + if (shippingOptions && (!selectedShippingOption || + !shippingOptions.find(opt => opt.id == selectedShippingOption))) { + this._cachedState.selectedShippingOption = selectedShippingOption; + this.requestStore.setState({ + // Use the DOM's computed selected shipping option: + selectedShippingOption: state.request.shippingOption, + }); + } + } + + let basicCards = paymentRequest.getBasicCards(state); + let oldPaymentMethod = selectedPaymentCard && oldBasicCards[selectedPaymentCard]; + let paymentMethod = selectedPaymentCard && basicCards[selectedPaymentCard]; + if (oldPaymentMethod && paymentMethod.guid == oldPaymentMethod.guid && + paymentMethod.timeLastModified != oldPaymentMethod.timeLastModified) { + delete this._cachedState.selectedPaymentCard; + } else { + // Changes to the billing address record don't change the `timeLastModified` + // on the card record so we have to check for changes to the address separately. + + let billingAddressGUID = paymentMethod && paymentMethod.billingAddressGUID; + let billingAddress = billingAddressGUID && addresses[billingAddressGUID]; + let oldBillingAddress = billingAddressGUID && oldAddresses[billingAddressGUID]; + + if (oldBillingAddress && billingAddress && + billingAddress.timeLastModified != oldBillingAddress.timeLastModified) { + delete this._cachedState.selectedPaymentCard; } - } else if (selectedShippingAddress !== null) { - // null out the `selectedShippingAddress` property if it is undefined, - // or if the address it pointed to was removed from storage. - log.debug("resetting invalid/deleted shipping address"); - this.requestStore.setState({ - selectedShippingAddress: null, - }); } // Ensure `selectedPaymentCard` never refers to a deleted payment card and refers // to a payment card if one exists. - let basicCards = paymentRequest.getBasicCards(state); if (!basicCards[selectedPaymentCard]) { // Determining the initial selection is tracked in bug 1455789 this.requestStore.setState({ @@ -239,23 +314,26 @@ export default class PaymentDialog extends PaymentStateSubscriberMixin(HTMLEleme }); } - // Ensure `selectedShippingOption` never refers to a deleted shipping option and - // matches the merchant's selected option if the user hasn't made a choice. - if (shippingOptions && (!selectedShippingOption || - !shippingOptions.find(option => option.id == selectedShippingOption))) { - this._cachedState.selectedShippingOption = selectedShippingOption; - this.requestStore.setState({ - // Use the DOM's computed selected shipping option: - selectedShippingOption: state.request.shippingOption, - }); - } + if (this._isPayerRequested(state.request.paymentOptions)) { + let payerAddress = selectedPayerAddress && addresses[selectedPayerAddress]; + let oldPayerAddress = selectedPayerAddress && oldAddresses[selectedPayerAddress]; - // Ensure `selectedPayerAddress` never refers to a deleted address and refers - // to an address if one exists. - if (!addresses[selectedPayerAddress]) { - this.requestStore.setState({ - selectedPayerAddress: Object.keys(addresses)[0] || null, - }); + if (oldPayerAddress && payerAddress && ( + (paymentOptions.requestPayerName && payerAddress.name != oldPayerAddress.name) || + (paymentOptions.requestPayerEmail && payerAddress.email != oldPayerAddress.email) || + (paymentOptions.requestPayerPhone && payerAddress.tel != oldPayerAddress.tel) + )) { + // invalidate the cached value if the payer address fields were modified + delete this._cachedState.selectedPayerAddress; + } + + // Ensure `selectedPayerAddress` never refers to a deleted address and refers + // to an address if one exists. + if (!addresses[selectedPayerAddress]) { + this.requestStore.setState({ + selectedPayerAddress: Object.keys(addresses)[0] || null, + }); + } } } @@ -354,8 +432,20 @@ export default class PaymentDialog extends PaymentStateSubscriberMixin(HTMLEleme } } + if (state.selectedPaymentCard != this._cachedState.selectedPaymentCard) { + this.changePaymentMethod(state.selectedPaymentCard); + } + + if (this._isPayerRequested(state.request.paymentOptions)) { + if (state.selectedPayerAddress != this._cachedState.selectedPayerAddress) { + this.changePayerAddress(state.selectedPayerAddress); + } + } + this._cachedState.selectedShippingAddress = state.selectedShippingAddress; this._cachedState.selectedShippingOption = state.selectedShippingOption; + this._cachedState.selectedPaymentCard = state.selectedPaymentCard; + this._cachedState.selectedPayerAddress = state.selectedPayerAddress; } render(state) { diff --git a/browser/components/payments/res/paymentRequest.js b/browser/components/payments/res/paymentRequest.js index 6eb37d80f748..3527c8c087b0 100644 --- a/browser/components/payments/res/paymentRequest.js +++ b/browser/components/payments/res/paymentRequest.js @@ -200,6 +200,10 @@ var paymentRequest = { this.sendMessageToChrome("changeShippingOption", data); }, + changePayerAddress(data) { + this.sendMessageToChrome("changePayerAddress", data); + }, + /** * Add/update an autofill storage record. * diff --git a/browser/components/payments/test/PaymentTestUtils.jsm b/browser/components/payments/test/PaymentTestUtils.jsm index 26d7d9c68fa7..f26fefd19667 100644 --- a/browser/components/payments/test/PaymentTestUtils.jsm +++ b/browser/components/payments/test/PaymentTestUtils.jsm @@ -76,16 +76,31 @@ var PaymentTestUtils = { content[eventName + "Promise"] = new Promise(resolve => { content.rq.addEventListener(eventName, () => { + info(`Received event: ${eventName}`); resolve(); }); }); }, - awaitPaymentRequestEventPromise: async ({eventName}) => { + /** + * Used for PaymentRequest and PaymentResponse event promises. + */ + awaitPaymentEventPromise: async ({eventName}) => { await content[eventName + "Promise"]; return true; }, + promisePaymentResponseEvent: async ({eventName}) => { + let response = await content.showPromise; + content[eventName + "Promise"] = + new Promise(resolve => { + response.addEventListener(eventName, () => { + info(`Received event: ${eventName}`); + resolve(); + }); + }); + }, + updateWith: async ({eventName, details}) => { /* globals ok */ if (details.error && @@ -176,15 +191,15 @@ var PaymentTestUtils = { doc.querySelector("address-picker[selected-state-key='selectedShippingAddress']"); let select = Cu.waiveXrays(addressPicker).dropdown.popupBox; let option = select.querySelector(`[country="${country}"]`); - if (Cu.waiveXrays(doc.activeElement) == select) { - // If the is already focused, blur and re-focus to reset the - // filter-as-you-type timer so that the synthesizeKey below will work - // correctly if this method was called recently. - select.blur(); - } - select.focus(); - // eslint-disable-next-line no-undef - EventUtils.synthesizeKey(option.label, {}, content.window); + content.fillField(select, guid); }, selectShippingOptionById: value => { @@ -209,33 +215,14 @@ var PaymentTestUtils = { let optionPicker = doc.querySelector("shipping-option-picker"); let select = Cu.waiveXrays(optionPicker).dropdown.popupBox; - let option = select.querySelector(`[value="${value}"]`); - if (Cu.waiveXrays(doc.activeElement) == select) { - // If the is already focused, blur and re-focus to reset the - // filter-as-you-type timer so that the synthesizeKey below will work - // correctly if this method was called recently. - select.blur(); - } - select.focus(); - // just type the first few characters to select the right option - // eslint-disable-next-line no-undef - EventUtils.synthesizeKey(option.textContent.substring(0, 4), {}, content.window); + content.fillField(select, guid); }, /** @@ -249,7 +236,7 @@ var PaymentTestUtils = { let {requestStore} = Cu.waiveXrays(content.document.querySelector("payment-dialog")); let {page} = requestStore.getState(); let button = content.document.querySelector(`#${page.id} button.primary`); - ok(!button.disabled, "Primary button should not be disabled when clicking it"); + ok(!button.disabled, `#${page.id} primary button should not be disabled when clicking it`); button.click(); }, @@ -273,7 +260,15 @@ var PaymentTestUtils = { * * @returns {undefined} */ - completePayment: () => { + completePayment: async () => { + let { + PaymentTestUtils: PTU, + } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {}); + + await PTU.DialogContentUtils.waitForState(content, (state) => { + return state.page.id == "payment-summary"; + }, "Wait for change to payment-summary before clicking Pay"); + let button = content.document.getElementById("pay"); ok(!button.disabled, "Pay button should not be disabled when clicking it"); button.click(); @@ -532,7 +527,7 @@ var PaymentTestUtils = { "postal-code": "02138", country: "DE", tel: "+16172535702", - email: "timbl@example.org", + email: "timbl@example.com", }, /* Used as a temporary (not persisted in autofill storage) address in tests */ Temp: { diff --git a/browser/components/payments/test/browser/browser.ini b/browser/components/payments/test/browser/browser.ini index c4e167a6ac07..cf27fcd2b815 100644 --- a/browser/components/payments/test/browser/browser.ini +++ b/browser/components/payments/test/browser/browser.ini @@ -25,6 +25,7 @@ skip-if = debug && (os == 'mac' || os == 'linux') # bug 1465673 [browser_request_serialization.js] [browser_request_shipping.js] [browser_retry.js] +[browser_retry_fieldErrors.js] [browser_shippingaddresschange_error.js] [browser_show_dialog.js] skip-if = os == 'win' && debug # bug 1418385 diff --git a/browser/components/payments/test/browser/browser_address_edit.js b/browser/components/payments/test/browser/browser_address_edit.js index 55b0c32dda67..387f670bfdc8 100644 --- a/browser/components/payments/test/browser/browser_address_edit.js +++ b/browser/components/payments/test/browser/browser_address_edit.js @@ -58,7 +58,7 @@ add_task(async function test_add_link() { for (let options of testOptions) { let shippingAddressChangePromise = ContentTask.spawn(browser, { eventName: "shippingaddresschange", - }, PTU.ContentTasks.awaitPaymentRequestEventPromise); + }, PTU.ContentTasks.awaitPaymentEventPromise); await manuallyAddShippingAddress(frame, newAddress, options); await shippingAddressChangePromise; @@ -108,7 +108,7 @@ add_task(async function test_edit_link() { let shippingAddressChangePromise = ContentTask.spawn(browser, { eventName: "shippingaddresschange", - }, PTU.ContentTasks.awaitPaymentRequestEventPromise); + }, PTU.ContentTasks.awaitPaymentEventPromise); const EXPECTED_ADDRESS = { "given-name": "Jaws", @@ -556,7 +556,7 @@ add_task(async function test_private_persist_addresses() { info("awaiting the shippingaddresschange event"); await ContentTask.spawn(browser, { eventName: "shippingaddresschange", - }, PTU.ContentTasks.awaitPaymentRequestEventPromise); + }, PTU.ContentTasks.awaitPaymentEventPromise); await spawnPaymentDialogTask(frame, async (args) => { let {address, tempAddressGuid} = args; diff --git a/browser/components/payments/test/browser/browser_address_edit_hidden_fields.js b/browser/components/payments/test/browser/browser_address_edit_hidden_fields.js index 7be8df90651b..0ce2c31e3f01 100644 --- a/browser/components/payments/test/browser/browser_address_edit_hidden_fields.js +++ b/browser/components/payments/test/browser/browser_address_edit_hidden_fields.js @@ -41,7 +41,7 @@ add_task(async function test_hiddenFieldNotSaved() { let shippingAddressChangePromise = ContentTask.spawn(browser, { eventName: "shippingaddresschange", - }, PTU.ContentTasks.awaitPaymentRequestEventPromise); + }, PTU.ContentTasks.awaitPaymentEventPromise); let options = { addLinkSelector: "address-picker.shipping-related .add-link", @@ -108,7 +108,7 @@ add_task(async function test_hiddenFieldRemovedWhenCountryChanged() { let shippingAddressChangePromise = ContentTask.spawn(browser, { eventName: "shippingaddresschange", - }, PTU.ContentTasks.awaitPaymentRequestEventPromise); + }, PTU.ContentTasks.awaitPaymentEventPromise); await spawnPaymentDialogTask(frame, async (args) => { let { diff --git a/browser/components/payments/test/browser/browser_change_shipping.js b/browser/components/payments/test/browser/browser_change_shipping.js index 6882da8a5058..b578a9b74d7f 100644 --- a/browser/components/payments/test/browser/browser_change_shipping.js +++ b/browser/components/payments/test/browser/browser_change_shipping.js @@ -58,7 +58,7 @@ add_task(async function test_change_shipping() { await ContentTask.spawn(browser, { eventName: "shippingaddresschange", - }, PTU.ContentTasks.awaitPaymentRequestEventPromise); + }, PTU.ContentTasks.awaitPaymentEventPromise); info("got shippingaddresschange event"); // verify update of shippingOptions @@ -177,7 +177,7 @@ add_task(async function test_default_shippingOptions_noneSelected() { await ContentTask.spawn(browser, { eventName: "shippingaddresschange", - }, PTU.ContentTasks.awaitPaymentRequestEventPromise); + }, PTU.ContentTasks.awaitPaymentEventPromise); info("got shippingaddresschange event"); shippingOptions = @@ -236,7 +236,7 @@ add_task(async function test_default_shippingOptions_allSelected() { await ContentTask.spawn(browser, { eventName: "shippingaddresschange", - }, PTU.ContentTasks.awaitPaymentRequestEventPromise); + }, PTU.ContentTasks.awaitPaymentEventPromise); info("got shippingaddresschange event"); shippingOptions = @@ -330,7 +330,7 @@ add_task(async function test_address_edit() { await ContentTask.spawn(browser, { eventName: "shippingaddresschange", - }, PTU.ContentTasks.awaitPaymentRequestEventPromise); + }, PTU.ContentTasks.awaitPaymentEventPromise); addressOptions = await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.getShippingAddresses); diff --git a/browser/components/payments/test/browser/browser_retry_fieldErrors.js b/browser/components/payments/test/browser/browser_retry_fieldErrors.js new file mode 100644 index 000000000000..58e24fea8130 --- /dev/null +++ b/browser/components/payments/test/browser/browser_retry_fieldErrors.js @@ -0,0 +1,543 @@ +"use strict"; + +/** + * Test the merchant calling .retry() with field-specific errors. + */ + +async function setup() { + await setupFormAutofillStorage(); + await cleanupFormAutofillStorage(); + // add 2 addresses and 2 cards to avoid the FTU sequence and test address errors + let prefilledGuids = await addSampleAddressesAndBasicCard( + [PTU.Addresses.TimBL, PTU.Addresses.TimBL2], + [PTU.BasicCards.JaneMasterCard, PTU.BasicCards.JohnDoe]); + + info("associating card1 with a billing address"); + await formAutofillStorage.creditCards.update(prefilledGuids.card1GUID, { + billingAddressGUID: prefilledGuids.address1GUID, + }, true); + info("associating card2 with a billing address"); + await formAutofillStorage.creditCards.update(prefilledGuids.card2GUID, { + billingAddressGUID: prefilledGuids.address1GUID, + }, true); + + return prefilledGuids; +} + +add_task(async function test_retry_with_shipppingAddressErrors() { + if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) { + todo(false, "Cannot test OS key store login on official builds."); + return; + } + await setup(); + await BrowserTestUtils.withNewTab({ + gBrowser, + url: BLANK_PAGE_URL, + }, async browser => { + let {win, frame} = await setupPaymentDialog(browser, { + methodData: [PTU.MethodData.basicCard], + details: Object.assign({}, PTU.Details.twoShippingOptions, PTU.Details.total60USD), + options: PTU.Options.requestShippingOption, + merchantTaskFn: PTU.ContentTasks.createAndShowRequest, + }); + + await selectPaymentDialogShippingAddressByCountry(frame, "DE"); + + await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.setSecurityCode, { + securityCode: "123", + }); + + info("clicking the button to try pay the 1st time"); + await loginAndCompletePayment(frame); + + let retryUpdatePromise = spawnPaymentDialogTask(frame, async function checkDialog() { + let { + PaymentTestUtils: PTU, + } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {}); + + let state = await PTU.DialogContentUtils.waitForState(content, ({request}) => { + return request.completeStatus === "processing"; + }, "Wait for completeStatus from pay button click"); + + is(state.request.completeStatus, "processing", "Check completeStatus is processing"); + is(state.request.paymentDetails.shippingAddressErrors.country, undefined, + "Check country error string is empty"); + ok(state.changesPrevented, "Changes prevented"); + + state = await PTU.DialogContentUtils.waitForState(content, ({request}) => { + return request.completeStatus === ""; + }, "Wait for completeStatus from DOM update"); + + is(state.request.completeStatus, "", "Check completeStatus"); + is(state.request.paymentDetails.shippingAddressErrors.country, "Can only ship to USA", + "Check country error string in state"); + ok(!state.changesPrevented, "Changes no longer prevented"); + is(state.page.id, "payment-summary", "Check still on payment-summary"); + + ok(content.document.querySelector("#payment-summary").innerText + .includes("Can only ship to USA"), + "Check error visibility on summary page"); + ok(content.document.getElementById("pay").disabled, + "Pay button should be disabled until the field error is addressed"); + }); + + // Add a handler to retry the payment above. + info("Tell merchant page to retry with a country error string"); + let retryPromise = ContentTask.spawn(browser, + { + delayMs: 1000, + validationErrors: { + shippingAddress: { + country: "Can only ship to USA", + }, + }, + }, + PTU.ContentTasks.addRetryHandler); + + await retryUpdatePromise; + + info("Changing to a US address to clear the error"); + await selectPaymentDialogShippingAddressByCountry(frame, "US"); + + info("Tell merchant page to retry with a regionCode error string"); + let retryPromise2 = ContentTask.spawn(browser, + { + delayMs: 1000, + validationErrors: { + shippingAddress: { + regionCode: "Can only ship to California", + }, + }, + }, + PTU.ContentTasks.addRetryHandler); + + + await loginAndCompletePayment(frame); + + await spawnPaymentDialogTask(frame, async function checkRegionError() { + let { + PaymentTestUtils: PTU, + } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {}); + + let state = await PTU.DialogContentUtils.waitForState(content, ({request}) => { + return request.completeStatus === ""; + }, "Wait for completeStatus from DOM update"); + + is(state.request.completeStatus, "", "Check completeStatus"); + is(state.request.paymentDetails.shippingAddressErrors.regionCode, + "Can only ship to California", + "Check regionCode error string in state"); + ok(!state.changesPrevented, "Changes no longer prevented"); + is(state.page.id, "payment-summary", "Check still on payment-summary"); + + ok(content.document.querySelector("#payment-summary").innerText + .includes("Can only ship to California"), + "Check error visibility on summary page"); + ok(content.document.getElementById("pay").disabled, + "Pay button should be disabled until the field error is addressed"); + }); + + info("Changing the shipping state to CA without changing selectedShippingAddress"); + await navigateToAddShippingAddressPage(frame, { + addLinkSelector: "address-picker[selected-state-key=\"selectedShippingAddress\"] .edit-link", + }); + await fillInShippingAddressForm(frame, { "address-level1": "CA" }); + await submitAddressForm(frame, null, {isEditing: true}); + + await loginAndCompletePayment(frame); + + // We can only check the retry response after the closing as it only resolves upon complete. + let {retryException} = await retryPromise; + ok(!retryException, "Expect no exception to be thrown when calling retry()"); + + let {retryException2} = await retryPromise2; + ok(!retryException2, "Expect no exception to be thrown when calling retry()"); + + // Add a handler to complete the payment above. + info("acknowledging the completion from the merchant page"); + let result = await ContentTask.spawn(browser, {}, PTU.ContentTasks.addCompletionHandler); + + // Verify response has the expected properties + let expectedDetails = Object.assign({ + "cc-security-code": "123", + }, PTU.BasicCards.JohnDoe); + + checkPaymentMethodDetailsMatchesCard(result.response.details, expectedDetails, + "Check response payment details"); + checkPaymentAddressMatchesStorageAddress(result.response.shippingAddress, + {...PTU.Addresses.TimBL, ...{"address-level1": "CA"}}, + "Check response shipping address"); + + await BrowserTestUtils.waitForCondition(() => win.closed, "dialog should be closed"); + }); +}); + +add_task(async function test_retry_with_payerErrors() { + if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) { + todo(false, "Cannot test OS key store login on official builds."); + return; + } + let prefilledGuids = 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.requestPayerNameEmailAndPhone, + merchantTaskFn: PTU.ContentTasks.createAndShowRequest, + }); + + await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.setSecurityCode, { + securityCode: "123", + }); + + info("clicking the button to try pay the 1st time"); + await loginAndCompletePayment(frame); + + let retryUpdatePromise = spawnPaymentDialogTask(frame, async function checkDialog() { + let { + PaymentTestUtils: PTU, + } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {}); + + let state = await PTU.DialogContentUtils.waitForState(content, ({request}) => { + return request.completeStatus === "processing"; + }, "Wait for completeStatus from pay button click"); + + is(state.request.completeStatus, "processing", "Check completeStatus is processing"); + + is(state.request.paymentDetails.payerErrors.email, undefined, + "Check email error isn't present"); + ok(state.changesPrevented, "Changes prevented"); + + state = await PTU.DialogContentUtils.waitForState(content, ({request}) => { + return request.completeStatus === ""; + }, "Wait for completeStatus from DOM update"); + + is(state.request.completeStatus, "", "Check completeStatus"); + is(state.request.paymentDetails.payerErrors.email, "You must use your employee email address", + "Check email error string in state"); + ok(!state.changesPrevented, "Changes no longer prevented"); + is(state.page.id, "payment-summary", "Check still on payment-summary"); + + ok(content.document.querySelector("#payment-summary").innerText + .includes("You must use your employee email address"), + "Check error visibility on summary page"); + ok(content.document.getElementById("pay").disabled, + "Pay button should be disabled until the field error is addressed"); + }); + + + // Add a handler to retry the payment above. + info("Tell merchant page to retry with a country error string"); + let retryPromise = ContentTask.spawn(browser, + { + delayMs: 1000, + validationErrors: { + payer: { + email: "You must use your employee email address", + }, + }, + }, + PTU.ContentTasks.addRetryHandler); + + await retryUpdatePromise; + + info("Changing to a different email address to clear the error"); + await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.selectPayerAddressByGuid, + prefilledGuids.address1GUID); + + info("Tell merchant page to retry with a phone error string"); + let retryPromise2 = ContentTask.spawn(browser, + { + delayMs: 1000, + validationErrors: { + payer: { + phone: "Your phone number isn't valid", + }, + }, + }, + PTU.ContentTasks.addRetryHandler); + + + await loginAndCompletePayment(frame); + + await spawnPaymentDialogTask(frame, async function checkRegionError() { + let { + PaymentTestUtils: PTU, + } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {}); + + let state = await PTU.DialogContentUtils.waitForState(content, ({request}) => { + return request.completeStatus === ""; + }, "Wait for completeStatus from DOM update"); + + is(state.request.completeStatus, "", "Check completeStatus"); + is(state.request.paymentDetails.payerErrors.phone, + "Your phone number isn't valid", + "Check regionCode error string in state"); + ok(!state.changesPrevented, "Changes no longer prevented"); + is(state.page.id, "payment-summary", "Check still on payment-summary"); + + ok(content.document.querySelector("#payment-summary").innerText + .includes("Your phone number isn't valid"), + "Check error visibility on summary page"); + ok(content.document.getElementById("pay").disabled, + "Pay button should be disabled until the field error is addressed"); + }); + + info("Changing the payer phone to be valid without changing selectedPayerAddress"); + await navigateToAddAddressPage(frame, { + addLinkSelector: "address-picker[selected-state-key=\"selectedPayerAddress\"] .edit-link", + initialPageId: "payment-summary", + addressPageId: "payer-address-page", + }); + + let newPhoneNumber = "+16175555555"; + await fillInPayerAddressForm(frame, { tel: newPhoneNumber }); + + await ContentTask.spawn(browser, { + eventName: "payerdetailchange", + }, PTU.ContentTasks.promisePaymentResponseEvent); + + await submitAddressForm(frame, null, {isEditing: true}); + + await ContentTask.spawn(browser, { + eventName: "payerdetailchange", + }, PTU.ContentTasks.awaitPaymentEventPromise); + + await loginAndCompletePayment(frame); + + // We can only check the retry response after the closing as it only resolves upon complete. + let {retryException} = await retryPromise; + ok(!retryException, "Expect no exception to be thrown when calling retry()"); + + let {retryException2} = await retryPromise2; + ok(!retryException2, "Expect no exception to be thrown when calling retry()"); + + // Add a handler to complete the payment above. + info("acknowledging the completion from the merchant page"); + let result = await ContentTask.spawn(browser, {}, PTU.ContentTasks.addCompletionHandler); + + // Verify response has the expected properties + let expectedDetails = Object.assign({ + "cc-security-code": "123", + }, PTU.BasicCards.JohnDoe); + + checkPaymentMethodDetailsMatchesCard(result.response.details, expectedDetails, + "Check response payment details"); + let { + "given-name": givenName, + "additional-name": additionalName, + "family-name": familyName, + email, + } = PTU.Addresses.TimBL; + is(result.response.payerName, `${givenName} ${additionalName} ${familyName}`, + "Check payer name"); + is(result.response.payerEmail, email, "Check payer email"); + is(result.response.payerPhone, newPhoneNumber, "Check payer phone"); + + await BrowserTestUtils.waitForCondition(() => win.closed, "dialog should be closed"); + }); +}); + +add_task(async function test_retry_with_paymentMethodErrors() { + if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) { + todo(false, "Cannot test OS key store login on official builds."); + return; + } + let prefilledGuids = 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, + merchantTaskFn: PTU.ContentTasks.createAndShowRequest, + }); + + await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.setSecurityCode, { + securityCode: "123", + }); + + info("clicking the button to try pay the 1st time"); + await loginAndCompletePayment(frame); + + let retryUpdatePromise = spawnPaymentDialogTask(frame, async function checkDialog() { + let { + PaymentTestUtils: PTU, + } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {}); + + let state = await PTU.DialogContentUtils.waitForState(content, ({request}) => { + return request.completeStatus === "processing"; + }, "Wait for completeStatus from pay button click"); + + is(state.request.completeStatus, "processing", "Check completeStatus is processing"); + + is(state.request.paymentDetails.paymentMethodErrors, null, + "Check no paymentMethod errors are present"); + ok(state.changesPrevented, "Changes prevented"); + + state = await PTU.DialogContentUtils.waitForState(content, ({request}) => { + return request.completeStatus === ""; + }, "Wait for completeStatus from DOM update"); + + is(state.request.completeStatus, "", "Check completeStatus"); + is(state.request.paymentDetails.paymentMethodErrors.cardSecurityCode, + "Your CVV is incorrect", + "Check cardSecurityCode error string in state"); + + ok(!state.changesPrevented, "Changes no longer prevented"); + is(state.page.id, "payment-summary", "Check still on payment-summary"); + + todo(content.document.querySelector("#payment-summary").innerText + .includes("Your CVV is incorrect"), + "Bug 1491815: Check error visibility on summary page"); + todo(content.document.getElementById("pay").disabled, + "Bug 1491815: Pay button should be disabled until the field error is addressed"); + }); + + // Add a handler to retry the payment above. + info("Tell merchant page to retry with a cardSecurityCode error string"); + let retryPromise = ContentTask.spawn(browser, + { + delayMs: 1000, + validationErrors: { + paymentMethod: { + cardSecurityCode: "Your CVV is incorrect", + }, + }, + }, + PTU.ContentTasks.addRetryHandler); + + await retryUpdatePromise; + + info("Changing to a different card to clear the error"); + await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.selectPaymentOptionByGuid, + prefilledGuids.card1GUID); + + info("Tell merchant page to retry with a billing postalCode error string"); + let retryPromise2 = ContentTask.spawn(browser, + { + delayMs: 1000, + validationErrors: { + paymentMethod: { + billingAddress: { + postalCode: "Your postal code isn't valid", + }, + }, + }, + }, + PTU.ContentTasks.addRetryHandler); + + + await loginAndCompletePayment(frame); + + await spawnPaymentDialogTask(frame, async function checkPostalCodeError() { + let { + PaymentTestUtils: PTU, + } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {}); + + let state = await PTU.DialogContentUtils.waitForState(content, ({request}) => { + return request.completeStatus === ""; + }, "Wait for completeStatus from DOM update"); + + is(state.request.completeStatus, "", "Check completeStatus"); + is(state.request.paymentDetails.paymentMethodErrors.billingAddress.postalCode, + "Your postal code isn't valid", + "Check postalCode error string in state"); + ok(!state.changesPrevented, "Changes no longer prevented"); + is(state.page.id, "payment-summary", "Check still on payment-summary"); + + todo(content.document.querySelector("#payment-summary").innerText + .includes("Your postal code isn't valid"), + "Bug 1491815: Check error visibility on summary page"); + todo(content.document.getElementById("pay").disabled, + "Bug 1491815: Pay button should be disabled until the field error is addressed"); + }); + + info("Changing the billingAddress postalCode to be valid without changing selectedPaymentCard"); + + await navigateToAddCardPage(frame, { + addLinkSelector: "payment-method-picker .edit-link", + }); + + await navigateToAddAddressPage(frame, { + addLinkSelector: ".billingAddressRow .edit-link", + initialPageId: "basic-card-page", + addressPageId: "billing-address-page", + }); + + let newPostalCode = "90210"; + await fillInBillingAddressForm(frame, { "postal-code": newPostalCode }); + + await ContentTask.spawn(browser, { + eventName: "paymentmethodchange", + }, PTU.ContentTasks.promisePaymentResponseEvent); + + await submitAddressForm(frame, null, { + isEditing: true, + nextPageId: "basic-card-page", + }); + + await spawnPaymentDialogTask(frame, async function checkErrorsCleared() { + let { + PaymentTestUtils: PTU, + } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {}); + + await PTU.DialogContentUtils.waitForState(content, (state) => { + return state.request.paymentDetails.paymentMethodErrors == null; + }, + "Check no paymentMethod errors are present"); + }); + + await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.clickPrimaryButton); + + await spawnPaymentDialogTask(frame, async function checkErrorsCleared() { + let { + PaymentTestUtils: PTU, + } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {}); + + await PTU.DialogContentUtils.waitForState(content, (state) => { + return state.request.paymentDetails.paymentMethodErrors == null; + }, + "Check no card errors are present after save"); + }); + + // TODO: Add an `await` here after bug 1477113. + ContentTask.spawn(browser, { + eventName: "paymentmethodchange", + }, PTU.ContentTasks.awaitPaymentEventPromise); + + await loginAndCompletePayment(frame); + + // We can only check the retry response after the closing as it only resolves upon complete. + let {retryException} = await retryPromise; + ok(!retryException, "Expect no exception to be thrown when calling retry()"); + + let {retryException2} = await retryPromise2; + ok(!retryException2, "Expect no exception to be thrown when calling retry()"); + + // Add a handler to complete the payment above. + info("acknowledging the completion from the merchant page"); + let result = await ContentTask.spawn(browser, {}, PTU.ContentTasks.addCompletionHandler); + + // Verify response has the expected properties + let expectedDetails = Object.assign({ + "cc-security-code": "123", + }, PTU.BasicCards.JaneMasterCard); + + let expectedBillingAddress = Object.assign({}, PTU.Addresses.TimBL, { + "postal-code": newPostalCode, + }); + + checkPaymentMethodDetailsMatchesCard(result.response.details, expectedDetails, + "Check response payment details"); + checkPaymentAddressMatchesStorageAddress(result.response.details.billingAddress, + expectedBillingAddress, + "Check response billing address"); + + await BrowserTestUtils.waitForCondition(() => win.closed, "dialog should be closed"); + }); +}); diff --git a/browser/components/payments/test/browser/browser_shippingaddresschange_error.js b/browser/components/payments/test/browser/browser_shippingaddresschange_error.js index 0c1b2056d0e9..0ac49a861945 100644 --- a/browser/components/payments/test/browser/browser_shippingaddresschange_error.js +++ b/browser/components/payments/test/browser/browser_shippingaddresschange_error.js @@ -34,7 +34,7 @@ add_task(async function test_show_error_on_addresschange() { info("awaiting the shippingoptionchange event"); await ContentTask.spawn(browser, { eventName: "shippingoptionchange", - }, PTU.ContentTasks.awaitPaymentRequestEventPromise); + }, PTU.ContentTasks.awaitPaymentEventPromise); await spawnPaymentDialogTask(frame, expectedText => { let errorText = content.document.querySelector("header .page-error"); @@ -56,7 +56,7 @@ add_task(async function test_show_error_on_addresschange() { info("awaiting the shippingaddresschange event"); await ContentTask.spawn(browser, { eventName: "shippingaddresschange", - }, PTU.ContentTasks.awaitPaymentRequestEventPromise); + }, PTU.ContentTasks.awaitPaymentEventPromise); await spawnPaymentDialogTask(frame, () => { let errorText = content.document.querySelector("header .page-error"); @@ -101,7 +101,7 @@ add_task(async function test_show_field_specific_error_on_addresschange() { info("awaiting the shippingaddresschange event"); await ContentTask.spawn(browser, { eventName: "shippingaddresschange", - }, PTU.ContentTasks.awaitPaymentRequestEventPromise); + }, PTU.ContentTasks.awaitPaymentEventPromise); await spawnPaymentDialogTask(frame, async () => { let { @@ -160,6 +160,52 @@ add_task(async function test_show_field_specific_error_on_addresschange() { } }); + info("setting up the event handler for a 2nd shippingaddresschange with a different error"); + await ContentTask.spawn(browser, { + eventName: "shippingaddresschange", + details: Object.assign({}, + { + shippingAddressErrors: { + phone: "Invalid phone number", + }, + }, + PTU.Details.noShippingOptions, + PTU.Details.total2USD), + }, PTU.ContentTasks.updateWith); + + await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.clickPrimaryButton); + + await spawnPaymentDialogTask(frame, async function checkForNewErrors() { + let { + PaymentTestUtils: PTU, + } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {}); + + await PTU.DialogContentUtils.waitForState(content, (state) => { + return state.page.id == "payment-summary" && + state.request.paymentDetails.shippingAddressErrors.phone == "Invalid phone number"; + }, "Check the new error is in state"); + + ok(content.document.querySelector("#payment-summary").innerText + .includes("Invalid phone number"), + "Check error visibility on summary page"); + ok(content.document.getElementById("pay").disabled, + "Pay button should be disabled until the field error is addressed"); + }); + + await navigateToAddShippingAddressPage(frame, { + addLinkSelector: "address-picker[selected-state-key=\"selectedShippingAddress\"] .edit-link", + }); + + await spawnPaymentDialogTask(frame, async function checkForNewErrorOnEdit() { + let addressForm = content.document.querySelector("#shipping-address-page"); + is(addressForm.querySelectorAll(".error-text:not(:empty)").length, 1, + "Check one error shown"); + }); + + await fillInShippingAddressForm(frame, { + tel: PTU.Addresses.TimBL2.tel, + }); + info("setup updateWith to clear errors"); await ContentTask.spawn(browser, { eventName: "shippingaddresschange", @@ -168,14 +214,13 @@ add_task(async function test_show_field_specific_error_on_addresschange() { PTU.Details.total2USD), }, PTU.ContentTasks.updateWith); - await spawnPaymentDialogTask(frame, async () => { + await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.clickPrimaryButton); + + await spawnPaymentDialogTask(frame, async function fixLastError() { let { PaymentTestUtils: PTU, } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {}); - info("saving corrections"); - content.document.querySelector("#shipping-address-page .save-button").click(); - await PTU.DialogContentUtils.waitForState(content, (state) => { return state.page.id == "payment-summary"; }, "Check we're back on summary view"); diff --git a/browser/components/payments/test/browser/browser_show_dialog.js b/browser/components/payments/test/browser/browser_show_dialog.js index 24e9225412a7..e2444f308879 100644 --- a/browser/components/payments/test/browser/browser_show_dialog.js +++ b/browser/components/payments/test/browser/browser_show_dialog.js @@ -128,7 +128,7 @@ add_task(async function test_show_completePayment2() { await ContentTask.spawn(browser, { eventName: "shippingoptionchange", - }, PTU.ContentTasks.awaitPaymentRequestEventPromise); + }, PTU.ContentTasks.awaitPaymentEventPromise); info("got shippingoptionchange event"); info("select the shipping address"); diff --git a/browser/components/payments/test/browser/head.js b/browser/components/payments/test/browser/head.js index 845d91e157a1..c02a8a95c7e2 100644 --- a/browser/components/payments/test/browser/head.js +++ b/browser/components/payments/test/browser/head.js @@ -431,7 +431,7 @@ async function navigateToAddAddressPage(frame, aOptions = {}) { async function navigateToAddShippingAddressPage(frame, aOptions = {}) { let options = Object.assign({ - addLinkSelector: "address-picker[selected-state-key=\"selectedShippingAddress\"] a.add-link", + addLinkSelector: "address-picker[selected-state-key=\"selectedShippingAddress\"] .add-link", initialPageId: "payment-summary", addressPageId: "shipping-address-page", }, aOptions); diff --git a/browser/components/places/content/menu.xml b/browser/components/places/content/menu.xml index be83d0bac1a2..c4e3bf0e733a 100644 --- a/browser/components/places/content/menu.xml +++ b/browser/components/places/content/menu.xml @@ -548,7 +548,7 @@ arrowbox.style.transform = "translate(0, " + -offset + "px)"; // The assigned side stays the same regardless of direction. - var isRTL = (window.getComputedStyle(this).direction == "rtl"); + let isRTL = this.matches(":-moz-locale-dir(rtl)"); if (position.indexOf("start_") == 0) { container.dir = "reverse"; diff --git a/browser/components/preferences/in-content/main.xul b/browser/components/preferences/in-content/main.xul index 57290645323d..98c1809b3752 100644 --- a/browser/components/preferences/in-content/main.xul +++ b/browser/components/preferences/in-content/main.xul @@ -91,7 +91,7 @@ onsyncfrompreference="return gMainPane.readLinkTarget();" onsynctopreference="return gMainPane.writeLinkTarget();"/> - - + diff --git a/browser/modules/test/browser/formValidation/.eslinrc.js b/browser/components/resistfingerprinting/test/chrome/.eslintrc.js similarity index 62% rename from browser/modules/test/browser/formValidation/.eslinrc.js rename to browser/components/resistfingerprinting/test/chrome/.eslintrc.js index b1c842d8a6db..0bb0fe72c247 100644 --- a/browser/modules/test/browser/formValidation/.eslinrc.js +++ b/browser/components/resistfingerprinting/test/chrome/.eslintrc.js @@ -2,6 +2,6 @@ module.exports = { "extends": [ - "plugin:mozilla/browser-test" + "plugin:mozilla/chrome-test", ] }; diff --git a/browser/locales/en-US/browser/preferences/preferences.ftl b/browser/locales/en-US/browser/preferences/preferences.ftl index 485ca5cb5d84..6d5a75b14448 100644 --- a/browser/locales/en-US/browser/preferences/preferences.ftl +++ b/browser/locales/en-US/browser/preferences/preferences.ftl @@ -4,8 +4,8 @@ do-not-track-description = Send websites a “Do Not Track” signal that you don’t want to be tracked do-not-track-learn-more = Learn more -do-not-track-option-default-content-blocking = - .label = Only when { -brand-short-name } is set to block Detected Trackers +do-not-track-option-default-content-blocking-known = + .label = Only when { -brand-short-name } is set to block known trackers do-not-track-option-always = .label = Always @@ -178,8 +178,8 @@ open-new-link-as-tabs = .label = Open links in tabs instead of new windows .accesskey = w -warn-on-quit-close-multiple-tabs = - .label = Warn you when quitting and closing multiple tabs +warn-on-close-multiple-tabs = + .label = Warn you when closing multiple tabs .accesskey = m warn-on-open-many-tabs = diff --git a/browser/themes/linux/controlcenter/panel.css b/browser/themes/linux/controlcenter/panel.css index 231caf877bb6..f1cb39e3de8c 100644 --- a/browser/themes/linux/controlcenter/panel.css +++ b/browser/themes/linux/controlcenter/panel.css @@ -18,3 +18,8 @@ .tracking-protection-button > .button-box > .button-icon { margin-inline-end: 5px; } + +/* This overrides color: -moz-buttonhovertext from toolbarbutton.css */ +.identity-popup-content-blocking-category:hover { + color: inherit; +} diff --git a/devtools/client/canvasdebugger/test/call-watcher-front.js b/devtools/client/canvasdebugger/test/call-watcher-front.js index 48fdc314b603..aafb592cc4d4 100644 --- a/devtools/client/canvasdebugger/test/call-watcher-front.js +++ b/devtools/client/canvasdebugger/test/call-watcher-front.js @@ -11,11 +11,10 @@ const protocol = require("devtools/shared/protocol"); /** * The corresponding Front object for the CallWatcherActor. */ -var CallWatcherFront = -exports.CallWatcherFront = -protocol.FrontClassWithSpec(callWatcherSpec, { - initialize: function(client, { callWatcherActor }) { - protocol.Front.prototype.initialize.call(this, client, { actor: callWatcherActor }); +class CallWatcherFront extends protocol.FrontClassWithSpec(callWatcherSpec) { + constructor(client, { callWatcherActor }) { + super(client, { actor: callWatcherActor }); this.manage(this); - }, -}); + } +} +exports.CallWatcherFront = CallWatcherFront; diff --git a/devtools/client/debugger/moz.build b/devtools/client/debugger/moz.build index 30b945738750..87c48db0b915 100644 --- a/devtools/client/debugger/moz.build +++ b/devtools/client/debugger/moz.build @@ -9,7 +9,6 @@ DIRS += [ BROWSER_CHROME_MANIFESTS += [ 'new/test/mochitest/browser.ini', - 'test/mochitest/browser.ini' ] with Files('**'): diff --git a/devtools/client/debugger/test/.eslintrc.js b/devtools/client/debugger/test/.eslintrc.js deleted file mode 100644 index 8d15a76d9b8c..000000000000 --- a/devtools/client/debugger/test/.eslintrc.js +++ /dev/null @@ -1,6 +0,0 @@ -"use strict"; - -module.exports = { - // Extend from the shared list of defined globals for mochitests. - "extends": "../../../.eslintrc.mochitests.js" -}; diff --git a/devtools/client/debugger/test/mochitest/browser.ini b/devtools/client/debugger/test/mochitest/browser.ini deleted file mode 100644 index 82f72294a014..000000000000 --- a/devtools/client/debugger/test/mochitest/browser.ini +++ /dev/null @@ -1,27 +0,0 @@ -[DEFAULT] -tags = devtools -subsuite = devtools -skip-if = (os == 'linux' && debug && bits == 32) -support-files = - doc_promise-get-allocation-stack.html - doc_promise-get-fulfillment-stack.html - doc_promise-get-rejection-stack.html - doc_terminate-on-tab-close.html - head.js - !/devtools/client/shared/test/shared-head.js - !/devtools/client/shared/test/telemetry-test-helpers.js -[browser_dbg_promises-allocation-stack.js] -uses-unsafe-cpows = true -skip-if = true -[browser_dbg_promises-chrome-allocation-stack.js] -uses-unsafe-cpows = true -skip-if = true # Bug 1177730 -[browser_dbg_promises-fulfillment-stack.js] -uses-unsafe-cpows = true -skip-if = true -[browser_dbg_promises-rejection-stack.js] -uses-unsafe-cpows = true -skip-if = true -[browser_dbg_terminate-on-tab-close.js] -uses-unsafe-cpows = true -skip-if = true \ No newline at end of file diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_promises-allocation-stack.js b/devtools/client/debugger/test/mochitest/browser_dbg_promises-allocation-stack.js deleted file mode 100644 index a73679c91652..000000000000 --- a/devtools/client/debugger/test/mochitest/browser_dbg_promises-allocation-stack.js +++ /dev/null @@ -1,87 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -/** - * Test that we can get a stack to a promise's allocation point. - */ - -"use strict"; - -const TAB_URL = EXAMPLE_URL + "doc_promise-get-allocation-stack.html"; -const { PromisesFront } = require("devtools/shared/fronts/promises"); -var EventEmitter = require("devtools/shared/event-emitter"); - -function test() { - Task.spawn(function* () { - DebuggerServer.init(); - DebuggerServer.registerAllActors(); - - let options = { - source: TAB_URL, - line: 1 - }; - const [ tab, panel ] = yield initDebugger(TAB_URL, options); - - let client = new DebuggerClient(DebuggerServer.connectPipe()); - yield connect(client); - - let { tabs } = yield listTabs(client); - let targetTab = findTab(tabs, TAB_URL); - yield attachTarget(client, targetTab); - - yield testGetAllocationStack(client, targetTab, tab); - - yield close(client); - yield closeDebuggerAndFinish(panel); - }).catch(error => { - ok(false, "Got an error: " + error.message + "\n" + error.stack); - }); -} - -function* testGetAllocationStack(client, form, tab) { - let front = PromisesFront(client, form); - - yield front.attach(); - yield front.listPromises(); - - // Get the grip for promise p - let onNewPromise = new Promise(resolve => { - EventEmitter.on(front, "new-promises", promises => { - for (let p of promises) { - if (p.preview.ownProperties.name && - p.preview.ownProperties.name.value === "p") { - resolve(p); - } - } - }); - }); - - callInTab(tab, "makePromises"); - - let grip = yield onNewPromise; - ok(grip, "Found our promise p"); - - let objectClient = new ObjectClient(client, grip); - ok(objectClient, "Got Object Client"); - - yield new Promise(resolve => { - objectClient.getPromiseAllocationStack(response => { - ok(response.allocationStack.length, "Got promise allocation stack."); - - for (let stack of response.allocationStack) { - is(stack.source.url, TAB_URL, "Got correct source URL."); - is(stack.functionDisplayName, "makePromises", - "Got correct function display name."); - is(typeof stack.line, "number", "Expect stack line to be a number."); - is(typeof stack.column, "number", - "Expect stack column to be a number."); - } - - resolve(); - }); - }); - - yield front.detach(); -} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_promises-chrome-allocation-stack.js b/devtools/client/debugger/test/mochitest/browser_dbg_promises-chrome-allocation-stack.js deleted file mode 100644 index 0aada5d37370..000000000000 --- a/devtools/client/debugger/test/mochitest/browser_dbg_promises-chrome-allocation-stack.js +++ /dev/null @@ -1,100 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -/** - * Test that we can get a stack to a promise's allocation point in the chrome - * process. - */ - -"use strict"; - -const SOURCE_URL = "browser_dbg_promises-chrome-allocation-stack.js"; -const PromisesFront = require("devtools/shared/fronts/promises"); -var EventEmitter = require("devtools/shared/event-emitter"); - -const STACK_DATA = [ - { functionDisplayName: "test/ { - let p = new Promise(() => {}); - p.name = "p"; - let q = p.then(); - q.name = "q"; - let r = p.catch(() => {}); - r.name = "r"; - }); - - yield close(client); - finish(); - }).catch(error => { - ok(false, "Got an error: " + error.message + "\n" + error.stack); - }); -} - -function* testGetAllocationStack(client, form, makePromises) { - let front = PromisesFront(client, form); - - yield front.attach(); - yield front.listPromises(); - - // Get the grip for promise p - let onNewPromise = new Promise(resolve => { - EventEmitter.on(front, "new-promises", promises => { - for (let p of promises) { - if (p.preview.ownProperties.name && - p.preview.ownProperties.name.value === "p") { - resolve(p); - } - } - }); - }); - - makePromises(); - - let grip = yield onNewPromise; - ok(grip, "Found our promise p"); - - let objectClient = new ObjectClient(client, grip); - ok(objectClient, "Got Object Client"); - - yield new Promise(resolve => { - objectClient.getPromiseAllocationStack(response => { - ok(response.allocationStack.length, "Got promise allocation stack."); - - for (let i = 0; i < STACK_DATA.length; i++) { - let data = STACK_DATA[i]; - let stack = response.allocationStack[i]; - - ok(stack.source.url.startsWith("chrome:"), "Got a chrome source URL"); - ok(stack.source.url.endsWith(SOURCE_URL), "Got correct source URL."); - is(stack.functionDisplayName, data.functionDisplayName, - "Got correct function display name."); - is(typeof stack.line, "number", "Expect stack line to be a number."); - is(typeof stack.column, "number", - "Expect stack column to be a number."); - } - - resolve(); - }); - }); - - yield front.detach(); -} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_promises-fulfillment-stack.js b/devtools/client/debugger/test/mochitest/browser_dbg_promises-fulfillment-stack.js deleted file mode 100644 index c4f678f5bbca..000000000000 --- a/devtools/client/debugger/test/mochitest/browser_dbg_promises-fulfillment-stack.js +++ /dev/null @@ -1,106 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -/** - * Test that we can get a stack to a promise's fulfillment point. - */ - -"use strict"; - -const TAB_URL = EXAMPLE_URL + "doc_promise-get-fulfillment-stack.html"; -const { PromisesFront } = require("devtools/shared/fronts/promises"); -var EventEmitter = require("devtools/shared/event-emitter"); - -const TEST_DATA = [ - { - functionDisplayName: "returnPromise/<", - line: 19, - column: 37 - }, - { - functionDisplayName: "returnPromise", - line: 19, - column: 14 - }, - { - functionDisplayName: "makePromise", - line: 14, - column: 15 - }, -]; - -function test() { - Task.spawn(function* () { - DebuggerServer.init(); - DebuggerServer.registerAllActors(); - - let options = { - source: TAB_URL, - line: 1 - }; - const [ tab, panel ] = yield initDebugger(TAB_URL, options); - - let client = new DebuggerClient(DebuggerServer.connectPipe()); - yield connect(client); - - let { tabs } = yield listTabs(client); - let targetTab = findTab(tabs, TAB_URL); - yield attachTarget(client, targetTab); - - yield testGetFulfillmentStack(client, targetTab, tab); - - yield close(client); - yield closeDebuggerAndFinish(panel); - }).catch(error => { - ok(false, "Got an error: " + error.message + "\n" + error.stack); - }); -} - -function* testGetFulfillmentStack(client, form, tab) { - let front = PromisesFront(client, form); - - yield front.attach(); - yield front.listPromises(); - - // Get the grip for promise p - let onNewPromise = new Promise(resolve => { - EventEmitter.on(front, "new-promises", promises => { - for (let p of promises) { - if (p.preview.ownProperties.name && - p.preview.ownProperties.name.value === "p") { - resolve(p); - } - } - }); - }); - - callInTab(tab, "makePromise"); - - let grip = yield onNewPromise; - ok(grip, "Found our promise p"); - - let objectClient = new ObjectClient(client, grip); - ok(objectClient, "Got Object Client"); - - yield new Promise(resolve => { - objectClient.getPromiseFulfillmentStack(response => { - ok(response.fulfillmentStack.length, "Got promise allocation stack."); - - for (let i = 0; i < TEST_DATA.length; i++) { - let stack = response.fulfillmentStack[i]; - let data = TEST_DATA[i]; - is(stack.source.url, TAB_URL, "Got correct source URL."); - is(stack.functionDisplayName, data.functionDisplayName, - "Got correct function display name."); - is(stack.line, data.line, "Got correct stack line number."); - is(stack.column, data.column, "Got correct stack column number."); - } - - resolve(); - }); - }); - - yield front.detach(); -} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_promises-rejection-stack.js b/devtools/client/debugger/test/mochitest/browser_dbg_promises-rejection-stack.js deleted file mode 100644 index f4d03f28d1f1..000000000000 --- a/devtools/client/debugger/test/mochitest/browser_dbg_promises-rejection-stack.js +++ /dev/null @@ -1,113 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -/** - * Test that we can get a stack to a promise's rejection point. - */ - -"use strict"; - -const TAB_URL = EXAMPLE_URL + "doc_promise-get-rejection-stack.html"; -const { PromisesFront } = require("devtools/shared/fronts/promises"); -var EventEmitter = require("devtools/shared/event-emitter"); - -// The code in the document above leaves an uncaught rejection. This is only -// reported to the testing framework if the code is loaded in the main process. -if (!gMultiProcessBrowser) { - ChromeUtils.import("resource://testing-common/PromiseTestUtils.jsm", this); - PromiseTestUtils.expectUncaughtRejection(/hello/); -} - -const TEST_DATA = [ - { - functionDisplayName: "returnPromise/<", - line: 19, - column: 47 - }, - { - functionDisplayName: "returnPromise", - line: 19, - column: 14 - }, - { - functionDisplayName: "makePromise", - line: 14, - column: 15 - }, -]; - -function test() { - Task.spawn(function* () { - DebuggerServer.init(); - DebuggerServer.registerAllActors(); - - let options = { - source: TAB_URL, - line: 1 - }; - const [ tab, panel ] = yield initDebugger(TAB_URL, options); - - let client = new DebuggerClient(DebuggerServer.connectPipe()); - yield connect(client); - - let { tabs } = yield listTabs(client); - let targetTab = findTab(tabs, TAB_URL); - yield attachTarget(client, targetTab); - - yield testGetRejectionStack(client, targetTab, tab); - - yield close(client); - yield closeDebuggerAndFinish(panel); - }).catch(error => { - ok(false, "Got an error: " + error.message + "\n" + error.stack); - }); -} - -function* testGetRejectionStack(client, form, tab) { - let front = PromisesFront(client, form); - - yield front.attach(); - yield front.listPromises(); - - // Get the grip for promise p - let onNewPromise = new Promise(resolve => { - EventEmitter.on(front, "new-promises", promises => { - for (let p of promises) { - if (p.preview.ownProperties.name && - p.preview.ownProperties.name.value === "p") { - resolve(p); - } - } - }); - }); - - callInTab(tab, "makePromise"); - - let grip = yield onNewPromise; - ok(grip, "Found our promise p"); - - let objectClient = new ObjectClient(client, grip); - ok(objectClient, "Got Object Client"); - - yield new Promise(resolve => { - objectClient.getPromiseRejectionStack(response => { - ok(response.rejectionStack.length, "Got promise allocation stack."); - - for (let i = 0; i < TEST_DATA.length; i++) { - let stack = response.rejectionStack[i]; - let data = TEST_DATA[i]; - is(stack.source.url, TAB_URL, "Got correct source URL."); - is(stack.functionDisplayName, data.functionDisplayName, - "Got correct function display name."); - is(stack.line, data.line, "Got correct stack line number."); - is(stack.column, data.column, "Got correct stack column number."); - } - - resolve(); - }); - }); - - yield front.detach(); -} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_terminate-on-tab-close.js b/devtools/client/debugger/test/mochitest/browser_dbg_terminate-on-tab-close.js deleted file mode 100644 index 51a1d0d4c1dd..000000000000 --- a/devtools/client/debugger/test/mochitest/browser_dbg_terminate-on-tab-close.js +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -/** - * Tests that debuggee scripts are terminated on tab closure. - */ - -// The following rejection should not be left uncaught. This test has been -// whitelisted until the issue is fixed. -if (!gMultiProcessBrowser) { - ChromeUtils.import("resource://testing-common/PromiseTestUtils.jsm", this); - PromiseTestUtils.expectUncaughtRejection(/error\.message is undefined/); -} - -const TAB_URL = EXAMPLE_URL + "doc_terminate-on-tab-close.html"; - -function test() { - let options = { - source: TAB_URL, - line: 1 - }; - initDebugger(TAB_URL, options).then(([aTab, aPanel]) => { - const gTab = aTab; - const gPanel = aPanel; - const gDebugger = gPanel.panelWin; - - gDebugger.gThreadClient.addOneTimeListener("paused", () => { - resumeDebuggerThenCloseAndFinish(gPanel).then(function () { - ok(true, "should not throw after this point"); - }); - }); - - callInTab(gTab, "debuggerThenThrow"); - }); -} diff --git a/devtools/client/debugger/test/mochitest/doc_terminate-on-tab-close.html b/devtools/client/debugger/test/mochitest/doc_terminate-on-tab-close.html deleted file mode 100644 index 2101b31034db..000000000000 --- a/devtools/client/debugger/test/mochitest/doc_terminate-on-tab-close.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - Debugger test page - - - - - - - diff --git a/devtools/client/debugger/test/mochitest/head.js b/devtools/client/debugger/test/mochitest/head.js deleted file mode 100644 index 6126e24c138a..000000000000 --- a/devtools/client/debugger/test/mochitest/head.js +++ /dev/null @@ -1,1068 +0,0 @@ - /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -// shared-head.js handles imports, constants, and utility functions -Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js", this); - -// Disable logging for faster test runs. Set this pref to true if you want to -// debug a test in your try runs. Both the debugger server and frontend will -// be affected by this pref. -var gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log"); -Services.prefs.setBoolPref("devtools.debugger.log", false); -Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true); - -var { BrowserToolboxProcess } = ChromeUtils.import("resource://devtools/client/framework/ToolboxProcess.jsm", {}); -var { DebuggerServer } = require("devtools/server/main"); -var { ActorRegistry } = require("devtools/server/actors/utils/actor-registry"); -var { DebuggerClient } = require("devtools/shared/client/debugger-client"); -var ObjectClient = require("devtools/shared/client/object-client"); -var { AddonManager } = ChromeUtils.import("resource://gre/modules/AddonManager.jsm", {}); -var EventEmitter = require("devtools/shared/event-emitter"); -var { Toolbox } = require("devtools/client/framework/toolbox"); -var { Task } = require("devtools/shared/task"); - -const chromeRegistry = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIChromeRegistry); - -// Override promise with deprecated-sync-thenables -promise = require("devtools/shared/deprecated-sync-thenables"); - -const EXAMPLE_URL = "http://example.com/browser/devtools/client/debugger/test/mochitest/"; -const FRAME_SCRIPT_URL = "chrome://mochitests/content/browser/devtools/client/shared/test/code_frame-script.js"; -const CHROME_URL = "chrome://mochitests/content/browser/devtools/client/debugger/test/mochitest/"; -const CHROME_URI = Services.io.newURI(CHROME_URL); - -registerCleanupFunction(async function() { - info("finish() was called, cleaning up..."); - Services.prefs.setBoolPref("devtools.debugger.log", gEnableLogging); - Services.prefs.clearUserPref("security.allow_eval_with_system_principal"); - - while (gBrowser && gBrowser.tabs && gBrowser.tabs.length > 1) { - info("Destroying toolbox."); - let target = await TargetFactory.forTab(gBrowser.selectedTab); - await gDevTools.closeToolbox(target); - - info("Removing tab."); - gBrowser.removeCurrentTab(); - } - - // Properly shut down the server to avoid memory leaks. - DebuggerServer.destroy(); - - // Debugger tests use a lot of memory, so force a GC to help fragmentation. - info("Forcing GC/CC after debugger test."); - await new Promise(resolve => { - Cu.forceGC(); - Cu.forceCC(); - Cu.schedulePreciseGC(resolve); - }); -}); - -var testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/")); -testDir = testDir.replace(/\/\//g, "/"); -testDir = testDir.replace("chrome:/mochitest", "chrome://mochitest"); - -function addWindow(aUrl) { - info("Adding window: " + aUrl); - return promise.resolve(getChromeWindow(window.open(aUrl))); -} - -function getChromeWindow(aWindow) { - return aWindow.docShell.rootTreeItem.domWindow; -} - -// Override addTab/removeTab as defined by shared-head, since these have -// an extra window parameter and add a frame script -this.addTab = function addTab(aUrl, aWindow) { - info("Adding tab: " + aUrl); - - let deferred = promise.defer(); - let targetWindow = aWindow || window; - let targetBrowser = targetWindow.gBrowser; - - targetWindow.focus(); - let tab = targetBrowser.selectedTab = BrowserTestUtils.addTab(targetBrowser, aUrl); - let linkedBrowser = tab.linkedBrowser; - - info("Loading frame script with url " + FRAME_SCRIPT_URL + "."); - linkedBrowser.messageManager.loadFrameScript(FRAME_SCRIPT_URL, false); - - BrowserTestUtils.browserLoaded(linkedBrowser) - .then(function () { - info("Tab added and finished loading: " + aUrl); - deferred.resolve(tab); - }); - - return deferred.promise; -}; - -this.removeTab = function removeTab(aTab, aWindow) { - info("Removing tab."); - - let deferred = promise.defer(); - let targetWindow = aWindow || window; - let targetBrowser = targetWindow.gBrowser; - let tabContainer = targetBrowser.tabContainer; - - tabContainer.addEventListener("TabClose", function (aEvent) { - info("Tab removed and finished closing."); - deferred.resolve(); - }, {once: true}); - - targetBrowser.removeTab(aTab); - return deferred.promise; -}; - -function getAddonURIFromPath(aPath) { - let chromeURI = Services.io.newURI(aPath, null, CHROME_URI); - return chromeRegistry.convertChromeURL(chromeURI).QueryInterface(Ci.nsIFileURL); -} - -function getTemporaryAddonURLFromPath(aPath) { - return getAddonURIFromPath(aPath).spec; -} - -function addTemporaryAddon(aPath) { - let addonFile = getAddonURIFromPath(aPath).file; - info("Installing addon: " + addonFile.path); - - return AddonManager.installTemporaryAddon(addonFile); -} - -function removeAddon(aAddon) { - info("Removing addon."); - - let deferred = promise.defer(); - - let listener = { - onUninstalled: function (aUninstalledAddon) { - if (aUninstalledAddon != aAddon) { - return; - } - AddonManager.removeAddonListener(listener); - deferred.resolve(); - } - }; - AddonManager.addAddonListener(listener); - aAddon.uninstall(); - - return deferred.promise; -} - -// Override once from shared-head, as some tests depend on trying native DOM listeners -// before EventEmitter. Since this directory is deprecated, there's little value in -// resolving the descrepency here. -this.once = function (aTarget, aEventName, aUseCapture = false) { - info("Waiting for event: '" + aEventName + "' on " + aTarget + "."); - - let deferred = promise.defer(); - - for (let [add, remove] of [ - ["addEventListener", "removeEventListener"], - ["addListener", "removeListener"], - ["on", "off"] - ]) { - if ((add in aTarget) && (remove in aTarget)) { - aTarget[add](aEventName, function onEvent(...aArgs) { - aTarget[remove](aEventName, onEvent, aUseCapture); - deferred.resolve.apply(deferred, aArgs); - }, aUseCapture); - break; - } - } - - return deferred.promise; -}; - -function waitForTick() { - let deferred = promise.defer(); - executeSoon(deferred.resolve); - return deferred.promise; -} - -function waitForTime(aDelay) { - let deferred = promise.defer(); - setTimeout(deferred.resolve, aDelay); - return deferred.promise; -} - -function waitForSourceLoaded(aPanel, aUrl) { - let { Sources } = aPanel.panelWin.DebuggerView; - let isLoaded = Sources.items.some(item => - item.attachment.source.url === aUrl); - if (isLoaded) { - info("The correct source has been loaded."); - return promise.resolve(null); - } else { - return waitForDebuggerEvents(aPanel, aPanel.panelWin.EVENTS.NEW_SOURCE).then(() => { - // Wait for it to be loaded in the UI and appear into Sources.items. - return waitForTick(); - }).then(() => { - return waitForSourceLoaded(aPanel, aUrl); - }); - } - -} - -function waitForSourceShown(aPanel, aUrl) { - return waitForDebuggerEvents(aPanel, aPanel.panelWin.EVENTS.SOURCE_SHOWN).then(aSource => { - let sourceUrl = aSource.url || aSource.introductionUrl; - info("Source shown: " + sourceUrl); - - if (!sourceUrl.includes(aUrl)) { - return waitForSourceShown(aPanel, aUrl); - } else { - ok(true, "The correct source has been shown."); - } - }); -} - -function waitForEditorLocationSet(aPanel) { - return waitForDebuggerEvents(aPanel, aPanel.panelWin.EVENTS.EDITOR_LOCATION_SET); -} - -function ensureSourceIs(aPanel, aUrlOrSource, aWaitFlag = false) { - let sources = aPanel.panelWin.DebuggerView.Sources; - - if (sources.selectedValue === aUrlOrSource || - (sources.selectedItem && - sources.selectedItem.attachment.source.url.includes(aUrlOrSource))) { - ok(true, "Expected source is shown: " + aUrlOrSource); - return promise.resolve(null); - } - if (aWaitFlag) { - return waitForSourceShown(aPanel, aUrlOrSource); - } - ok(false, "Expected source was not already shown: " + aUrlOrSource); - return promise.reject(null); -} - -function waitForCaretUpdated(aPanel, aLine, aCol = 1) { - return waitForEditorEvents(aPanel, "cursorActivity").then(() => { - let cursor = aPanel.panelWin.DebuggerView.editor.getCursor(); - info("Caret updated: " + (cursor.line + 1) + ", " + (cursor.ch + 1)); - - if (!isCaretPos(aPanel, aLine, aCol)) { - return waitForCaretUpdated(aPanel, aLine, aCol); - } else { - ok(true, "The correct caret position has been set."); - } - }); -} - -function ensureCaretAt(aPanel, aLine, aCol = 1, aWaitFlag = false) { - if (isCaretPos(aPanel, aLine, aCol)) { - ok(true, "Expected caret position is set: " + aLine + "," + aCol); - return promise.resolve(null); - } - if (aWaitFlag) { - return waitForCaretUpdated(aPanel, aLine, aCol); - } - ok(false, "Expected caret position was not already set: " + aLine + "," + aCol); - return promise.reject(null); -} - -function isCaretPos(aPanel, aLine, aCol = 1) { - let editor = aPanel.panelWin.DebuggerView.editor; - let cursor = editor.getCursor(); - - // Source editor starts counting line and column numbers from 0. - info("Current editor caret position: " + (cursor.line + 1) + ", " + (cursor.ch + 1)); - return cursor.line == (aLine - 1) && cursor.ch == (aCol - 1); -} - -function isDebugPos(aPanel, aLine) { - let editor = aPanel.panelWin.DebuggerView.editor; - let location = editor.getDebugLocation(); - - // Source editor starts counting line and column numbers from 0. - info("Current editor debug position: " + (location + 1)); - return location != null && editor.hasLineClass(aLine - 1, "debug-line"); -} - -function isEditorSel(aPanel, [start, end]) { - let editor = aPanel.panelWin.DebuggerView.editor; - let range = { - start: editor.getOffset(editor.getCursor("start")), - end: editor.getOffset(editor.getCursor()) - }; - - // Source editor starts counting line and column numbers from 0. - info("Current editor selection: " + (range.start + 1) + ", " + (range.end + 1)); - return range.start == (start - 1) && range.end == (end - 1); -} - -function waitForSourceAndCaret(aPanel, aUrl, aLine, aCol) { - return promise.all([ - waitForSourceShown(aPanel, aUrl), - waitForCaretUpdated(aPanel, aLine, aCol) - ]); -} - -function waitForCaretAndScopes(aPanel, aLine, aCol) { - return promise.all([ - waitForCaretUpdated(aPanel, aLine, aCol), - waitForDebuggerEvents(aPanel, aPanel.panelWin.EVENTS.FETCHED_SCOPES) - ]); -} - -function waitForSourceAndCaretAndScopes(aPanel, aUrl, aLine, aCol) { - return promise.all([ - waitForSourceAndCaret(aPanel, aUrl, aLine, aCol), - waitForDebuggerEvents(aPanel, aPanel.panelWin.EVENTS.FETCHED_SCOPES) - ]); -} - -function waitForDebuggerEvents(aPanel, aEventName, aEventRepeat = 1) { - info("Waiting for debugger event: '" + aEventName + "' to fire: " + aEventRepeat + " time(s)."); - - let deferred = promise.defer(); - let panelWin = aPanel.panelWin; - let count = 0; - - panelWin.on(aEventName, function onEvent(...aArgs) { - info("Debugger event '" + aEventName + "' fired: " + (++count) + " time(s)."); - - if (count == aEventRepeat) { - ok(true, "Enough '" + aEventName + "' panel events have been fired."); - panelWin.off(aEventName, onEvent); - deferred.resolve.apply(deferred, aArgs); - } - }); - - return deferred.promise; -} - -function waitForEditorEvents(aPanel, aEventName, aEventRepeat = 1) { - info("Waiting for editor event: '" + aEventName + "' to fire: " + aEventRepeat + " time(s)."); - - let deferred = promise.defer(); - let editor = aPanel.panelWin.DebuggerView.editor; - let count = 0; - - editor.on(aEventName, function onEvent(...aArgs) { - info("Editor event '" + aEventName + "' fired: " + (++count) + " time(s)."); - - if (count == aEventRepeat) { - ok(true, "Enough '" + aEventName + "' editor events have been fired."); - editor.off(aEventName, onEvent); - deferred.resolve.apply(deferred, aArgs); - } - }); - - return deferred.promise; -} - -function waitForThreadEvents(aPanel, aEventName, aEventRepeat = 1) { - info("Waiting for thread event: '" + aEventName + "' to fire: " + aEventRepeat + " time(s)."); - - let deferred = promise.defer(); - let thread = aPanel.panelWin.gThreadClient; - let count = 0; - - thread.addListener(aEventName, function onEvent(aEventName, ...aArgs) { - info("Thread event '" + aEventName + "' fired: " + (++count) + " time(s)."); - - if (count == aEventRepeat) { - ok(true, "Enough '" + aEventName + "' thread events have been fired."); - thread.removeListener(aEventName, onEvent); - deferred.resolve.apply(deferred, aArgs); - } - }); - - return deferred.promise; -} - -function waitForClientEvents(aPanel, aEventName, aEventRepeat = 1) { - info("Waiting for client event: '" + aEventName + "' to fire: " + aEventRepeat + " time(s)."); - - let deferred = promise.defer(); - let client = aPanel.panelWin.gClient; - let count = 0; - - client.addListener(aEventName, function onEvent(aEventName, ...aArgs) { - info("Thread event '" + aEventName + "' fired: " + (++count) + " time(s)."); - - if (count == aEventRepeat) { - ok(true, "Enough '" + aEventName + "' thread events have been fired."); - client.removeListener(aEventName, onEvent); - deferred.resolve.apply(deferred, aArgs); - } - }); - - return deferred.promise; -} - -function ensureThreadClientState(aPanel, aState) { - let thread = aPanel.panelWin.gThreadClient; - let state = thread.state; - - info("Thread is: '" + state + "'."); - - if (state == aState) { - return promise.resolve(null); - } else { - return waitForThreadEvents(aPanel, aState); - } -} - -function reload(aPanel, aUrl) { - let activeTab = aPanel.panelWin.DebuggerController._target.activeTab; - aUrl ? activeTab.navigateTo({ url: aUrl }) : activeTab.reload(); -} - -function navigateActiveTabTo(aPanel, aUrl, aWaitForEventName, aEventRepeat) { - let finished = waitForDebuggerEvents(aPanel, aWaitForEventName, aEventRepeat); - reload(aPanel, aUrl); - return finished; -} - -function navigateActiveTabInHistory(aPanel, aDirection, aWaitForEventName, aEventRepeat) { - let finished = waitForDebuggerEvents(aPanel, aWaitForEventName, aEventRepeat); - content.history[aDirection](); - return finished; -} - -function reloadActiveTab(aPanel, aWaitForEventName, aEventRepeat) { - return navigateActiveTabTo(aPanel, null, aWaitForEventName, aEventRepeat); -} - -function clearText(aElement) { - info("Clearing text..."); - aElement.focus(); - aElement.value = ""; -} - -function setText(aElement, aText) { - clearText(aElement); - info("Setting text: " + aText); - aElement.value = aText; -} - -function typeText(aElement, aText) { - info("Typing text: " + aText); - aElement.focus(); - EventUtils.sendString(aText, aElement.ownerDocument.defaultView); -} - -function backspaceText(aElement, aTimes) { - info("Pressing backspace " + aTimes + " times."); - for (let i = 0; i < aTimes; i++) { - aElement.focus(); - EventUtils.sendKey("BACK_SPACE", aElement.ownerDocument.defaultView); - } -} - -function getTab(aTarget, aWindow) { - if (aTarget instanceof XULElement) { - return promise.resolve(aTarget); - } else { - return addTab(aTarget, aWindow); - } -} - -function getSources(aClient) { - info("Getting sources."); - - let deferred = promise.defer(); - - aClient.getSources((packet) => { - deferred.resolve(packet.sources); - }); - - return deferred.promise; -} - -/** - * Optionaly open a new tab and then open the debugger panel. - * The returned promise resolves only one the panel is fully set. - - * @param {String|xul:tab} urlOrTab - * If a string, consider it as the url of the tab to open before opening the - * debugger panel. - * Otherwise, if a , do nothing, but open the debugger panel against - * the given tab. - * @param {Object} options - * Set of optional arguments: - * - {String} source - * If given, assert the default loaded source once the debugger is loaded. - * This string can be partial to only match a part of the source name. - * If null, do not expect any source and skip SOURCE_SHOWN wait. - * - {Number} line - * If given, wait for the caret to be set on a precise line - * - * @return {Promise} - * Resolves once debugger panel is fully set according to the given options. - */ -let initDebugger = Task.async(function*(urlOrTab, options) { - let { window, source, line } = options || {}; - info("Initializing a debugger panel."); - - let tab, url; - if (urlOrTab instanceof XULElement) { - // `urlOrTab` Is a Tab. - tab = urlOrTab; - } else { - // `urlOrTab` is an url. Open an empty tab first in order to load the page - // only once the panel is ready. That to be able to safely catch the - // SOURCE_SHOWN event. - tab = yield addTab("about:blank", window); - url = urlOrTab; - } - info("Debugee tab added successfully: " + urlOrTab); - - let target = yield TargetFactory.forTab(tab); - - let toolbox = yield gDevTools.showToolbox(target, "jsdebugger"); - info("Debugger panel shown successfully."); - - let debuggerPanel = toolbox.getCurrentPanel(); - let panelWin = debuggerPanel.panelWin; - let { Sources } = panelWin.DebuggerView; - - prepareDebugger(debuggerPanel); - - if (url && url != "about:blank") { - let onCaretUpdated; - if (line) { - onCaretUpdated = waitForCaretUpdated(debuggerPanel, line); - } - if (source === null) { - // When there is no source in the document, we shouldn't wait for - // SOURCE_SHOWN event - yield reload(debuggerPanel, url); - } else { - yield navigateActiveTabTo(debuggerPanel, - url, - panelWin.EVENTS.SOURCE_SHOWN); - } - if (source) { - let isSelected = Sources.selectedItem.attachment.source.url === source; - if (!isSelected) { - // Ensure that the source is loaded first before trying to select it - yield waitForSourceLoaded(debuggerPanel, source); - // Select the js file. - let onSource = waitForSourceAndCaret(debuggerPanel, source, line ? line : 1); - Sources.selectedValue = getSourceActor(Sources, source); - yield onSource; - } - } - yield onCaretUpdated; - } - - return [tab, debuggerPanel, window]; -}); - -function initChromeDebugger(aOnClose) { - info("Initializing a chrome debugger process."); - - let deferred = promise.defer(); - - // Wait for the toolbox process to start... - BrowserToolboxProcess.init(aOnClose, aProcess => { - info("Browser toolbox process started successfully."); - - prepareDebugger(aProcess); - deferred.resolve(aProcess); - }); - - return deferred.promise; -} - -function prepareDebugger(aDebugger) { - if ("target" in aDebugger) { - let view = aDebugger.panelWin.DebuggerView; - view.Variables.lazyEmpty = false; - view.Variables.lazySearch = false; - view.Filtering.FilteredSources._autoSelectFirstItem = true; - view.Filtering.FilteredFunctions._autoSelectFirstItem = true; - } else { - // Nothing to do here yet. - } -} - -function teardown(aPanel, aFlags = {}) { - info("Destroying the specified debugger."); - - let toolbox = aPanel._toolbox; - let tab = aPanel.target.tab; - let debuggerRootActorDisconnected = once(window, "Debugger:Shutdown"); - let debuggerPanelDestroyed = once(aPanel, "destroyed"); - let devtoolsToolboxDestroyed = toolbox.destroy(); - - return promise.all([ - debuggerRootActorDisconnected, - debuggerPanelDestroyed, - devtoolsToolboxDestroyed - ]).then(() => aFlags.noTabRemoval ? null : removeTab(tab)); -} - -function closeDebuggerAndFinish(aPanel, aFlags = {}) { - let thread = aPanel.panelWin.gThreadClient; - if (thread.state == "paused" && !aFlags.whilePaused) { - ok(false, "You should use 'resumeDebuggerThenCloseAndFinish' instead, " + - "unless you're absolutely sure about what you're doing."); - } - return teardown(aPanel, aFlags).then(finish); -} - -function resumeDebuggerThenCloseAndFinish(aPanel, aFlags = {}) { - let deferred = promise.defer(); - let thread = aPanel.panelWin.gThreadClient; - thread.resume(() => closeDebuggerAndFinish(aPanel, aFlags).then(deferred.resolve)); - return deferred.promise; -} - -// Blackboxing helpers - -function getBlackBoxButton(aPanel) { - return aPanel.panelWin.document.getElementById("black-box"); -} - -/** - * Returns the node that has the black-boxed class applied to it. - */ -function getSelectedSourceElement(aPanel) { - return aPanel.panelWin.DebuggerView.Sources.selectedItem.prebuiltNode; -} - -function toggleBlackBoxing(aPanel, aSourceActor = null) { - function clickBlackBoxButton() { - getBlackBoxButton(aPanel).click(); - } - - const blackBoxChanged = waitForDispatch( - aPanel, - aPanel.panelWin.constants.BLACKBOX - ).then(() => { - return aSourceActor ? - getSource(aPanel, aSourceActor) : - getSelectedSource(aPanel); - }); - - if (aSourceActor) { - aPanel.panelWin.DebuggerView.Sources.selectedValue = aSourceActor; - ensureSourceIs(aPanel, aSourceActor, true).then(clickBlackBoxButton); - } else { - clickBlackBoxButton(); - } - - return blackBoxChanged; -} - -function selectSourceAndGetBlackBoxButton(aPanel, aUrl) { - function returnBlackboxButton() { - return getBlackBoxButton(aPanel); - } - - let sources = aPanel.panelWin.DebuggerView.Sources; - sources.selectedValue = getSourceActor(sources, aUrl); - return ensureSourceIs(aPanel, aUrl, true).then(returnBlackboxButton); -} - -// Variables view inspection popup helpers - -function openVarPopup(aPanel, aCoords, aWaitForFetchedProperties) { - let events = aPanel.panelWin.EVENTS; - let editor = aPanel.panelWin.DebuggerView.editor; - let bubble = aPanel.panelWin.DebuggerView.VariableBubble; - let tooltip = bubble._tooltip.panel; - - let popupShown = once(tooltip, "popupshown"); - let fetchedProperties = aWaitForFetchedProperties - ? waitForDebuggerEvents(aPanel, events.FETCHED_BUBBLE_PROPERTIES) - : promise.resolve(null); - let updatedFrame = waitForDebuggerEvents(aPanel, events.FETCHED_SCOPES); - - let { left, top } = editor.getCoordsFromPosition(aCoords); - bubble._findIdentifier(left, top); - return promise.all([popupShown, fetchedProperties, updatedFrame]).then(waitForTick); -} - -// Simulates the mouse hovering a variable in the debugger -// Takes in account the position of the cursor in the text, if the text is -// selected and if a button is currently pushed (aButtonPushed > 0). -// The function returns a promise which returns true if the popup opened or -// false if it didn't -function intendOpenVarPopup(aPanel, aPosition, aButtonPushed) { - let bubble = aPanel.panelWin.DebuggerView.VariableBubble; - let editor = aPanel.panelWin.DebuggerView.editor; - let tooltip = bubble._tooltip; - - let { left, top } = editor.getCoordsFromPosition(aPosition); - - const eventDescriptor = { - clientX: left, - clientY: top, - buttons: aButtonPushed - }; - - bubble._onMouseMove(eventDescriptor); - - const deferred = promise.defer(); - window.setTimeout( - function () { - if (tooltip.isEmpty()) { - deferred.resolve(false); - } else { - deferred.resolve(true); - } - }, - bubble.TOOLTIP_SHOW_DELAY + 1000 - ); - - return deferred.promise; -} - -function hideVarPopup(aPanel) { - let bubble = aPanel.panelWin.DebuggerView.VariableBubble; - let tooltip = bubble._tooltip.panel; - - let popupHiding = once(tooltip, "popuphiding"); - bubble.hideContents(); - return popupHiding.then(waitForTick); -} - -function hideVarPopupByScrollingEditor(aPanel) { - let editor = aPanel.panelWin.DebuggerView.editor; - let bubble = aPanel.panelWin.DebuggerView.VariableBubble; - let tooltip = bubble._tooltip.panel; - - let popupHiding = once(tooltip, "popuphiding"); - editor.setFirstVisibleLine(0); - return popupHiding.then(waitForTick); -} - -function reopenVarPopup(...aArgs) { - return hideVarPopup.apply(this, aArgs).then(() => openVarPopup.apply(this, aArgs)); -} - -function doResume(aPanel) { - const threadClient = aPanel.panelWin.gThreadClient; - return threadClient.resume(); -} - -function doInterrupt(aPanel) { - const threadClient = aPanel.panelWin.gThreadClient; - return threadClient.interrupt(); -} - -function pushPrefs(...aPrefs) { - let deferred = promise.defer(); - SpecialPowers.pushPrefEnv({"set": aPrefs}, deferred.resolve); - return deferred.promise; -} - -function popPrefs() { - let deferred = promise.defer(); - SpecialPowers.popPrefEnv(deferred.resolve); - return deferred.promise; -} - -// Source helpers - -function getSelectedSource(panel) { - const win = panel.panelWin; - return win.queries.getSelectedSource(win.DebuggerController.getState()); -} - -function getSource(panel, actor) { - const win = panel.panelWin; - return win.queries.getSource(win.DebuggerController.getState(), actor); -} - -function getSelectedSourceURL(aSources) { - return (aSources.selectedItem && - aSources.selectedItem.attachment.source.url); -} - -function getSourceURL(aSources, aActor) { - let item = aSources.getItemByValue(aActor); - return item && item.attachment.source.url; -} - -function getSourceActor(aSources, aURL) { - let item = aSources.getItemForAttachment(a => a.source && a.source.url === aURL); - return item && item.value; -} - -function getSourceForm(aSources, aURL) { - let item = aSources.getItemByValue(getSourceActor(aSources, aURL)); - return item.attachment.source; -} - -var nextId = 0; - -function jsonrpc(tab, method, params) { - return new Promise(function (resolve, reject) { - let currentId = nextId++; - let messageManager = tab.linkedBrowser.messageManager; - messageManager.sendAsyncMessage("jsonrpc", { - method: method, - params: params, - id: currentId - }); - messageManager.addMessageListener("jsonrpc", function listener(res) { - const { data: { result, error, id } } = res; - if (id !== currentId) { - return; - } - - messageManager.removeMessageListener("jsonrpc", listener); - if (error != null) { - reject(error); - } - - resolve(result); - }); - }); -} - -function callInTab(tab, name) { - info("Calling function with name '" + name + "' in tab."); - - return jsonrpc(tab, "call", [name, Array.prototype.slice.call(arguments, 2)]); -} - -function evalInTab(tab, string) { - info("Evalling string in tab."); - - return jsonrpc(tab, "_eval", [string]); -} - -function createWorkerInTab(tab, url) { - info("Creating worker with url '" + url + "' in tab."); - - return jsonrpc(tab, "createWorker", [url]); -} - -function terminateWorkerInTab(tab, url) { - info("Terminating worker with url '" + url + "' in tab."); - - return jsonrpc(tab, "terminateWorker", [url]); -} - -function postMessageToWorkerInTab(tab, url, message) { - info("Posting message to worker with url '" + url + "' in tab."); - - return jsonrpc(tab, "postMessageToWorker", [url, message]); -} - -function generateMouseClickInTab(tab, path) { - info("Generating mouse click in tab."); - - return jsonrpc(tab, "generateMouseClick", [path]); -} - -function connect(client) { - info("Connecting client."); - return client.connect(); -} - -function close(client) { - info("Waiting for client to close.\n"); - return client.close(); -} - -function listTabs(client) { - info("Listing tabs."); - return client.listTabs(); -} - -function findTab(tabs, url) { - info("Finding tab with url '" + url + "'."); - for (let tab of tabs) { - if (tab.url === url) { - return tab; - } - } - return null; -} - -function attachTarget(client, tab) { - info("Attaching to tab with url '" + tab.url + "'."); - return client.attachTarget(tab); -} - -function listWorkers(targetFront) { - info("Listing workers."); - return targetFront.listWorkers(); -} - -function findWorker(workers, url) { - info("Finding worker with url '" + url + "'."); - for (let worker of workers) { - if (worker.url === url) { - return worker; - } - } - return null; -} - -function waitForWorkerListChanged(targetFront) { - info("Waiting for worker list to change."); - return targetFront.once("workerListChanged"); -} - -function attachThread(workerTargetFront, options) { - info("Attaching to thread."); - return workerTargetFront.attachThread(options); -} - -async function waitForWorkerClose(workerTargetFront) { - info("Waiting for worker to close."); - await workerTargetFront.once("close"); - info("Worker did close."); -} - -function resume(threadClient) { - info("Resuming thread."); - return threadClient.resume(); -} - -function findSource(sources, url) { - info("Finding source with url '" + url + "'.\n"); - for (let source of sources) { - if (source.url === url) { - return source; - } - } - return null; -} - -function waitForEvent(client, type, predicate) { - return new Promise(function (resolve) { - function listener(type, packet) { - if (!predicate(packet)) { - return; - } - client.removeListener(listener); - resolve(packet); - } - - if (predicate) { - client.addListener(type, listener); - } else { - client.addOneTimeListener(type, function (type, packet) { - resolve(packet); - }); - } - }); -} - -function waitForPause(threadClient) { - info("Waiting for pause.\n"); - return waitForEvent(threadClient, "paused"); -} - -function setBreakpoint(sourceClient, location) { - info("Setting breakpoint.\n"); - return sourceClient.setBreakpoint(location); -} - -function source(sourceClient) { - info("Getting source.\n"); - return sourceClient.source(); -} - -// Return a promise with a reference to jsterm, opening the split -// console if necessary. This cleans up the split console pref so -// it won't pollute other tests. -function getSplitConsole(toolbox, win) { - if (!win) { - win = toolbox.win; - } - - if (!toolbox.splitConsole) { - EventUtils.synthesizeKey("VK_ESCAPE", {}, win); - } - - return new Promise(resolve => { - toolbox.getPanelWhenReady("webconsole").then(() => { - ok(toolbox.splitConsole, "Split console is shown."); - let jsterm = toolbox.getPanel("webconsole").hud.jsterm; - resolve(jsterm); - }); - }); -} - -// navigation - -function waitForNavigation(gPanel) { - const target = gPanel.panelWin.gTarget; - const deferred = promise.defer(); - target.once("navigate", () => { - deferred.resolve(); - }); - info("Waiting for navigation..."); - return deferred.promise; -} - -// actions - -function bindActionCreators(panel) { - const win = panel.panelWin; - const dispatch = win.DebuggerController.dispatch; - const { bindActionCreators } = win.require("devtools/client/shared/vendor/redux"); - return bindActionCreators(win.actions, dispatch); -} - -// Wait until an action of `type` is dispatched. This is different -// then `_afterDispatchDone` because it doesn't wait for async actions -// to be done/errored. Use this if you want to listen for the "start" -// action of an async operation (somewhat rare). -function waitForNextDispatch(store, type) { - return new Promise(resolve => { - store.dispatch({ - // Normally we would use `services.WAIT_UNTIL`, but use the - // internal name here so tests aren't forced to always pass it - // in - type: "@@service/waitUntil", - predicate: action => action.type === type, - run: (dispatch, getState, action) => { - resolve(action); - } - }); - }); -} - -// Wait until an action of `type` is dispatched. If it's part of an -// async operation, wait until the `status` field is "done" or "error" -function _afterDispatchDone(store, type) { - return new Promise(resolve => { - store.dispatch({ - // Normally we would use `services.WAIT_UNTIL`, but use the - // internal name here so tests aren't forced to always pass it - // in - type: "@@service/waitUntil", - predicate: action => { - if (action.type === type) { - return action.status ? - (action.status === "done" || action.status === "error") : - true; - } - }, - run: (dispatch, getState, action) => { - resolve(action); - } - }); - }); -} - -function waitForDispatch(panel, type, eventRepeat = 1) { - const controller = panel.panelWin.DebuggerController; - const actionType = panel.panelWin.constants[type]; - let count = 0; - - return Task.spawn(function* () { - info("Waiting for " + type + " to dispatch " + eventRepeat + " time(s)"); - while (count < eventRepeat) { - yield _afterDispatchDone(controller, actionType); - count++; - info(type + " dispatched " + count + " time(s)"); - } - }); -} diff --git a/devtools/client/responsive.html/manager.js b/devtools/client/responsive.html/manager.js index bda038973343..b6cf937d0827 100644 --- a/devtools/client/responsive.html/manager.js +++ b/devtools/client/responsive.html/manager.js @@ -473,7 +473,7 @@ ResponsiveUI.prototype = { this.client = new DebuggerClient(DebuggerServer.connectPipe()); await this.client.connect(); const { tab } = await this.client.getTab(); - this.emulationFront = EmulationFront(this.client, tab); + this.emulationFront = new EmulationFront(this.client, tab); }, /** diff --git a/devtools/client/shared/test/test-actor.js b/devtools/client/shared/test/test-actor.js index 72f69626c110..c91b86d3af0a 100644 --- a/devtools/client/shared/test/test-actor.js +++ b/devtools/client/shared/test/test-actor.js @@ -791,12 +791,13 @@ var TestActor = exports.TestActor = protocol.ActorClassWithSpec(testSpec, { }, }); -var TestActorFront = exports.TestActorFront = protocol.FrontClassWithSpec(testSpec, { - initialize: function(client, { testActor }, toolbox) { - protocol.Front.prototype.initialize.call(this, client, { actor: testActor }); +class TestActorFront extends protocol.FrontClassWithSpec(testSpec) { + constructor(client, { testActor }, toolbox) { + super(client, { actor: testActor }); + this.manage(this); this.toolbox = toolbox; - }, + } /** * Zoom the current page to a given level. @@ -805,19 +806,17 @@ var TestActorFront = exports.TestActorFront = protocol.FrontClassWithSpec(testSp * @return {Promise} The returned promise will only resolve when the * highlighter has updated to the new zoom level. */ - zoomPageTo: function(level, actorID = this.toolbox.highlighter.actorID) { + zoomPageTo(level, actorID = this.toolbox.highlighter.actorID) { return this.changeZoomLevel(level, actorID); - }, + } /* eslint-disable max-len */ - changeHighlightedNodeWaitForUpdate: protocol.custom(function(name, value, highlighter) { + changeHighlightedNodeWaitForUpdate(name, value, highlighter) { /* eslint-enable max-len */ - return this._changeHighlightedNodeWaitForUpdate( + return super.changeHighlightedNodeWaitForUpdate( name, value, (highlighter || this.toolbox.highlighter).actorID ); - }, { - impl: "_changeHighlightedNodeWaitForUpdate", - }), + } /** * Get the value of an attribute on one of the highlighter's node. @@ -826,27 +825,25 @@ var TestActorFront = exports.TestActorFront = protocol.FrontClassWithSpec(testSp * @param {Object} highlighter Optional custom highlither to target * @return {String} value */ - getHighlighterNodeAttribute: function(nodeID, name, highlighter) { + getHighlighterNodeAttribute(nodeID, name, highlighter) { return this.getHighlighterAttribute( nodeID, name, (highlighter || this.toolbox.highlighter).actorID ); - }, + } - getHighlighterNodeTextContent: protocol.custom(function(nodeID, highlighter) { - return this._getHighlighterNodeTextContent( + getHighlighterNodeTextContent(nodeID, highlighter) { + return super.getHighlighterNodeTextContent( nodeID, (highlighter || this.toolbox.highlighter).actorID ); - }, { - impl: "_getHighlighterNodeTextContent", - }), + } /** * Is the highlighter currently visible on the page? */ - isHighlighting: function() { + isHighlighting() { return this.getHighlighterNodeAttribute("box-model-elements", "hidden") .then(value => value === null); - }, + } /** * Assert that the box-model highlighter's current position corresponds to the @@ -871,7 +868,7 @@ var TestActorFront = exports.TestActorFront = protocol.FrontClassWithSpec(testSp prefix + boxType + " point " + point + " y coordinate is correct"); } } - }, + } /** * Get the current rect of the border region of the box-model highlighter @@ -886,7 +883,7 @@ var TestActorFront = exports.TestActorFront = protocol.FrontClassWithSpec(testSp width: p2.x - p1.x, height: p4.y - p1.y, }; - }, + } /** * Get the current positions and visibility of the various box-model highlighter @@ -911,7 +908,7 @@ var TestActorFront = exports.TestActorFront = protocol.FrontClassWithSpec(testSp } return ret; - }, + } /** * Check that the box-model highlighter is currently highlighting the node matching the @@ -922,7 +919,7 @@ var TestActorFront = exports.TestActorFront = protocol.FrontClassWithSpec(testSp async assertHighlightedNode(selector) { const rect = await this.getNodeRect(selector); return this.isNodeRectHighlighted(rect); - }, + } /** * Check that the box-model highlighter is currently highlighting the text node that can @@ -935,7 +932,7 @@ var TestActorFront = exports.TestActorFront = protocol.FrontClassWithSpec(testSp async assertHighlightedTextNode(parentSelector, childNodeIndex) { const rect = await this.getTextNodeRect(parentSelector, childNodeIndex); return this.isNodeRectHighlighted(rect); - }, + } /** * Check that the box-model highlighter is currently highlighting the given rect. @@ -966,7 +963,7 @@ var TestActorFront = exports.TestActorFront = protocol.FrontClassWithSpec(testSp isInside([right, top], points) && isInside([right, bottom], points) && isInside([left, bottom], points); - }, + } /** * Get the coordinate (points attribute) from one of the polygon elements in the @@ -1002,7 +999,7 @@ var TestActorFront = exports.TestActorFront = protocol.FrontClassWithSpec(testSp y: parseFloat(points[3][1]), }, }; - }, + } /** * Is a given region polygon element of the box-model highlighter currently @@ -1011,7 +1008,7 @@ var TestActorFront = exports.TestActorFront = protocol.FrontClassWithSpec(testSp async _isRegionHidden(region) { const value = await this.getHighlighterNodeAttribute("box-model-" + region, "hidden"); return value !== null; - }, + } async _getGuideStatus(location) { const id = "box-model-guide-" + location; @@ -1029,7 +1026,7 @@ var TestActorFront = exports.TestActorFront = protocol.FrontClassWithSpec(testSp x2: x2, y2: y2, }; - }, + } /** * Get the coordinates of the rectangle that is defined by the 4 guides displayed @@ -1053,13 +1050,11 @@ var TestActorFront = exports.TestActorFront = protocol.FrontClassWithSpec(testSp p3: {x: +rGuide.x1 + 1, y: +bGuide.y1 + 1}, p4: {x: lGuide.x1, y: +bGuide.y1 + 1}, }; - }, + } - waitForHighlighterEvent: protocol.custom(function(event) { - return this._waitForHighlighterEvent(event, this.toolbox.highlighter.actorID); - }, { - impl: "_waitForHighlighterEvent", - }), + waitForHighlighterEvent(event) { + return super.waitForHighlighterEvent(event, this.toolbox.highlighter.actorID); + } /** * Get the "d" attribute value for one of the box-model highlighter's region @@ -1092,8 +1087,9 @@ var TestActorFront = exports.TestActorFront = protocol.FrontClassWithSpec(testSp } return {d, points}; - }, -}); + } +} +exports.TestActorFront = TestActorFront; /** * Check whether a point is included in a polygon. diff --git a/devtools/docs/backend/protocol.js.md b/devtools/docs/backend/protocol.js.md index b5175374b9da..857c127ce43c 100644 --- a/devtools/docs/backend/protocol.js.md +++ b/devtools/docs/backend/protocol.js.md @@ -97,7 +97,7 @@ How do you get an initial reference to the front? That's a bit tricky, but basi Manually - If you're using a DebuggerClient instance, you can discover the actorID manually and create a Front for it: - let hello = HelloFront(this.client, { actor: }); + let hello = new HelloFront(this.client, { actor: }); Magically - Once you have an initial reference to a protocol.js object, it can return other protocol.js objects and fronts will automatically be created. diff --git a/devtools/server/tests/browser/browser.ini b/devtools/server/tests/browser/browser.ini index d9988c6bfeea..fdf8769a8027 100644 --- a/devtools/server/tests/browser/browser.ini +++ b/devtools/server/tests/browser/browser.ini @@ -11,6 +11,9 @@ support-files = doc_force_gc.html doc_innerHTML.html doc_perf.html + doc_promise-get-allocation-stack.html + doc_promise-get-fulfillment-stack.html + doc_promise-get-rejection-stack.html error-actor.js grid.html inspectedwindow-reload-target.sjs @@ -102,3 +105,7 @@ skip-if = (verify && debug && (os == 'mac' || os == 'linux')) [browser_stylesheets_nested-iframes.js] [browser_register_actor.js] [browser_webextension_inspected_window.js] +[browser_dbg_promises-allocation-stack.js] +[browser_dbg_promises-chrome-allocation-stack.js] +[browser_dbg_promises-fulfillment-stack.js] +[browser_dbg_promises-rejection-stack.js] diff --git a/devtools/server/tests/browser/browser_dbg_promises-allocation-stack.js b/devtools/server/tests/browser/browser_dbg_promises-allocation-stack.js new file mode 100644 index 000000000000..20aa5c5eff9e --- /dev/null +++ b/devtools/server/tests/browser/browser_dbg_promises-allocation-stack.js @@ -0,0 +1,68 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Test that we can get a stack to a promise's allocation point. + */ + +"use strict"; + +const TAB_URL = URL_ROOT + "doc_promise-get-allocation-stack.html"; + +const ObjectClient = require("devtools/shared/client/object-client"); + +add_task(async function test() { + const browser = await addTab(TAB_URL); + const tab = gBrowser.getTabForBrowser(browser); + const target = await TargetFactory.forTab(tab); + await target.attach(); + + await testGetAllocationStack(tab, target); + + await target.destroy(); +}); + +async function testGetAllocationStack(tab, target) { + const front = await target.getFront("promises"); + + await front.attach(); + await front.listPromises(); + + // Get the grip for promise p + const onNewPromise = new Promise(resolve => { + front.on("new-promises", promises => { + for (const p of promises) { + if (p.preview.ownProperties.name && + p.preview.ownProperties.name.value === "p") { + resolve(p); + } + } + }); + }); + + await ContentTask.spawn(tab.linkedBrowser, {}, () => { + content.wrappedJSObject.makePromises(); + }); + + const form = await onNewPromise; + ok(form, "Found our promise p"); + + const objectClient = new ObjectClient(target.client, form); + ok(objectClient, "Got Object Client"); + + const response = await objectClient.getPromiseAllocationStack(); + ok(response.allocationStack.length, "Got promise allocation stack."); + + for (const stack of response.allocationStack) { + is(stack.source.url, TAB_URL, "Got correct source URL."); + is(stack.functionDisplayName, "makePromises", + "Got correct function display name."); + is(typeof stack.line, "number", "Expect stack line to be a number."); + is(typeof stack.column, "number", + "Expect stack column to be a number."); + } + + await front.detach(); +} diff --git a/devtools/server/tests/browser/browser_dbg_promises-chrome-allocation-stack.js b/devtools/server/tests/browser/browser_dbg_promises-chrome-allocation-stack.js new file mode 100644 index 000000000000..91fa049e0975 --- /dev/null +++ b/devtools/server/tests/browser/browser_dbg_promises-chrome-allocation-stack.js @@ -0,0 +1,94 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Test that we can get a stack to a promise's allocation point in the chrome + * process. + */ + +"use strict"; + +const SOURCE_URL = "browser_dbg_promises-chrome-allocation-stack.js"; + +const ObjectClient = require("devtools/shared/client/object-client"); + +const STACK_DATA = [ + { functionDisplayName: "test/<" }, + { functionDisplayName: "testGetAllocationStack" }, +]; + +add_task(async function test() { + requestLongerTimeout(10); + + DebuggerServer.init(); + DebuggerServer.registerAllActors(); + DebuggerServer.allowChromeProcess = true; + + const client = new DebuggerClient(DebuggerServer.connectPipe()); + await client.connect(); + const targetFront = await client.mainRoot.getMainProcess(); + const target = await TargetFactory.forRemoteTab({ + client, + activeTab: targetFront, + chrome: true, + }); + await target.attach(); + + await testGetAllocationStack(client, target, () => { + const p = new Promise(() => {}); + p.name = "p"; + const q = p.then(); + q.name = "q"; + const r = p.catch(() => {}); + r.name = "r"; + }); + + await target.destroy(); +}); + +async function testGetAllocationStack(client, target, makePromises) { + const front = await target.getFront("promises"); + + await front.attach(); + await front.listPromises(); + + // Get the grip for promise p + const onNewPromise = new Promise(resolve => { + front.on("new-promises", promises => { + for (const p of promises) { + if (p.preview.ownProperties.name && + p.preview.ownProperties.name.value === "p") { + resolve(p); + } + } + }); + }); + + makePromises(); + + const form = await onNewPromise; + ok(form, "Found our promise p"); + + const objectClient = new ObjectClient(client, form); + ok(objectClient, "Got Object Client"); + + const response = await objectClient.getPromiseAllocationStack(); + ok(response.allocationStack.length, "Got promise allocation stack."); + + for (let i = 0; i < STACK_DATA.length; i++) { + const data = STACK_DATA[i]; + const stack = response.allocationStack[i]; + + ok(stack.source.url.startsWith("chrome:"), "Got a chrome source URL"); + ok(stack.source.url.endsWith(SOURCE_URL), "Got correct source URL."); + is(stack.functionDisplayName, data.functionDisplayName, + "Got correct function display name."); + is(typeof stack.line, "number", "Expect stack line to be a number."); + is(typeof stack.column, "number", + "Expect stack column to be a number."); + } + + await front.detach(); +} diff --git a/devtools/server/tests/browser/browser_dbg_promises-fulfillment-stack.js b/devtools/server/tests/browser/browser_dbg_promises-fulfillment-stack.js new file mode 100644 index 000000000000..b335f9602535 --- /dev/null +++ b/devtools/server/tests/browser/browser_dbg_promises-fulfillment-stack.js @@ -0,0 +1,86 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Test that we can get a stack to a promise's fulfillment point. + */ + +"use strict"; + +const TAB_URL = URL_ROOT + "doc_promise-get-fulfillment-stack.html"; + +const ObjectClient = require("devtools/shared/client/object-client"); + +const TEST_DATA = [ + { + functionDisplayName: "returnPromise/<", + line: 21, + column: 37, + }, + { + functionDisplayName: "returnPromise", + line: 21, + column: 14, + }, + { + functionDisplayName: "makePromise", + line: 16, + column: 17, + }, +]; + +add_task(async () => { + const browser = await addTab(TAB_URL); + const tab = gBrowser.getTabForBrowser(browser); + const target = await TargetFactory.forTab(tab); + await target.attach(); + + await testGetFulfillmentStack(tab, target); + await target.destroy(); +}); + +async function testGetFulfillmentStack(tab, target) { + const front = await target.getFront("promises"); + + await front.attach(); + await front.listPromises(); + + // Get the grip for promise p + const onNewPromise = new Promise(resolve => { + front.on("new-promises", promises => { + for (const p of promises) { + if (p.preview.ownProperties.name && + p.preview.ownProperties.name.value === "p") { + resolve(p); + } + } + }); + }); + + await ContentTask.spawn(tab.linkedBrowser, {}, async function() { + content.wrappedJSObject.makePromise(); + }); + + const form = await onNewPromise; + ok(form, "Found our promise p"); + + const objectClient = new ObjectClient(target.client, form); + ok(objectClient, "Got Object Client"); + + const response = await objectClient.getPromiseFulfillmentStack(); + ok(response.fulfillmentStack.length, "Got promise allocation stack."); + + for (let i = 0; i < TEST_DATA.length; i++) { + const stack = response.fulfillmentStack[i]; + const data = TEST_DATA[i]; + is(stack.source.url, TAB_URL, "Got correct source URL."); + is(stack.functionDisplayName, data.functionDisplayName, + "Got correct function display name."); + is(stack.line, data.line, "Got correct stack line number."); + is(stack.column, data.column, "Got correct stack column number."); + } + + await front.detach(); +} diff --git a/devtools/server/tests/browser/browser_dbg_promises-rejection-stack.js b/devtools/server/tests/browser/browser_dbg_promises-rejection-stack.js new file mode 100644 index 000000000000..5a9aa8b513d0 --- /dev/null +++ b/devtools/server/tests/browser/browser_dbg_promises-rejection-stack.js @@ -0,0 +1,93 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Test that we can get a stack to a promise's rejection point. + */ + +"use strict"; + +const TAB_URL = URL_ROOT + "doc_promise-get-rejection-stack.html"; + +const ObjectClient = require("devtools/shared/client/object-client"); + +// The code in the document above leaves an uncaught rejection. This is only +// reported to the testing framework if the code is loaded in the main process. +if (!gMultiProcessBrowser) { + ChromeUtils.import("resource://testing-common/PromiseTestUtils.jsm", this); + PromiseTestUtils.expectUncaughtRejection(/hello/); +} + +const TEST_DATA = [ + { + functionDisplayName: "returnPromise/<", + line: 21, + column: 47, + }, + { + functionDisplayName: "returnPromise", + line: 21, + column: 14, + }, + { + functionDisplayName: "makePromise", + line: 16, + column: 17, + }, +]; + +add_task(async () => { + const browser = await addTab(TAB_URL); + const tab = gBrowser.getTabForBrowser(browser); + const target = await TargetFactory.forTab(tab); + await target.attach(); + + await testGetRejectionStack(tab, target); + await target.destroy(); +}); + +async function testGetRejectionStack(tab, target) { + const front = await target.getFront("promises"); + + await front.attach(); + await front.listPromises(); + + // Get the grip for promise p + const onNewPromise = new Promise(resolve => { + front.on("new-promises", promises => { + for (const p of promises) { + if (p.preview.ownProperties.name && + p.preview.ownProperties.name.value === "p") { + resolve(p); + } + } + }); + }); + + await ContentTask.spawn(tab.linkedBrowser, {}, async function() { + content.wrappedJSObject.makePromise(); + }); + + const form = await onNewPromise; + ok(form, "Found our promise p"); + + const objectClient = new ObjectClient(target.client, form); + ok(objectClient, "Got Object Client"); + + const response = await objectClient.getPromiseRejectionStack(); + ok(response.rejectionStack.length, "Got promise allocation stack."); + + for (let i = 0; i < TEST_DATA.length; i++) { + const stack = response.rejectionStack[i]; + const data = TEST_DATA[i]; + is(stack.source.url, TAB_URL, "Got correct source URL."); + is(stack.functionDisplayName, data.functionDisplayName, + "Got correct function display name."); + is(stack.line, data.line, "Got correct stack line number."); + is(stack.column, data.column, "Got correct stack column number."); + } + + await front.detach(); +} diff --git a/devtools/server/tests/browser/browser_spawn_actor_in_parent.js b/devtools/server/tests/browser/browser_spawn_actor_in_parent.js index dbedd5775dc7..9d2c19c9f3d3 100644 --- a/devtools/server/tests/browser/browser_spawn_actor_in_parent.js +++ b/devtools/server/tests/browser/browser_spawn_actor_in_parent.js @@ -29,11 +29,11 @@ add_task(async function() { const { client } = target; const form = targetFront.targetForm; - const inContentFront = InContentFront(client, form); + const inContentFront = new InContentFront(client, form); const isInContent = await inContentFront.isInContent(); ok(isInContent, "ContentActor really runs in the content process"); const formSpawn = await inContentFront.spawnInParent(ACTOR_URL); - const inParentFront = InParentFront(client, formSpawn); + const inParentFront = new InParentFront(client, formSpawn); const { args, isInParent, diff --git a/devtools/client/debugger/test/mochitest/doc_promise-get-allocation-stack.html b/devtools/server/tests/browser/doc_promise-get-allocation-stack.html similarity index 69% rename from devtools/client/debugger/test/mochitest/doc_promise-get-allocation-stack.html rename to devtools/server/tests/browser/doc_promise-get-allocation-stack.html index cd4796b4389c..b92bf49a0fd2 100644 --- a/devtools/client/debugger/test/mochitest/doc_promise-get-allocation-stack.html +++ b/devtools/server/tests/browser/doc_promise-get-allocation-stack.html @@ -10,12 +10,15 @@ diff --git a/devtools/client/debugger/test/mochitest/doc_promise-get-fulfillment-stack.html b/devtools/server/tests/browser/doc_promise-get-fulfillment-stack.html similarity index 82% rename from devtools/client/debugger/test/mochitest/doc_promise-get-fulfillment-stack.html rename to devtools/server/tests/browser/doc_promise-get-fulfillment-stack.html index 0b311a69a483..f0d4a64f32fc 100644 --- a/devtools/client/debugger/test/mochitest/doc_promise-get-fulfillment-stack.html +++ b/devtools/server/tests/browser/doc_promise-get-fulfillment-stack.html @@ -10,8 +10,10 @@ + + + diff --git a/dom/media/test/.eslintrc.js b/dom/media/test/.eslintrc.js new file mode 100644 index 000000000000..407338aa6910 --- /dev/null +++ b/dom/media/test/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + ] +}; diff --git a/dom/media/tests/mochitest/identity/.eslintrc.js b/dom/media/tests/mochitest/identity/.eslintrc.js new file mode 100644 index 000000000000..407338aa6910 --- /dev/null +++ b/dom/media/tests/mochitest/identity/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + ] +}; diff --git a/dom/media/webspeech/recognition/test/.eslintrc.js b/dom/media/webspeech/recognition/test/.eslintrc.js new file mode 100644 index 000000000000..407338aa6910 --- /dev/null +++ b/dom/media/webspeech/recognition/test/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + ] +}; diff --git a/dom/media/webspeech/synth/test/.eslintrc.js b/dom/media/webspeech/synth/test/.eslintrc.js new file mode 100644 index 000000000000..407338aa6910 --- /dev/null +++ b/dom/media/webspeech/synth/test/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + ] +}; diff --git a/dom/messagechannel/tests/.eslintrc.js b/dom/messagechannel/tests/.eslintrc.js new file mode 100644 index 000000000000..bd5cce43ed84 --- /dev/null +++ b/dom/messagechannel/tests/.eslintrc.js @@ -0,0 +1,8 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + "plugin:mozilla/chrome-test", + ] +}; diff --git a/dom/network/tests/.eslintrc.js b/dom/network/tests/.eslintrc.js new file mode 100644 index 000000000000..bd5cce43ed84 --- /dev/null +++ b/dom/network/tests/.eslintrc.js @@ -0,0 +1,8 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + "plugin:mozilla/chrome-test", + ] +}; diff --git a/dom/payments/test/.eslintrc.js b/dom/payments/test/.eslintrc.js new file mode 100644 index 000000000000..428b2aa23736 --- /dev/null +++ b/dom/payments/test/.eslintrc.js @@ -0,0 +1,8 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + "plugin:mozilla/browser-test", + ] +}; diff --git a/dom/performance/tests/.eslintrc.js b/dom/performance/tests/.eslintrc.js new file mode 100644 index 000000000000..407338aa6910 --- /dev/null +++ b/dom/performance/tests/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + ] +}; diff --git a/dom/permission/tests/.eslintrc.js b/dom/permission/tests/.eslintrc.js new file mode 100644 index 000000000000..407338aa6910 --- /dev/null +++ b/dom/permission/tests/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + ] +}; diff --git a/dom/promise/tests/.eslintrc.js b/dom/promise/tests/.eslintrc.js new file mode 100644 index 000000000000..bd5cce43ed84 --- /dev/null +++ b/dom/promise/tests/.eslintrc.js @@ -0,0 +1,8 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + "plugin:mozilla/chrome-test", + ] +}; diff --git a/dom/push/test/.eslintrc.js b/dom/push/test/.eslintrc.js new file mode 100644 index 000000000000..407338aa6910 --- /dev/null +++ b/dom/push/test/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + ] +}; diff --git a/dom/quota/test/.eslintrc.js b/dom/quota/test/.eslintrc.js new file mode 100644 index 000000000000..428b2aa23736 --- /dev/null +++ b/dom/quota/test/.eslintrc.js @@ -0,0 +1,8 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + "plugin:mozilla/browser-test", + ] +}; diff --git a/dom/quota/test/unit/.eslintrc.js b/dom/quota/test/unit/.eslintrc.js new file mode 100644 index 000000000000..9db908f38e33 --- /dev/null +++ b/dom/quota/test/unit/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/xpcshell-test", + ] +}; diff --git a/dom/security/featurepolicy/test/mochitest/.eslintrc.js b/dom/security/featurepolicy/test/mochitest/.eslintrc.js new file mode 100644 index 000000000000..407338aa6910 --- /dev/null +++ b/dom/security/featurepolicy/test/mochitest/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + ] +}; diff --git a/dom/serviceworkers/test/.eslintrc.js b/dom/serviceworkers/test/.eslintrc.js new file mode 100644 index 000000000000..c5f01a099b44 --- /dev/null +++ b/dom/serviceworkers/test/.eslintrc.js @@ -0,0 +1,9 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + "plugin:mozilla/browser-test", + "plugin:mozilla/chrome-test", + ] +}; diff --git a/dom/smil/test/.eslintrc.js b/dom/smil/test/.eslintrc.js new file mode 100644 index 000000000000..407338aa6910 --- /dev/null +++ b/dom/smil/test/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + ] +}; diff --git a/dom/u2f/tests/.eslintrc.js b/dom/u2f/tests/.eslintrc.js new file mode 100644 index 000000000000..407338aa6910 --- /dev/null +++ b/dom/u2f/tests/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + ] +}; diff --git a/dom/url/tests/.eslintrc.js b/dom/url/tests/.eslintrc.js new file mode 100644 index 000000000000..c5f01a099b44 --- /dev/null +++ b/dom/url/tests/.eslintrc.js @@ -0,0 +1,9 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + "plugin:mozilla/browser-test", + "plugin:mozilla/chrome-test", + ] +}; diff --git a/dom/webauthn/tests/.eslintrc.js b/dom/webauthn/tests/.eslintrc.js new file mode 100644 index 000000000000..407338aa6910 --- /dev/null +++ b/dom/webauthn/tests/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + ] +}; diff --git a/dom/webauthn/tests/browser/.eslintrc.js b/dom/webauthn/tests/browser/.eslintrc.js new file mode 100644 index 000000000000..58a15b48622a --- /dev/null +++ b/dom/webauthn/tests/browser/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/browser-test", + ] +}; diff --git a/dom/webgpu/mochitest/.eslintrc.js b/dom/webgpu/mochitest/.eslintrc.js new file mode 100644 index 000000000000..407338aa6910 --- /dev/null +++ b/dom/webgpu/mochitest/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + ] +}; diff --git a/dom/websocket/tests/.eslintrc.js b/dom/websocket/tests/.eslintrc.js new file mode 100644 index 000000000000..bd5cce43ed84 --- /dev/null +++ b/dom/websocket/tests/.eslintrc.js @@ -0,0 +1,8 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + "plugin:mozilla/chrome-test", + ] +}; diff --git a/dom/workers/test/.eslintrc.js b/dom/workers/test/.eslintrc.js new file mode 100644 index 000000000000..c5f01a099b44 --- /dev/null +++ b/dom/workers/test/.eslintrc.js @@ -0,0 +1,9 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + "plugin:mozilla/browser-test", + "plugin:mozilla/chrome-test", + ] +}; diff --git a/dom/workers/test/xpcshell/.eslintrc.js b/dom/workers/test/xpcshell/.eslintrc.js new file mode 100644 index 000000000000..9db908f38e33 --- /dev/null +++ b/dom/workers/test/xpcshell/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/xpcshell-test", + ] +}; diff --git a/dom/worklet/tests/.eslintrc.js b/dom/worklet/tests/.eslintrc.js new file mode 100644 index 000000000000..407338aa6910 --- /dev/null +++ b/dom/worklet/tests/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + ] +}; diff --git a/dom/xbl/test/.eslintrc.js b/dom/xbl/test/.eslintrc.js new file mode 100644 index 000000000000..bd5cce43ed84 --- /dev/null +++ b/dom/xbl/test/.eslintrc.js @@ -0,0 +1,8 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + "plugin:mozilla/chrome-test", + ] +}; diff --git a/dom/xhr/tests/.eslintrc.js b/dom/xhr/tests/.eslintrc.js new file mode 100644 index 000000000000..428b2aa23736 --- /dev/null +++ b/dom/xhr/tests/.eslintrc.js @@ -0,0 +1,8 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + "plugin:mozilla/browser-test", + ] +}; diff --git a/dom/xml/test/.eslintrc.js b/dom/xml/test/.eslintrc.js new file mode 100644 index 000000000000..407338aa6910 --- /dev/null +++ b/dom/xml/test/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + ] +}; diff --git a/dom/xul/test/.eslintrc.js b/dom/xul/test/.eslintrc.js new file mode 100644 index 000000000000..bd5cce43ed84 --- /dev/null +++ b/dom/xul/test/.eslintrc.js @@ -0,0 +1,8 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + "plugin:mozilla/chrome-test", + ] +}; diff --git a/extensions/cookie/test/.eslintrc.js b/extensions/cookie/test/.eslintrc.js new file mode 100644 index 000000000000..428b2aa23736 --- /dev/null +++ b/extensions/cookie/test/.eslintrc.js @@ -0,0 +1,8 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + "plugin:mozilla/browser-test", + ] +}; diff --git a/extensions/cookie/test/unit/.eslintrc.js b/extensions/cookie/test/unit/.eslintrc.js new file mode 100644 index 000000000000..9db908f38e33 --- /dev/null +++ b/extensions/cookie/test/unit/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/xpcshell-test", + ] +}; diff --git a/extensions/spellcheck/hunspell/tests/unit/.eslintrc.js b/extensions/spellcheck/hunspell/tests/unit/.eslintrc.js new file mode 100644 index 000000000000..9db908f38e33 --- /dev/null +++ b/extensions/spellcheck/hunspell/tests/unit/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/xpcshell-test", + ] +}; diff --git a/extensions/universalchardet/tests/.eslintrc.js b/extensions/universalchardet/tests/.eslintrc.js new file mode 100644 index 000000000000..0bb0fe72c247 --- /dev/null +++ b/extensions/universalchardet/tests/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/chrome-test", + ] +}; diff --git a/gfx/layers/apz/test/mochitest/.eslintrc.js b/gfx/layers/apz/test/mochitest/.eslintrc.js new file mode 100644 index 000000000000..bd5cce43ed84 --- /dev/null +++ b/gfx/layers/apz/test/mochitest/.eslintrc.js @@ -0,0 +1,8 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + "plugin:mozilla/chrome-test", + ] +}; diff --git a/gfx/tests/browser/.eslintrc.js b/gfx/tests/browser/.eslintrc.js new file mode 100644 index 000000000000..58a15b48622a --- /dev/null +++ b/gfx/tests/browser/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/browser-test", + ] +}; diff --git a/gfx/tests/chrome/.eslintrc.js b/gfx/tests/chrome/.eslintrc.js new file mode 100644 index 000000000000..0bb0fe72c247 --- /dev/null +++ b/gfx/tests/chrome/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/chrome-test", + ] +}; diff --git a/gfx/tests/mochitest/.eslintrc.js b/gfx/tests/mochitest/.eslintrc.js new file mode 100644 index 000000000000..407338aa6910 --- /dev/null +++ b/gfx/tests/mochitest/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + ] +}; diff --git a/image/test/browser/.eslintrc.js b/image/test/browser/.eslintrc.js new file mode 100644 index 000000000000..58a15b48622a --- /dev/null +++ b/image/test/browser/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/browser-test", + ] +}; diff --git a/image/test/mochitest/.eslintrc.js b/image/test/mochitest/.eslintrc.js new file mode 100644 index 000000000000..bd5cce43ed84 --- /dev/null +++ b/image/test/mochitest/.eslintrc.js @@ -0,0 +1,8 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + "plugin:mozilla/chrome-test", + ] +}; diff --git a/image/test/unit/.eslintrc.js b/image/test/unit/.eslintrc.js new file mode 100644 index 000000000000..9db908f38e33 --- /dev/null +++ b/image/test/unit/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/xpcshell-test", + ] +}; diff --git a/intl/uconv/tests/.eslintrc.js b/intl/uconv/tests/.eslintrc.js new file mode 100644 index 000000000000..407338aa6910 --- /dev/null +++ b/intl/uconv/tests/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + ] +}; diff --git a/intl/uconv/tests/unit/.eslintrc.js b/intl/uconv/tests/unit/.eslintrc.js new file mode 100644 index 000000000000..9db908f38e33 --- /dev/null +++ b/intl/uconv/tests/unit/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/xpcshell-test", + ] +}; diff --git a/layout/base/tests/.eslintrc.js b/layout/base/tests/.eslintrc.js new file mode 100644 index 000000000000..428b2aa23736 --- /dev/null +++ b/layout/base/tests/.eslintrc.js @@ -0,0 +1,8 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + "plugin:mozilla/browser-test", + ] +}; diff --git a/layout/forms/test/.eslintrc.js b/layout/forms/test/.eslintrc.js new file mode 100644 index 000000000000..bd5cce43ed84 --- /dev/null +++ b/layout/forms/test/.eslintrc.js @@ -0,0 +1,8 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + "plugin:mozilla/chrome-test", + ] +}; diff --git a/layout/generic/test/.eslintrc.js b/layout/generic/test/.eslintrc.js new file mode 100644 index 000000000000..bd5cce43ed84 --- /dev/null +++ b/layout/generic/test/.eslintrc.js @@ -0,0 +1,8 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + "plugin:mozilla/chrome-test", + ] +}; diff --git a/layout/inspector/tests/.eslintrc.js b/layout/inspector/tests/.eslintrc.js new file mode 100644 index 000000000000..407338aa6910 --- /dev/null +++ b/layout/inspector/tests/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + ] +}; diff --git a/layout/inspector/tests/chrome/.eslintrc.js b/layout/inspector/tests/chrome/.eslintrc.js new file mode 100644 index 000000000000..0bb0fe72c247 --- /dev/null +++ b/layout/inspector/tests/chrome/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/chrome-test", + ] +}; diff --git a/layout/mathml/tests/.eslintrc.js b/layout/mathml/tests/.eslintrc.js new file mode 100644 index 000000000000..bd5cce43ed84 --- /dev/null +++ b/layout/mathml/tests/.eslintrc.js @@ -0,0 +1,8 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + "plugin:mozilla/chrome-test", + ] +}; diff --git a/layout/style/test/.eslintrc.js b/layout/style/test/.eslintrc.js new file mode 100644 index 000000000000..428b2aa23736 --- /dev/null +++ b/layout/style/test/.eslintrc.js @@ -0,0 +1,8 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + "plugin:mozilla/browser-test", + ] +}; diff --git a/layout/style/test/chrome/.eslintrc.js b/layout/style/test/chrome/.eslintrc.js new file mode 100644 index 000000000000..0bb0fe72c247 --- /dev/null +++ b/layout/style/test/chrome/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/chrome-test", + ] +}; diff --git a/layout/svg/tests/.eslintrc.js b/layout/svg/tests/.eslintrc.js new file mode 100644 index 000000000000..bd5cce43ed84 --- /dev/null +++ b/layout/svg/tests/.eslintrc.js @@ -0,0 +1,8 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + "plugin:mozilla/chrome-test", + ] +}; diff --git a/layout/tables/test/.eslintrc.js b/layout/tables/test/.eslintrc.js new file mode 100644 index 000000000000..407338aa6910 --- /dev/null +++ b/layout/tables/test/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + ] +}; diff --git a/layout/tools/layout-debug/tests/browser/.eslintrc.js b/layout/tools/layout-debug/tests/browser/.eslintrc.js new file mode 100644 index 000000000000..58a15b48622a --- /dev/null +++ b/layout/tools/layout-debug/tests/browser/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/browser-test", + ] +}; diff --git a/layout/tools/layout-debug/tests/unit/.eslintrc.js b/layout/tools/layout-debug/tests/unit/.eslintrc.js new file mode 100644 index 000000000000..9db908f38e33 --- /dev/null +++ b/layout/tools/layout-debug/tests/unit/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/xpcshell-test", + ] +}; diff --git a/layout/xul/test/.eslintrc.js b/layout/xul/test/.eslintrc.js new file mode 100644 index 000000000000..c5f01a099b44 --- /dev/null +++ b/layout/xul/test/.eslintrc.js @@ -0,0 +1,9 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/mochitest-test", + "plugin:mozilla/browser-test", + "plugin:mozilla/chrome-test", + ] +}; diff --git a/media/webrtc/trunk/webrtc/modules/utility/source/process_thread_impl.cc b/media/webrtc/trunk/webrtc/modules/utility/source/process_thread_impl.cc index cc395db6ed4e..014624e48cb5 100644 --- a/media/webrtc/trunk/webrtc/modules/utility/source/process_thread_impl.cc +++ b/media/webrtc/trunk/webrtc/modules/utility/source/process_thread_impl.cc @@ -117,7 +117,7 @@ void ProcessThreadImpl::PostTask(std::unique_ptr task) { void ProcessThreadImpl::RegisterModule(Module* module, const rtc::Location& from) { - // RTC_DCHECK(thread_checker_.CalledOnValidThread()); Not really needed + RTC_DCHECK(thread_checker_.CalledOnValidThread()); RTC_DCHECK(module) << from.ToString(); #if RTC_DCHECK_IS_ON diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java index 7104e528d5f4..c8bb735cfce5 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java @@ -1960,7 +1960,7 @@ public class GeckoAppShell public static int getAudioOutputFramesPerBuffer() { final int DEFAULT = 512; - if (SysInfo.getVersion() < 17) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { return DEFAULT; } final AudioManager am = (AudioManager)getApplicationContext() @@ -1979,7 +1979,7 @@ public class GeckoAppShell public static int getAudioOutputSampleRate() { final int DEFAULT = 44100; - if (SysInfo.getVersion() < 17) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { return DEFAULT; } final AudioManager am = (AudioManager)getApplicationContext() diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoProfile.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoProfile.java index 8a8da4cb47d1..697ce85c95e5 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoProfile.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoProfile.java @@ -154,7 +154,8 @@ public final class GeckoProfile { } } - if (profileName == null && profilePath == null) { + if (TextUtils.isEmpty(profileName) && profilePath == null) { + informIfCustomProfileIsUnavailable(profileName, false); // Get the default profile for the Activity. return getDefaultProfile(context); } @@ -212,14 +213,20 @@ public final class GeckoProfile { // No | Yes | Profile with specified name at default dir. // Yes | No | Custom (anonymous) profile with specified dir. // No | No | Profile with specified name at specified dir. + // + // Empty name?| Null dir? | Returned profile + // ------------------------------------------ + // Yes | Yes | Active profile or default profile - if (profileName == null && profileDir == null) { + if (TextUtils.isEmpty(profileName) && profileDir == null) { // If no profile info was passed in, look for the active profile or a default profile. final GeckoProfile profile = GeckoThread.getActiveProfile(); if (profile != null) { + informIfCustomProfileIsUnavailable(profileName, true); return profile; } + informIfCustomProfileIsUnavailable(profileName, false); return GeckoProfile.initFromArgs(context, sIntentArgs); } else if (profileName == null) { // If only profile dir was passed in, use custom (anonymous) profile. @@ -273,6 +280,28 @@ public final class GeckoProfile { return profile; } + /** + * Custom profiles are an edge use case (must be passed in via Intent arguments)
+ * Will inform users if the received arguments are invalid and the app fallbacks to use + * the currently active or the default Gecko profile.
+ * Only to be called if other conditions than the profile name are already checked. + * + * @see Reasoning behind custom profiles + * + * @param profileName intended profile name. Will be checked against {{@link #CUSTOM_PROFILE}} + * to decide if we should inform or not about using the fallback profile. + * @param activeOrDefaultProfileFallback true - will fallback to use the currently active Gecko profile + * false - will fallback to use the default Gecko profile + */ + private static void informIfCustomProfileIsUnavailable( + final String profileName, final boolean activeOrDefaultProfileFallback) { + if (CUSTOM_PROFILE.equals(profileName)) { + final String fallbackProfileName = activeOrDefaultProfileFallback ? "active" : "default"; + Log.w(LOGTAG, String.format("Custom profile must have a directory specified! " + + "Reverting to use the %s profile", fallbackProfileName)); + } + } + // Currently unused outside of testing. @RobocopTarget public static boolean removeProfile(final Context context, final GeckoProfile profile) { @@ -313,8 +342,6 @@ public final class GeckoProfile { private GeckoProfile(Context context, String profileName, File profileDir) throws NoMozillaDirectoryException { if (profileName == null) { throw new IllegalArgumentException("Unable to create GeckoProfile for empty profile name."); - } else if (CUSTOM_PROFILE.equals(profileName) && profileDir == null) { - throw new IllegalArgumentException("Custom profile must have a directory"); } mName = profileName; diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoSystemStateListener.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoSystemStateListener.java index ad8b09ced97d..bd9bf1355a04 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoSystemStateListener.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoSystemStateListener.java @@ -10,9 +10,11 @@ import android.content.Context; import android.database.ContentObserver; import android.hardware.input.InputManager; import android.net.Uri; +import android.os.Build; import android.os.Handler; import android.os.Looper; import android.provider.Settings; +import android.support.annotation.RequiresApi; import android.util.Log; import android.view.InputDevice; import org.mozilla.gecko.annotation.WrapForJNI; @@ -81,8 +83,13 @@ public class GeckoSystemStateListener mContentObserver = null; } + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1) @WrapForJNI(calledFrom = "gecko") - // For prefers-reduced-motion media queries feature. + /** + * For prefers-reduced-motion media queries feature. + * + * Uses `Settings.Global` which was introduced in API version 17. + */ private static boolean prefersReducedMotion() { ContentResolver contentResolver = sApplicationContext.getContentResolver(); diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/SpeechSynthesisService.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/SpeechSynthesisService.java index c9c381133f34..a131ad2ad208 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/SpeechSynthesisService.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/SpeechSynthesisService.java @@ -53,7 +53,9 @@ public class SpeechSynthesisService { ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { - Locale defaultLocale = sTTS.getDefaultLanguage(); + Locale defaultLocale = Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 + ? sTTS.getDefaultLanguage() + : sTTS.getLanguage(); for (Locale locale : getAvailableLanguages()) { final Set features = sTTS.getFeatures(locale); boolean isLocal = features.contains(TextToSpeech.Engine.KEY_FEATURE_EMBEDDED_SYNTHESIS); diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoSurfaceTexture.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoSurfaceTexture.java index 83193c56eb37..e3361772402d 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoSurfaceTexture.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoSurfaceTexture.java @@ -7,6 +7,7 @@ package org.mozilla.gecko.gfx; import android.graphics.SurfaceTexture; import android.os.Build; +import android.support.annotation.RequiresApi; import android.util.Log; import java.util.concurrent.atomic.AtomicInteger; @@ -40,6 +41,7 @@ import org.mozilla.gecko.annotation.WrapForJNI; init(handle, false); } + @RequiresApi(api = Build.VERSION_CODES.KITKAT) private GeckoSurfaceTexture(int handle, boolean singleBufferMode) { super(0, singleBufferMode); init(handle, singleBufferMode); diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/VsyncSource.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/VsyncSource.java index 3d0ba6cf832d..f23a8ccf3c54 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/VsyncSource.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/VsyncSource.java @@ -7,8 +7,10 @@ package org.mozilla.gecko.gfx; import android.content.Context; import android.hardware.display.DisplayManager; +import android.os.Build; import android.os.Handler; import android.os.Looper; +import android.support.annotation.RequiresApi; import android.view.Choreographer; import android.view.Display; import org.mozilla.gecko.annotation.WrapForJNI; @@ -83,7 +85,15 @@ import org.mozilla.gecko.GeckoAppShell; return mObservingVsync; } - /** Gets the refresh rate of default display in frames per second. */ + /** + * Gets the refresh rate of default display in frames per second. + * + * The {@link DisplayManager} used by this method to determine the refresh rate + * was introduced in API level 17. + * + * @return the refresh rate of default display in frames per second. + **/ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1) @WrapForJNI public float getRefreshRate() { DisplayManager dm = (DisplayManager) diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoMediaDrmBridgeV21.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoMediaDrmBridgeV21.java index 05e1b6eca786..3a7cace2f138 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoMediaDrmBridgeV21.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoMediaDrmBridgeV21.java @@ -19,7 +19,9 @@ import java.util.UUID; import java.util.ArrayDeque; import android.annotation.SuppressLint; +import android.annotation.TargetApi; import android.os.AsyncTask; +import android.os.Build; import android.os.Handler; import android.os.HandlerThread; import android.media.DeniedByServerException; @@ -33,6 +35,7 @@ import android.util.Log; import org.mozilla.gecko.util.StringUtils; import org.mozilla.gecko.util.ProxySelector; +@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) public class GeckoMediaDrmBridgeV21 implements GeckoMediaDrm { protected final String LOGTAG; private static final String INVALID_SESSION_ID = "Invalid"; @@ -301,6 +304,7 @@ public class GeckoMediaDrmBridgeV21 implements GeckoMediaDrm { return mCrypto; } + @SuppressLint("WrongConstant") @Override public void setServerCertificate(final byte[] cert) { if (DEBUG) Log.d(LOGTAG, "setServerCertificate()"); @@ -612,7 +616,7 @@ public class GeckoMediaDrmBridgeV21 implements GeckoMediaDrm { } try { mProvisioningPromiseId = promiseId; - MediaDrm.ProvisionRequest request = mDrm.getProvisionRequest(); + @SuppressLint("NewApi") MediaDrm.ProvisionRequest request = mDrm.getProvisionRequest(); PostRequestTask postTask = new PostRequestTask(promiseId, request.getDefaultUrl(), request.getData()); postTask.execute(); diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/JellyBeanAsyncCodec.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/JellyBeanAsyncCodec.java index b94a077db807..1763bb71c815 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/JellyBeanAsyncCodec.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/JellyBeanAsyncCodec.java @@ -10,6 +10,7 @@ import android.media.MediaCodec; import android.media.MediaCodecInfo.CodecCapabilities; import android.media.MediaCrypto; import android.media.MediaFormat; +import android.os.Build; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; @@ -361,7 +362,8 @@ final class JellyBeanAsyncCodec implements AsyncCodec { mInputEnded = (flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0; - if ((android.os.Build.VERSION.SDK_INT >= 19) && ((flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0)) { + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT + && ((flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0)) { Bundle params = new Bundle(); params.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0); mCodec.setParameters(params); diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/LollipopAsyncCodec.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/LollipopAsyncCodec.java index 503de3237e68..9c9d797bc288 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/LollipopAsyncCodec.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/LollipopAsyncCodec.java @@ -6,10 +6,12 @@ package org.mozilla.gecko.media; import org.mozilla.gecko.util.HardwareCodecCapabilityUtils; +import android.annotation.TargetApi; import android.media.MediaCodec; import android.media.MediaCodecInfo.CodecCapabilities; import android.media.MediaCrypto; import android.media.MediaFormat; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -21,6 +23,7 @@ import android.view.Surface; import java.io.IOException; import java.nio.ByteBuffer; +@TargetApi(Build.VERSION_CODES.LOLLIPOP) /* package */ final class LollipopAsyncCodec implements AsyncCodec { private final MediaCodec mCodec; @@ -88,7 +91,11 @@ import java.nio.ByteBuffer; private void onError(final MediaCodec.CodecException e) { e.printStackTrace(); - notify(obtainMessage(MSG_ERROR, e.getErrorCode())); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + notify(obtainMessage(MSG_ERROR, e.getErrorCode())); + } else { + notify(obtainMessage(MSG_ERROR, e.getLocalizedMessage())); + } } } diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/MediaDrmProxy.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/MediaDrmProxy.java index e1cb2db68c83..4cd258ef9bde 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/MediaDrmProxy.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/MediaDrmProxy.java @@ -11,6 +11,7 @@ import java.util.UUID; import org.mozilla.gecko.mozglue.JNIObject; import org.mozilla.gecko.annotation.WrapForJNI; +import android.annotation.SuppressLint; import android.media.MediaCodecInfo; import android.media.MediaCodecList; import android.media.MediaCrypto; @@ -49,13 +50,14 @@ public final class MediaDrmProxy { private static boolean isSystemSupported() { // Support versions >= Marshmallow - if (Build.VERSION.SDK_INT < 23) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { if (DEBUG) Log.d(LOGTAG, "System Not supported !!, current SDK version is " + Build.VERSION.SDK_INT); return false; } return true; } + @SuppressLint("NewApi") @WrapForJNI public static boolean isSchemeSupported(String keySystem) { if (!isSystemSupported()) { @@ -69,6 +71,7 @@ public final class MediaDrmProxy { return false; } + @SuppressLint("NewApi") @WrapForJNI public static boolean IsCryptoSchemeSupported(String keySystem, String container) { diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ContentUriUtils.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ContentUriUtils.java index 4ad296567f76..fa85d854a71f 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ContentUriUtils.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ContentUriUtils.java @@ -16,6 +16,7 @@ package org.mozilla.gecko.util; +import android.annotation.SuppressLint; import android.content.ContentUris; import android.content.Context; import android.database.Cursor; @@ -45,6 +46,7 @@ public class ContentUriUtils { * @param uri The Uri to query. * @author paulburke */ + @SuppressLint("NewAPI") public static @Nullable String getOriginalFilePathFromUri(final Context context, final Uri uri) throws IllegalArgumentException { final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java index 71a8660cac66..c80679e110b7 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java @@ -441,8 +441,8 @@ public class GeckoSession implements Parcelable { callback.sendError("Blocked unsafe intent URI"); delegate.onLoadError(GeckoSession.this, request.uri, - new WebRequestError(WebRequestError.ERROR_CATEGORY_URI, - WebRequestError.ERROR_MALFORMED_URI)); + new WebRequestError(WebRequestError.ERROR_MALFORMED_URI, + WebRequestError.ERROR_CATEGORY_URI)); return; } @@ -707,7 +707,7 @@ public class GeckoSession implements Parcelable { final SelectionActionDelegate.Selection selection = new SelectionActionDelegate.Selection(message); - final String[] actions = message.getStringArray("actions"); + final @SelectionActionDelegate.Action String[] actions = message.getStringArray("actions"); final int seqNo = message.getInt("seqNo"); final GeckoResponse response = new GeckoResponse() { @Override diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoView.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoView.java index d5c024807aaa..4d276599c760 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoView.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoView.java @@ -12,6 +12,7 @@ import org.mozilla.gecko.InputMethods; import org.mozilla.gecko.util.ActivityUtils; import org.mozilla.gecko.util.ThreadUtils; +import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.Activity; import android.content.Context; @@ -412,10 +413,14 @@ public class GeckoView extends FrameLayout { return; } + // Release the display before we detach from the window. + mSession.releaseDisplay(mDisplay.release()); + // If we saved state earlier, we don't want to close the window. if (!mStateSaved && mSession.isOpen()) { mSession.close(); } + } @Override @@ -634,6 +639,7 @@ public class GeckoView extends FrameLayout { } } + @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouchEvent(final MotionEvent event) { if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebRequestError.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebRequestError.java index 8a18f7c098a1..ad4dca57dbff 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebRequestError.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebRequestError.java @@ -8,6 +8,7 @@ package org.mozilla.geckoview; import org.mozilla.gecko.annotation.WrapForJNI; +import android.annotation.SuppressLint; import android.support.annotation.IntDef; import java.lang.annotation.Retention; @@ -250,6 +251,7 @@ public class WebRequestError extends Exception { return new WebRequestError(code, category); } + @SuppressLint("WrongConstant") @WrapForJNI /* package */ static @ErrorCategory int getErrorCategory( long errorModule, @Error int error) { diff --git a/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java index 273e49d4b015..df6966588fd5 100644 --- a/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java +++ b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java @@ -136,22 +136,25 @@ public class GeckoViewActivity extends AppCompatActivity { sGeckoRuntime = GeckoRuntime.create(this, runtimeSettingsBuilder.build()); } - mGeckoSession = (GeckoSession)getIntent().getParcelableExtra("session"); - if (mGeckoSession != null) { - connectSession(mGeckoSession); + if(savedInstanceState == null) { + mGeckoSession = (GeckoSession)getIntent().getParcelableExtra("session"); + if (mGeckoSession != null) { + connectSession(mGeckoSession); - if (!mGeckoSession.isOpen()) { - mGeckoSession.open(sGeckoRuntime); + if (!mGeckoSession.isOpen()) { + mGeckoSession.open(sGeckoRuntime); + } + + mUseMultiprocess = mGeckoSession.getSettings().getBoolean(GeckoSessionSettings.USE_MULTIPROCESS); + mFullAccessibilityTree = mGeckoSession.getSettings().getBoolean(GeckoSessionSettings.FULL_ACCESSIBILITY_TREE); + + mGeckoView.setSession(mGeckoSession); + } else { + mGeckoSession = createSession(); + mGeckoView.setSession(mGeckoSession, sGeckoRuntime); + + loadFromIntent(getIntent()); } - - mUseMultiprocess = mGeckoSession.getSettings().getBoolean(GeckoSessionSettings.USE_MULTIPROCESS); - mFullAccessibilityTree = mGeckoSession.getSettings().getBoolean(GeckoSessionSettings.FULL_ACCESSIBILITY_TREE); - - mGeckoView.setSession(mGeckoSession); - } else { - mGeckoSession = createSession(); - mGeckoView.setSession(mGeckoSession, sGeckoRuntime); - loadFromIntent(getIntent()); } mLocationView.setCommitListener(mCommitListener); @@ -193,7 +196,9 @@ public class GeckoViewActivity extends AppCompatActivity { } private void recreateSession() { - mGeckoSession.close(); + if(mGeckoSession != null) { + mGeckoSession.close(); + } mGeckoSession = createSession(); mGeckoSession.open(sGeckoRuntime); @@ -201,6 +206,16 @@ public class GeckoViewActivity extends AppCompatActivity { mGeckoSession.loadUri(mCurrentUri != null ? mCurrentUri : DEFAULT_URL); } + @Override + public void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + if(savedInstanceState != null) { + mGeckoSession = mGeckoView.getSession(); + } else { + recreateSession(); + } + } + private void updateTrackingProtection(GeckoSession session) { session.getSettings().setBoolean( GeckoSessionSettings.USE_TRACKING_PROTECTION, mUseTrackingProtection); @@ -295,7 +310,7 @@ public class GeckoViewActivity extends AppCompatActivity { } - private void loadFromIntent(final Intent intent) { + private void loadFromIntent(final Intent intent) { final Uri uri = intent.getData(); mGeckoSession.loadUri(uri != null ? uri.toString() : DEFAULT_URL); } diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/server/protocol.js b/testing/talos/talos/tests/devtools/addon/content/tests/server/protocol.js index 28d888d241ca..fa23d40adfe0 100644 --- a/testing/talos/talos/tests/devtools/addon/content/tests/server/protocol.js +++ b/testing/talos/talos/tests/devtools/addon/content/tests/server/protocol.js @@ -8,6 +8,7 @@ const { openToolbox, closeToolbox, testSetup, testTeardown, runTest, SIMPLE_URL } = require("../head"); const protocol = require("devtools/shared/protocol"); +const { FrontClassWithSpec } = protocol; const { dampTestSpec } = require("./spec"); // Test parameters @@ -16,14 +17,14 @@ const STRING_SIZE = 1000; const ARRAY_SIZE = 50; const REPEAT = 300; -const DampTestFront = protocol.FrontClassWithSpec(dampTestSpec, { - initialize(client, tabForm) { +class DampTestFront extends FrontClassWithSpec(dampTestSpec) { + constructor(client, tabForm) { + super(client, tabForm); this.actorID = tabForm.dampTestActor; - protocol.Front.prototype.initialize.call(this, client); // Root owns itself. this.manage(this); - }, -}); + } +} module.exports = async function() { let tab = await testSetup(SIMPLE_URL); @@ -63,7 +64,7 @@ module.exports = async function() { // Instanciate a front for this test actor let { target } = toolbox; - let front = DampTestFront(target.client, target.form); + let front = new DampTestFront(target.client, target.form); // Execute the core of this test, call one method multiple times // and listen for an event sent by this method diff --git a/testing/web-platform/meta/__dir__.ini b/testing/web-platform/meta/__dir__.ini index 12e84533cd4b..18f36e02fe77 100644 --- a/testing/web-platform/meta/__dir__.ini +++ b/testing/web-platform/meta/__dir__.ini @@ -1,5 +1,6 @@ lsan-allowed: [js_pod_malloc, js_pod_calloc, js_pod_realloc, js_arena_calloc,js_pod_arena_calloc, maybe_pod_calloc, pod_calloc, make_zeroed_pod_array, js_arena_malloc] leak-threshold: if webrender: [tab:10000, geckomediaplugin:20000, default:16000] + if os == "mac": [tab:10000, geckomediaplugin:20000, default:2000, rdd:400] [tab:10000, geckomediaplugin:20000, rdd:400] diff --git a/testing/web-platform/meta/fetch/range/__dir__.ini b/testing/web-platform/meta/fetch/range/__dir__.ini index 610e5b8d2cc7..5082e2e1f3d0 100644 --- a/testing/web-platform/meta/fetch/range/__dir__.ini +++ b/testing/web-platform/meta/fetch/range/__dir__.ini @@ -1,2 +1,2 @@ lsan-allowed: [Alloc, Create, CreateInner, FetchDriverObserver, Malloc, NewPage, PLDHashTable::Add, Realloc, allocate, mozilla::ThrottledEventQueue::Create, mozilla::dom::InternalRequest::GetRequestConstructorCopy, mozilla::dom::PerformanceStorageWorker::Create, mozilla::dom::PromiseWorkerProxy::Create, mozilla::dom::WorkerCSPEventListener::Create, mozilla::dom::WorkerFetchResolver::Create, mozilla::dom::WorkerLoadInfo::InterfaceRequestor::InterfaceRequestor, nsSegmentedBuffer::AppendNewSegment, nsSupportsWeakReference::GetWeakReference] -leak-threshold: [default:51200] +leak-threshold: [default:51200, tab:51200] diff --git a/testing/web-platform/meta/service-workers/service-worker/__dir__.ini b/testing/web-platform/meta/service-workers/service-worker/__dir__.ini index f294c83ec358..96d32411ee98 100644 --- a/testing/web-platform/meta/service-workers/service-worker/__dir__.ini +++ b/testing/web-platform/meta/service-workers/service-worker/__dir__.ini @@ -1,3 +1,3 @@ prefs: [dom.serviceWorkers.enabled:true] lsan-allowed: [Alloc, Create, CreateInner, MakeUnique, Malloc, NewChannelFromURIWithProxyFlagsInternal, NewEmptyScopeData, NewPage, OrInsert, PLDHashTable::Add, Realloc, SharedMutex, __rdl_alloc, __rdl_realloc, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::ThrottledEventQueue::Create, mozilla::WeakPtr, mozilla::dom::ChromeUtils::GenerateQI, mozilla::dom::Performance::CreateForMainThread, mozilla::dom::PerformanceStorageWorker::Create, mozilla::dom::WorkerPrivate::WorkerPrivate, mozilla::net::HttpBaseChannel::HttpBaseChannel, mozilla::net::HttpChannelChild::HttpChannelChild, mozilla::net::nsHttpAuthIdentity::Set, mozilla::net::nsHttpHandler::NewProxiedChannel2, mozilla::net::nsStandardURL::Clone, mozilla::net::nsStandardURL::TemplatedMutator, nsDocShell::Create, nsNodeSupportsWeakRefTearoff::GetWeakReference, nsPrefetchService::Preload, nsSegmentedBuffer::AppendNewSegment, nsSupportsWeakReference::GetWeakReference] -leak-threshold: [default:51200] +leak-threshold: [default:51200, tab:51200] diff --git a/testing/web-platform/meta/shadow-dom/scroll-to-the-fragment-in-shadow-tree.html.ini b/testing/web-platform/meta/shadow-dom/scroll-to-the-fragment-in-shadow-tree.html.ini index e9450fef57a2..f42ff0edf2da 100644 --- a/testing/web-platform/meta/shadow-dom/scroll-to-the-fragment-in-shadow-tree.html.ini +++ b/testing/web-platform/meta/shadow-dom/scroll-to-the-fragment-in-shadow-tree.html.ini @@ -1,5 +1,4 @@ [scroll-to-the-fragment-in-shadow-tree.html] [The user agent scroll to the fragment when there is an element with an ID exactly equal to the decoded fragid] expected: - if not debug and not webrender and not e10s and (os == "android") and (version == "Ubuntu 16.04") and (processor == "x86") and (bits == 32): FAIL - + if os == "android": FAIL diff --git a/toolkit/components/find/nsWebBrowserFind.cpp b/toolkit/components/find/nsWebBrowserFind.cpp index b68fa00ffd22..82f5a7e8f02b 100644 --- a/toolkit/components/find/nsWebBrowserFind.cpp +++ b/toolkit/components/find/nsWebBrowserFind.cpp @@ -316,19 +316,6 @@ nsWebBrowserFind::SetMatchCase(bool aMatchCase) { return NS_OK; } -static bool IsInNativeAnonymousSubtree(nsIContent* aContent) { - while (aContent) { - nsIContent* bindingParent = aContent->GetBindingParent(); - if (bindingParent == aContent) { - return true; - } - - aContent = bindingParent; - } - - return false; -} - void nsWebBrowserFind::SetSelectionAndScroll(nsPIDOMWindowOuter* aWindow, nsRange* aRange) { nsCOMPtr doc = aWindow->GetDoc(); @@ -355,7 +342,7 @@ void nsWebBrowserFind::SetSelectionAndScroll(nsPIDOMWindowOuter* aWindow, //