Merge mozilla-central to autoland. a=merge CLOSED TREE

This commit is contained in:
Narcis Beleuzu 2018-12-21 00:04:35 +02:00
commit e154ee4c06
67 changed files with 1469 additions and 1213 deletions

View File

@ -75,11 +75,6 @@ tasks:
- $if: 'repository.project == "try"'
then:
"notify.email.${ownerEmail}.on-completed"
# BUG 1500166 Notify ciduty by email if a nightly hook fails
- "notify.email.ciduty+failedcron@mozilla.com.on-failed"
- "notify.email.ciduty+exceptioncron@mozilla.com.on-exception"
- "notify.email.sheriffs+failedcron@mozilla.com.on-failed"
- "notify.email.sheriffs+exceptioncron@mozilla.com.on-exception"
# These are the old index routes for the decision task.
# They are still here so external tools that referenced them continue to work.
- "index.gecko.v2.${repository.project}.latest.firefox.decision"
@ -96,6 +91,11 @@ tasks:
- "index.gecko.v2.${repository.project}.pushlog-id.${push.pushlog_id}.decision-${cron.job_name}"
# list each cron task on this revision, so actions can find them
- 'index.gecko.v2.${repository.project}.revision.${push.revision}.cron.${as_slugid("decision")}'
# BUG 1500166 Notify ciduty by email if a nightly hook fails
- "notify.email.ciduty+failedcron@mozilla.com.on-failed"
- "notify.email.ciduty+exceptioncron@mozilla.com.on-exception"
- "notify.email.sheriffs+failedcron@mozilla.org.on-failed"
- "notify.email.sheriffs+exceptioncron@mozilla.org.on-exception"
# These are the old index routes for the decision task.
- "index.gecko.v2.${repository.project}.latest.firefox.decision-${cron.job_name}"

View File

