mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 00:05:36 +00:00
Merge mozilla-central to autoland. a=merge CLOSED TREE
This commit is contained in:
commit
e154ee4c06
@ -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}"
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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}) {
|
||||
|
@ -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({
|
||||
|
28
browser/components/payments/res/mixins/HandleEventMixin.js
Normal file
28
browser/components/payments/res/mixins/HandleEventMixin.js
Normal 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;
|
||||
}
|
||||
};
|
||||
}
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
66
dom/localstorage/test/unit/test_usage.js
Normal file
66
dom/localstorage/test/unit/test_usage.js
Normal 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");
|
||||
}
|
@ -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]
|
||||
|
@ -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]
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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_);
|
||||
|
@ -1,4 +1,4 @@
|
||||
// |jit-test| allow-overrecursed
|
||||
// |jit-test| slow; allow-overrecursed
|
||||
|
||||
(function f(x) {
|
||||
f(x - 1);
|
||||
|
@ -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"
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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));
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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)),
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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).
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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"})
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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; ")
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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(), [])
|
||||
|
@ -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(), [])
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
@ -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};
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
[promise.py]
|
||||
expected: TIMEOUT
|
||||
[test_promise_timeout]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user