@ -1515,8 +1515,11 @@ pref("browser.ping-centre.production.endpoint", "https://tiles.services.mozilla.
// Enable GMP support in the addon manager.
pref("media.gmp-provider.enabled", true);
// Enable blocking access to storage from tracking resources by default
// Enable blocking access to storage from tracking resources only in nightly
// and early beta. By default the value is 0: BEHAVIOR_ACCEPT
#ifdef NIGHTLY_OR_EARLY_BETA
pref("network.cookie.cookieBehavior", 4 /* BEHAVIOR_REJECT_TRACKER */);
#endif
pref("browser.contentblocking.allowlist.storage.enabled", true);

View File

@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import HandleEventMixin from "../mixins/HandleEventMixin.js";
import ObservedPropertiesMixin from "../mixins/ObservedPropertiesMixin.js";
import RichOption from "./rich-option.js";
@ -13,7 +14,7 @@ import RichOption from "./rich-option.js";
* Note: The only supported way to change the selected option is via the
* `value` setter.
*/
export default class RichSelect extends ObservedPropertiesMixin(HTMLElement) {
export default class RichSelect extends HandleEventMixin(ObservedPropertiesMixin(HTMLElement)) {
static get observedAttributes() {
return [
"disabled",
@ -57,15 +58,10 @@ export default class RichSelect extends ObservedPropertiesMixin(HTMLElement) {
return this.popupBox.querySelector(`:scope > [value="${CSS.escape(value)}"]`);
}
handleEvent(event) {
switch (event.type) {
case "change": {
onChange(event) {
// Since the render function depends on the popupBox's value, we need to
// re-render if the value changes.
this.render();
break;
}
}
}
render() {

View File

@ -7,6 +7,7 @@ import LabelledCheckbox from "../components/labelled-checkbox.js";
import PaymentRequestPage from "../components/payment-request-page.js";
import PaymentStateSubscriberMixin from "../mixins/PaymentStateSubscriberMixin.js";
import paymentRequest from "../paymentRequest.js";
import HandleEventMixin from "../mixins/HandleEventMixin.js";
/* import-globals-from ../unprivileged-fallbacks.js */
/**
@ -20,7 +21,8 @@ import paymentRequest from "../paymentRequest.js";
* as it will be much easier to share the logic once we switch to Fluent.
*/
export default class AddressForm extends PaymentStateSubscriberMixin(PaymentRequestPage) {
export default class AddressForm extends
HandleEventMixin(PaymentStateSubscriberMixin(PaymentRequestPage)) {
constructor() {
super();
@ -216,32 +218,18 @@ export default class AddressForm extends PaymentStateSubscriberMixin(PaymentRequ
this.updateSaveButtonState();
}
handleEvent(event) {
switch (event.type) {
case "change": {
onChange(event) {
if (event.target.id == "country") {
this.updateRequiredState();
}
this.updateSaveButtonState();
break;
}
case "click": {
this.onClick(event);
break;
}
case "input": {
this.onInput(event);
break;
}
case "invalid": {
if (event.target instanceof HTMLFormElement) {
this.onInvalidForm(event);
break;
}
onInvalid(event) {
if (event.target instanceof HTMLFormElement) {
this.onInvalidForm(event);
} else {
this.onInvalidField(event);
break;
}
}
}

View File

@ -6,6 +6,7 @@ import AddressForm from "./address-form.js";
import AddressOption from "../components/address-option.js";
import RichPicker from "./rich-picker.js";
import paymentRequest from "../paymentRequest.js";
import HandleEventMixin from "../mixins/HandleEventMixin.js";
/**
* <address-picker></address-picker>
@ -13,7 +14,7 @@ import paymentRequest from "../paymentRequest.js";
* <address-option> listening to savedAddresses & tempAddresses.
*/
export default class AddressPicker extends RichPicker {
export default class AddressPicker extends HandleEventMixin(RichPicker) {
static get pickerAttributes() {
return [
"address-fields",
@ -197,18 +198,6 @@ export default class AddressPicker extends RichPicker {
}) || "";
}
handleEvent(event) {
switch (event.type) {
case "change": {
this.onChange(event);
break;
}
case "click": {
this.onClick(event);
}
}
}
onChange(event) {
let [selectedKey, selectedLeaf] = this.selectedStateKey.split("|");
if (!selectedKey) {

View File

@ -10,6 +10,7 @@ import LabelledCheckbox from "../components/labelled-checkbox.js";
import PaymentRequestPage from "../components/payment-request-page.js";
import PaymentStateSubscriberMixin from "../mixins/PaymentStateSubscriberMixin.js";
import paymentRequest from "../paymentRequest.js";
import HandleEventMixin from "../mixins/HandleEventMixin.js";
/* import-globals-from ../unprivileged-fallbacks.js */
@ -20,7 +21,8 @@ import paymentRequest from "../paymentRequest.js";
* as it will be much easier to share the logic once we switch to Fluent.
*/
export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRequestPage) {
export default class BasicCardForm extends
HandleEventMixin(PaymentStateSubscriberMixin(PaymentRequestPage)) {
constructor() {
super();
@ -268,32 +270,6 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
this.updateSaveButtonState();
}
handleEvent(event) {
switch (event.type) {
case "change": {
this.onChange(event);
break;
}
case "click": {
this.onClick(event);
break;
}
case "input": {
this.onInput(event);
break;
}
case "invalid": {
if (event.target instanceof HTMLFormElement) {
this.onInvalidForm(event);
break;
}
this.onInvalidField(event);
break;
}
}
}
onChange(evt) {
let ccType = this.form.querySelector("#cc-type");
this.cscInput.setAttribute("card-type", ccType.value);
@ -390,6 +366,14 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
this.updateSaveButtonState();
}
onInvalid(event) {
if (event.target instanceof HTMLFormElement) {
this.onInvalidForm(event);
} else {
this.onInvalidField(event);
}
}
/**
* @param {Event} event - "invalid" event
* Note: Keep this in-sync with the equivalent version in address-form.js

View File

@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import HandleEventMixin from "../mixins/HandleEventMixin.js";
import PaymentRequestPage from "../components/payment-request-page.js";
import PaymentStateSubscriberMixin from "../mixins/PaymentStateSubscriberMixin.js";
import paymentRequest from "../paymentRequest.js";
@ -15,7 +16,8 @@ import paymentRequest from "../paymentRequest.js";
* as it will be much easier to implement and share the logic once we switch to Fluent.
*/
export default class CompletionErrorPage extends PaymentStateSubscriberMixin(PaymentRequestPage) {
export default class CompletionErrorPage extends
HandleEventMixin(PaymentStateSubscriberMixin(PaymentRequestPage)) {
constructor() {
super();
@ -80,8 +82,7 @@ export default class CompletionErrorPage extends PaymentStateSubscriberMixin(Pay
this.suggestionsList.appendChild(suggestionsFragment);
}
handleEvent(event) {
if (event.type == "click") {
onClick(event) {
switch (event.target) {
case this.doneButton: {
this.onDoneButtonClick(event);
@ -92,7 +93,6 @@ export default class CompletionErrorPage extends PaymentStateSubscriberMixin(Pay
}
}
}
}
onDoneButtonClick(event) {
paymentRequest.closeDialog();

View File

@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import HandleEventMixin from "../mixins/HandleEventMixin.js";
import PaymentStateSubscriberMixin from "../mixins/PaymentStateSubscriberMixin.js";
import paymentRequest from "../paymentRequest.js";
@ -26,7 +27,8 @@ import "./shipping-option-picker.js";
* being exported once tests stop depending on it.
*/
export default class PaymentDialog extends PaymentStateSubscriberMixin(HTMLElement) {
export default class PaymentDialog extends
HandleEventMixin(PaymentStateSubscriberMixin(HTMLElement)) {
constructor() {
super();
this._template = document.getElementById("payment-dialog-template");
@ -77,8 +79,7 @@ export default class PaymentDialog extends PaymentStateSubscriberMixin(HTMLEleme
super.disconnectedCallback();
}
handleEvent(event) {
if (event.type == "click") {
onClick(event) {
switch (event.currentTarget) {
case this._viewAllButton:
let orderDetailsShowing = !this.requestStore.getState().orderDetailsShowing;
@ -94,7 +95,6 @@ export default class PaymentDialog extends PaymentStateSubscriberMixin(HTMLEleme
break;
}
}
}
openPreferences(event) {
paymentRequest.openPreferences();

View File

@ -4,8 +4,10 @@
import BasicCardOption from "../components/basic-card-option.js";
import CscInput from "../components/csc-input.js";
import HandleEventMixin from "../mixins/HandleEventMixin.js";
import RichPicker from "./rich-picker.js";
import paymentRequest from "../paymentRequest.js";
/* import-globals-from ../unprivileged-fallbacks.js */
/**
@ -14,7 +16,7 @@ import paymentRequest from "../paymentRequest.js";
* <basic-card-option> listening to savedBasicCards.
*/
export default class PaymentMethodPicker extends RichPicker {
export default class PaymentMethodPicker extends HandleEventMixin(RichPicker) {
constructor() {
super();
this.dropdown.setAttribute("option-type", "basic-card-option");
@ -115,18 +117,12 @@ export default class PaymentMethodPicker extends RichPicker {
return this.getAttribute("selected-state-key");
}
handleEvent(event) {
switch (event.type) {
case "input":
case "change": {
onInput(event) {
this.onInputOrChange(event);
break;
}
case "click": {
this.onClick(event);
break;
}
}
onChange(event) {
this.onInputOrChange(event);
}
onInputOrChange({currentTarget}) {

View File

@ -4,6 +4,7 @@
import RichPicker from "./rich-picker.js";
import ShippingOption from "../components/shipping-option.js";
import HandleEventMixin from "../mixins/HandleEventMixin.js";
/**
* <shipping-option-picker></shipping-option-picker>
@ -11,7 +12,7 @@ import ShippingOption from "../components/shipping-option.js";
* <option> listening to shippingOptions.
*/
export default class ShippingOptionPicker extends RichPicker {
export default class ShippingOptionPicker extends HandleEventMixin(RichPicker) {
constructor() {
super();
this.dropdown.setAttribute("option-type", "shipping-option");
@ -55,15 +56,6 @@ export default class ShippingOptionPicker extends RichPicker {
}
}
handleEvent(event) {
switch (event.type) {
case "change": {
this.onChange(event);
break;
}
}
}
onChange(event) {
let selectedOptionId = this.dropdown.value;
this.requestStore.setState({

View File

@ -0,0 +1,28 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* A mixin to forward events to on* methods if defined.
*
* @param {string} superclass The class to extend.
* @returns {class}
*/
export default function HandleEventMixin(superclass) {
return class HandleEvent extends superclass {
handleEvent(evt) {
function capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
if (super.handleEvent) {
super.handleEvent(evt);
}
// Check whether event name is a defined function in object.
let fn = "on" + capitalize(evt.type);
if (this[fn] && typeof(this[fn]) === "function") {
return this[fn](evt);
}
return null;
}
};
}

View File

@ -232,7 +232,7 @@ add_task(async function testContentBlockingCustomCategory() {
// Changing the NCB_PREF should necessarily set CAT_PREF to "custom"
Services.prefs.setIntPref(NCB_PREF, Ci.nsICookieService.BEHAVIOR_ACCEPT);
await TestUtils.waitForCondition(() => Services.prefs.prefHasUserValue(NCB_PREF));
await TestUtils.waitForCondition(() => !Services.prefs.prefHasUserValue(NCB_PREF));
is(Services.prefs.getStringPref(CAT_PREF), "custom", `${CAT_PREF} has been set to custom`);
gBrowser.removeCurrentTab();

View File

@ -3,8 +3,5 @@
ac_add_options --target=aarch64-windows-mingw32
ac_add_options --host=x86_64-pc-mingw32
# WebRTC is busted in various ways.
ac_add_options --disable-webrtc
# Accessibility doesn't work.
ac_add_options --disable-accessibility

View File

@ -10,6 +10,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -25,6 +27,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -40,6 +44,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -55,6 +61,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -70,6 +78,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -85,6 +95,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -100,6 +112,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -115,6 +129,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -130,6 +146,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -145,6 +163,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -160,6 +180,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -175,6 +197,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -190,6 +214,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -205,6 +231,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -220,6 +248,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -235,6 +265,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -250,6 +282,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -265,6 +299,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -280,6 +316,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -295,6 +333,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -310,6 +350,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -325,6 +367,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -340,6 +384,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -355,6 +401,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -370,6 +418,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -385,6 +435,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -400,6 +452,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -415,6 +469,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -430,6 +486,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -445,6 +503,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -460,6 +520,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -475,6 +537,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -490,6 +554,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -505,6 +571,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -520,6 +588,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -535,6 +605,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -550,6 +622,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -565,6 +639,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -580,6 +656,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -595,6 +673,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -610,6 +690,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -625,6 +707,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -640,6 +724,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -655,6 +741,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -670,6 +758,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -685,6 +775,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -700,6 +792,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -715,6 +809,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -730,6 +826,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -745,6 +843,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -760,6 +860,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -775,6 +877,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -788,6 +892,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -810,6 +916,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -825,6 +933,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -840,6 +950,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -855,6 +967,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -870,6 +984,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -885,6 +1001,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -900,6 +1018,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -915,6 +1035,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -930,6 +1052,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -945,6 +1069,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -960,6 +1086,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -975,6 +1103,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -990,6 +1120,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1005,6 +1137,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1020,6 +1154,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1035,6 +1171,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1050,6 +1188,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1065,6 +1205,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1080,6 +1222,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1095,6 +1239,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1110,6 +1256,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1125,6 +1273,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1140,6 +1290,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1155,6 +1307,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1170,6 +1324,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1185,6 +1341,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1200,6 +1358,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1215,6 +1375,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1230,6 +1392,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1245,6 +1409,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1260,6 +1426,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1275,6 +1443,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1290,6 +1460,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1305,6 +1477,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1320,6 +1494,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1335,6 +1511,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1350,6 +1528,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1365,6 +1545,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1380,6 +1562,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1395,6 +1579,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1410,6 +1596,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1425,6 +1613,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1440,6 +1630,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1455,6 +1647,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1470,6 +1664,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1485,6 +1681,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1500,6 +1698,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1515,6 +1715,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1530,6 +1732,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1545,6 +1749,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"
@ -1560,6 +1766,8 @@
"win32",
"win32-devedition",
"win64",
"win64-aarch64-msvc",
"win64-aarch64-msvc-devedition",
"win64-devedition"
],
"revision": "default"

View File

@ -4330,6 +4330,9 @@ bool Datastore::UpdateUsage(int64_t aDelta) {
// Check internal LocalStorage origin limit.
int64_t newUsage = mUsage + aDelta;
MOZ_ASSERT(newUsage >= 0);
if (newUsage > gOriginLimitKB * 1024) {
return false;
}
@ -5625,7 +5628,8 @@ nsresult PrepareDatastoreOp::DatabaseWork() {
if (alreadyExisted) {
MOZ_ASSERT(gUsages);
MOZ_ASSERT(gUsages->Get(mOrigin, &mUsage));
DebugOnly<bool> hasUsage = gUsages->Get(mOrigin, &mUsage);
MOZ_ASSERT(hasUsage);
} else {
MOZ_ASSERT(mUsage == 0);
InitUsageForOrigin(mOrigin, mUsage);
@ -6911,6 +6915,8 @@ nsresult QuotaClient::InitOrigin(PersistenceType aPersistenceType,
return rv;
}
MOZ_ASSERT(usage >= 0);
InitUsageForOrigin(aOrigin, usage);
aUsageInfo->AppendToDatabaseUsage(uint64_t(usage));
@ -6988,6 +6994,7 @@ nsresult QuotaClient::GetUsageForOrigin(PersistenceType aPersistenceType,
if (gUsages) {
int64_t usage;
if (gUsages->Get(aOrigin, &usage)) {
MOZ_ASSERT(usage >= 0);
aUsageInfo->AppendToDatabaseUsage(usage);
}
}

View File

@ -0,0 +1,66 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
async function testSteps() {
const data = {};
data.key = "key1";
data.value = "value1";
data.usage = data.key.length + data.value.length;
const principal = getPrincipal("http://example.com");
info("Setting prefs");
Services.prefs.setBoolPref("dom.storage.next_gen", true);
info("Stage 1 - Testing usage after adding item");
info("Getting storage");
let storage = getLocalStorage(principal);
info("Adding item");
storage.setItem(data.key, data.value);
info("Resetting origin");
let request = resetOrigin(principal);
await requestFinished(request);
info("Getting usage");
request = getOriginUsage(principal);
await requestFinished(request);
is(request.result.usage, data.usage, "Correct usage");
info("Resetting");
request = reset();
await requestFinished(request);
info("Stage 2 - Testing usage after removing item");
info("Getting storage");
storage = getLocalStorage(principal);
info("Removing item");
storage.removeItem(data.key);
info("Resetting origin");
request = resetOrigin(principal);
await requestFinished(request);
info("Getting usage");
request = getOriginUsage(principal);
await requestFinished(request);
is(request.result.usage, 0, "Correct usage");
}

View File

@ -29,3 +29,4 @@ run-sequentially = this test depends on a file produced by test_databaseShadowin
[test_groupLimit.js]
[test_migration.js]
[test_snapshotting.js]
[test_usage.js]

View File

@ -40,6 +40,7 @@ run-if = nightly_build # Bug 1390737: Depends on the Nightly-only UI service
skip-if = debug # Bug 1507251 - Leak
[test_closePayment.html]
[test_constructor.html]
skip-if = (os == "linux") # Bug 1514425
[test_currency_amount_validation.html]
skip-if = (verify && debug)
[test_payerDetails.html]

View File

@ -25,7 +25,6 @@ namespace detail {
/* static */
UniquePtr<BumpChunk> BumpChunk::newWithCapacity(size_t size) {
MOZ_DIAGNOSTIC_ASSERT(RoundUpPow2(size) == size);
MOZ_DIAGNOSTIC_ASSERT(size >= sizeof(BumpChunk));
void* mem = js_malloc(size);
if (!mem) {
@ -104,12 +103,17 @@ void LifoAlloc::reset(size_t defaultChunkSize) {
while (!chunks_.empty()) {
chunks_.popFirst();
}
while (!oversize_.empty()) {
chunks_.popFirst();
}
while (!unused_.empty()) {
unused_.popFirst();
}
defaultChunkSize_ = defaultChunkSize;
oversizeThreshold_ = defaultChunkSize;
markCount = 0;
curSize_ = 0;
oversizeSize_ = 0;
}
void LifoAlloc::freeAll() {
@ -117,6 +121,11 @@ void LifoAlloc::freeAll() {
UniqueBumpChunk bc = chunks_.popFirst();
decrementCurSize(bc->computedSizeOfIncludingThis());
}
while (!oversize_.empty()) {
UniqueBumpChunk bc = oversize_.popFirst();
decrementCurSize(bc->computedSizeOfIncludingThis());
oversizeSize_ -= bc->computedSizeOfIncludingThis();
}
while (!unused_.empty()) {
UniqueBumpChunk bc = unused_.popFirst();
decrementCurSize(bc->computedSizeOfIncludingThis());
@ -125,9 +134,36 @@ void LifoAlloc::freeAll() {
// Nb: maintaining curSize_ correctly isn't easy. Fortunately, this is an
// excellent sanity check.
MOZ_ASSERT(curSize_ == 0);
MOZ_ASSERT(oversizeSize_ == 0);
}
LifoAlloc::UniqueBumpChunk LifoAlloc::newChunkWithCapacity(size_t n) {
// Round at the same page granularity used by malloc.
static size_t MallocGoodSize(size_t aSize) {
#if defined(MOZ_MEMORY)
return malloc_good_size(aSize);
#else
return aSize;
#endif
}
// Heuristic to choose the size of the next BumpChunk for small allocations.
// `start` is the size of the first chunk. `used` is the total size of all
// BumpChunks in this LifoAlloc so far.
static size_t NextSize(size_t start, size_t used) {
// Double the size, up to 1 MB.
const size_t mb = 1 * 1024 * 1024;
if (used < mb) {
return Max(start, used);
}
// After 1 MB, grow more gradually, to waste less memory.
// The sequence (in megabytes) begins:
// 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5, ...
return JS_ROUNDUP(used / 8, mb);
}
LifoAlloc::UniqueBumpChunk LifoAlloc::newChunkWithCapacity(size_t n,
bool oversize) {
MOZ_ASSERT(fallibleScope_,
"[OOM] Cannot allocate a new chunk in an infallible scope.");
@ -140,8 +176,11 @@ LifoAlloc::UniqueBumpChunk LifoAlloc::newChunkWithCapacity(size_t n) {
return nullptr;
}
MOZ_ASSERT(curSize_ >= oversizeSize_);
const size_t chunkSize =
minSize > defaultChunkSize_ ? RoundUpPow2(minSize) : defaultChunkSize_;
(oversize || minSize > defaultChunkSize_)
? MallocGoodSize(minSize)
: NextSize(defaultChunkSize_, curSize_ - oversizeSize_);
// Create a new BumpChunk, and allocate space for it.
UniqueBumpChunk result = detail::BumpChunk::newWithCapacity(chunkSize);
@ -152,14 +191,13 @@ LifoAlloc::UniqueBumpChunk LifoAlloc::newChunkWithCapacity(size_t n) {
return result;
}
bool LifoAlloc::getOrCreateChunk(size_t n) {
LifoAlloc::UniqueBumpChunk LifoAlloc::getOrCreateChunk(size_t n) {
// Look for existing unused BumpChunks to satisfy the request, and pick the
// first one which is large enough, and move it into the list of used
// chunks.
if (!unused_.empty()) {
if (unused_.begin()->canAlloc(n)) {
chunks_.append(unused_.popFirst());
return true;
return unused_.popFirst();
}
BumpChunkList::Iterator e(unused_.end());
@ -169,32 +207,171 @@ bool LifoAlloc::getOrCreateChunk(size_t n) {
MOZ_ASSERT(elem->empty());
if (elem->canAlloc(n)) {
BumpChunkList temp = unused_.splitAfter(i.get());
chunks_.append(temp.popFirst());
UniqueBumpChunk newChunk = temp.popFirst();
unused_.appendAll(std::move(temp));
return true;
return newChunk;
}
}
}
// Allocate a new BumpChunk with enough space for the next allocation.
UniqueBumpChunk newChunk = newChunkWithCapacity(n);
UniqueBumpChunk newChunk = newChunkWithCapacity(n, false);
if (!newChunk) {
return newChunk;
}
size_t size = newChunk->computedSizeOfIncludingThis();
incrementCurSize(size);
return newChunk;
}
void* LifoAlloc::allocImplColdPath(size_t n) {
void* result;
UniqueBumpChunk newChunk = getOrCreateChunk(n);
if (!newChunk) {
return nullptr;
}
// Since we just created a large enough chunk, this can't fail.
chunks_.append(std::move(newChunk));
result = chunks_.last()->tryAlloc(n);
MOZ_ASSERT(result);
return result;
}
void* LifoAlloc::allocImplOversize(size_t n) {
void* result;
UniqueBumpChunk newChunk = newChunkWithCapacity(n, true);
if (!newChunk) {
return nullptr;
}
incrementCurSize(newChunk->computedSizeOfIncludingThis());
oversizeSize_ += newChunk->computedSizeOfIncludingThis();
// Since we just created a large enough chunk, this can't fail.
oversize_.append(std::move(newChunk));
result = oversize_.last()->tryAlloc(n);
MOZ_ASSERT(result);
return result;
}
bool LifoAlloc::ensureUnusedApproximateColdPath(size_t n, size_t total) {
for (detail::BumpChunk& bc : unused_) {
total += bc.unused();
if (total >= n) {
return true;
}
}
UniqueBumpChunk newChunk = newChunkWithCapacity(n, false);
if (!newChunk) {
return false;
}
size_t size = newChunk->computedSizeOfIncludingThis();
chunks_.append(std::move(newChunk));
unused_.pushFront(std::move(newChunk));
incrementCurSize(size);
return true;
}
LifoAlloc::Mark LifoAlloc::mark() {
markCount++;
Mark res;
if (!chunks_.empty()) {
res.chunk = chunks_.last()->mark();
}
if (!oversize_.empty()) {
res.oversize = oversize_.last()->mark();
}
return res;
}
void LifoAlloc::release(Mark mark) {
markCount--;
#ifdef DEBUG
auto assertIsContained = [](const detail::BumpChunk::Mark& m,
BumpChunkList& list) {
if (m.markedChunk()) {
bool contained = false;
for (const detail::BumpChunk& chunk : list) {
if (&chunk == m.markedChunk() && chunk.contains(m)) {
contained = true;
break;
}
}
MOZ_ASSERT(contained);
}
};
assertIsContained(mark.chunk, chunks_);
assertIsContained(mark.oversize, oversize_);
#endif
BumpChunkList released;
auto cutAtMark = [&released](const detail::BumpChunk::Mark& m,
BumpChunkList& list) {
// Move the blocks which are after the mark to the set released chunks.
if (!m.markedChunk()) {
released = std::move(list);
} else {
released = list.splitAfter(m.markedChunk());
}
// Release everything which follows the mark in the last chunk.
if (!list.empty()) {
list.last()->release(m);
}
};
// Release the content of all the blocks which are after the marks, and keep
// blocks as unused.
cutAtMark(mark.chunk, chunks_);
for (detail::BumpChunk& bc : released) {
bc.release();
}
unused_.appendAll(std::move(released));
// Free the content of all the blocks which are after the marks.
cutAtMark(mark.oversize, oversize_);
while (!released.empty()) {
UniqueBumpChunk bc = released.popFirst();
decrementCurSize(bc->computedSizeOfIncludingThis());
oversizeSize_ -= bc->computedSizeOfIncludingThis();
}
}
void LifoAlloc::steal(LifoAlloc* other) {
MOZ_ASSERT(!other->markCount);
MOZ_DIAGNOSTIC_ASSERT(unused_.empty());
MOZ_DIAGNOSTIC_ASSERT(chunks_.empty());
MOZ_DIAGNOSTIC_ASSERT(oversize_.empty());
// Copy everything from |other| to |this| except for |peakSize_|, which
// requires some care.
chunks_ = std::move(other->chunks_);
oversize_ = std::move(other->oversize_);
unused_ = std::move(other->unused_);
markCount = other->markCount;
defaultChunkSize_ = other->defaultChunkSize_;
oversizeThreshold_ = other->oversizeThreshold_;
curSize_ = other->curSize_;
peakSize_ = Max(peakSize_, other->peakSize_);
oversizeSize_ = other->oversizeSize_;
#if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
fallibleScope_ = other->fallibleScope_;
#endif
other->reset(defaultChunkSize_);
}
void LifoAlloc::transferFrom(LifoAlloc* other) {
MOZ_ASSERT(!markCount);
MOZ_ASSERT(!other->markCount);
incrementCurSize(other->curSize_);
oversizeSize_ += other->oversizeSize_;
appendUnused(std::move(other->unused_));
appendUsed(std::move(other->chunks_));
oversize_.appendAll(std::move(other->oversize_));
other->curSize_ = 0;
other->oversizeSize_ = 0;
}
void LifoAlloc::transferUnusedFrom(LifoAlloc* other) {
@ -209,3 +386,29 @@ void LifoAlloc::transferUnusedFrom(LifoAlloc* other) {
incrementCurSize(size);
other->decrementCurSize(size);
}
#ifdef LIFO_CHUNK_PROTECT
void LifoAlloc::setReadOnly() {
for (detail::BumpChunk& bc : chunks_) {
bc.setReadOnly();
}
for (detail::BumpChunk& bc : oversize_) {
bc.setReadOnly();
}
for (detail::BumpChunk& bc : unused_) {
bc.setReadOnly();
}
}
void LifoAlloc::setReadWrite() {
for (detail::BumpChunk& bc : chunks_) {
bc.setReadWrite();
}
for (detail::BumpChunk& bc : oversize_) {
bc.setReadWrite();
}
for (detail::BumpChunk& bc : unused_) {
bc.setReadWrite();
}
}
#endif

View File

@ -362,8 +362,7 @@ class BumpChunk : public SingleLinkedListElement<BumpChunk> {
uint8_t* bump_;
friend class BumpChunk;
explicit Mark(BumpChunk* chunk, uint8_t* bump)
: chunk_(chunk), bump_(bump) {}
Mark(BumpChunk* chunk, uint8_t* bump) : chunk_(chunk), bump_(bump) {}
public:
Mark() : chunk_(nullptr), bump_(nullptr) {}
@ -497,19 +496,29 @@ class LifoAlloc {
using UniqueBumpChunk = js::UniquePtr<detail::BumpChunk>;
using BumpChunkList = detail::SingleLinkedList<detail::BumpChunk>;
// List of chunks containing allocated data. In the common case, the last
// chunk of this list is always used to perform the allocations. When the
// allocation cannot be performed, we move a Chunk from the unused set to
// the list of used chunks.
// List of chunks containing allocated data of size smaller than the default
// chunk size. In the common case, the last chunk of this list is always
// used to perform the allocations. When the allocation cannot be performed,
// we move a Chunk from the unused set to the list of used chunks.
BumpChunkList chunks_;
// List of chunks containing allocated data where each allocation is larger
// than the oversize threshold. Each chunk contains exactly on allocation.
// This reduces wasted space in the normal chunk list.
//
// Oversize chunks are allocated on demand and freed as soon as they are
// released, instead of being pushed to the unused list.
BumpChunkList oversize_;
// Set of unused chunks, which can be reused for future allocations.
BumpChunkList unused_;
size_t markCount;
size_t defaultChunkSize_;
size_t oversizeThreshold_;
size_t curSize_;
size_t peakSize_;
size_t oversizeSize_;
#if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
bool fallibleScope_;
#endif
@ -518,11 +527,11 @@ class LifoAlloc {
LifoAlloc(const LifoAlloc&) = delete;
// Return a BumpChunk that can perform an allocation of at least size |n|.
UniqueBumpChunk newChunkWithCapacity(size_t n);
UniqueBumpChunk newChunkWithCapacity(size_t n, bool oversize);
// Reuse or allocate a BumpChunk that can perform an allocation of at least
// size |n|, if successful it is placed at the end the list of |chunks_|.
MOZ_MUST_USE bool getOrCreateChunk(size_t n);
UniqueBumpChunk getOrCreateChunk(size_t n);
void reset(size_t defaultChunkSize);
@ -554,22 +563,26 @@ class LifoAlloc {
curSize_ -= size;
}
void* allocImplColdPath(size_t n);
void* allocImplOversize(size_t n);
MOZ_ALWAYS_INLINE
void* allocImpl(size_t n) {
void* result;
if (!chunks_.empty() && (result = chunks_.last()->tryAlloc(n))) {
// Give oversized allocations their own chunk instead of wasting space
// due to fragmentation at the end of normal chunk.
if (MOZ_UNLIKELY(n > oversizeThreshold_)) {
return allocImplOversize(n);
}
if (MOZ_LIKELY(!chunks_.empty() &&
(result = chunks_.last()->tryAlloc(n)))) {
return result;
}
if (!getOrCreateChunk(n)) {
return nullptr;
return allocImplColdPath(n);
}
// Since we just created a large enough chunk, this can't fail.
result = chunks_.last()->tryAlloc(n);
MOZ_ASSERT(result);
return result;
}
// Check for space in unused chunks or allocate a new unused chunk.
MOZ_MUST_USE bool ensureUnusedApproximateColdPath(size_t n, size_t total);
public:
explicit LifoAlloc(size_t defaultChunkSize)
@ -582,27 +595,17 @@ class LifoAlloc {
reset(defaultChunkSize);
}
// Steal allocated chunks from |other|.
void steal(LifoAlloc* other) {
MOZ_ASSERT(!other->markCount);
MOZ_DIAGNOSTIC_ASSERT(unused_.empty());
MOZ_DIAGNOSTIC_ASSERT(chunks_.empty());
// Copy everything from |other| to |this| except for |peakSize_|, which
// requires some care.
chunks_ = std::move(other->chunks_);
unused_ = std::move(other->unused_);
markCount = other->markCount;
defaultChunkSize_ = other->defaultChunkSize_;
curSize_ = other->curSize_;
peakSize_ = Max(peakSize_, other->peakSize_);
#if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
fallibleScope_ = other->fallibleScope_;
#endif
other->reset(defaultChunkSize_);
// Set the threshold to allocate data in its own chunk outside the space for
// small allocations.
void disableOversize() { oversizeThreshold_ = SIZE_MAX; }
void setOversizeThreshold(size_t oversizeThreshold) {
MOZ_ASSERT(oversizeThreshold <= defaultChunkSize_);
oversizeThreshold_ = oversizeThreshold;
}
// Steal allocated chunks from |other|.
void steal(LifoAlloc* other);
// Append all chunks from |other|. They are removed from |other|.
void transferFrom(LifoAlloc* other);
@ -644,7 +647,7 @@ class LifoAlloc {
JS_OOM_POSSIBLY_FAIL();
MOZ_ASSERT(fallibleScope_);
detail::BumpChunk::Mark m = mark();
Mark m = mark();
void* result = allocImpl(n);
if (!ensureUnusedApproximate(needed)) {
release(m);
@ -655,7 +658,7 @@ class LifoAlloc {
}
template <typename T, typename... Args>
MOZ_ALWAYS_INLINE T* allocInSize(size_t n, Args&&... args) {
MOZ_ALWAYS_INLINE T* newWithSize(size_t n, Args&&... args) {
MOZ_ASSERT(n >= sizeof(T), "must request enough space to store a T");
static_assert(alignof(T) <= detail::LIFO_ALLOC_ALIGN,
"LifoAlloc must provide enough alignment to store T");
@ -691,21 +694,7 @@ class LifoAlloc {
}
}
for (detail::BumpChunk& bc : unused_) {
total += bc.unused();
if (total >= n) {
return true;
}
}
UniqueBumpChunk newChunk = newChunkWithCapacity(n);
if (!newChunk) {
return false;
}
size_t size = newChunk->computedSizeOfIncludingThis();
unused_.pushFront(std::move(newChunk));
incrementCurSize(size);
return true;
return ensureUnusedApproximateColdPath(n, total);
}
MOZ_ALWAYS_INLINE
@ -756,38 +745,13 @@ class LifoAlloc {
return static_cast<T*>(alloc(bytes));
}
using Mark = detail::BumpChunk::Mark;
Mark mark() {
markCount++;
if (chunks_.empty()) {
return Mark();
}
return chunks_.last()->mark();
}
void release(Mark mark) {
markCount--;
// Move the blocks which are after the mark to the set of unused chunks.
BumpChunkList released;
if (!mark.markedChunk()) {
released = std::move(chunks_);
} else {
released = chunks_.splitAfter(mark.markedChunk());
}
// Release the content of all the blocks which are after the marks.
for (detail::BumpChunk& bc : released) {
bc.release();
}
unused_.appendAll(std::move(released));
// Release everything which follows the mark in the last chunk.
if (!chunks_.empty()) {
chunks_.last()->release(mark);
}
}
class Mark {
friend class LifoAlloc;
detail::BumpChunk::Mark chunk;
detail::BumpChunk::Mark oversize;
};
Mark mark();
void release(Mark mark);
private:
void cancelMark(Mark mark) { markCount--; }
@ -799,26 +763,19 @@ class LifoAlloc {
bc.release();
}
unused_.appendAll(std::move(chunks_));
// On release, we free any oversize allocations instead of keeping them
// in unused chunks.
while (!oversize_.empty()) {
UniqueBumpChunk bc = oversize_.popFirst();
decrementCurSize(bc->computedSizeOfIncludingThis());
oversizeSize_ -= bc->computedSizeOfIncludingThis();
}
}
// Protect the content of the LifoAlloc chunks.
#ifdef LIFO_CHUNK_PROTECT
void setReadOnly() {
for (detail::BumpChunk& bc : chunks_) {
bc.setReadOnly();
}
for (detail::BumpChunk& bc : unused_) {
bc.setReadOnly();
}
}
void setReadWrite() {
for (detail::BumpChunk& bc : chunks_) {
bc.setReadWrite();
}
for (detail::BumpChunk& bc : unused_) {
bc.setReadWrite();
}
}
void setReadOnly();
void setReadWrite();
#else
void setReadOnly() const {}
void setReadWrite() const {}
@ -835,8 +792,10 @@ class LifoAlloc {
// Return true if the LifoAlloc does not currently contain any allocations.
bool isEmpty() const {
return chunks_.empty() ||
bool empty = chunks_.empty() ||
(chunks_.begin() == chunks_.last() && chunks_.last()->empty());
MOZ_ASSERT_IF(!oversize_.empty(), !oversize_.last()->empty());
return empty && oversize_.empty();
}
// Return the number of bytes remaining to allocate in the current chunk.
@ -854,6 +813,9 @@ class LifoAlloc {
for (const detail::BumpChunk& chunk : chunks_) {
n += chunk.sizeOfIncludingThis(mallocSizeOf);
}
for (const detail::BumpChunk& chunk : oversize_) {
n += chunk.sizeOfIncludingThis(mallocSizeOf);
}
for (const detail::BumpChunk& chunk : unused_) {
n += chunk.sizeOfIncludingThis(mallocSizeOf);
}
@ -866,6 +828,9 @@ class LifoAlloc {
for (const detail::BumpChunk& chunk : chunks_) {
n += chunk.computedSizeOfIncludingThis();
}
for (const detail::BumpChunk& chunk : oversize_) {
n += chunk.computedSizeOfIncludingThis();
}
for (const detail::BumpChunk& chunk : unused_) {
n += chunk.computedSizeOfIncludingThis();
}
@ -897,6 +862,11 @@ class LifoAlloc {
return true;
}
}
for (const detail::BumpChunk& chunk : oversize_) {
if (chunk.contains(ptr)) {
return true;
}
}
return false;
}
#endif
@ -935,6 +905,7 @@ class LifoAlloc {
: chunkIt_(alloc.chunks_.begin()),
chunkEnd_(alloc.chunks_.end()),
head_(nullptr) {
MOZ_RELEASE_ASSERT(alloc.oversize_.empty());
if (chunkIt_ != chunkEnd_) {
head_ = chunkIt_->begin();
}

View File

@ -1043,7 +1043,7 @@ typename Scope::Data* NewEmptyBindingData(JSContext* cx, LifoAlloc& alloc,
uint32_t numBindings) {
using Data = typename Scope::Data;
size_t allocSize = SizeOfData<typename Scope::Data>(numBindings);
auto* bindings = alloc.allocInSize<Data>(allocSize, numBindings);
auto* bindings = alloc.newWithSize<Data>(allocSize, numBindings);
if (!bindings) {
ReportOutOfMemory(cx);
}

View File

@ -148,6 +148,12 @@ class StoreBuffer {
MOZ_ASSERT(!head_);
if (!storage_) {
storage_ = js_new<LifoAlloc>(LifoAllocBlockSize);
// This prevents LifoAlloc::Enum from crashing with a release
// assertion if we ever allocate one entry larger than
// LifoAllocBlockSize.
if (storage_) {
storage_->disableOversize();
}
}
clear();
return bool(storage_);

View File

@ -1,4 +1,4 @@
// |jit-test| allow-overrecursed
// |jit-test| slow; allow-overrecursed
(function f(x) {
f(x - 1);

View File

@ -10,7 +10,11 @@
#include "common_audio/fir_filter_neon.h"
#if defined(_MSC_VER) && !defined(__clang__)
#include <arm64_neon.h>
#else
#include <arm_neon.h>
#endif
#include <string.h>
#include "rtc_base/checks.h"

View File

@ -13,7 +13,11 @@
#include "common_audio/resampler/sinc_resampler.h"
#if defined(_MSC_VER) && !defined(__clang__)
#include <arm64_neon.h>
#else
#include <arm_neon.h>
#endif
namespace webrtc {

View File

@ -10,7 +10,11 @@
#include "common_audio/signal_processing/include/signal_processing_library.h"
#if defined(_MSC_VER) && !defined(__clang__)
#include <arm64_neon.h>
#else
#include <arm_neon.h>
#endif
static inline void DotProductWithScaleNeon(int32_t* cross_correlation,
const int16_t* vector1,
@ -51,7 +55,7 @@ static inline void DotProductWithScaleNeon(int32_t* cross_correlation,
}
sum0 = vaddq_s64(sum0, sum1);
#if defined(WEBRTC_ARCH_ARM64)
#if defined(WEBRTC_ARCH_ARM64) && (!defined(_MSC_VER) || defined(__clang__))
int64_t sum2 = vaddvq_s64(sum0);
*cross_correlation = (int32_t)((sum2 + sum_res) >> scaling);
#else

View File

@ -10,7 +10,11 @@
#include "common_audio/signal_processing/include/signal_processing_library.h"
#if defined(_MSC_VER) && !defined(__clang__)
#include <arm64_neon.h>
#else
#include <arm_neon.h>
#endif
// NEON intrinsics version of WebRtcSpl_DownsampleFast()
// for ARM 32-bit/64-bit platforms.

View File

@ -8,7 +8,11 @@
* be found in the AUTHORS file in the root of the source tree.
*/
#if defined(_MSC_VER) && !defined(__clang__)
#include <arm64_neon.h>
#else
#include <arm_neon.h>
#endif
#include <stdlib.h>
#include "rtc_base/checks.h"
@ -37,7 +41,7 @@ int16_t WebRtcSpl_MaxAbsValueW16Neon(const int16_t* vector, size_t length) {
p_start += 8;
}
#ifdef WEBRTC_ARCH_ARM64
#if defined(WEBRTC_ARCH_ARM64) && (!defined(_MSC_VER) || defined(__clang__))
maximum = (int)vmaxvq_u16(max_qv);
#else
uint16x4_t max_dv;
@ -97,7 +101,7 @@ int32_t WebRtcSpl_MaxAbsValueW32Neon(const int32_t* vector, size_t length) {
}
uint32x4_t max32x4 = vmaxq_u32(max32x4_0, max32x4_1);
#if defined(WEBRTC_ARCH_ARM64)
#if defined(WEBRTC_ARCH_ARM64) && (!defined(_MSC_VER) || defined(__clang__))
maximum = vmaxvq_u32(max32x4);
#else
uint32x2_t max32x2 = vmax_u32(vget_low_u32(max32x4), vget_high_u32(max32x4));
@ -140,7 +144,7 @@ int16_t WebRtcSpl_MaxValueW16Neon(const int16_t* vector, size_t length) {
p_start += 8;
}
#if defined(WEBRTC_ARCH_ARM64)
#if defined(WEBRTC_ARCH_ARM64) && (!defined(_MSC_VER) || defined(__clang__))
maximum = vmaxvq_s16(max16x8);
#else
int16x4_t max16x4 = vmax_s16(vget_low_s16(max16x8), vget_high_s16(max16x8));
@ -183,7 +187,7 @@ int32_t WebRtcSpl_MaxValueW32Neon(const int32_t* vector, size_t length) {
}
int32x4_t max32x4 = vmaxq_s32(max32x4_0, max32x4_1);
#if defined(WEBRTC_ARCH_ARM64)
#if defined(WEBRTC_ARCH_ARM64) && (!defined(_MSC_VER) || defined(__clang__))
maximum = vmaxvq_s32(max32x4);
#else
int32x2_t max32x2 = vmax_s32(vget_low_s32(max32x4), vget_high_s32(max32x4));
@ -220,7 +224,7 @@ int16_t WebRtcSpl_MinValueW16Neon(const int16_t* vector, size_t length) {
p_start += 8;
}
#if defined(WEBRTC_ARCH_ARM64)
#if defined(WEBRTC_ARCH_ARM64) && (!defined(_MSC_VER) || defined(__clang__))
minimum = vminvq_s16(min16x8);
#else
int16x4_t min16x4 = vmin_s16(vget_low_s16(min16x8), vget_high_s16(min16x8));
@ -263,7 +267,7 @@ int32_t WebRtcSpl_MinValueW32Neon(const int32_t* vector, size_t length) {
}
int32x4_t min32x4 = vminq_s32(min32x4_0, min32x4_1);
#if defined(WEBRTC_ARCH_ARM64)
#if defined(WEBRTC_ARCH_ARM64) && (!defined(_MSC_VER) || defined(__clang__))
minimum = vminvq_s32(min32x4);
#else
int32x2_t min32x2 = vmin_s32(vget_low_s32(min32x4), vget_high_s32(min32x4));

View File

@ -16,7 +16,11 @@
#include "entropy_coding.h"
#if defined(_MSC_VER) && !defined(__clang__)
#include <arm64_neon.h>
#else
#include <arm_neon.h>
#endif
#include <stddef.h>
#include "signal_processing_library.h"

View File

@ -13,7 +13,11 @@
// WebRtcIsacfix_AllpassFilter2FixDec16C() in filterbanks.c. Prototype
// C code is at end of this file.
#if defined(_MSC_VER) && !defined(__clang__)
#include <arm64_neon.h>
#else
#include <arm_neon.h>
#endif
#include "rtc_base/checks.h"

View File

@ -8,7 +8,11 @@
* be found in the AUTHORS file in the root of the source tree.
*/
#if defined(_MSC_VER) && !defined(__clang__)
#include <arm64_neon.h>
#else
#include <arm_neon.h>
#endif
#include "rtc_base/checks.h"
#include "modules/audio_coding/codecs/isac/fix/source/codec.h"
@ -44,7 +48,7 @@ int WebRtcIsacfix_AutocorrNeon(int32_t* __restrict r,
x_start += 4;
}
#ifdef WEBRTC_ARCH_ARM64
#if defined(WEBRTC_ARCH_ARM64) && (!defined(_MSC_VER) || defined(__clang__))
prod = vaddvq_s64(tmpb_v);
#else
prod = vget_lane_s64(vadd_s64(vget_low_s64(tmpb_v), vget_high_s64(tmpb_v)),
@ -90,7 +94,7 @@ int WebRtcIsacfix_AutocorrNeon(int32_t* __restrict r,
x_start += 4;
y_start += 4;
}
#ifdef WEBRTC_ARCH_ARM64
#if defined(WEBRTC_ARCH_ARM64) && (!defined(_MSC_VER) || defined(__clang__))
prod = vaddvq_s64(tmpb_v);
#else
prod = vget_lane_s64(vadd_s64(vget_low_s64(tmpb_v), vget_high_s64(tmpb_v)),

View File

@ -8,7 +8,11 @@
* be found in the AUTHORS file in the root of the source tree.
*/
#if defined(_MSC_VER) && !defined(__clang__)
#include <arm64_neon.h>
#else
#include <arm_neon.h>
#endif
#include "modules/audio_coding/codecs/isac/fix/source/codec.h"
#include "modules/audio_coding/codecs/isac/fix/source/settings.h"

View File

@ -11,8 +11,12 @@
#include "modules/audio_coding/codecs/isac/fix/source/pitch_estimator.h"
#ifdef WEBRTC_HAS_NEON
#if defined(_MSC_VER) && !defined(__clang__)
#include <arm64_neon.h>
#else
#include <arm_neon.h>
#endif
#endif
#include "common_audio/signal_processing/include/signal_processing_library.h"
#include "rtc_base/compile_assert_c.h"

View File

@ -8,7 +8,11 @@
* be found in the AUTHORS file in the root of the source tree.
*/
#if defined(_MSC_VER) && !defined(__clang__)
#include <arm64_neon.h>
#else
#include <arm_neon.h>
#endif
#include "modules/audio_coding/codecs/isac/fix/source/codec.h"
#include "modules/audio_coding/codecs/isac/fix/source/fft.h"
@ -51,7 +55,7 @@ static inline int32_t ComplexMulAndFindMaxNeon(int16_t* inre1Q9,
int32x4_t tmp1 = vmull_s16(vget_low_s16(tmpr), vget_low_s16(inre2));
tmp0 = vmlal_s16(tmp0, vget_low_s16(tmpi), vget_low_s16(inre2));
tmp1 = vmlsl_s16(tmp1, vget_low_s16(tmpi), vget_low_s16(inre1));
#if defined(WEBRTC_ARCH_ARM64)
#if defined(WEBRTC_ARCH_ARM64) && (!defined(_MSC_VER) || defined(__clang__))
int32x4_t tmp2 = vmull_high_s16(tmpr, inre1);
int32x4_t tmp3 = vmull_high_s16(tmpr, inre2);
tmp2 = vmlal_high_s16(tmp2, tmpi, inre2);
@ -90,7 +94,7 @@ static inline int32_t ComplexMulAndFindMaxNeon(int16_t* inre1Q9,
}
max_r = vmaxq_u32(max_r, max_i);
#if defined(WEBRTC_ARCH_ARM64)
#if defined(WEBRTC_ARCH_ARM64) && (!defined(_MSC_VER) || defined(__clang__))
uint32_t maximum = vmaxvq_u32(max_r);
#else
uint32x2_t max32x2_r = vmax_u32(vget_low_u32(max_r), vget_high_u32(max_r));
@ -331,7 +335,7 @@ static inline int32_t TransformAndFindMaxNeon(int16_t* inre,
}
max_r = vmaxq_u32(max_r, max_i);
#if defined(WEBRTC_ARCH_ARM64)
#if defined(WEBRTC_ARCH_ARM64) && (!defined(_MSC_VER) || defined(__clang__))
uint32_t maximum = vmaxvq_u32(max_r);
#else
uint32x2_t max32x2_r = vmax_u32(vget_low_u32(max_r), vget_high_u32(max_r));

View File

@ -14,7 +14,11 @@
* Based on aec_core_sse2.c.
*/
#if defined(_MSC_VER) && !defined(__clang__)
#include <arm64_neon.h>
#else
#include <arm_neon.h>
#endif
#include <math.h>
#include <string.h> // memset
@ -99,7 +103,8 @@ static float32x4_t vdivq_f32(float32x4_t a, float32x4_t b) {
// a/b = a*(1/b)
return vmulq_f32(a, x);
}
#endif
#if !defined(WEBRTC_ARCH_ARM64) || (defined(_MSC_VER) && !defined(__clang__))
static float32x4_t vsqrtq_f32(float32x4_t s) {
int i;
float32x4_t x = vrsqrteq_f32(s);

View File

@ -11,8 +11,12 @@
#include "modules/audio_processing/aec3/adaptive_fir_filter.h"
#if defined(WEBRTC_HAS_NEON)
#if defined(_MSC_VER) && !defined(__clang__)
#include <arm64_neon.h>
#else
#include <arm_neon.h>
#endif
#endif
#include "typedefs.h" // NOLINT(build/include)
#if defined(WEBRTC_ARCH_X86_FAMILY)
#include <emmintrin.h>

View File

@ -10,8 +10,12 @@
#include "modules/audio_processing/aec3/matched_filter.h"
#if defined(WEBRTC_HAS_NEON)
#if defined(_MSC_VER) && !defined(__clang__)
#include <arm64_neon.h>
#else
#include <arm_neon.h>
#endif
#endif
#include "typedefs.h" // NOLINT(build/include)
#if defined(WEBRTC_ARCH_X86_FAMILY)
#include <emmintrin.h>

View File

@ -13,8 +13,12 @@
#include "typedefs.h" // NOLINT(build/include)
#if defined(WEBRTC_HAS_NEON)
#if defined(_MSC_VER) && !defined(__clang__)
#include <arm64_neon.h>
#else
#include <arm_neon.h>
#endif
#endif
#if defined(WEBRTC_ARCH_X86_FAMILY)
#include <emmintrin.h>
#endif
@ -64,7 +68,7 @@ class VectorMath {
int j = 0;
for (; j < vector_limit * 4; j += 4) {
float32x4_t g = vld1q_f32(&x[j]);
#if !defined(WEBRTC_ARCH_ARM64)
#if !defined(WEBRTC_ARCH_ARM64) || (defined(_MSC_VER) && !defined(__clang__))
float32x4_t y = vrsqrteq_f32(g);
// Code to handle sqrt(0).

View File

@ -10,7 +10,11 @@
#include "modules/audio_processing/aecm/aecm_core.h"
#if defined(_MSC_VER) && !defined(__clang__)
#include <arm64_neon.h>
#else
#include <arm_neon.h>
#endif
#include "common_audio/signal_processing/include/real_fft.h"
#include "rtc_base/checks.h"
@ -19,7 +23,7 @@
// generating script and makefile, to replace these C functions.
static inline void AddLanes(uint32_t* ptr, uint32x4_t v) {
#if defined(WEBRTC_ARCH_ARM64)
#if defined(WEBRTC_ARCH_ARM64) && (!defined(_MSC_VER) || defined(__clang__))
*(ptr) = vaddvq_u32(v);
#else
uint32x2_t tmp_v;

View File

@ -10,7 +10,11 @@
#include "modules/audio_processing/ns/nsx_core.h"
#if defined(_MSC_VER) && !defined(__clang__)
#include <arm64_neon.h>
#else
#include <arm_neon.h>
#endif
#include "rtc_base/checks.h"

View File

@ -16,7 +16,11 @@
#include "modules/audio_processing/utility/ooura_fft.h"
#if defined(_MSC_VER) && !defined(__clang__)
#include <arm64_neon.h>
#else
#include <arm_neon.h>
#endif
#include "modules/audio_processing/utility/ooura_fft_tables_common.h"
#include "modules/audio_processing/utility/ooura_fft_tables_neon_sse2.h"

View File

@ -8,7 +8,11 @@
* be found in the AUTHORS file in the root of the source tree.
*/
#if defined(_MSC_VER) && !defined(__clang__)
#include <arm64_neon.h>
#else
#include <arm_neon.h>
#endif
#include "modules/video_processing/util/denoiser_filter_neon.h"

View File

@ -12,8 +12,7 @@ const {
UnsupportedOperationError,
} = ChromeUtils.import("chrome://marionette/content/error.js", {});
const {
waitForEvent,
waitForObserverTopic,
MessageManagerDestroyedPromise,
} = ChromeUtils.import("chrome://marionette/content/sync.js", {});
this.EXPORTED_SYMBOLS = ["browser", "Context", "WindowState"];
@ -71,11 +70,11 @@ this.Context = Context;
*/
browser.getBrowserForTab = function(tab) {
// Fennec
if (tab && "browser" in tab) {
if ("browser" in tab) {
return tab.browser;
// Firefox
} else if (tab && "linkedBrowser" in tab) {
} else if ("linkedBrowser" in tab) {
return tab.linkedBrowser;
}
@ -288,59 +287,17 @@ browser.Context = class {
* A promise which is resolved when the current window has been closed.
*/
closeWindow() {
// Create a copy of the messageManager before it is disconnected
let messageManager = this.window.messageManager;
let disconnected = waitForObserverTopic("message-manager-disconnect",
subject => subject === messageManager);
let unloaded = waitForEvent(this.window, "unload");
return new Promise(resolve => {
// Wait for the window message manager to be destroyed
let destroyed = new MessageManagerDestroyedPromise(
this.window.messageManager);
this.window.addEventListener("unload", async () => {
await destroyed;
resolve();
}, {once: true});
this.window.close();
return Promise.all([disconnected, unloaded]);
}
/**
* Open a new browser window.
*
* @return {Promise}
* A promise resolving to the newly created chrome window.
*/
async openBrowserWindow(focus = false) {
switch (this.driver.appName) {
case "firefox":
// Open new browser window, and wait until it is fully loaded.
// Also wait for the window to be focused and activated to prevent a
// race condition when promptly focusing to the original window again.
let win = this.window.OpenBrowserWindow();
// Bug 1509380 - Missing focus/activate event when Firefox is not
// the top-most application. As such wait for the next tick, and
// manually focus the newly opened window.
win.setTimeout(() => win.focus(), 0);
let activated = waitForEvent(win, "activate");
let focused = waitForEvent(win, "focus", {capture: true});
let startup = waitForObserverTopic("browser-delayed-startup-finished",
subject => subject == win);
await Promise.all([activated, focused, startup]);
if (!focus) {
// The new window shouldn't get focused. As such set the
// focus back to the currently selected window.
activated = waitForEvent(this.window, "activate");
focused = waitForEvent(this.window, "focus", {capture: true});
this.window.focus();
await Promise.all([activated, focused]);
}
return win;
default:
throw new UnsupportedOperationError(
`openWindow() not supported in ${this.driver.appName}`);
}
});
}
/**
@ -362,65 +319,40 @@ browser.Context = class {
return this.closeWindow();
}
// Create a copy of the messageManager before it is disconnected
let messageManager = this.messageManager;
let disconnected = waitForObserverTopic("message-manager-disconnect",
subject => subject === messageManager);
return new Promise((resolve, reject) => {
// Wait for the browser message manager to be destroyed
let browserDetached = async () => {
await new MessageManagerDestroyedPromise(this.messageManager);
resolve();
};
let tabClosed;
switch (this.driver.appName) {
case "fennec":
if (this.tabBrowser.closeTab) {
// Fennec
tabClosed = waitForEvent(this.tabBrowser.deck, "TabClose");
this.tabBrowser.deck.addEventListener(
"TabClose", browserDetached, {once: true});
this.tabBrowser.closeTab(this.tab);
break;
case "firefox":
tabClosed = waitForEvent(this.tab, "TabClose");
} else if (this.tabBrowser.removeTab) {
// Firefox
this.tab.addEventListener(
"TabClose", browserDetached, {once: true});
this.tabBrowser.removeTab(this.tab);
break;
default:
throw new UnsupportedOperationError(
`closeTab() not supported in ${this.driver.appName}`);
} else {
reject(new UnsupportedOperationError(
`closeTab() not supported in ${this.driver.appName}`));
}
return Promise.all([disconnected, tabClosed]);
});
}
/**
* Open a new tab in the currently selected chrome window.
* Opens a tab with given URI.
*
* @param {string} uri
* URI to open.
*/
async openTab(focus = false) {
let tab = null;
let tabOpened = waitForEvent(this.window, "TabOpen");
switch (this.driver.appName) {
case "fennec":
tab = this.tabBrowser.addTab(null, {selected: focus});
break;
case "firefox":
this.window.BrowserOpenTab();
tab = this.tabBrowser.selectedTab;
// The new tab is always selected by default. If focus is not wanted,
// the previously tab needs to be selected again.
if (!focus) {
this.tabBrowser.selectedTab = this.tab;
}
break;
default:
throw new UnsupportedOperationError(
`openTab() not supported in ${this.driver.appName}`);
}
await tabOpened;
return tab;
addTab(uri) {
return this.tabBrowser.addTab(uri, true);
}
/**
@ -454,18 +386,16 @@ browser.Context = class {
this.tab = this.tabBrowser.tabs[index];
if (focus) {
switch (this.driver.appName) {
case "fennec":
if (this.tabBrowser.selectTab) {
// Fennec
this.tabBrowser.selectTab(this.tab);
break;
case "firefox":
} else if ("selectedTab" in this.tabBrowser) {
// Firefox
this.tabBrowser.selectedTab = this.tab;
break;
default:
throw new UnsupportedOperationError(
`switchToTab() not supported in ${this.driver.appName}`);
} else {
throw new UnsupportedOperationError("switchToTab() not supported");
}
}
}

View File

@ -1428,20 +1428,6 @@ class Marionette(object):
return self._send_message("WebDriver:GetPageSource",
key="value")
def open(self, type=None, focus=False):
"""Open a new window, or tab based on the specified context type.
If no context type is given the application will choose the best
option based on tab and window support.
:param type: Type of window to be opened. Can be one of "tab" or "window"
:param focus: If true, the opened window will be focused
:returns: Dict with new window handle, and type of opened window
"""
body = {"type": type, "focus": focus}
return self._send_message("WebDriver:NewWindow", body)
def close(self):
"""Close the current window, ending the session if it's the last
window currently open.

View File

@ -3,7 +3,8 @@ sync module
Provides an assortment of synchronisation primitives.
.. js:autofunction:: executeSoon
.. js:autoclass:: MessageManagerDestroyedPromise
:members:
.. js:autoclass:: PollPromise
:members:
@ -13,9 +14,3 @@ Provides an assortment of synchronisation primitives.
.. js:autoclass:: TimedPromise
:members:
.. js:autofunction:: waitForEvent
.. js:autofunction:: waitForMessage
.. js:autofunction:: waitForObserverTopic

View File

@ -62,8 +62,6 @@ const {
IdlePromise,
PollPromise,
TimedPromise,
waitForEvent,
waitForObserverTopic,
} = ChromeUtils.import("chrome://marionette/content/sync.js", {});
XPCOMUtils.defineLazyGetter(this, "logger", Log.get);
@ -108,12 +106,13 @@ const globalMessageManager = Services.mm;
*
* @class GeckoDriver
*
* @param {string} appId
* Unique identifier of the application.
* @param {MarionetteServer} server
* The instance of Marionette server.
*/
this.GeckoDriver = function(server) {
this.appId = Services.appinfo.ID;
this.appName = Services.appinfo.name.toLowerCase();
this.GeckoDriver = function(appId, server) {
this.appId = appId;
this._server = server;
this.sessionID = null;
@ -1308,7 +1307,6 @@ GeckoDriver.prototype.getIdForBrowser = function(browser) {
if (browser === null) {
return null;
}
let permKey = browser.permanentKey;
if (this._browserIds.has(permKey)) {
return this._browserIds.get(permKey);
@ -2724,73 +2722,6 @@ GeckoDriver.prototype.deleteCookie = async function(cmd) {
}
};
/**
* Open a new top-level browsing context.
*
* @param {string=} type
* Optional type of the new top-level browsing context. Can be one of
* `tab` or `window`.
* @param {boolean=} focus
* Optional flag if the new top-level browsing context should be opened
* in foreground (focused) or background (not focused).
*
* @return {Object.<string, string>}
* Handle and type of the new browsing context.
*/
GeckoDriver.prototype.newWindow = async function(cmd) {
assert.open(this.getCurrentWindow(Context.Content));
await this._handleUserPrompts();
let focus = false;
if (typeof cmd.parameters.focus != "undefined") {
focus = assert.boolean(cmd.parameters.focus,
pprint`Expected "focus" to be a boolean, got ${cmd.parameters.focus}`);
}
let type;
if (typeof cmd.parameters.type != "undefined") {
type = assert.string(cmd.parameters.type,
pprint`Expected "type" to be a string, got ${cmd.parameters.type}`);
}
let types = ["tab", "window"];
switch (this.appName) {
case "firefox":
if (typeof type == "undefined" || !types.includes(type)) {
type = "window";
}
break;
case "fennec":
if (typeof type == "undefined" || !types.includes(type)) {
type = "tab";
}
break;
}
let contentBrowser;
switch (type) {
case "tab":
let tab = await this.curBrowser.openTab(focus);
contentBrowser = browser.getBrowserForTab(tab);
break;
default:
let win = await this.curBrowser.openBrowserWindow(focus);
contentBrowser = browser.getTabBrowser(win).selectedBrowser;
}
// Even with the framescript registered, the browser might not be known to
// the parent process yet. Wait until it is available.
// TODO: Fix by using `Browser:Init` or equivalent on bug 1311041
let windowId = await new PollPromise((resolve, reject) => {
let id = this.getIdForBrowser(contentBrowser);
this.windowHandles.includes(id) ? resolve(id) : reject();
});
return {"handle": windowId.toString(), type};
};
/**
* Close the currently selected tab/window.
*
@ -3170,14 +3101,16 @@ GeckoDriver.prototype.dismissDialog = async function() {
let win = assert.open(this.getCurrentWindow());
this._checkIfAlertIsPresent();
let dialogClosed = waitForEvent(win, "DOMModalDialogClosed");
await new Promise(resolve => {
win.addEventListener("DOMModalDialogClosed", async () => {
await new IdlePromise(win);
this.dialog = null;
resolve();
}, {once: true});
let {button0, button1} = this.dialog.ui;
(button1 ? button1 : button0).click();
await dialogClosed;
this.dialog = null;
});
};
/**
@ -3188,14 +3121,16 @@ GeckoDriver.prototype.acceptDialog = async function() {
let win = assert.open(this.getCurrentWindow());
this._checkIfAlertIsPresent();
let dialogClosed = waitForEvent(win, "DOMModalDialogClosed");
await new Promise(resolve => {
win.addEventListener("DOMModalDialogClosed", async () => {
await new IdlePromise(win);
this.dialog = null;
resolve();
}, {once: true});
let {button0} = this.dialog.ui;
button0.click();
await dialogClosed;
this.dialog = null;
});
};
/**
@ -3366,10 +3301,15 @@ GeckoDriver.prototype.quit = async function(cmd) {
this.deleteSession();
// delay response until the application is about to quit
let quitApplication = waitForObserverTopic("quit-application");
let quitApplication = new Promise(resolve => {
Services.obs.addObserver(
(subject, topic, data) => resolve(data),
"quit-application");
});
Services.startup.quit(mode);
return {cause: (await quitApplication).data};
return {cause: await quitApplication};
};
GeckoDriver.prototype.installAddon = function(cmd) {
@ -3631,7 +3571,6 @@ GeckoDriver.prototype.commands = {
"WebDriver:MaximizeWindow": GeckoDriver.prototype.maximizeWindow,
"WebDriver:Navigate": GeckoDriver.prototype.get,
"WebDriver:NewSession": GeckoDriver.prototype.newSession,
"WebDriver:NewWindow": GeckoDriver.prototype.newWindow,
"WebDriver:PerformActions": GeckoDriver.prototype.performActions,
"WebDriver:Refresh": GeckoDriver.prototype.refresh,
"WebDriver:ReleaseActions": GeckoDriver.prototype.releaseActions,

View File

@ -6,12 +6,14 @@ from __future__ import absolute_import
import sys
from marionette_driver import Wait
from marionette_driver import By, Wait
from six import reraise
class WindowManagerMixin(object):
_menu_item_new_tab = (By.ID, "menu_newNavigatorTab")
def setUp(self):
super(WindowManagerMixin, self).setUp()
@ -58,18 +60,15 @@ class WindowManagerMixin(object):
self.marionette.switch_to_window(self.start_window)
def open_tab(self, callback=None, focus=False):
def open_tab(self, trigger="menu"):
current_tabs = self.marionette.window_handles
try:
if callable(callback):
callback()
else:
result = self.marionette.open(type="tab", focus=focus)
if result["type"] != "tab":
raise Exception(
"Newly opened browsing context is of type {} and not tab.".format(
result["type"]))
if callable(trigger):
trigger()
elif trigger == 'menu':
with self.marionette.using_context("chrome"):
self.marionette.find_element(*self._menu_item_new_tab).click()
except Exception:
exc, val, tb = sys.exc_info()
reraise(exc, 'Failed to trigger opening a new tab: {}'.format(val), tb)
@ -83,9 +82,8 @@ class WindowManagerMixin(object):
return new_tab
def open_window(self, callback=None, focus=False):
def open_window(self, trigger=None):
current_windows = self.marionette.chrome_window_handles
current_tabs = self.marionette.window_handles
def loaded(handle):
with self.marionette.using_context("chrome"):
@ -97,14 +95,11 @@ class WindowManagerMixin(object):
""", script_args=[handle])
try:
if callable(callback):
callback()
if callable(trigger):
trigger()
else:
result = self.marionette.open(type="window", focus=focus)
if result["type"] != "window":
raise Exception(
"Newly opened browsing context is of type {} and not window.".format(
result["type"]))
with self.marionette.using_context("chrome"):
self.marionette.execute_script("OpenBrowserWindow();")
except Exception:
exc, val, tb = sys.exc_info()
reraise(exc, 'Failed to trigger opening a new window: {}'.format(val), tb)
@ -121,16 +116,9 @@ class WindowManagerMixin(object):
lambda _: loaded(new_window),
message="Window with handle '{}'' did not finish loading".format(new_window))
# Bug 1507771 - Return the correct handle based on the currently selected context
# as long as "WebDriver:NewWindow" is not handled separtely in chrome context
context = self.marionette._send_message("Marionette:GetContext", key="value")
if context == "chrome":
return new_window
elif context == "content":
[new_tab] = list(set(self.marionette.window_handles) - set(current_tabs))
return new_tab
def open_chrome_window(self, url, focus=False):
def open_chrome_window(self, url):
"""Open a new chrome window with the specified chrome URL.
Can be replaced with "WebDriver:NewWindow" once the command
@ -178,5 +166,4 @@ class WindowManagerMixin(object):
})();
""", script_args=(url,))
with self.marionette.using_context("chrome"):
return self.open_window(callback=open_with_js, focus=focus)
return self.open_window(trigger=open_with_js)

View File

@ -1,5 +1,22 @@
#Copyright 2007-2009 WebDriver committers
#Copyright 2007-2009 Google Inc.
#
#Licensed under the Apache License, Version 2.0 (the "License");
#you may not use this file except in compliance with the License.
#You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
#Unless required by applicable law or agreed to in writing, software
#distributed under the License is distributed on an "AS IS" BASIS,
#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#See the License for the specific language governing permissions and
#limitations under the License.
from __future__ import absolute_import
from marionette_driver import By
from marionette_harness import MarionetteTestCase, WindowManagerMixin
@ -8,13 +25,18 @@ class ChromeTests(WindowManagerMixin, MarionetteTestCase):
def setUp(self):
super(ChromeTests, self).setUp()
self.marionette.set_context('chrome')
def tearDown(self):
self.close_all_windows()
super(ChromeTests, self).tearDown()
def test_hang_until_timeout(self):
with self.marionette.using_context("chrome"):
new_window = self.open_window()
def open_with_menu():
menu = self.marionette.find_element(By.ID, 'aboutName')
menu.click()
new_window = self.open_window(trigger=open_with_menu)
self.marionette.switch_to_window(new_window)
try:
@ -23,8 +45,7 @@ class ChromeTests(WindowManagerMixin, MarionetteTestCase):
# while running this test. Otherwise it would mask eg. IOError as
# thrown for a socket timeout.
raise NotImplementedError('Exception should not cause a hang when '
'closing the chrome window in content '
'context')
'closing the chrome window')
finally:
self.marionette.close_chrome_window()
self.marionette.switch_to_window(self.start_window)

View File

@ -449,16 +449,20 @@ class TestClickCloseContext(WindowManagerMixin, MarionetteTestCase):
super(TestClickCloseContext, self).tearDown()
def test_click_close_tab(self):
new_tab = self.open_tab()
self.marionette.switch_to_window(new_tab)
self.marionette.navigate(self.marionette.absolute_url("windowHandles.html"))
tab = self.open_tab(
lambda: self.marionette.find_element(By.ID, "new-tab").click())
self.marionette.switch_to_window(tab)
self.marionette.navigate(self.test_page)
self.marionette.find_element(By.ID, "close-window").click()
@skip_if_mobile("Fennec doesn't support other chrome windows")
def test_click_close_window(self):
new_tab = self.open_window()
self.marionette.switch_to_window(new_tab)
self.marionette.navigate(self.marionette.absolute_url("windowHandles.html"))
win = self.open_window(
lambda: self.marionette.find_element(By.ID, "new-window").click())
self.marionette.switch_to_window(win)
self.marionette.navigate(self.test_page)
self.marionette.find_element(By.ID, "close-window").click()

View File

@ -77,3 +77,22 @@ class TestKeyActions(WindowManagerMixin, MarionetteTestCase):
.key_down("x")
.perform())
self.assertEqual(self.key_reporter_value, "")
@skip_if_mobile("Interacting with chrome windows not available for Fennec")
def test_open_in_new_window_shortcut(self):
def open_window_with_action():
el = self.marionette.find_element(By.TAG_NAME, "a")
(self.key_action.key_down(Keys.SHIFT)
.press(el)
.release()
.key_up(Keys.SHIFT)
.perform())
self.marionette.navigate(inline("<a href='#'>Click</a>"))
new_window = self.open_window(trigger=open_window_with_action)
self.marionette.switch_to_window(new_window)
self.marionette.close_chrome_window()
self.marionette.switch_to_window(self.start_window)

View File

@ -55,8 +55,13 @@ class BaseNavigationTestCase(WindowManagerMixin, MarionetteTestCase):
else:
self.mod_key = Keys.CONTROL
def open_with_link():
link = self.marionette.find_element(By.ID, "new-blank-tab")
link.click()
# Always use a blank new tab for an empty history
self.new_tab = self.open_tab()
self.marionette.navigate(self.marionette.absolute_url("windowHandles.html"))
self.new_tab = self.open_tab(open_with_link)
self.marionette.switch_to_window(self.new_tab)
Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
lambda _: self.history_length == 1,
@ -293,6 +298,7 @@ class TestNavigate(BaseNavigationTestCase):
focus_el = self.marionette.find_element(By.CSS_SELECTOR, ":focus")
self.assertEqual(self.marionette.get_active_element(), focus_el)
@skip_if_mobile("Needs application independent method to open a new tab")
def test_no_hang_when_navigating_after_closing_original_tab(self):
# Close the start tab
self.marionette.switch_to_window(self.start_tab)
@ -334,6 +340,22 @@ class TestNavigate(BaseNavigationTestCase):
message="'{}' hasn't been loaded".format(self.test_page_remote))
self.assertTrue(self.is_remote_tab)
@skip_if_mobile("On Android no shortcuts are available")
def test_navigate_shortcut_key(self):
def open_with_shortcut():
self.marionette.navigate(self.test_page_remote)
with self.marionette.using_context("chrome"):
main_win = self.marionette.find_element(By.ID, "main-window")
main_win.send_keys(self.mod_key, Keys.SHIFT, "a")
new_tab = self.open_tab(trigger=open_with_shortcut)
self.marionette.switch_to_window(new_tab)
Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
lambda mn: mn.get_url() == "about:addons",
message="'about:addons' hasn't been loaded")
class TestBackForwardNavigation(BaseNavigationTestCase):
@ -801,7 +823,7 @@ class TestPageLoadStrategy(BaseNavigationTestCase):
@skip("Bug 1422741 - Causes following tests to fail in loading remote browser")
@run_if_e10s("Requires e10s mode enabled")
def test_strategy_after_remoteness_change(self):
"""Bug 1378191 - Reset of capabilities after listener reload."""
"""Bug 1378191 - Reset of capabilities after listener reload"""
self.marionette.delete_session()
self.marionette.start_session({"pageLoadStrategy": "eager"})

View File

@ -253,8 +253,7 @@ class TestScreenCaptureChrome(WindowManagerMixin, ScreenCaptureTestCase):
chrome_document_element = self.document_element
with self.marionette.using_context('content'):
self.assertRaisesRegexp(NoSuchElementException,
"Web element reference not seen before",
self.assertRaisesRegexp(NoSuchElementException, "Web element reference not seen before",
self.marionette.screenshot,
highlights=[chrome_document_element])
@ -275,9 +274,10 @@ class TestScreenCaptureContent(WindowManagerMixin, ScreenCaptureTestCase):
return [document.body.scrollWidth, document.body.scrollHeight]
"""))
@skip_if_mobile("Needs application independent method to open a new tab")
def test_capture_tab_already_closed(self):
new_tab = self.open_tab()
self.marionette.switch_to_window(new_tab)
tab = self.open_tab()
self.marionette.switch_to_window(tab)
self.marionette.close()
self.assertRaises(NoSuchWindowException, self.marionette.screenshot)

View File

@ -6,9 +6,10 @@ from __future__ import absolute_import
import os
import sys
from unittest import skipIf
from marionette_driver import By
# add this directory to the path
sys.path.append(os.path.dirname(__file__))
@ -27,70 +28,119 @@ class TestSwitchWindowChrome(TestSwitchToWindowContent):
super(TestSwitchWindowChrome, self).tearDown()
@skipIf(sys.platform.startswith("linux"),
"Bug 1511970 - New window isn't moved to the background on Linux")
def open_window_in_background(self):
with self.marionette.using_context("chrome"):
self.marionette.execute_async_script("""
let callback = arguments[0];
(async function() {
function promiseEvent(target, type, args) {
return new Promise(r => {
let params = Object.assign({once: true}, args);
target.addEventListener(type, r, params);
});
}
function promiseWindowFocus(w) {
return Promise.all([
promiseEvent(w, "focus", {capture: true}),
promiseEvent(w, "activate"),
]);
}
// Open a window, wait for it to receive focus
let win = OpenBrowserWindow();
await promiseWindowFocus(win);
// Now refocus our original window and wait for that to happen.
let windowFocusPromise = promiseWindowFocus(window);
window.focus();
return windowFocusPromise;
})().then(() => {
// can't just pass `callback`, as we can't JSON-ify the events it'd get passed.
callback()
});
""")
def open_window_in_foreground(self):
with self.marionette.using_context("content"):
self.marionette.navigate(self.test_page)
link = self.marionette.find_element(By.ID, "new-window")
link.click()
def test_switch_tabs_for_new_background_window_without_focus_change(self):
# Open an additional tab in the original window so we can better check
# Open an addition tab in the original window so we can better check
# the selected index in thew new window to be opened.
second_tab = self.open_tab(focus=True)
second_tab = self.open_tab(trigger=self.open_tab_in_foreground)
self.marionette.switch_to_window(second_tab, focus=True)
second_tab_index = self.get_selected_tab_index()
self.assertNotEqual(second_tab_index, self.selected_tab_index)
# Open a new background window, but we are interested in the tab
with self.marionette.using_context("content"):
tab_in_new_window = self.open_window()
# Opens a new background window, but we are interested in the tab
tab_in_new_window = self.open_tab(trigger=self.open_window_in_background)
self.assertEqual(self.marionette.current_window_handle, second_tab)
self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
self.assertEqual(self.get_selected_tab_index(), second_tab_index)
with self.marionette.using_context("content"):
self.assertEqual(self.marionette.get_url(), self.empty_page)
# Switch to the tab in the new window but don't focus it
self.marionette.switch_to_window(tab_in_new_window, focus=False)
self.assertEqual(self.marionette.current_window_handle, tab_in_new_window)
self.assertNotEqual(self.marionette.current_chrome_window_handle, self.start_window)
self.assertEqual(self.get_selected_tab_index(), second_tab_index)
with self.marionette.using_context("content"):
self.assertEqual(self.marionette.get_url(), "about:blank")
def test_switch_tabs_for_new_foreground_window_with_focus_change(self):
# Open an addition tab in the original window so we can better check
# the selected index in thew new window to be opened.
second_tab = self.open_tab()
second_tab = self.open_tab(trigger=self.open_tab_in_foreground)
self.marionette.switch_to_window(second_tab, focus=True)
second_tab_index = self.get_selected_tab_index()
self.assertNotEqual(second_tab_index, self.selected_tab_index)
# Opens a new window, but we are interested in the tab
with self.marionette.using_context("content"):
tab_in_new_window = self.open_window(focus=True)
tab_in_new_window = self.open_tab(trigger=self.open_window_in_foreground)
self.assertEqual(self.marionette.current_window_handle, second_tab)
self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
self.assertNotEqual(self.get_selected_tab_index(), second_tab_index)
with self.marionette.using_context("content"):
self.assertEqual(self.marionette.get_url(), self.test_page)
self.marionette.switch_to_window(tab_in_new_window)
self.assertEqual(self.marionette.current_window_handle, tab_in_new_window)
self.assertNotEqual(self.marionette.current_chrome_window_handle, self.start_window)
self.assertNotEqual(self.get_selected_tab_index(), second_tab_index)
with self.marionette.using_context("content"):
self.assertEqual(self.marionette.get_url(), self.empty_page)
self.marionette.switch_to_window(second_tab, focus=True)
self.assertEqual(self.marionette.current_window_handle, second_tab)
self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
# Bug 1335085 - The focus doesn't change even as requested so.
# self.assertEqual(self.get_selected_tab_index(), second_tab_index)
with self.marionette.using_context("content"):
self.assertEqual(self.marionette.get_url(), self.test_page)
def test_switch_tabs_for_new_foreground_window_without_focus_change(self):
# Open an addition tab in the original window so we can better check
# the selected index in thew new window to be opened.
second_tab = self.open_tab()
second_tab = self.open_tab(trigger=self.open_tab_in_foreground)
self.marionette.switch_to_window(second_tab, focus=True)
second_tab_index = self.get_selected_tab_index()
self.assertNotEqual(second_tab_index, self.selected_tab_index)
self.open_window(focus=True)
# Opens a new window, but we are interested in the tab which automatically
# gets the focus.
self.open_tab(trigger=self.open_window_in_foreground)
self.assertEqual(self.marionette.current_window_handle, second_tab)
self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
self.assertNotEqual(self.get_selected_tab_index(), second_tab_index)
with self.marionette.using_context("content"):
self.assertEqual(self.marionette.get_url(), self.test_page)
# Switch to the second tab in the first window, but don't focus it.
self.marionette.switch_to_window(second_tab, focus=False)
self.assertEqual(self.marionette.current_window_handle, second_tab)
self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
self.assertNotEqual(self.get_selected_tab_index(), second_tab_index)
with self.marionette.using_context("content"):
self.assertEqual(self.marionette.get_url(), self.test_page)

View File

@ -4,10 +4,10 @@
from __future__ import absolute_import
from marionette_driver import By
from marionette_driver import Actions, By, Wait
from marionette_driver.keys import Keys
from marionette_harness import MarionetteTestCase, WindowManagerMixin
from marionette_harness import MarionetteTestCase, skip_if_mobile, WindowManagerMixin
class TestSwitchToWindowContent(WindowManagerMixin, MarionetteTestCase):
@ -20,8 +20,14 @@ class TestSwitchToWindowContent(WindowManagerMixin, MarionetteTestCase):
else:
self.mod_key = Keys.CONTROL
self.empty_page = self.marionette.absolute_url("empty.html")
self.test_page = self.marionette.absolute_url("windowHandles.html")
self.selected_tab_index = self.get_selected_tab_index()
with self.marionette.using_context("content"):
self.marionette.navigate(self.test_page)
def tearDown(self):
self.close_all_tabs()
@ -63,51 +69,78 @@ class TestSwitchToWindowContent(WindowManagerMixin, MarionetteTestCase):
}
""")
def open_tab_in_background(self):
with self.marionette.using_context("content"):
link = self.marionette.find_element(By.ID, "new-tab")
action = Actions(self.marionette)
action.key_down(self.mod_key).click(link).perform()
def open_tab_in_foreground(self):
with self.marionette.using_context("content"):
link = self.marionette.find_element(By.ID, "new-tab")
link.click()
def test_switch_tabs_with_focus_change(self):
new_tab = self.open_tab(focus=True)
new_tab = self.open_tab(self.open_tab_in_foreground)
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
self.assertNotEqual(self.get_selected_tab_index(), self.selected_tab_index)
with self.marionette.using_context("content"):
self.assertEqual(self.marionette.get_url(), self.test_page)
# Switch to new tab first because it is already selected
self.marionette.switch_to_window(new_tab)
self.assertEqual(self.marionette.current_window_handle, new_tab)
self.assertNotEqual(self.get_selected_tab_index(), self.selected_tab_index)
# Switch to original tab by explicitely setting the focus
with self.marionette.using_context("content"):
Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
lambda _: self.marionette.get_url() == self.empty_page,
message="{} has been loaded in the newly opened tab.".format(self.empty_page))
self.marionette.switch_to_window(self.start_tab, focus=True)
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
self.assertEqual(self.get_selected_tab_index(), self.selected_tab_index)
with self.marionette.using_context("content"):
self.assertEqual(self.marionette.get_url(), self.test_page)
self.marionette.switch_to_window(new_tab)
self.marionette.close()
self.marionette.switch_to_window(self.start_tab)
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
self.assertEqual(self.get_selected_tab_index(), self.selected_tab_index)
with self.marionette.using_context("content"):
self.assertEqual(self.marionette.get_url(), self.test_page)
def test_switch_tabs_without_focus_change(self):
new_tab = self.open_tab(focus=True)
new_tab = self.open_tab(self.open_tab_in_foreground)
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
self.assertNotEqual(self.get_selected_tab_index(), self.selected_tab_index)
with self.marionette.using_context("content"):
self.assertEqual(self.marionette.get_url(), self.test_page)
# Switch to new tab first because it is already selected
self.marionette.switch_to_window(new_tab)
self.assertEqual(self.marionette.current_window_handle, new_tab)
# Switch to original tab by explicitely not setting the focus
self.marionette.switch_to_window(self.start_tab, focus=False)
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
self.assertNotEqual(self.get_selected_tab_index(), self.selected_tab_index)
with self.marionette.using_context("content"):
self.assertEqual(self.marionette.get_url(), self.test_page)
self.marionette.switch_to_window(new_tab)
self.marionette.close()
self.marionette.switch_to_window(self.start_tab)
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
self.assertEqual(self.get_selected_tab_index(), self.selected_tab_index)
with self.marionette.using_context("content"):
self.assertEqual(self.marionette.get_url(), self.test_page)
def test_switch_from_content_to_chrome_window_should_not_change_selected_tab(self):
new_tab = self.open_tab(focus=True)
new_tab = self.open_tab(self.open_tab_in_foreground)
self.marionette.switch_to_window(new_tab)
self.assertEqual(self.marionette.current_window_handle, new_tab)
@ -117,31 +150,24 @@ class TestSwitchToWindowContent(WindowManagerMixin, MarionetteTestCase):
self.assertEqual(self.marionette.current_window_handle, new_tab)
self.assertEqual(self.get_selected_tab_index(), new_tab_index)
def test_switch_to_new_private_browsing_tab(self):
@skip_if_mobile("New windows not supported in Fennec")
def test_switch_to_new_private_browsing_window_has_to_register_browsers(self):
# Test that tabs (browsers) are correctly registered for a newly opened
# private browsing window/tab. This has to also happen without explicitely
# private browsing window. This has to also happen without explicitely
# switching to the tab itself before using any commands in content scope.
#
# Note: Not sure why this only affects private browsing windows only.
new_tab = self.open_tab(focus=True)
self.marionette.switch_to_window(new_tab)
def open_private_browsing_window_firefox():
with self.marionette.using_context("content"):
self.marionette.find_element(By.ID, "startPrivateBrowsing").click()
def open_private_browsing_tab_fennec():
with self.marionette.using_context("content"):
self.marionette.find_element(By.ID, "newPrivateTabLink").click()
def open_private_browsing_window():
with self.marionette.using_context("content"):
self.marionette.navigate("about:privatebrowsing")
if self.marionette.session_capabilities["browserName"] == "fennec":
new_pb_tab = self.open_tab(open_private_browsing_tab_fennec)
else:
new_pb_tab = self.open_tab(open_private_browsing_window_firefox)
button = self.marionette.find_element(By.ID, "startPrivateBrowsing")
button.click()
self.marionette.switch_to_window(new_pb_tab)
self.assertEqual(self.marionette.current_window_handle, new_pb_tab)
new_window = self.open_window(open_private_browsing_window)
self.marionette.switch_to_window(new_window)
self.assertEqual(self.marionette.current_chrome_window_handle, new_window)
self.assertNotEqual(self.marionette.current_window_handle, self.start_tab)
with self.marionette.using_context("content"):
self.marionette.execute_script(" return true; ")

View File

@ -21,14 +21,14 @@ class TestCloseWindow(WindowManagerMixin, MarionetteTestCase):
super(TestCloseWindow, self).tearDown()
def test_close_chrome_window_for_browser_window(self):
new_window = self.open_window()
self.marionette.switch_to_window(new_window)
win = self.open_window()
self.marionette.switch_to_window(win)
self.assertNotIn(new_window, self.marionette.window_handles)
self.assertNotIn(win, self.marionette.window_handles)
chrome_window_handles = self.marionette.close_chrome_window()
self.assertNotIn(new_window, chrome_window_handles)
self.assertNotIn(win, chrome_window_handles)
self.assertListEqual(self.start_windows, chrome_window_handles)
self.assertNotIn(new_window, self.marionette.window_handles)
self.assertNotIn(win, self.marionette.window_handles)
def test_close_chrome_window_for_non_browser_window(self):
win = self.open_chrome_window("chrome://marionette/content/test.xul")
@ -50,20 +50,20 @@ class TestCloseWindow(WindowManagerMixin, MarionetteTestCase):
self.assertIsNotNone(self.marionette.session)
def test_close_window_for_browser_tab(self):
new_tab = self.open_tab()
self.marionette.switch_to_window(new_tab)
tab = self.open_tab()
self.marionette.switch_to_window(tab)
window_handles = self.marionette.close()
self.assertNotIn(new_tab, window_handles)
self.assertNotIn(tab, window_handles)
self.assertListEqual(self.start_tabs, window_handles)
def test_close_window_for_browser_window_with_single_tab(self):
new_window = self.open_window()
self.marionette.switch_to_window(new_window)
win = self.open_window()
self.marionette.switch_to_window(win)
self.assertEqual(len(self.start_tabs) + 1, len(self.marionette.window_handles))
window_handles = self.marionette.close()
self.assertNotIn(new_window, window_handles)
self.assertNotIn(win, window_handles)
self.assertListEqual(self.start_tabs, window_handles)
self.assertListEqual(self.start_windows, self.marionette.chrome_window_handles)

View File

@ -24,27 +24,26 @@ class TestCloseWindow(WindowManagerMixin, MarionetteTestCase):
@skip_if_mobile("Interacting with chrome windows not available for Fennec")
def test_close_chrome_window_for_browser_window(self):
with self.marionette.using_context("chrome"):
new_window = self.open_window()
self.marionette.switch_to_window(new_window)
win = self.open_window()
self.marionette.switch_to_window(win)
self.assertIn(new_window, self.marionette.chrome_window_handles)
self.assertNotIn(win, self.marionette.window_handles)
chrome_window_handles = self.marionette.close_chrome_window()
self.assertNotIn(new_window, chrome_window_handles)
self.assertNotIn(win, chrome_window_handles)
self.assertListEqual(self.start_windows, chrome_window_handles)
self.assertNotIn(new_window, self.marionette.window_handles)
self.assertNotIn(win, self.marionette.window_handles)
@skip_if_mobile("Interacting with chrome windows not available for Fennec")
def test_close_chrome_window_for_non_browser_window(self):
new_window = self.open_chrome_window("chrome://marionette/content/test.xul")
self.marionette.switch_to_window(new_window)
win = self.open_chrome_window("chrome://marionette/content/test.xul")
self.marionette.switch_to_window(win)
self.assertIn(new_window, self.marionette.chrome_window_handles)
self.assertNotIn(new_window, self.marionette.window_handles)
self.assertIn(win, self.marionette.chrome_window_handles)
self.assertNotIn(win, self.marionette.window_handles)
chrome_window_handles = self.marionette.close_chrome_window()
self.assertNotIn(new_window, chrome_window_handles)
self.assertNotIn(win, chrome_window_handles)
self.assertListEqual(self.start_windows, chrome_window_handles)
self.assertNotIn(new_window, self.marionette.window_handles)
self.assertNotIn(win, self.marionette.window_handles)
@skip_if_mobile("Interacting with chrome windows not available for Fennec")
def test_close_chrome_window_for_last_open_window(self):
@ -55,17 +54,19 @@ class TestCloseWindow(WindowManagerMixin, MarionetteTestCase):
self.assertListEqual([self.start_window], self.marionette.chrome_window_handles)
self.assertIsNotNone(self.marionette.session)
@skip_if_mobile("Needs application independent method to open a new tab")
def test_close_window_for_browser_tab(self):
new_tab = self.open_tab()
self.marionette.switch_to_window(new_tab)
tab = self.open_tab()
self.marionette.switch_to_window(tab)
window_handles = self.marionette.close()
self.assertNotIn(new_tab, window_handles)
self.assertNotIn(tab, window_handles)
self.assertListEqual(self.start_tabs, window_handles)
@skip_if_mobile("Needs application independent method to open a new tab")
def test_close_window_with_dismissed_beforeunload_prompt(self):
new_tab = self.open_tab()
self.marionette.switch_to_window(new_tab)
tab = self.open_tab()
self.marionette.switch_to_window(tab)
self.marionette.navigate(inline("""
<input type="text">
@ -81,12 +82,12 @@ class TestCloseWindow(WindowManagerMixin, MarionetteTestCase):
@skip_if_mobile("Interacting with chrome windows not available for Fennec")
def test_close_window_for_browser_window_with_single_tab(self):
new_tab = self.open_window()
self.marionette.switch_to_window(new_tab)
win = self.open_window()
self.marionette.switch_to_window(win)
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1)
self.assertEqual(len(self.start_tabs) + 1, len(self.marionette.window_handles))
window_handles = self.marionette.close()
self.assertNotIn(new_tab, window_handles)
self.assertNotIn(win, window_handles)
self.assertListEqual(self.start_tabs, window_handles)
self.assertListEqual(self.start_windows, self.marionette.chrome_window_handles)
@ -103,8 +104,8 @@ class TestCloseWindow(WindowManagerMixin, MarionetteTestCase):
self.close_all_tabs()
test_page = self.marionette.absolute_url("windowHandles.html")
new_tab = self.open_tab()
self.marionette.switch_to_window(new_tab)
tab = self.open_tab()
self.marionette.switch_to_window(tab)
self.marionette.navigate(test_page)
self.marionette.switch_to_window(self.start_tab)

View File

@ -6,7 +6,7 @@ from __future__ import absolute_import
import types
from marionette_driver import errors
from marionette_driver import By, errors, Wait
from marionette_harness import MarionetteTestCase, WindowManagerMixin
@ -16,7 +16,9 @@ class TestWindowHandles(WindowManagerMixin, MarionetteTestCase):
def setUp(self):
super(TestWindowHandles, self).setUp()
self.xul_dialog = "chrome://marionette/content/test_dialog.xul"
self.empty_page = self.marionette.absolute_url("empty.html")
self.test_page = self.marionette.absolute_url("windowHandles.html")
self.marionette.navigate(self.test_page)
self.marionette.set_context("chrome")
@ -40,16 +42,17 @@ class TestWindowHandles(WindowManagerMixin, MarionetteTestCase):
self.assertIsInstance(handle, types.StringTypes)
def test_chrome_window_handles_with_scopes(self):
new_browser = self.open_window()
# Open a browser and a non-browser (about window) chrome window
self.open_window(
trigger=lambda: self.marionette.execute_script("OpenBrowserWindow();"))
self.assert_window_handles()
self.assertEqual(len(self.marionette.chrome_window_handles), len(self.start_windows) + 1)
self.assertIn(new_browser, self.marionette.chrome_window_handles)
self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
new_dialog = self.open_chrome_window(self.xul_dialog)
self.open_window(
trigger=lambda: self.marionette.find_element(By.ID, "aboutName").click())
self.assert_window_handles()
self.assertEqual(len(self.marionette.chrome_window_handles), len(self.start_windows) + 2)
self.assertIn(new_dialog, self.marionette.chrome_window_handles)
self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
chrome_window_handles_in_chrome_scope = self.marionette.chrome_window_handles
@ -61,112 +64,117 @@ class TestWindowHandles(WindowManagerMixin, MarionetteTestCase):
self.assertEqual(self.marionette.window_handles,
window_handles_in_chrome_scope)
def test_chrome_window_handles_after_opening_new_chrome_window(self):
new_window = self.open_chrome_window(self.xul_dialog)
def test_chrome_window_handles_after_opening_new_dialog(self):
xul_dialog = "chrome://marionette/content/test_dialog.xul"
new_win = self.open_chrome_window(xul_dialog)
self.assert_window_handles()
self.assertEqual(len(self.marionette.chrome_window_handles), len(self.start_windows) + 1)
self.assertIn(new_window, self.marionette.chrome_window_handles)
self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
# Check that the new chrome window has the correct URL loaded
self.marionette.switch_to_window(new_window)
# Check that the new tab has the correct page loaded
self.marionette.switch_to_window(new_win)
self.assert_window_handles()
self.assertEqual(self.marionette.current_chrome_window_handle, new_window)
self.assertEqual(self.marionette.get_url(), self.xul_dialog)
self.assertEqual(self.marionette.current_chrome_window_handle, new_win)
self.assertEqual(self.marionette.get_url(), xul_dialog)
# Close the chrome window, and carry on in our original window.
# Close the opened dialog and carry on in our original tab.
self.marionette.close_chrome_window()
self.assert_window_handles()
self.assertEqual(len(self.marionette.chrome_window_handles), len(self.start_windows))
self.assertNotIn(new_window, self.marionette.chrome_window_handles)
self.marionette.switch_to_window(self.start_window)
self.assert_window_handles()
self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
with self.marionette.using_context("content"):
self.assertEqual(self.marionette.get_url(), self.test_page)
def test_chrome_window_handles_after_opening_new_window(self):
new_window = self.open_window()
def open_with_link():
with self.marionette.using_context("content"):
link = self.marionette.find_element(By.ID, "new-window")
link.click()
# We open a new window but are actually interested in the new tab
new_win = self.open_window(trigger=open_with_link)
self.assert_window_handles()
self.assertEqual(len(self.marionette.chrome_window_handles), len(self.start_windows) + 1)
self.assertIn(new_window, self.marionette.chrome_window_handles)
self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
self.marionette.switch_to_window(new_window)
# Check that the new tab has the correct page loaded
self.marionette.switch_to_window(new_win)
self.assert_window_handles()
self.assertEqual(self.marionette.current_chrome_window_handle, new_window)
self.assertEqual(self.marionette.current_chrome_window_handle, new_win)
with self.marionette.using_context("content"):
Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
lambda mn: mn.get_url() == self.empty_page,
message="{} did not load after opening a new tab".format(self.empty_page))
# Close the opened window and carry on in our original window.
# Ensure navigate works in our current window
other_page = self.marionette.absolute_url("test.html")
with self.marionette.using_context("content"):
self.marionette.navigate(other_page)
self.assertEqual(self.marionette.get_url(), other_page)
# Close the opened window and carry on in our original tab.
self.marionette.close()
self.assert_window_handles()
self.assertEqual(len(self.marionette.chrome_window_handles), len(self.start_windows))
self.assertNotIn(new_window, self.marionette.chrome_window_handles)
self.marionette.switch_to_window(self.start_window)
self.assert_window_handles()
self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
with self.marionette.using_context("content"):
self.assertEqual(self.marionette.get_url(), self.test_page)
def test_window_handles_after_opening_new_tab(self):
def open_with_link():
with self.marionette.using_context("content"):
new_tab = self.open_tab()
link = self.marionette.find_element(By.ID, "new-tab")
link.click()
new_tab = self.open_tab(trigger=open_with_link)
self.assert_window_handles()
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1)
self.assertIn(new_tab, self.marionette.window_handles)
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
self.marionette.switch_to_window(new_tab)
self.assert_window_handles()
self.assertEqual(self.marionette.current_window_handle, new_tab)
with self.marionette.using_context("content"):
Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
lambda mn: mn.get_url() == self.empty_page,
message="{} did not load after opening a new tab".format(self.empty_page))
# Ensure navigate works in our current tab
other_page = self.marionette.absolute_url("test.html")
with self.marionette.using_context("content"):
self.marionette.navigate(other_page)
self.assertEqual(self.marionette.get_url(), other_page)
self.marionette.switch_to_window(self.start_tab)
self.assert_window_handles()
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
with self.marionette.using_context("content"):
self.assertEqual(self.marionette.get_url(), self.test_page)
self.marionette.switch_to_window(new_tab)
self.marionette.close()
self.assert_window_handles()
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs))
self.assertNotIn(new_tab, self.marionette.window_handles)
self.marionette.switch_to_window(self.start_tab)
self.assert_window_handles()
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
def test_window_handles_after_opening_new_foreground_tab(self):
with self.marionette.using_context("content"):
new_tab = self.open_tab(focus=True)
self.assert_window_handles()
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1)
self.assertIn(new_tab, self.marionette.window_handles)
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
# We still have the default tab set as our window handle. This
# get_url command should be sent immediately, and not be forever-queued.
with self.marionette.using_context("content"):
self.marionette.get_url()
self.marionette.switch_to_window(new_tab)
self.assert_window_handles()
self.assertEqual(self.marionette.current_window_handle, new_tab)
self.marionette.close()
def test_window_handles_after_opening_new_dialog(self):
xul_dialog = "chrome://marionette/content/test_dialog.xul"
new_win = self.open_chrome_window(xul_dialog)
self.assert_window_handles()
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs))
self.assertNotIn(new_tab, self.marionette.window_handles)
self.marionette.switch_to_window(self.start_tab)
self.assert_window_handles()
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
def test_window_handles_after_opening_new_chrome_window(self):
new_window = self.open_chrome_window(self.xul_dialog)
self.marionette.switch_to_window(new_win)
self.assert_window_handles()
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs))
self.assertNotIn(new_window, self.marionette.window_handles)
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
self.marionette.switch_to_window(new_window)
self.assert_window_handles()
self.assertEqual(self.marionette.get_url(), self.xul_dialog)
self.assertEqual(self.marionette.get_url(), xul_dialog)
# Check that the opened dialog is not accessible via window handles
with self.assertRaises(errors.NoSuchWindowException):
@ -182,24 +190,112 @@ class TestWindowHandles(WindowManagerMixin, MarionetteTestCase):
self.marionette.switch_to_window(self.start_tab)
self.assert_window_handles()
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
def test_window_handles_after_closing_original_tab(self):
with self.marionette.using_context("content"):
new_tab = self.open_tab()
self.assertEqual(self.marionette.get_url(), self.test_page)
def test_window_handles_after_opening_new_window(self):
def open_with_link():
with self.marionette.using_context("content"):
link = self.marionette.find_element(By.ID, "new-window")
link.click()
# We open a new window but are actually interested in the new tab
new_tab = self.open_tab(trigger=open_with_link)
self.assert_window_handles()
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1)
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
# Check that the new tab has the correct page loaded
self.marionette.switch_to_window(new_tab)
self.assert_window_handles()
self.assertEqual(self.marionette.current_window_handle, new_tab)
with self.marionette.using_context("content"):
Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
lambda mn: mn.get_url() == self.empty_page,
message="{} did not load after opening a new tab".format(self.empty_page))
# Ensure navigate works in our current window
other_page = self.marionette.absolute_url("test.html")
with self.marionette.using_context("content"):
self.marionette.navigate(other_page)
self.assertEqual(self.marionette.get_url(), other_page)
# Close the opened window and carry on in our original tab.
self.marionette.close()
self.assert_window_handles()
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs))
self.marionette.switch_to_window(self.start_tab)
self.assert_window_handles()
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
with self.marionette.using_context("content"):
self.assertEqual(self.marionette.get_url(), self.test_page)
def test_window_handles_after_closing_original_tab(self):
def open_with_link():
with self.marionette.using_context("content"):
link = self.marionette.find_element(By.ID, "new-tab")
link.click()
new_tab = self.open_tab(trigger=open_with_link)
self.assert_window_handles()
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1)
self.assertIn(new_tab, self.marionette.window_handles)
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
self.marionette.close()
self.assert_window_handles()
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs))
self.assertIn(new_tab, self.marionette.window_handles)
self.marionette.switch_to_window(new_tab)
self.assert_window_handles()
self.assertEqual(self.marionette.current_window_handle, new_tab)
with self.marionette.using_context("content"):
Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
lambda mn: mn.get_url() == self.empty_page,
message="{} did not load after opening a new tab".format(self.empty_page))
def test_window_handles_no_switch(self):
"""Regression test for bug 1294456.
This test is testing the case where Marionette attempts to send a
command to a window handle when the browser has opened and selected
a new tab. Before bug 1294456 landed, the Marionette driver was getting
confused about which window handle the client cared about, and assumed
it was the window handle for the newly opened and selected tab.
This caused Marionette to think that the browser needed to do a remoteness
flip in the e10s case, since the tab opened by menu_newNavigatorTab is
about:newtab (which is currently non-remote). This meant that commands
sent to what should have been the original window handle would be
queued and never sent, since the remoteness flip in the new tab was
never going to happen.
"""
def open_with_menu():
menu_new_tab = self.marionette.find_element(By.ID, 'menu_newNavigatorTab')
menu_new_tab.click()
new_tab = self.open_tab(trigger=open_with_menu)
self.assert_window_handles()
# We still have the default tab set as our window handle. This
# get_url command should be sent immediately, and not be forever-queued.
with self.marionette.using_context("content"):
self.assertEqual(self.marionette.get_url(), self.test_page)
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1)
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
self.marionette.switch_to_window(new_tab)
self.assert_window_handles()
self.assertEqual(self.marionette.current_window_handle, new_tab)
self.marionette.close()
self.assert_window_handles()
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs))
self.marionette.switch_to_window(self.start_tab)
self.assert_window_handles()
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
def test_window_handles_after_closing_last_window(self):
self.close_all_windows()
self.assertEqual(self.marionette.close_chrome_window(), [])

View File

@ -7,7 +7,7 @@ from __future__ import absolute_import
import types
import urllib
from marionette_driver import errors
from marionette_driver import By, errors, Wait
from marionette_harness import MarionetteTestCase, skip_if_mobile, WindowManagerMixin
@ -21,7 +21,9 @@ class TestWindowHandles(WindowManagerMixin, MarionetteTestCase):
def setUp(self):
super(TestWindowHandles, self).setUp()
self.xul_dialog = "chrome://marionette/content/test_dialog.xul"
self.empty_page = self.marionette.absolute_url("empty.html")
self.test_page = self.marionette.absolute_url("windowHandles.html")
self.marionette.navigate(self.test_page)
def tearDown(self):
self.close_all_tabs()
@ -37,8 +39,12 @@ class TestWindowHandles(WindowManagerMixin, MarionetteTestCase):
for handle in self.marionette.window_handles:
self.assertIsInstance(handle, types.StringTypes)
def tst_window_handles_after_opening_new_tab(self):
new_tab = self.open_tab()
def test_window_handles_after_opening_new_tab(self):
def open_with_link():
link = self.marionette.find_element(By.ID, "new-tab")
link.click()
new_tab = self.open_tab(trigger=open_with_link)
self.assert_window_handles()
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1)
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
@ -46,9 +52,13 @@ class TestWindowHandles(WindowManagerMixin, MarionetteTestCase):
self.marionette.switch_to_window(new_tab)
self.assert_window_handles()
self.assertEqual(self.marionette.current_window_handle, new_tab)
Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
lambda mn: mn.get_url() == self.empty_page,
message="{} did not load after opening a new tab".format(self.empty_page))
self.marionette.switch_to_window(self.start_tab)
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
self.assertEqual(self.marionette.get_url(), self.test_page)
self.marionette.switch_to_window(new_tab)
self.marionette.close()
@ -59,15 +69,29 @@ class TestWindowHandles(WindowManagerMixin, MarionetteTestCase):
self.assert_window_handles()
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
def tst_window_handles_after_opening_new_browser_window(self):
new_tab = self.open_window()
def test_window_handles_after_opening_new_browser_window(self):
def open_with_link():
link = self.marionette.find_element(By.ID, "new-window")
link.click()
# We open a new window but are actually interested in the new tab
new_tab = self.open_tab(trigger=open_with_link)
self.assert_window_handles()
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1)
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
# Check that the new tab has the correct page loaded
self.marionette.switch_to_window(new_tab)
self.assert_window_handles()
self.assertEqual(self.marionette.current_window_handle, new_tab)
Wait(self.marionette, self.marionette.timeout.page_load).until(
lambda _: self.marionette.get_url() == self.empty_page,
message="The expected page '{}' has not been loaded".format(self.empty_page))
# Ensure navigate works in our current window
other_page = self.marionette.absolute_url("test.html")
self.marionette.navigate(other_page)
self.assertEqual(self.marionette.get_url(), other_page)
# Close the opened window and carry on in our original tab.
self.marionette.close()
@ -77,16 +101,31 @@ class TestWindowHandles(WindowManagerMixin, MarionetteTestCase):
self.marionette.switch_to_window(self.start_tab)
self.assert_window_handles()
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
self.assertEqual(self.marionette.get_url(), self.test_page)
@skip_if_mobile("Fennec doesn't support other chrome windows")
def tst_window_handles_after_opening_new_non_browser_window(self):
new_window = self.open_chrome_window(self.xul_dialog)
def test_window_handles_after_opening_new_non_browser_window(self):
def open_with_link():
self.marionette.navigate(inline("""
<a id="blob-download" download="foo.html">Download</a>
<script>
const string = "test";
const blob = new Blob([string], { type: "text/html" });
const link = document.getElementById("blob-download");
link.href = URL.createObjectURL(blob);
</script>
"""))
link = self.marionette.find_element(By.ID, "blob-download")
link.click()
new_win = self.open_window(trigger=open_with_link)
self.assert_window_handles()
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs))
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
self.assertNotIn(new_window, self.marionette.window_handles)
self.marionette.switch_to_window(new_window)
self.marionette.switch_to_window(new_win)
self.assert_window_handles()
# Check that the opened window is not accessible via window handles
@ -105,21 +144,26 @@ class TestWindowHandles(WindowManagerMixin, MarionetteTestCase):
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
def test_window_handles_after_closing_original_tab(self):
new_tab = self.open_tab()
def open_with_link():
link = self.marionette.find_element(By.ID, "new-tab")
link.click()
new_tab = self.open_tab(trigger=open_with_link)
self.assert_window_handles()
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1)
self.assertEqual(self.marionette.current_window_handle, self.start_tab)
self.assertIn(new_tab, self.marionette.window_handles)
self.marionette.close()
self.assert_window_handles()
self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs))
self.assertNotIn(self.start_tab, self.marionette.window_handles)
self.marionette.switch_to_window(new_tab)
self.assert_window_handles()
self.assertEqual(self.marionette.current_window_handle, new_tab)
Wait(self.marionette, self.marionette.timeout.page_load).until(
lambda _: self.marionette.get_url() == self.empty_page,
message="The expected page '{}' has not been loaded".format(self.empty_page))
def tst_window_handles_after_closing_last_tab(self):
def test_window_handles_after_closing_last_tab(self):
self.close_all_tabs()
self.assertEqual(self.marionette.close(), [])

View File

@ -21,9 +21,15 @@ class TestNoSuchWindowContent(WindowManagerMixin, MarionetteTestCase):
@skip_if_mobile("Fennec doesn't support other chrome windows")
def test_closed_chrome_window(self):
with self.marionette.using_context("chrome"):
new_window = self.open_window()
self.marionette.switch_to_window(new_window)
def open_with_link():
with self.marionette.using_context("content"):
test_page = self.marionette.absolute_url("windowHandles.html")
self.marionette.navigate(test_page)
self.marionette.find_element(By.ID, "new-window").click()
win = self.open_window(open_with_link)
self.marionette.switch_to_window(win)
self.marionette.close_chrome_window()
# When closing a browser window both handles are not available
@ -37,12 +43,12 @@ class TestNoSuchWindowContent(WindowManagerMixin, MarionetteTestCase):
self.marionette.switch_to_window(self.start_window)
with self.assertRaises(NoSuchWindowException):
self.marionette.switch_to_window(new_window)
self.marionette.switch_to_window(win)
@skip_if_mobile("Fennec doesn't support other chrome windows")
def test_closed_chrome_window_while_in_frame(self):
new_window = self.open_chrome_window("chrome://marionette/content/test.xul")
self.marionette.switch_to_window(new_window)
win = self.open_chrome_window("chrome://marionette/content/test.xul")
self.marionette.switch_to_window(win)
with self.marionette.using_context("chrome"):
self.marionette.switch_to_frame("iframe")
self.marionette.close_chrome_window()
@ -55,11 +61,12 @@ class TestNoSuchWindowContent(WindowManagerMixin, MarionetteTestCase):
self.marionette.switch_to_window(self.start_window)
with self.assertRaises(NoSuchWindowException):
self.marionette.switch_to_window(new_window)
self.marionette.switch_to_window(win)
def test_closed_tab(self):
new_tab = self.open_tab()
self.marionette.switch_to_window(new_tab)
with self.marionette.using_context("content"):
tab = self.open_tab()
self.marionette.switch_to_window(tab)
self.marionette.close()
# Check that only the content window is not available in both contexts
@ -72,13 +79,12 @@ class TestNoSuchWindowContent(WindowManagerMixin, MarionetteTestCase):
self.marionette.switch_to_window(self.start_tab)
with self.assertRaises(NoSuchWindowException):
self.marionette.switch_to_window(new_tab)
self.marionette.switch_to_window(tab)
def test_closed_tab_while_in_frame(self):
new_tab = self.open_tab()
self.marionette.switch_to_window(new_tab)
with self.marionette.using_context("content"):
tab = self.open_tab()
self.marionette.switch_to_window(tab)
self.marionette.navigate(self.marionette.absolute_url("test_iframe.html"))
frame = self.marionette.find_element(By.ID, "test_iframe")
self.marionette.switch_to_frame(frame)
@ -91,7 +97,7 @@ class TestNoSuchWindowContent(WindowManagerMixin, MarionetteTestCase):
self.marionette.switch_to_window(self.start_tab)
with self.assertRaises(NoSuchWindowException):
self.marionette.switch_to_window(new_tab)
self.marionette.switch_to_window(tab)
class TestNoSuchWindowChrome(TestNoSuchWindowContent):
@ -115,22 +121,42 @@ class TestSwitchWindow(WindowManagerMixin, MarionetteTestCase):
self.close_all_windows()
super(TestSwitchWindow, self).tearDown()
def test_switch_window_after_open_and_close(self):
with self.marionette.using_context("chrome"):
new_window = self.open_window()
self.assertEqual(len(self.marionette.chrome_window_handles), len(self.start_windows) + 1)
self.assertIn(new_window, self.marionette.chrome_window_handles)
def test_windows(self):
def open_browser_with_js():
self.marionette.execute_script(" window.open(); ")
new_window = self.open_window(trigger=open_browser_with_js)
self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
# switch to the new chrome window and close it
# switch to the other window
self.marionette.switch_to_window(new_window)
self.assertEqual(self.marionette.current_chrome_window_handle, new_window)
self.assertNotEqual(self.marionette.current_chrome_window_handle, self.start_window)
self.marionette.close_chrome_window()
self.assertEqual(len(self.marionette.chrome_window_handles), len(self.start_windows))
self.assertNotIn(new_window, self.marionette.chrome_window_handles)
# switch back to the original chrome window
# switch back and close original window
self.marionette.switch_to_window(self.start_window)
self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
self.marionette.close_chrome_window()
self.assertNotIn(self.start_window, self.marionette.chrome_window_handles)
self.assertEqual(len(self.marionette.chrome_window_handles), len(self.start_windows))
def test_should_load_and_close_a_window(self):
def open_window_with_link():
test_html = self.marionette.absolute_url("test_windows.html")
with self.marionette.using_context("content"):
self.marionette.navigate(test_html)
self.marionette.find_element(By.LINK_TEXT, "Open new window").click()
new_window = self.open_window(trigger=open_window_with_link)
self.marionette.switch_to_window(new_window)
self.assertEqual(self.marionette.current_chrome_window_handle, new_window)
self.assertEqual(len(self.marionette.chrome_window_handles), 2)
with self.marionette.using_context('content'):
self.assertEqual(self.marionette.title, "We Arrive Here")
# Let's close and check
self.marionette.close_chrome_window()
self.marionette.switch_to_window(self.start_window)
self.assertEqual(len(self.marionette.chrome_window_handles), 1)

View File

@ -15,15 +15,30 @@ class TestNoSuchWindowContent(WindowManagerMixin, MarionetteTestCase):
def setUp(self):
super(TestNoSuchWindowContent, self).setUp()
self.test_page = self.marionette.absolute_url("windowHandles.html")
with self.marionette.using_context("content"):
self.marionette.navigate(self.test_page)
def tearDown(self):
self.close_all_windows()
super(TestNoSuchWindowContent, self).tearDown()
def open_tab_in_foreground(self):
with self.marionette.using_context("content"):
link = self.marionette.find_element(By.ID, "new-tab")
link.click()
@skip_if_mobile("Fennec doesn't support other chrome windows")
def test_closed_chrome_window(self):
with self.marionette.using_context("chrome"):
new_window = self.open_window()
self.marionette.switch_to_window(new_window)
def open_with_link():
with self.marionette.using_context("content"):
test_page = self.marionette.absolute_url("windowHandles.html")
self.marionette.navigate(test_page)
self.marionette.find_element(By.ID, "new-window").click()
win = self.open_window(open_with_link)
self.marionette.switch_to_window(win)
self.marionette.close_chrome_window()
# When closing a browser window both handles are not available
@ -37,13 +52,12 @@ class TestNoSuchWindowContent(WindowManagerMixin, MarionetteTestCase):
self.marionette.switch_to_window(self.start_window)
with self.assertRaises(NoSuchWindowException):
self.marionette.switch_to_window(new_window)
self.marionette.switch_to_window(win)
@skip_if_mobile("Fennec doesn't support other chrome windows")
def test_closed_chrome_window_while_in_frame(self):
new_window = self.open_chrome_window("chrome://marionette/content/test.xul")
self.marionette.switch_to_window(new_window)
win = self.open_chrome_window("chrome://marionette/content/test.xul")
self.marionette.switch_to_window(win)
with self.marionette.using_context("chrome"):
self.marionette.switch_to_frame("iframe")
self.marionette.close_chrome_window()
@ -56,11 +70,12 @@ class TestNoSuchWindowContent(WindowManagerMixin, MarionetteTestCase):
self.marionette.switch_to_window(self.start_window)
with self.assertRaises(NoSuchWindowException):
self.marionette.switch_to_window(new_window)
self.marionette.switch_to_window(win)
def test_closed_tab(self):
new_tab = self.open_tab(focus=True)
self.marionette.switch_to_window(new_tab)
with self.marionette.using_context("content"):
tab = self.open_tab(self.open_tab_in_foreground)
self.marionette.switch_to_window(tab)
self.marionette.close()
# Check that only the content window is not available in both contexts
@ -73,17 +88,15 @@ class TestNoSuchWindowContent(WindowManagerMixin, MarionetteTestCase):
self.marionette.switch_to_window(self.start_tab)
with self.assertRaises(NoSuchWindowException):
self.marionette.switch_to_window(new_tab)
self.marionette.switch_to_window(tab)
def test_closed_tab_while_in_frame(self):
new_tab = self.open_tab()
self.marionette.switch_to_window(new_tab)
with self.marionette.using_context("content"):
tab = self.open_tab(self.open_tab_in_foreground)
self.marionette.switch_to_window(tab)
self.marionette.navigate(self.marionette.absolute_url("test_iframe.html"))
frame = self.marionette.find_element(By.ID, "test_iframe")
self.marionette.switch_to_frame(frame)
self.marionette.close()
with self.assertRaises(NoSuchWindowException):
@ -93,4 +106,4 @@ class TestNoSuchWindowContent(WindowManagerMixin, MarionetteTestCase):
self.marionette.switch_to_window(self.start_tab)
with self.assertRaises(NoSuchWindowException):
self.marionette.switch_to_window(new_tab)
self.marionette.switch_to_window(tab)

View File

@ -15,7 +15,7 @@ ChromeUtils.import("chrome://marionette/content/evaluate.js");
const {Log} = ChromeUtils.import("chrome://marionette/content/log.js", {});
ChromeUtils.import("chrome://marionette/content/modal.js");
const {
waitForObserverTopic,
MessageManagerDestroyedPromise,
} = ChromeUtils.import("chrome://marionette/content/sync.js", {});
this.EXPORTED_SYMBOLS = ["proxy"];
@ -156,9 +156,7 @@ proxy.AsyncMessageChannel = class {
break;
}
await waitForObserverTopic("message-manager-disconnect",
subject => subject === messageManager);
await new MessageManagerDestroyedPromise(messageManager);
this.removeHandlers();
resolve();
};

View File

@ -11,6 +11,7 @@ const ServerSocket = CC(
"nsIServerSocket",
"initSpecialConnection");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("chrome://marionette/content/assert.js");
@ -73,7 +74,7 @@ class TCPListener {
*/
driverFactory() {
MarionettePrefs.contentListener = false;
return new GeckoDriver(this);
return new GeckoDriver(Services.appinfo.ID, this);
}
set acceptConnections(value) {

View File

@ -13,42 +13,23 @@ const {
stack,
TimeoutError,
} = ChromeUtils.import("chrome://marionette/content/error.js", {});
const {truncate} = ChromeUtils.import("chrome://marionette/content/format.js", {});
const {Log} = ChromeUtils.import("chrome://marionette/content/log.js", {});
XPCOMUtils.defineLazyGetter(this, "log", Log.get);
this.EXPORTED_SYMBOLS = [
"executeSoon",
"DebounceCallback",
"IdlePromise",
"MessageManagerDestroyedPromise",
"PollPromise",
"Sleep",
"TimedPromise",
"waitForEvent",
"waitForMessage",
"waitForObserverTopic",
];
const {TYPE_ONE_SHOT, TYPE_REPEATING_SLACK} = Ci.nsITimer;
const PROMISE_TIMEOUT = AppConstants.DEBUG ? 4500 : 1500;
/**
* Dispatch a function to be executed on the main thread.
*
* @param {function} func
* Function to be executed.
*/
function executeSoon(func) {
if (typeof func != "function") {
throw new TypeError();
}
Services.tm.dispatchToMainThread(func);
}
/**
* @callback Condition
*
@ -255,6 +236,46 @@ function Sleep(timeout) {
return new TimedPromise(() => {}, {timeout, throws: null});
}
/**
* Detects when the specified message manager has been destroyed.
*
* One can observe the removal and detachment of a content browser
* (`<xul:browser>`) or a chrome window by its message manager
* disconnecting.
*
* When a browser is associated with a tab, this is safer than only
* relying on the event `TabClose` which signalises the _intent to_
* remove a tab and consequently would lead to the destruction of
* the content browser and its browser message manager.
*
* When closing a chrome window it is safer than only relying on
* the event 'unload' which signalises the _intent to_ close the
* chrome window and consequently would lead to the destruction of
* the window and its window message manager.
*
* @param {MessageListenerManager} messageManager
* The message manager to observe for its disconnect state.
* Use the browser message manager when closing a content browser,
* and the window message manager when closing a chrome window.
*
* @return {Promise}
* A promise that resolves when the message manager has been destroyed.
*/
function MessageManagerDestroyedPromise(messageManager) {
return new Promise(resolve => {
function observe(subject, topic) {
log.trace(`Received observer notification ${topic}`);
if (subject == messageManager) {
Services.obs.removeObserver(this, "message-manager-disconnect");
resolve();
}
}
Services.obs.addObserver(observe, "message-manager-disconnect");
});
}
/**
* Throttle until the main thread is idle and `window` has performed
* an animation frame (in that order).
@ -330,192 +351,3 @@ class DebounceCallback {
}
}
this.DebounceCallback = DebounceCallback;
/**
* Wait for an event to be fired on a specified element.
*
* This method has been duplicated from BrowserTestUtils.jsm.
*
* Because this function is intended for testing, any error in checkFn
* will cause the returned promise to be rejected instead of waiting for
* the next event, since this is probably a bug in the test.
*
* Usage::
*
* let promiseEvent = waitForEvent(element, "eventName");
* // Do some processing here that will cause the event to be fired
* // ...
* // Now wait until the Promise is fulfilled
* let receivedEvent = await promiseEvent;
*
* The promise resolution/rejection handler for the returned promise is
* guaranteed not to be called until the next event tick after the event
* listener gets called, so that all other event listeners for the element
* are executed before the handler is executed::
*
* let promiseEvent = waitForEvent(element, "eventName");
* // Same event tick here.
* await promiseEvent;
* // Next event tick here.
*
* If some code, such like adding yet another event listener, needs to be
* executed in the same event tick, use raw addEventListener instead and
* place the code inside the event listener::
*
* element.addEventListener("load", () => {
* // Add yet another event listener in the same event tick as the load
* // event listener.
* p = waitForEvent(element, "ready");
* }, { once: true });
*
* @param {Element} subject
* The element that should receive the event.
* @param {string} eventName
* Name of the event to listen to.
* @param {Object=} options
* Extra options.
* @param {boolean=} options.capture
* True to use a capturing listener.
* @param {function(Event)=} options.checkFn
* Called with the ``Event`` object as argument, should return ``true``
* if the event is the expected one, or ``false`` if it should be
* ignored and listening should continue. If not specified, the first
* event with the specified name resolves the returned promise.
* @param {boolean=} options.wantsUntrusted
* True to receive synthetic events dispatched by web content.
*
* @return {Promise.<Event>}
* Promise which resolves to the received ``Event`` object, or rejects
* in case of a failure.
*/
function waitForEvent(subject, eventName,
{capture = false, checkFn = null, wantsUntrusted = false} = {}) {
if (subject == null || !("addEventListener" in subject)) {
throw new TypeError();
}
if (typeof eventName != "string") {
throw new TypeError();
}
if (capture != null && typeof capture != "boolean") {
throw new TypeError();
}
if (checkFn != null && typeof checkFn != "function") {
throw new TypeError();
}
if (wantsUntrusted != null && typeof wantsUntrusted != "boolean") {
throw new TypeError();
}
return new Promise((resolve, reject) => {
subject.addEventListener(eventName, function listener(event) {
log.trace(`Received DOM event ${event.type} for ${event.target}`);
try {
if (checkFn && !checkFn(event)) {
return;
}
subject.removeEventListener(eventName, listener, capture);
executeSoon(() => resolve(event));
} catch (ex) {
try {
subject.removeEventListener(eventName, listener, capture);
} catch (ex2) {
// Maybe the provided object does not support removeEventListener.
}
executeSoon(() => reject(ex));
}
}, capture, wantsUntrusted);
});
}
/**
* Wait for a message to be fired from a particular message manager.
*
* This method has been duplicated from BrowserTestUtils.jsm.
*
* @param {nsIMessageManager} messageManager
* The message manager that should be used.
* @param {string} messageName
* The message to wait for.
* @param {Object=} options
* Extra options.
* @param {function(Message)=} options.checkFn
* Called with the ``Message`` object as argument, should return ``true``
* if the message is the expected one, or ``false`` if it should be
* ignored and listening should continue. If not specified, the first
* message with the specified name resolves the returned promise.
*
* @return {Promise.<Object>}
* Promise which resolves to the data property of the received
* ``Message``.
*/
function waitForMessage(messageManager, messageName,
{checkFn = undefined} = {}) {
if (messageManager == null || !("addMessageListener" in messageManager)) {
throw new TypeError();
}
if (typeof messageName != "string") {
throw new TypeError();
}
if (checkFn && typeof checkFn != "function") {
throw new TypeError();
}
return new Promise(resolve => {
messageManager.addMessageListener(messageName, function onMessage(msg) {
log.trace(`Received ${messageName} for ${msg.target}`);
if (checkFn && !checkFn(msg)) {
return;
}
messageManager.removeMessageListener(messageName, onMessage);
resolve(msg.data);
});
});
}
/**
* Wait for the specified observer topic to be observed.
*
* This method has been duplicated from TestUtils.jsm.
*
* Because this function is intended for testing, any error in checkFn
* will cause the returned promise to be rejected instead of waiting for
* the next notification, since this is probably a bug in the test.
*
* @param {string} topic
* The topic to observe.
* @param {Object=} options
* Extra options.
* @param {function(String,Object)=} options.checkFn
* Called with ``subject``, and ``data`` as arguments, should return true
* if the notification is the expected one, or false if it should be
* ignored and listening should continue. If not specified, the first
* notification for the specified topic resolves the returned promise.
*
* @return {Promise.<Array<String, Object>>}
* Promise which resolves to an array of ``subject``, and ``data`` from
* the observed notification.
*/
function waitForObserverTopic(topic, {checkFn = null} = {}) {
if (typeof topic != "string") {
throw new TypeError();
}
if (checkFn != null && typeof checkFn != "function") {
throw new TypeError();
}
return new Promise((resolve, reject) => {
Services.obs.addObserver(function observer(subject, topic, data) {
log.trace(`Received observer notification ${topic}`);
try {
if (checkFn && !checkFn(subject, data)) {
return;
}
Services.obs.removeObserver(observer, topic);
resolve({subject, data});
} catch (ex) {
Services.obs.removeObserver(observer, topic);
reject(ex);
}
}, topic);
});
}

View File

@ -2,93 +2,16 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
ChromeUtils.import("resource://gre/modules/Services.jsm");
const {
DebounceCallback,
IdlePromise,
PollPromise,
Sleep,
TimedPromise,
waitForEvent,
waitForMessage,
waitForObserverTopic,
} = ChromeUtils.import("chrome://marionette/content/sync.js", {});
const DEFAULT_TIMEOUT = 2000;
/**
* Mimic a DOM node for listening for events.
*/
class MockElement {
constructor() {
this.capture = false;
this.func = null;
this.eventName = null;
this.untrusted = false;
}
addEventListener(name, func, capture, untrusted) {
this.eventName = name;
this.func = func;
if (capture != null) {
this.capture = capture;
}
if (untrusted != null) {
this.untrusted = untrusted;
}
}
click() {
if (this.func) {
let details = {
capture: this.capture,
target: this,
type: this.eventName,
untrusted: this.untrusted,
};
this.func(details);
}
}
removeEventListener(name, func) {
this.capture = false;
this.func = null;
this.eventName = null;
this.untrusted = false;
}
}
/**
* Mimic a message manager for sending messages.
*/
class MessageManager {
constructor() {
this.func = null;
this.message = null;
}
addMessageListener(message, func) {
this.func = func;
this.message = message;
}
removeMessageListener(message) {
this.func = null;
this.message = null;
}
send(message, data) {
if (this.func) {
this.func({
data,
message,
target: this,
});
}
}
}
/**
* Mimics nsITimer, but instead of using a system clock you can
* preprogram it to invoke the callback after a given number of ticks.
@ -112,23 +35,6 @@ class MockTimer {
}
}
add_test(function test_executeSoon_callback() {
// executeSoon() is already defined for xpcshell in head.js. As such import
// our implementation into a custom namespace.
let sync = {};
ChromeUtils.import("chrome://marionette/content/sync.js", sync);
for (let func of ["foo", null, true, [], {}]) {
Assert.throws(() => sync.executeSoon(func), /TypeError/);
}
let a;
sync.executeSoon(() => { a = 1; });
executeSoon(() => equal(1, a));
run_next_test();
});
add_test(function test_PollPromise_funcTypes() {
for (let type of ["foo", 42, null, undefined, true, [], {}]) {
Assert.throws(() => new PollPromise(type), /TypeError/);
@ -307,155 +213,3 @@ add_task(async function test_DebounceCallback_repeatedCallback() {
equal(ncalls, 1);
ok(debouncer.timer.cancelled);
});
add_task(async function test_waitForEvent_subjectAndEventNameTypes() {
let element = new MockElement();
for (let subject of ["foo", 42, null, undefined, true, [], {}]) {
Assert.throws(() => waitForEvent(subject, "click"), /TypeError/);
}
for (let eventName of [42, null, undefined, true, [], {}]) {
Assert.throws(() => waitForEvent(element, eventName), /TypeError/);
}
let clicked = waitForEvent(element, "click");
element.click();
let event = await clicked;
equal(element, event.target);
});
add_task(async function test_waitForEvent_captureTypes() {
let element = new MockElement();
for (let capture of ["foo", 42, [], {}]) {
Assert.throws(() => waitForEvent(
element, "click", {capture}), /TypeError/);
}
for (let capture of [null, undefined, false, true]) {
let expected_capture = (capture == null) ? false : capture;
element = new MockElement();
let clicked = waitForEvent(element, "click", {capture});
element.click();
let event = await clicked;
equal(element, event.target);
equal(expected_capture, event.capture);
}
});
add_task(async function test_waitForEvent_checkFnTypes() {
let element = new MockElement();
for (let checkFn of ["foo", 42, true, [], {}]) {
Assert.throws(() => waitForEvent(
element, "click", {checkFn}), /TypeError/);
}
let count;
for (let checkFn of [null, undefined, event => count++ > 0]) {
let expected_count = (checkFn == null) ? 0 : 2;
count = 0;
element = new MockElement();
let clicked = waitForEvent(element, "click", {checkFn});
element.click();
element.click();
let event = await clicked;
equal(element, event.target);
equal(expected_count, count);
}
});
add_task(async function test_waitForEvent_wantsUntrustedTypes() {
let element = new MockElement();
for (let wantsUntrusted of ["foo", 42, [], {}]) {
Assert.throws(() => waitForEvent(
element, "click", {wantsUntrusted}), /TypeError/);
}
for (let wantsUntrusted of [null, undefined, false, true]) {
let expected_untrusted = (wantsUntrusted == null) ? false : wantsUntrusted;
element = new MockElement();
let clicked = waitForEvent(element, "click", {wantsUntrusted});
element.click();
let event = await clicked;
equal(element, event.target);
equal(expected_untrusted, event.untrusted);
}
});
add_task(async function test_waitForMessage_messageManagerAndMessageTypes() {
let messageManager = new MessageManager();
for (let manager of ["foo", 42, null, undefined, true, [], {}]) {
Assert.throws(() => waitForMessage(manager, "message"), /TypeError/);
}
for (let message of [42, null, undefined, true, [], {}]) {
Assert.throws(() => waitForEvent(messageManager, message), /TypeError/);
}
let data = {"foo": "bar"};
let sent = waitForMessage(messageManager, "message");
messageManager.send("message", data);
equal(data, await sent);
});
add_task(async function test_waitForMessage_checkFnTypes() {
let messageManager = new MessageManager();
for (let checkFn of ["foo", 42, true, [], {}]) {
Assert.throws(() => waitForMessage(
messageManager, "message", {checkFn}), /TypeError/);
}
let data1 = {"fo": "bar"};
let data2 = {"foo": "bar"};
for (let checkFn of [null, undefined, msg => "foo" in msg.data]) {
let expected_data = (checkFn == null) ? data1 : data2;
messageManager = new MessageManager();
let sent = waitForMessage(messageManager, "message", {checkFn});
messageManager.send("message", data1);
messageManager.send("message", data2);
equal(expected_data, await sent);
}
});
add_task(async function test_waitForObserverTopic_topicTypes() {
for (let topic of [42, null, undefined, true, [], {}]) {
Assert.throws(() => waitForObserverTopic(topic), /TypeError/);
}
let data = {"foo": "bar"};
let sent = waitForObserverTopic("message");
Services.obs.notifyObservers(this, "message", data);
let result = await sent;
equal(this, result.subject);
equal(data, result.data);
});
add_task(async function test_waitForObserverTopic_checkFnTypes() {
for (let checkFn of ["foo", 42, true, [], {}]) {
Assert.throws(() => waitForObserverTopic(
"message", {checkFn}), /TypeError/);
}
let data1 = {"fo": "bar"};
let data2 = {"foo": "bar"};
for (let checkFn of [null, undefined, (subject, data) => data == data2]) {
let expected_data = (checkFn == null) ? data1 : data2;
let sent = waitForObserverTopic("message");
Services.obs.notifyObservers(this, "message", data1);
Services.obs.notifyObservers(this, "message", data2);
let result = await sent;
equal(expected_data, result.data);
}
});

View File

@ -10,17 +10,14 @@ const CC = Components.Constructor;
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/EventEmitter.jsm");
const {
StreamUtils,
} = ChromeUtils.import("chrome://marionette/content/stream-utils.js", {});
const {
BulkPacket,
JSONPacket,
Packet,
} = ChromeUtils.import("chrome://marionette/content/packets.js", {});
const {
executeSoon,
} = ChromeUtils.import("chrome://marionette/content/sync.js", {});
const {StreamUtils} =
ChromeUtils.import("chrome://marionette/content/stream-utils.js", {});
const {Packet, JSONPacket, BulkPacket} =
ChromeUtils.import("chrome://marionette/content/packets.js", {});
const executeSoon = function(func) {
Services.tm.dispatchToMainThread(func);
};
const flags = {wantVerbose: false, wantLogging: false};

View File

@ -1,4 +1,5 @@
[promise.py]
expected: TIMEOUT
[test_promise_timeout]
expected: FAIL

View File

@ -21,6 +21,7 @@ skip-if = appname == "thunderbird" || os == "android"
[test_ext_cookieBehaviors.js]
[test_ext_cookies_samesite.js]
[test_ext_content_security_policy.js]
skip-if = (os == "win" && debug) #Bug 1485567
[test_ext_contentscript_api_injection.js]
[test_ext_contentscript_async_loading.js]
skip-if = os == 'android' && debug # The generated script takes too long to load on Android debug