зеркало из https://github.com/mozilla/gecko-dev.git
Merge autoland to central, a=merge
MozReview-Commit-ID: 9TWPWlHDRi2
This commit is contained in:
Коммит
315b5d099f
|
@ -1669,6 +1669,7 @@ pref("extensions.formautofill.experimental", true);
|
|||
pref("extensions.formautofill.experimental", false);
|
||||
#endif
|
||||
pref("extensions.formautofill.addresses.enabled", true);
|
||||
pref("extensions.formautofill.firstTimeUse", true);
|
||||
pref("extensions.formautofill.heuristics.enabled", true);
|
||||
pref("extensions.formautofill.loglevel", "Warn");
|
||||
|
||||
|
|
|
@ -324,9 +324,11 @@ var FormAutofillContent = {
|
|||
* Send the profile to parent for doorhanger and storage saving/updating.
|
||||
*
|
||||
* @param {Object} profile Submitted form's address/creditcard guid and record.
|
||||
* @param {Object} domWin Current content window.
|
||||
*/
|
||||
_onFormSubmit(profile) {
|
||||
Services.cpmm.sendAsyncMessage("FormAutofill:OnFormSubmit", profile);
|
||||
_onFormSubmit(profile, domWin) {
|
||||
let mm = this._messageManagerFromWindow(domWin);
|
||||
mm.sendAsyncMessage("FormAutofill:OnFormSubmit", profile);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -365,7 +367,7 @@ var FormAutofillContent = {
|
|||
record: pendingAddress,
|
||||
},
|
||||
// creditCard: {}
|
||||
});
|
||||
}, domWin);
|
||||
|
||||
return true;
|
||||
},
|
||||
|
@ -507,6 +509,14 @@ var FormAutofillContent = {
|
|||
ProfileAutocomplete._previewSelectedProfile(selectedIndex);
|
||||
}
|
||||
},
|
||||
|
||||
_messageManagerFromWindow(win) {
|
||||
return win.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell)
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIContentFrameMessageManager);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,173 @@
|
|||
/* 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/. */
|
||||
|
||||
/*
|
||||
* Implements doorhanger singleton that wraps up the PopupNotifications and handles
|
||||
* the doorhager UI for formautofill related features.
|
||||
*/
|
||||
|
||||
/* exported FormAutofillDoorhanger */
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["FormAutofillDoorhanger"];
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://formautofill/FormAutofillUtils.jsm");
|
||||
|
||||
this.log = null;
|
||||
FormAutofillUtils.defineLazyLogGetter(this, this.EXPORTED_SYMBOLS[0]);
|
||||
|
||||
const BUNDLE_URI = "chrome://formautofill/locale/formautofill.properties";
|
||||
const GetStringFromName = Services.strings.createBundle(BUNDLE_URI).GetStringFromName;
|
||||
|
||||
const CONTENT = {
|
||||
firstTimeUse: {
|
||||
notificationId: "autofill-address",
|
||||
message: GetStringFromName("saveAddressMessage"),
|
||||
anchor: {
|
||||
id: "autofill-address-notification-icon",
|
||||
URL: "chrome://formautofill/content/icon-address-save.svg",
|
||||
tooltiptext: GetStringFromName("openAutofillMessagePanel"),
|
||||
},
|
||||
options: {
|
||||
persistWhileVisible: true,
|
||||
popupIconURL: "chrome://formautofill/content/icon-address-save.svg",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
let FormAutofillDoorhanger = {
|
||||
/**
|
||||
* Generate the main action and secondary actions from content parameters and
|
||||
* promise resolve.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} mainActionParams
|
||||
* Parameters for main action.
|
||||
* @param {Array<Object>} secondaryActionParams
|
||||
* Array of the parameters for secondary actions.
|
||||
* @param {Function} resolve Should be called in action callback.
|
||||
* @returns {Array<Object>}
|
||||
Return the mainAction and secondary actions in an array for showing doorhanger
|
||||
*/
|
||||
_createActions(mainActionParams, secondaryActionParams, resolve) {
|
||||
if (!mainActionParams) {
|
||||
return [null, null];
|
||||
}
|
||||
|
||||
let {label, accessKey, callbackState} = mainActionParams;
|
||||
let callback = resolve.bind(null, callbackState);
|
||||
let mainAction = {label, accessKey, callback};
|
||||
|
||||
if (!secondaryActionParams) {
|
||||
return [mainAction, null];
|
||||
}
|
||||
|
||||
let secondaryActions = [];
|
||||
for (let params of secondaryActionParams) {
|
||||
let cb = resolve.bind(null, params.callbackState);
|
||||
secondaryActions.push({
|
||||
label: params.label,
|
||||
accessKey: params.accessKey,
|
||||
callback: cb,
|
||||
});
|
||||
}
|
||||
|
||||
return [mainAction, secondaryActions];
|
||||
},
|
||||
/**
|
||||
* Append the link label element to the popupnotificationcontent.
|
||||
* @param {XULElement} browser
|
||||
* Target browser element for showing doorhanger.
|
||||
* @param {string} id
|
||||
* The ID of the doorhanger.
|
||||
*/
|
||||
_appendPrivacyPanelLink(browser, id) {
|
||||
let notificationId = id + "-notification";
|
||||
let chromeDoc = browser.ownerDocument;
|
||||
let notification = chromeDoc.getElementById(notificationId);
|
||||
|
||||
if (!notification.querySelector("popupnotificationcontent")) {
|
||||
let notificationcontent = chromeDoc.createElement("popupnotificationcontent");
|
||||
let privacyLinkElement = chromeDoc.createElement("label");
|
||||
privacyLinkElement.className = "text-link";
|
||||
privacyLinkElement.setAttribute("useoriginprincipal", true);
|
||||
privacyLinkElement.setAttribute("href", "about:preferences#privacy");
|
||||
privacyLinkElement.setAttribute("value", GetStringFromName("viewAutofillOptions"));
|
||||
notificationcontent.appendChild(privacyLinkElement);
|
||||
notification.append(notificationcontent);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Create an image element for notification anchor if it doesn't already exist.
|
||||
* @param {XULElement} browser
|
||||
* Target browser element for showing doorhanger.
|
||||
* @param {Object} anchor
|
||||
* Anchor options for setting the anchor element.
|
||||
* @param {string} anchor.id
|
||||
* ID of the anchor element.
|
||||
* @param {string} anchor.URL
|
||||
* Path of the icon asset.
|
||||
* @param {string} anchor.tooltiptext
|
||||
* Tooltip string for the anchor.
|
||||
*/
|
||||
_setAnchor(browser, anchor) {
|
||||
let chromeDoc = browser.ownerDocument;
|
||||
let {id, URL, tooltiptext} = anchor;
|
||||
let anchorEt = chromeDoc.getElementById(id);
|
||||
if (!anchorEt) {
|
||||
let notificationPopupBox =
|
||||
chromeDoc.getElementById("notification-popup-box");
|
||||
// Icon shown on URL bar
|
||||
let anchorElement = chromeDoc.createElement("image");
|
||||
anchorElement.id = id;
|
||||
anchorElement.setAttribute("src", URL);
|
||||
anchorElement.classList.add("notification-anchor-icon");
|
||||
anchorElement.setAttribute("role", "button");
|
||||
anchorElement.setAttribute("tooltiptext", tooltiptext);
|
||||
anchorElement.style.setProperty("-moz-context-properties", "fill");
|
||||
anchorElement.style.fill = "currentcolor";
|
||||
notificationPopupBox.appendChild(anchorElement);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Show different types of doorhanger by leveraging PopupNotifications.
|
||||
* @param {XULElement} browser
|
||||
* Target browser element for showing doorhanger.
|
||||
* @param {string} type
|
||||
* The type of the doorhanger. There will have first time use/update/credit card.
|
||||
* @returns {Promise}
|
||||
Resolved with action type when action callback is triggered.
|
||||
*/
|
||||
async show(browser, type) {
|
||||
log.debug("show doorhanger with type:", type);
|
||||
return new Promise((resolve) => {
|
||||
let content = CONTENT[type];
|
||||
let chromeWin = browser.ownerGlobal;
|
||||
content.options.eventCallback = (topic) => {
|
||||
log.debug("eventCallback:", topic);
|
||||
|
||||
switch (topic) {
|
||||
// We can only append label element when notification box is shown
|
||||
case "shown":
|
||||
this._appendPrivacyPanelLink(browser, content.notificationId);
|
||||
break;
|
||||
}
|
||||
};
|
||||
this._setAnchor(browser, content.anchor);
|
||||
chromeWin.PopupNotifications.show(
|
||||
browser,
|
||||
content.notificationId,
|
||||
content.message,
|
||||
content.anchor.id,
|
||||
...this._createActions(content.mainAction, content.secondaryActions, resolve),
|
||||
content.options,
|
||||
);
|
||||
});
|
||||
},
|
||||
};
|
|
@ -35,12 +35,13 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
|||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
Cu.import("resource://formautofill/FormAutofillUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FormAutofillPreferences",
|
||||
"resource://formautofill/FormAutofillPreferences.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FormAutofillDoorhanger",
|
||||
"resource://formautofill/FormAutofillDoorhanger.jsm");
|
||||
|
||||
this.log = null;
|
||||
FormAutofillUtils.defineLazyLogGetter(this, this.EXPORTED_SYMBOLS[0]);
|
||||
|
@ -80,7 +81,7 @@ FormAutofillParent.prototype = {
|
|||
Services.ppmm.addMessageListener("FormAutofill:GetAddresses", this);
|
||||
Services.ppmm.addMessageListener("FormAutofill:SaveAddress", this);
|
||||
Services.ppmm.addMessageListener("FormAutofill:RemoveAddresses", this);
|
||||
Services.ppmm.addMessageListener("FormAutofill:OnFormSubmit", this);
|
||||
Services.mm.addMessageListener("FormAutofill:OnFormSubmit", this);
|
||||
|
||||
// Observing the pref and storage changes
|
||||
Services.prefs.addObserver(ENABLED_PREF, this);
|
||||
|
@ -276,8 +277,16 @@ FormAutofillParent.prototype = {
|
|||
}
|
||||
this.profileStorage.addresses.notifyUsed(address.guid);
|
||||
} else {
|
||||
// TODO: Add first time use probe(bug 990199) and doorhanger(bug 1303510)
|
||||
// profileStorage.addresses.add(address.record);
|
||||
if (!Services.prefs.getBoolPref("extensions.formautofill.firstTimeUse")) {
|
||||
if (!this.profileStorage.addresses.mergeToStorage(address.record)) {
|
||||
this.profileStorage.addresses.add(address.record);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this.profileStorage.addresses.add(address.record);
|
||||
Services.prefs.setBoolPref("extensions.formautofill.firstTimeUse", false);
|
||||
FormAutofillDoorhanger.show(target, "firstTimeUse");
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<!-- 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/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<path fill="#999899" d="M22 13.7H9.4c-.6 0-1.2.5-1.2 1.2 0 .6.5 1.2 1.2 1.2H22c.6 0 1.2-.5 1.2-1.2s-.6-1.2-1.2-1.2zM6.1 26.6V5.5c0-.8.7-1.5 1.5-1.5h16c.9 0 1.5.6 1.5 1.5V16h2V3.8c0-1-.7-1.8-1.8-1.8H5.9c-1 0-1.8.8-1.8 1.8v24.5c0 1 .8 1.7 1.8 1.7h9.3v-2H7.6c-.8 0-1.5-.6-1.5-1.4zm21.1-1.9h-2.5V20c0-.4-.3-.8-.8-.8h-3.1c-.4 0-.8.3-.8.8v4.6h-2.5c-.6 0-.8.4-.3.8l4.3 4.2c.2.2.5.3.8.3s.6-.1.8-.3l4.3-4.2c.6-.4.4-.7-.2-.7zm-11.3-5.6H9.4c-.6 0-1.2.5-1.2 1.2s.5 1.2 1.2 1.2h6.5c.6 0 1.2-.5 1.2-1.2s-.6-1.2-1.2-1.2zM22 7.8H9.4c-.6 0-1.2.5-1.2 1.2s.5 1.2 1.2 1.2H22c.6 0 1.2-.5 1.2-1.2s-.6-1.2-1.2-1.2z"/>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 877 B |
|
@ -5,3 +5,6 @@
|
|||
preferenceGroupTitle = Form Autofill
|
||||
enableProfileAutofill = Enable Profile Autofill
|
||||
savedProfiles = Saved Profiles…
|
||||
saveAddressMessage = Firefox now saves your form data to help you fill out forms faster!
|
||||
viewAutofillOptions = View Form Autofill options…
|
||||
openAutofillMessagePanel = Open Form Autofill message panel
|
||||
|
|
|
@ -6,6 +6,7 @@ support-files =
|
|||
|
||||
[browser_check_installed.js]
|
||||
[browser_editProfileDialog.js]
|
||||
[browser_first_time_use_doorhanger.js]
|
||||
[browser_privacyPreferences.js]
|
||||
[browser_manageProfilesDialog.js]
|
||||
[browser_submission_in_private_mode.js]
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
"use strict";
|
||||
|
||||
const FORM_URL = "http://mochi.test:8888/browser/browser/extensions/formautofill/test/browser/autocomplete_basic.html";
|
||||
const FTU_PREF = "extensions.formautofill.firstTimeUse";
|
||||
const ENABLED_PREF = "extensions.formautofill.addresses.enabled";
|
||||
|
||||
registerCleanupFunction(async function() {
|
||||
let addresses = await getAddresses();
|
||||
if (addresses.length) {
|
||||
await removeAddresses(addresses.map(address => address.guid));
|
||||
}
|
||||
});
|
||||
|
||||
add_task(async function test_first_time_save() {
|
||||
let addresses = await getAddresses();
|
||||
is(addresses.length, 0, "No profile in storage");
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
"set": [
|
||||
[FTU_PREF, true],
|
||||
[ENABLED_PREF, true],
|
||||
],
|
||||
});
|
||||
|
||||
await BrowserTestUtils.withNewTab({gBrowser, url: FORM_URL},
|
||||
async function(browser) {
|
||||
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
|
||||
"popupshown");
|
||||
let tabPromise = BrowserTestUtils.waitForNewTab(gBrowser, "about:preferences#privacy");
|
||||
await ContentTask.spawn(browser, null, async function() {
|
||||
let form = content.document.getElementById("form");
|
||||
form.querySelector("#organization").focus();
|
||||
form.querySelector("#organization").value = "Sesame Street";
|
||||
form.querySelector("#street-address").value = "123 Sesame Street";
|
||||
form.querySelector("#tel").value = "1-345-345-3456";
|
||||
|
||||
// Wait 500ms before submission to make sure the input value applied
|
||||
setTimeout(() => {
|
||||
form.querySelector("input[type=submit]").click();
|
||||
}, 500);
|
||||
});
|
||||
|
||||
await promiseShown;
|
||||
let notificationElement = PopupNotifications.panel.firstChild;
|
||||
// Open the panel via link
|
||||
let link = notificationElement.querySelector("popupnotificationcontent label");
|
||||
link.click();
|
||||
let tab = await tabPromise;
|
||||
ok(tab, "Privacy panel opened");
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
);
|
||||
|
||||
addresses = await getAddresses();
|
||||
is(addresses.length, 1, "Profile saved");
|
||||
let ftuPref = SpecialPowers.getBoolPref(FTU_PREF);
|
||||
is(ftuPref, false, "First time use flag is false");
|
||||
});
|
||||
|
||||
add_task(async function test_non_first_time_save() {
|
||||
let addresses = await getAddresses();
|
||||
let ftuPref = SpecialPowers.getBoolPref(FTU_PREF);
|
||||
is(ftuPref, false, "First time use flag is false");
|
||||
is(addresses.length, 1, "1 address in storage");
|
||||
|
||||
await BrowserTestUtils.withNewTab({gBrowser, url: FORM_URL},
|
||||
async function(browser) {
|
||||
await ContentTask.spawn(browser, null, async function() {
|
||||
let form = content.document.getElementById("form");
|
||||
form.querySelector("#organization").focus();
|
||||
form.querySelector("#organization").value = "Mozilla";
|
||||
form.querySelector("#street-address").value = "331 E. Evelyn Avenue";
|
||||
form.querySelector("#tel").value = "1-650-903-0800";
|
||||
|
||||
// Wait 500ms before submission to make sure the input value applied
|
||||
setTimeout(() => {
|
||||
form.querySelector("input[type=submit]").click();
|
||||
}, 500);
|
||||
});
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
is(PopupNotifications.panel.state, "closed", "Doorhanger is hidden");
|
||||
}
|
||||
);
|
||||
|
||||
addresses = await getAddresses();
|
||||
is(addresses.length, 2, "Another address saved");
|
||||
});
|
|
@ -23,6 +23,26 @@ function setInput(selector, value) {
|
|||
}, 500));
|
||||
}
|
||||
|
||||
function clickOnElement(selector) {
|
||||
let element = document.querySelector(selector);
|
||||
|
||||
if (!element) {
|
||||
throw new Error("Can not find the element");
|
||||
}
|
||||
|
||||
SimpleTest.executeSoon(() => element.click());
|
||||
}
|
||||
|
||||
async function onAddressChanged(type) {
|
||||
return new Promise(resolve => {
|
||||
formFillChromeScript.addMessageListener("formautofill-storage-changed", function onChanged(data) {
|
||||
formFillChromeScript.removeMessageListener("formautofill-storage-changed", onChanged);
|
||||
is(data.data, type, `Receive ${type} storage changed event`);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function checkMenuEntries(expectedValues) {
|
||||
let actualValues = getMenuEntries();
|
||||
|
||||
|
@ -32,7 +52,7 @@ function checkMenuEntries(expectedValues) {
|
|||
}
|
||||
}
|
||||
|
||||
function addAddress(address) {
|
||||
async function addAddress(address) {
|
||||
return new Promise(resolve => {
|
||||
formFillChromeScript.sendAsyncMessage("FormAutofillTest:AddAddress", {address});
|
||||
formFillChromeScript.addMessageListener("FormAutofillTest:AddressAdded", function onAdded(data) {
|
||||
|
@ -43,7 +63,7 @@ function addAddress(address) {
|
|||
});
|
||||
}
|
||||
|
||||
function removeAddress(guid) {
|
||||
async function removeAddress(guid) {
|
||||
return new Promise(resolve => {
|
||||
formFillChromeScript.sendAsyncMessage("FormAutofillTest:RemoveAddress", {guid});
|
||||
formFillChromeScript.addMessageListener("FormAutofillTest:AddressRemoved", function onDeleted(data) {
|
||||
|
@ -54,7 +74,7 @@ function removeAddress(guid) {
|
|||
});
|
||||
}
|
||||
|
||||
function updateAddress(guid, address) {
|
||||
async function updateAddress(guid, address) {
|
||||
return new Promise(resolve => {
|
||||
formFillChromeScript.sendAsyncMessage("FormAutofillTest:UpdateAddress", {address, guid});
|
||||
formFillChromeScript.addMessageListener("FormAutofillTest:AddressUpdated", function onUpdated(data) {
|
||||
|
@ -65,6 +85,17 @@ function updateAddress(guid, address) {
|
|||
});
|
||||
}
|
||||
|
||||
async function checkAddresses(expectedAddresses) {
|
||||
return new Promise(resolve => {
|
||||
formFillChromeScript.sendAsyncMessage("FormAutofillTest:CheckAddresses", {expectedAddresses});
|
||||
formFillChromeScript.addMessageListener("FormAutofillTest:areAddressesMatching", function onChecked(data) {
|
||||
formFillChromeScript.removeMessageListener("FormAutofillTest:areAddressesMatching", onChecked);
|
||||
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function formAutoFillCommonSetup() {
|
||||
let chromeURL = SimpleTest.getTestFileURL("formautofill_parent_utils.js");
|
||||
formFillChromeScript = SpecialPowers.loadChromeScript(chromeURL);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
let {profileStorage} = Cu.import("resource://formautofill/ProfileStorage.jsm", {});
|
||||
|
||||
var ParentUtils = {
|
||||
cleanUpAddress() {
|
||||
|
@ -42,6 +43,41 @@ var ParentUtils = {
|
|||
Services.obs.removeObserver(this, "formautofill-storage-changed");
|
||||
this.cleanUpAddress();
|
||||
},
|
||||
|
||||
areAddressesMatching(addressA, addressB) {
|
||||
for (let field of profileStorage.addresses.VALID_FIELDS) {
|
||||
if (addressA[field] !== addressB[field]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
checkAddresses({expectedAddresses}) {
|
||||
Services.cpmm.addMessageListener("FormAutofill:Addresses", function getResult(result) {
|
||||
Services.cpmm.removeMessageListener("FormAutofill:Addresses", getResult);
|
||||
let addresses = result.data;
|
||||
if (addresses.length !== expectedAddresses.length) {
|
||||
sendAsyncMessage("FormAutofillTest:areAddressesMatching", false);
|
||||
return;
|
||||
}
|
||||
|
||||
for (let address of addresses) {
|
||||
let matching = expectedAddresses.some((expectedAddress) => {
|
||||
return ParentUtils.areAddressesMatching(address, expectedAddress);
|
||||
});
|
||||
|
||||
if (!matching) {
|
||||
sendAsyncMessage("FormAutofillTest:areAddressesMatching", false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
sendAsyncMessage("FormAutofillTest:areAddressesMatching", true);
|
||||
});
|
||||
|
||||
Services.cpmm.sendAsyncMessage("FormAutofill:GetAddresses", {searchString: ""});
|
||||
},
|
||||
};
|
||||
|
||||
Services.obs.addObserver(ParentUtils, "formautofill-storage-changed");
|
||||
|
@ -58,6 +94,10 @@ addMessageListener("FormAutofillTest:UpdateAddress", (msg) => {
|
|||
ParentUtils.updateAddress("update", "FormAutofill:SaveAddress", msg, "FormAutofillTest:AddressUpdated");
|
||||
});
|
||||
|
||||
addMessageListener("FormAutofillTest:CheckAddresses", (msg) => {
|
||||
ParentUtils.checkAddresses(msg);
|
||||
});
|
||||
|
||||
addMessageListener("cleanup", () => {
|
||||
ParentUtils.cleanup();
|
||||
});
|
||||
|
|
|
@ -8,3 +8,4 @@ support-files =
|
|||
[test_autofocus_form.html]
|
||||
[test_basic_autocomplete_form.html]
|
||||
[test_formautofill_preview_highlight.html]
|
||||
[test_on_address_submission.html]
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test autofill submit</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
|
||||
<script type="text/javascript" src="formautofill_common.js"></script>
|
||||
<script type="text/javascript" src="satchel_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
Form autofill test: check if address is saved/updated correctly
|
||||
|
||||
<script>
|
||||
/* import-globals-from ../../../../../testing/mochitest/tests/SimpleTest/SpawnTask.js */
|
||||
/* import-globals-from ../../../../../toolkit/components/satchel/test/satchel_common.js */
|
||||
/* import-globals-from formautofill_common.js */
|
||||
|
||||
"use strict";
|
||||
|
||||
let TEST_ADDRESSES = [{
|
||||
organization: "Sesame Street",
|
||||
"street-address": "123 Sesame Street.",
|
||||
tel: "1-345-345-3456",
|
||||
}, {
|
||||
organization: "Mozilla",
|
||||
"street-address": "331 E. Evelyn Avenue",
|
||||
tel: "1-650-903-0800",
|
||||
}];
|
||||
|
||||
// Autofill the address from dropdown menu.
|
||||
add_task(async function check_storage_after_form_submitted() {
|
||||
// We already verified the first time use case in browser test
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["extensions.formautofill.firstTimeUse", false]],
|
||||
});
|
||||
|
||||
for (let key in TEST_ADDRESSES[0]) {
|
||||
await setInput("#" + key, TEST_ADDRESSES[0][key]);
|
||||
}
|
||||
|
||||
clickOnElement("input[type=submit]");
|
||||
|
||||
let expectedAddresses = TEST_ADDRESSES.slice(0, 1);
|
||||
await onAddressChanged("add");
|
||||
let matching = await checkAddresses(expectedAddresses);
|
||||
ok(matching, "Address saved as expected");
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<div>
|
||||
|
||||
<form onsubmit="return false">
|
||||
<p>This is a basic form for submitting test.</p>
|
||||
<p><label>organization: <input id="organization" name="organization" autocomplete="organization" type="text"></label></p>
|
||||
<p><label>streetAddress: <input id="street-address" name="street-address" autocomplete="street-address" type="text"></label></p>
|
||||
<p><label>tel: <input id="tel" name="tel" autocomplete="tel" type="text"></label></p>
|
||||
<p><label>country: <input id="country" name="country" autocomplete="country" type="text"></label></p>
|
||||
<p><input type="submit"></p>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -108,7 +108,7 @@ TESTCASES.forEach(testcase => {
|
|||
let input = MOCK_DOC.getElementById(key);
|
||||
input.value = testcase.formValue[key];
|
||||
}
|
||||
sinon.spy(FormAutofillContent, "_onFormSubmit");
|
||||
sinon.stub(FormAutofillContent, "_onFormSubmit");
|
||||
|
||||
FormAutofillContent.identifyAutofillFields(MOCK_DOC);
|
||||
FormAutofillContent.notify(form);
|
||||
|
|
|
@ -11,15 +11,17 @@
|
|||
|
||||
add_task(function* () {
|
||||
yield addTab(URL_ROOT + "doc_negative_animation.html");
|
||||
let {controller, panel} = yield openAnimationInspector();
|
||||
const {controller, panel} = yield openAnimationInspector();
|
||||
const timeline = panel.animationsTimelineComponent;
|
||||
|
||||
info("Wait until all animations have been added " +
|
||||
"(they're added with setTimeout)");
|
||||
while (controller.animationPlayers.length < 3) {
|
||||
yield controller.once(controller.PLAYERS_UPDATED_EVENT);
|
||||
const areTracksReady = () => timeline.animations.every(a => timeline.tracksMap.has(a));
|
||||
|
||||
// We need to wait for all tracks to be ready, cause this is an async part of the init
|
||||
// of the panel.
|
||||
while (controller.animationPlayers.length < 3 || !areTracksReady()) {
|
||||
yield waitForAnimationTimelineRendering(panel);
|
||||
}
|
||||
|
||||
// Same for animation targets, they're retrieved asynchronously.
|
||||
yield waitForAllAnimationTargets(panel);
|
||||
|
||||
is(panel.animationsTimelineComponent.animations.length, 3,
|
||||
|
@ -28,6 +30,5 @@ add_task(function* () {
|
|||
// Reduce the known nodeFronts to a set to make them unique.
|
||||
let nodeFronts = new Set(panel.animationsTimelineComponent
|
||||
.targetNodes.map(n => n.previewer.nodeFront));
|
||||
is(nodeFronts.size, 3,
|
||||
"The animations are applied to 3 different node fronts");
|
||||
is(nodeFronts.size, 3, "The animations are applied to 3 different node fronts");
|
||||
});
|
||||
|
|
|
@ -11,10 +11,12 @@
|
|||
*
|
||||
* @param {object} target
|
||||
* The object the toolbox is debugging.
|
||||
* @param {object} threadClient
|
||||
* The toolbox's thread client
|
||||
* @param {SourceMapService} sourceMapService
|
||||
* The devtools-source-map functions
|
||||
*/
|
||||
function SourceMapURLService(target, sourceMapService) {
|
||||
function SourceMapURLService(target, threadClient, sourceMapService) {
|
||||
this._target = target;
|
||||
this._sourceMapService = sourceMapService;
|
||||
this._urls = new Map();
|
||||
|
@ -24,6 +26,14 @@ function SourceMapURLService(target, sourceMapService) {
|
|||
|
||||
target.on("source-updated", this._onSourceUpdated);
|
||||
target.on("will-navigate", this.reset);
|
||||
|
||||
// Start fetching the sources now.
|
||||
this._loadingPromise = new Promise(resolve => {
|
||||
threadClient.getSources(({sources}) => {
|
||||
// Just ignore errors.
|
||||
resolve(sources);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -75,6 +85,14 @@ SourceMapURLService.prototype._onSourceUpdated = function (_, sourceEvent) {
|
|||
* A promise resolving either to the original location, or null.
|
||||
*/
|
||||
SourceMapURLService.prototype.originalPositionFor = async function (url, line, column) {
|
||||
// Ensure the sources are loaded before replying.
|
||||
await this._loadingPromise;
|
||||
|
||||
// Maybe we were shut down while waiting.
|
||||
if (!this._urls) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const urlInfo = this._urls.get(url);
|
||||
if (!urlInfo) {
|
||||
return null;
|
||||
|
|
|
@ -13,6 +13,8 @@ support-files =
|
|||
code_binary_search.map
|
||||
code_binary_search_absolute.js
|
||||
code_binary_search_absolute.map
|
||||
code_bundle_no_race.js
|
||||
code_bundle_no_race.js.map
|
||||
code_bundle_reload_1.js
|
||||
code_bundle_reload_1.js.map
|
||||
code_bundle_reload_2.js
|
||||
|
@ -20,6 +22,7 @@ support-files =
|
|||
code_inline_bundle.js
|
||||
code_inline_original.js
|
||||
code_math.js
|
||||
code_no_race.js
|
||||
code_reload_1.js
|
||||
code_reload_2.js
|
||||
doc_empty-tab-01.html
|
||||
|
@ -52,6 +55,7 @@ support-files =
|
|||
[browser_source_map-01.js]
|
||||
[browser_source_map-absolute.js]
|
||||
[browser_source_map-inline.js]
|
||||
[browser_source_map-no-race.js]
|
||||
[browser_source_map-reload.js]
|
||||
[browser_target_from_url.js]
|
||||
[browser_target_events.js]
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that the source map service doesn't race against source
|
||||
// reporting.
|
||||
|
||||
"use strict";
|
||||
|
||||
const JS_URL = URL_ROOT + "code_bundle_no_race.js";
|
||||
|
||||
const PAGE_URL = `data:text/html,
|
||||
<!doctype html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Empty test page to test race case</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script src="${JS_URL}"></script>
|
||||
</body>
|
||||
|
||||
</html>`;
|
||||
|
||||
const ORIGINAL_URL = "webpack:///code_no_race.js";
|
||||
|
||||
const GENERATED_LINE = 84;
|
||||
const ORIGINAL_LINE = 11;
|
||||
|
||||
add_task(function* () {
|
||||
// Start with the empty page, then navigate, so that we can properly
|
||||
// listen for new sources arriving.
|
||||
const toolbox = yield openNewTabAndToolbox(PAGE_URL, "webconsole");
|
||||
const service = toolbox.sourceMapURLService;
|
||||
|
||||
info(`checking original location for ${JS_URL}:${GENERATED_LINE}`);
|
||||
let newLoc = yield service.originalPositionFor(JS_URL, GENERATED_LINE);
|
||||
is(newLoc.sourceUrl, ORIGINAL_URL, "check mapped URL");
|
||||
is(newLoc.line, ORIGINAL_LINE, "check mapped line number");
|
||||
});
|
|
@ -0,0 +1,95 @@
|
|||
/******/ (function(modules) { // webpackBootstrap
|
||||
/******/ // The module cache
|
||||
/******/ var installedModules = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/
|
||||
/******/ // Check if module is in cache
|
||||
/******/ if(installedModules[moduleId]) {
|
||||
/******/ return installedModules[moduleId].exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = installedModules[moduleId] = {
|
||||
/******/ i: moduleId,
|
||||
/******/ l: false,
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Flag the module as loaded
|
||||
/******/ module.l = true;
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/******/
|
||||
/******/ // expose the modules object (__webpack_modules__)
|
||||
/******/ __webpack_require__.m = modules;
|
||||
/******/
|
||||
/******/ // expose the module cache
|
||||
/******/ __webpack_require__.c = installedModules;
|
||||
/******/
|
||||
/******/ // identity function for calling harmony imports with the correct context
|
||||
/******/ __webpack_require__.i = function(value) { return value; };
|
||||
/******/
|
||||
/******/ // define getter function for harmony exports
|
||||
/******/ __webpack_require__.d = function(exports, name, getter) {
|
||||
/******/ if(!__webpack_require__.o(exports, name)) {
|
||||
/******/ Object.defineProperty(exports, name, {
|
||||
/******/ configurable: false,
|
||||
/******/ enumerable: true,
|
||||
/******/ get: getter
|
||||
/******/ });
|
||||
/******/ }
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||||
/******/ __webpack_require__.n = function(module) {
|
||||
/******/ var getter = module && module.__esModule ?
|
||||
/******/ function getDefault() { return module['default']; } :
|
||||
/******/ function getModuleExports() { return module; };
|
||||
/******/ __webpack_require__.d(getter, 'a', getter);
|
||||
/******/ return getter;
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Object.prototype.hasOwnProperty.call
|
||||
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
|
||||
/******/
|
||||
/******/ // __webpack_public_path__
|
||||
/******/ __webpack_require__.p = "";
|
||||
/******/
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 0);
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ([
|
||||
/* 0 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Original source code for the inline source map test.
|
||||
// The generated file was made with
|
||||
// webpack --devtool source-map code_no_race.js code_bundle_no_race.js
|
||||
|
||||
|
||||
|
||||
function f() {
|
||||
console.log("anything will do");
|
||||
}
|
||||
|
||||
f();
|
||||
|
||||
// Avoid script GC.
|
||||
window.f = f;
|
||||
|
||||
|
||||
/***/ })
|
||||
/******/ ]);
|
||||
//# sourceMappingURL=code_bundle_no_race.js.map
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["webpack:///webpack/bootstrap bac8dffc0cc5eb13fa9d","webpack:///./code_no_race.js"],"names":[],"mappings":";AAAA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;;AAGA;AACA;;AAEA;AACA;;AAEA;AACA,mDAA2C,cAAc;;AAEzD;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAK;AACL;AACA;;AAEA;AACA;AACA;AACA,mCAA2B,0BAA0B,EAAE;AACvD,yCAAiC,eAAe;AAChD;AACA;AACA;;AAEA;AACA,8DAAsD,+DAA+D;;AAErH;AACA;;AAEA;AACA;;;;;;;;AChEA;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA","file":"code_bundle_no_race.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// identity function for calling harmony imports with the correct context\n \t__webpack_require__.i = function(value) { return value; };\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap bac8dffc0cc5eb13fa9d","/* Any copyright is dedicated to the Public Domain.\n http://creativecommons.org/publicdomain/zero/1.0/ */\n\n// Original source code for the inline source map test.\n// The generated file was made with\n// webpack --devtool source-map code_no_race.js code_bundle_no_race.js\n\n\"use strict\";\n\nfunction f() {\n console.log(\"anything will do\");\n}\n\nf();\n\n// Avoid script GC.\nwindow.f = f;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./code_no_race.js\n// module id = 0\n// module chunks = 0"],"sourceRoot":""}
|
|
@ -0,0 +1,17 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Original source code for the inline source map test.
|
||||
// The generated file was made with
|
||||
// webpack --devtool source-map code_no_race.js code_bundle_no_race.js
|
||||
|
||||
"use strict";
|
||||
|
||||
function f() {
|
||||
console.log("anything will do");
|
||||
}
|
||||
|
||||
f();
|
||||
|
||||
// Avoid script GC.
|
||||
window.f = f;
|
|
@ -567,7 +567,8 @@ Toolbox.prototype = {
|
|||
if (!sourceMaps) {
|
||||
return null;
|
||||
}
|
||||
this._sourceMapURLService = new SourceMapURLService(this._target, sourceMaps);
|
||||
this._sourceMapURLService = new SourceMapURLService(this._target, this.threadClient,
|
||||
sourceMaps);
|
||||
return this._sourceMapURLService;
|
||||
},
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
#include "nsIURI.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsEscape.h"
|
||||
#include "nsITextToSubURI.h"
|
||||
#include "nsCRT.h"
|
||||
#include "nsIParserService.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
@ -132,10 +131,6 @@ nsHTMLContentSerializer::SerializeHTMLAttributes(nsIContent* aContent,
|
|||
}
|
||||
}
|
||||
}
|
||||
// Need to escape URI.
|
||||
nsAutoString tempURI(valueStr);
|
||||
if (!isJS && NS_FAILED(EscapeURI(aContent, tempURI, valueStr)))
|
||||
valueStr = tempURI;
|
||||
}
|
||||
|
||||
if (mRewriteEncodingDeclaration && aTagName == nsGkAtoms::meta &&
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
#include "nsIURI.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsEscape.h"
|
||||
#include "nsITextToSubURI.h"
|
||||
#include "nsCRT.h"
|
||||
#include "nsIParserService.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
@ -157,70 +156,6 @@ nsXHTMLContentSerializer::AppendText(nsIContent* aText,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsXHTMLContentSerializer::EscapeURI(nsIContent* aContent, const nsAString& aURI, nsAString& aEscapedURI)
|
||||
{
|
||||
// URL escape %xx cannot be used in JS.
|
||||
// No escaping if the scheme is 'javascript'.
|
||||
if (IsJavaScript(aContent, nsGkAtoms::href, kNameSpaceID_None, aURI)) {
|
||||
aEscapedURI = aURI;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsITextToSubURI does charset convert plus uri escape
|
||||
// This is needed to convert to a document charset which is needed to support existing browsers.
|
||||
// But we eventually want to use UTF-8 instead of a document charset, then the code would be much simpler.
|
||||
// See HTML 4.01 spec, "Appendix B.2.1 Non-ASCII characters in URI attribute values"
|
||||
nsCOMPtr<nsITextToSubURI> textToSubURI;
|
||||
nsAutoString uri(aURI); // in order to use FindCharInSet()
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
if (!mCharset.IsEmpty() && !IsASCII(uri)) {
|
||||
textToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
int32_t start = 0;
|
||||
int32_t end;
|
||||
nsAutoString part;
|
||||
nsXPIDLCString escapedURI;
|
||||
aEscapedURI.Truncate(0);
|
||||
|
||||
// Loop and escape parts by avoiding escaping reserved characters
|
||||
// (and '%', '#', as well as '[' and ']' for IPv6 address literals).
|
||||
while ((end = uri.FindCharInSet("%#;/?:@&=+$,[]", start)) != -1) {
|
||||
part = Substring(aURI, start, (end-start));
|
||||
if (textToSubURI && !IsASCII(part)) {
|
||||
rv = textToSubURI->ConvertAndEscape(mCharset.get(), part.get(), getter_Copies(escapedURI));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else if (NS_WARN_IF(!NS_Escape(NS_ConvertUTF16toUTF8(part), escapedURI,
|
||||
url_Path))) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
AppendASCIItoUTF16(escapedURI, aEscapedURI);
|
||||
|
||||
// Append a reserved character without escaping.
|
||||
part = Substring(aURI, end, 1);
|
||||
aEscapedURI.Append(part);
|
||||
start = end + 1;
|
||||
}
|
||||
|
||||
if (start < (int32_t) aURI.Length()) {
|
||||
// Escape the remaining part.
|
||||
part = Substring(aURI, start, aURI.Length()-start);
|
||||
if (textToSubURI) {
|
||||
rv = textToSubURI->ConvertAndEscape(mCharset.get(), part.get(), getter_Copies(escapedURI));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else if (NS_WARN_IF(!NS_Escape(NS_ConvertUTF16toUTF8(part), escapedURI,
|
||||
url_Path))) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
AppendASCIItoUTF16(escapedURI, aEscapedURI);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool
|
||||
nsXHTMLContentSerializer::SerializeAttributes(nsIContent* aContent,
|
||||
nsIContent *aOriginalElement,
|
||||
|
@ -373,10 +308,6 @@ nsXHTMLContentSerializer::SerializeAttributes(nsIContent* aContent,
|
|||
}
|
||||
}
|
||||
}
|
||||
// Need to escape URI.
|
||||
nsAutoString tempURI(valueStr);
|
||||
if (!isJS && NS_FAILED(EscapeURI(aContent, tempURI, valueStr)))
|
||||
valueStr = tempURI;
|
||||
}
|
||||
|
||||
if (mRewriteEncodingDeclaration && aTagName == nsGkAtoms::meta &&
|
||||
|
|
|
@ -92,10 +92,6 @@ class nsXHTMLContentSerializer : public nsXMLContentSerializer {
|
|||
virtual bool AppendAndTranslateEntities(const nsAString& aStr,
|
||||
nsAString& aOutputStr) override;
|
||||
|
||||
nsresult EscapeURI(nsIContent* aContent,
|
||||
const nsAString& aURI,
|
||||
nsAString& aEscapedURI);
|
||||
|
||||
private:
|
||||
bool IsElementPreformatted(nsIContent* aNode);
|
||||
|
||||
|
|
|
@ -67,8 +67,6 @@ function beginTest() {
|
|||
[ "-moz-MenubarHoverText", 0x00, 0x00, 0x00 ],
|
||||
[ "-moz-NativeHyperlinkText", 0x00, 0x66, 0xCC ],
|
||||
[ "-moz-OddTreeRow", 0xFF, 0xFF, 0xFF ],
|
||||
[ "-moz-html-CellHighlight", 0x33, 0x99, 0xFF ],
|
||||
[ "-moz-html-CellHighlightText", 0xFF, 0xFF, 0xFF ],
|
||||
[ "-moz-mac-chrome-active", 0xB2, 0xB2, 0xB2 ],
|
||||
[ "-moz-mac-chrome-inactive", 0xE1, 0xE1, 0xE1 ],
|
||||
[ "-moz-mac-focusring", 0x60, 0x9D, 0xD7 ],
|
||||
|
@ -81,6 +79,8 @@ function beginTest() {
|
|||
[ "-moz-mac-SecondaryHighlight", 0xD4, 0xD4, 0xD4 ],
|
||||
[ "-moz-win-MediaText", 0xFF, 0xFF, 0xFF ],
|
||||
[ "-moz-win-CommunicationsText", 0xFF, 0xFF, 0xFF ],
|
||||
[ "-moz-html-CellHighlight", 0x33, 0x99, 0xFF ],
|
||||
[ "-moz-html-CellHighlightText", 0xFF, 0xFF, 0xFF ],
|
||||
|
||||
// These five are configured via Tools -> Options -> Content -> Colors.
|
||||
//"-moz-ActiveHyperlinkText",
|
||||
|
|
|
@ -4,17 +4,20 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ChromiumCDMParent.h"
|
||||
#include "mozilla/gmp/GMPTypes.h"
|
||||
|
||||
#include "ChromiumCDMProxy.h"
|
||||
#include "content_decryption_module.h"
|
||||
#include "GMPContentChild.h"
|
||||
#include "GMPContentParent.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "ChromiumCDMProxy.h"
|
||||
#include "mozilla/dom/MediaKeyMessageEventBinding.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "content_decryption_module.h"
|
||||
#include "GMPLog.h"
|
||||
#include "MediaPrefs.h"
|
||||
#include "GMPUtils.h"
|
||||
#include "MediaPrefs.h"
|
||||
#include "mozilla/dom/MediaKeyMessageEventBinding.h"
|
||||
#include "mozilla/gmp/GMPTypes.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mp4_demuxer/AnnexB.h"
|
||||
#include "mp4_demuxer/H264.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gmp {
|
||||
|
@ -731,7 +734,7 @@ ChromiumCDMParent::RecvDecodedData(const CDMVideoFrame& aFrame,
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
mDecodePromise.ResolveIfExists({ Move(v) }, __func__);
|
||||
ReorderAndReturnOutput(Move(v));
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
@ -740,7 +743,11 @@ ipc::IPCResult
|
|||
ChromiumCDMParent::RecvDecodedShmem(const CDMVideoFrame& aFrame,
|
||||
ipc::Shmem&& aShmem)
|
||||
{
|
||||
GMP_LOG("ChromiumCDMParent::RecvDecodedShmem(this=%p)", this);
|
||||
GMP_LOG("ChromiumCDMParent::RecvDecodedShmem(this=%p) time=%" PRId64
|
||||
" duration=%" PRId64,
|
||||
this,
|
||||
aFrame.mTimestamp(),
|
||||
aFrame.mDuration());
|
||||
|
||||
// On failure we need to deallocate the shmem we're to return to the
|
||||
// CDM. On success we return it to the CDM to be reused.
|
||||
|
@ -775,11 +782,26 @@ ChromiumCDMParent::RecvDecodedShmem(const CDMVideoFrame& aFrame,
|
|||
// for it again.
|
||||
autoDeallocateShmem.release();
|
||||
|
||||
mDecodePromise.ResolveIfExists({ Move(v) }, __func__);
|
||||
ReorderAndReturnOutput(Move(v));
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMParent::ReorderAndReturnOutput(RefPtr<VideoData>&& aFrame)
|
||||
{
|
||||
if (mMaxRefFrames == 0) {
|
||||
mDecodePromise.ResolveIfExists({ Move(aFrame) }, __func__);
|
||||
return;
|
||||
}
|
||||
mReorderQueue.Push(Move(aFrame));
|
||||
MediaDataDecoder::DecodedData results;
|
||||
while (mReorderQueue.Length() > mMaxRefFrames) {
|
||||
results.AppendElement(mReorderQueue.Pop());
|
||||
}
|
||||
mDecodePromise.Resolve(Move(results), __func__);
|
||||
}
|
||||
|
||||
already_AddRefed<VideoData>
|
||||
ChromiumCDMParent::CreateVideoFrame(const CDMVideoFrame& aFrame,
|
||||
Span<uint8_t> aData)
|
||||
|
@ -915,6 +937,13 @@ ChromiumCDMParent::InitializeVideoDecoder(
|
|||
__func__);
|
||||
}
|
||||
|
||||
mMaxRefFrames =
|
||||
(aConfig.mCodec() == cdm::VideoDecoderConfig::kCodecH264)
|
||||
? mp4_demuxer::AnnexB::HasSPS(aInfo.mExtraData)
|
||||
? mp4_demuxer::H264::ComputeMaxRefFrames(aInfo.mExtraData)
|
||||
: 16
|
||||
: 0;
|
||||
|
||||
mVideoDecoderInitialized = true;
|
||||
mImageContainer = aImageContainer;
|
||||
mVideoInfo = aInfo;
|
||||
|
@ -987,12 +1016,15 @@ RefPtr<MediaDataDecoder::FlushPromise>
|
|||
ChromiumCDMParent::FlushVideoDecoder()
|
||||
{
|
||||
if (mIsShutdown) {
|
||||
MOZ_ASSERT(mReorderQueue.IsEmpty());
|
||||
return MediaDataDecoder::FlushPromise::CreateAndReject(
|
||||
MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
|
||||
RESULT_DETAIL("ChromiumCDMParent is shutdown")),
|
||||
__func__);
|
||||
}
|
||||
|
||||
mReorderQueue.Clear();
|
||||
|
||||
mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
|
||||
if (!SendResetVideoDecoder()) {
|
||||
return MediaDataDecoder::FlushPromise::CreateAndReject(
|
||||
|
@ -1005,6 +1037,7 @@ ChromiumCDMParent::FlushVideoDecoder()
|
|||
ipc::IPCResult
|
||||
ChromiumCDMParent::RecvResetVideoDecoderComplete()
|
||||
{
|
||||
MOZ_ASSERT(mReorderQueue.IsEmpty());
|
||||
if (mIsShutdown) {
|
||||
MOZ_ASSERT(mFlushDecoderPromise.IsEmpty());
|
||||
return IPC_OK();
|
||||
|
@ -1038,7 +1071,13 @@ ChromiumCDMParent::RecvDrainComplete()
|
|||
MOZ_ASSERT(mDecodePromise.IsEmpty());
|
||||
return IPC_OK();
|
||||
}
|
||||
mDecodePromise.ResolveIfExists(MediaDataDecoder::DecodedData(), __func__);
|
||||
|
||||
MediaDataDecoder::DecodedData samples;
|
||||
while (!mReorderQueue.IsEmpty()) {
|
||||
samples.AppendElement(Move(mReorderQueue.Pop()));
|
||||
}
|
||||
|
||||
mDecodePromise.ResolveIfExists(Move(samples), __func__);
|
||||
return IPC_OK();
|
||||
}
|
||||
RefPtr<ShutdownPromise>
|
||||
|
@ -1097,6 +1136,8 @@ ChromiumCDMParent::Shutdown()
|
|||
// (including from an already-queued task, e.g.: ActorDestroy).
|
||||
mProxy = nullptr;
|
||||
|
||||
mReorderQueue.Clear();
|
||||
|
||||
for (RefPtr<DecryptJob>& decrypt : mDecrypts) {
|
||||
decrypt->PostResult(eme::AbortedErr);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "PlatformDecoderModule.h"
|
||||
#include "ImageContainer.h"
|
||||
#include "mozilla/Span.h"
|
||||
#include "ReorderQueue.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
@ -128,6 +129,8 @@ protected:
|
|||
void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
bool SendBufferToCDM(uint32_t aSizeInBytes);
|
||||
|
||||
void ReorderAndReturnOutput(RefPtr<VideoData>&& aFrame);
|
||||
|
||||
void RejectPromise(uint32_t aPromiseId,
|
||||
nsresult aError,
|
||||
const nsCString& aErrorMessage);
|
||||
|
@ -172,6 +175,15 @@ protected:
|
|||
bool mIsShutdown = false;
|
||||
bool mVideoDecoderInitialized = false;
|
||||
bool mActorDestroyed = false;
|
||||
|
||||
// The H.264 decoder in Widevine CDM versions 970 and later output in decode
|
||||
// order rather than presentation order, so we reorder in presentation order
|
||||
// before presenting. mMaxRefFrames is non-zero if we have an initialized
|
||||
// decoder and we are decoding H.264. If so, it stores the maximum length of
|
||||
// the reorder queue that we need. Note we may have multiple decoders for the
|
||||
// life time of this object, but never more than one active at once.
|
||||
uint32_t mMaxRefFrames = 0;
|
||||
ReorderQueue mReorderQueue;
|
||||
};
|
||||
|
||||
} // namespace gmp
|
||||
|
|
|
@ -14,6 +14,7 @@ EXPORTS += [
|
|||
'MediaTelemetryConstants.h',
|
||||
'PDMFactory.h',
|
||||
'PlatformDecoderModule.h',
|
||||
'ReorderQueue.h',
|
||||
'SimpleMap.h',
|
||||
'wrappers/H264Converter.h',
|
||||
'wrappers/MediaDataDecoderProxy.h'
|
||||
|
|
|
@ -100,6 +100,10 @@ using namespace mozilla::layers;
|
|||
|
||||
uint8_t gNotifySubDocInvalidationData;
|
||||
|
||||
// This preference was first introduced in Bug 232227, in order to prevent
|
||||
// system colors from being exposed to CSS or canvas.
|
||||
constexpr char kUseStandinsForNativeColors[] = "ui.use_standins_for_native_colors";
|
||||
|
||||
/**
|
||||
* Layer UserData for ContainerLayers that want to be notified
|
||||
* of local invalidations of them and their descendant layers.
|
||||
|
@ -381,6 +385,9 @@ nsPresContext::Destroy()
|
|||
Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
|
||||
"nglayout.debug.paint_flashing_chrome",
|
||||
this);
|
||||
Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
|
||||
kUseStandinsForNativeColors,
|
||||
this);
|
||||
|
||||
mRefreshDriver = nullptr;
|
||||
}
|
||||
|
@ -468,11 +475,17 @@ nsPresContext::GetDocumentColorPreferences()
|
|||
bool isChromeDocShell = false;
|
||||
static int32_t sDocumentColorsSetting;
|
||||
static bool sDocumentColorsSettingPrefCached = false;
|
||||
static bool sUseStandinsForNativeColors = false;
|
||||
if (!sDocumentColorsSettingPrefCached) {
|
||||
sDocumentColorsSettingPrefCached = true;
|
||||
Preferences::AddIntVarCache(&sDocumentColorsSetting,
|
||||
"browser.display.document_color_use",
|
||||
0);
|
||||
|
||||
// The preference "ui.use_standins_for_native_colors" also affects
|
||||
// default foreground and background colors.
|
||||
Preferences::AddBoolVarCache(&sUseStandinsForNativeColors,
|
||||
kUseStandinsForNativeColors);
|
||||
}
|
||||
|
||||
nsIDocument* doc = mDocument->GetDisplayDocument();
|
||||
|
@ -501,7 +514,14 @@ nsPresContext::GetDocumentColorPreferences()
|
|||
!Preferences::GetBool("browser.display.use_system_colors", false);
|
||||
}
|
||||
|
||||
if (usePrefColors) {
|
||||
if (sUseStandinsForNativeColors) {
|
||||
// Once the preference "ui.use_standins_for_native_colors" is enabled,
|
||||
// use fixed color values instead of prefered colors and system colors.
|
||||
mDefaultColor = LookAndFeel::GetColorUsingStandins(
|
||||
LookAndFeel::eColorID_windowtext, NS_RGB(0x00, 0x00, 0x00));
|
||||
mBackgroundColor = LookAndFeel::GetColorUsingStandins(
|
||||
LookAndFeel::eColorID_window, NS_RGB(0xff, 0xff, 0xff));
|
||||
} else if (usePrefColors) {
|
||||
nsAdoptingString colorStr =
|
||||
Preferences::GetString("browser.display.foreground_color");
|
||||
|
||||
|
@ -924,6 +944,9 @@ nsPresContext::Init(nsDeviceContext* aDeviceContext)
|
|||
Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
|
||||
"nglayout.debug.paint_flashing_chrome",
|
||||
this);
|
||||
Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
|
||||
kUseStandinsForNativeColors,
|
||||
this);
|
||||
|
||||
nsresult rv = mEventManager->Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
|
|
@ -149,9 +149,12 @@ SERVO_BINDING_FUNC(Servo_StyleRule_SetStyle, void,
|
|||
RawServoDeclarationBlockBorrowed declarations)
|
||||
SERVO_BINDING_FUNC(Servo_StyleRule_GetSelectorText, void,
|
||||
RawServoStyleRuleBorrowed rule, nsAString* result)
|
||||
SERVO_BINDING_FUNC(Servo_StyleRule_GetSelectorTextFromIndex, void,
|
||||
SERVO_BINDING_FUNC(Servo_StyleRule_GetSelectorTextAtIndex, void,
|
||||
RawServoStyleRuleBorrowed rule, uint32_t index,
|
||||
nsAString* result)
|
||||
SERVO_BINDING_FUNC(Servo_StyleRule_GetSpecificityAtIndex, void,
|
||||
RawServoStyleRuleBorrowed rule, uint32_t index,
|
||||
uint64_t* specificity)
|
||||
SERVO_BINDING_FUNC(Servo_StyleRule_GetSelectorCount, void,
|
||||
RawServoStyleRuleBorrowed rule, uint32_t* count)
|
||||
SERVO_BINDING_FUNC(Servo_ImportRule_GetHref, void,
|
||||
|
|
|
@ -262,14 +262,14 @@ ServoStyleRule::GetSelectorCount()
|
|||
nsresult
|
||||
ServoStyleRule::GetSelectorText(uint32_t aSelectorIndex, nsAString& aText)
|
||||
{
|
||||
Servo_StyleRule_GetSelectorTextFromIndex(mRawRule, aSelectorIndex, &aText);
|
||||
Servo_StyleRule_GetSelectorTextAtIndex(mRawRule, aSelectorIndex, &aText);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ServoStyleRule::GetSpecificity(uint32_t aSelectorIndex, uint64_t* aSpecificity)
|
||||
{
|
||||
// TODO Bug 1370501
|
||||
Servo_StyleRule_GetSpecificityAtIndex(mRawRule, aSelectorIndex, aSpecificity);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -49,3 +49,9 @@ if CONFIG['CPU_ARCH'] == 'arm' and CONFIG['BUILD_ARM_NEON']:
|
|||
# Suppress warnings in third-party code.
|
||||
if CONFIG['GNU_CC']:
|
||||
CFLAGS += ['-Wno-sign-compare']
|
||||
|
||||
if CONFIG['_MSC_VER'] and not CONFIG['CLANG_CL']:
|
||||
CFLAGS += [
|
||||
'-wd4018', # '<' : signed/unsigned mismatch
|
||||
'-wd4101', # unreferenced local variable
|
||||
]
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -4,10 +4,12 @@
|
|||
"repository": {},
|
||||
"license": "MPL-2.0",
|
||||
"dependencies": {
|
||||
"escope": "^3.6.0",
|
||||
"eslint": "3.19.0",
|
||||
"eslint-plugin-html": "2.0.3",
|
||||
"eslint-plugin-mozilla": "file:tools\\lint\\eslint\\eslint-plugin-mozilla",
|
||||
"eslint-plugin-react": "6.10.3",
|
||||
"escope": "^3.6.0",
|
||||
"eslint-plugin-spidermonkey-js": "file:tools\\lint\\eslint\\eslint-plugin-spidermonkey-js",
|
||||
"espree": "^3.4.0",
|
||||
"estraverse": "^4.2.0",
|
||||
"ini-parser": "^0.0.2",
|
||||
|
|
|
@ -431,7 +431,7 @@ var vectors = [
|
|||
},
|
||||
{
|
||||
"data": "<!-- IE 5-8 standards mode -->\r\n<a href=http://foo.bar/#x=`y></a><img alt=\"`><img src=xx:x onerror=alert(1)></a>\">\r\n\r\n<!-- IE 5-9 standards mode -->\r\n<!a foo=x=`y><img alt=\"`><img src=xx:x onerror=alert(2)//\">\r\n<?a foo=x=`y><img alt=\"`><img src=xx:x onerror=alert(3)//\">",
|
||||
"sanitized": "<html><head></head><body><a href=\"http://foo.bar/#x=%60y\"></a><img alt=\"`><img src=xx:x onerror=alert(1)></a>\">\n\n\n<img alt=\"`><img src=xx:x onerror=alert(2)//\">\n<img alt=\"`><img src=xx:x onerror=alert(3)//\"></body></html>"
|
||||
"sanitized": "<html><head></head><body><a href=\"http://foo.bar/#x=`y\"></a><img alt=\"`><img src=xx:x onerror=alert(1)></a>\">\n\n\n<img alt=\"`><img src=xx:x onerror=alert(2)//\">\n<img alt=\"`><img src=xx:x onerror=alert(3)//\"></body></html>"
|
||||
},
|
||||
{
|
||||
"data": "<svg xmlns=\"http://www.w3.org/2000/svg\">\n<a id=\"x\"><rect fill=\"white\" width=\"1000\" height=\"1000\"/></a>\n<rect fill=\"white\" style=\"clip-path:url(test3.svg#a);fill:url(#b);filter:url(#c);marker:url(#d);mask:url(#e);stroke:url(#f);\"/>\n</svg>",
|
||||
|
|
|
@ -17,11 +17,12 @@ import functools
|
|||
from WebIDL import (
|
||||
BuiltinTypes,
|
||||
IDLBuiltinType,
|
||||
IDLNullValue,
|
||||
IDLNullableType,
|
||||
IDLObject,
|
||||
IDLType,
|
||||
IDLInterfaceMember,
|
||||
IDLNullableType,
|
||||
IDLNullValue,
|
||||
IDLObject,
|
||||
IDLPromiseType,
|
||||
IDLType,
|
||||
IDLUndefinedValue,
|
||||
IDLWrapperType,
|
||||
)
|
||||
|
@ -94,14 +95,14 @@ def stripTrailingWhitespace(text):
|
|||
|
||||
|
||||
def innerContainerType(type):
|
||||
assert type.isSequence() or type.isMozMap()
|
||||
assert type.isSequence() or type.isRecord()
|
||||
return type.inner.inner if type.nullable() else type.inner
|
||||
|
||||
|
||||
def wrapInNativeContainerType(type, inner):
|
||||
if type.isSequence():
|
||||
containerType = "Vec"
|
||||
elif type.isMozMap():
|
||||
elif type.isRecord():
|
||||
containerType = "MozMap"
|
||||
else:
|
||||
raise TypeError("Unexpected container type %s", type)
|
||||
|
@ -697,7 +698,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
|
|||
|
||||
assert not (isEnforceRange and isClamp) # These are mutually exclusive
|
||||
|
||||
if type.isSequence() or type.isMozMap():
|
||||
if type.isSequence() or type.isRecord():
|
||||
innerInfo = getJSToNativeConversionInfo(innerContainerType(type),
|
||||
descriptorProvider,
|
||||
isMember=isMember)
|
||||
|
@ -754,6 +755,56 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
|
|||
|
||||
return handleOptional(templateBody, declType, default)
|
||||
|
||||
if type.isPromise():
|
||||
assert not type.nullable()
|
||||
# Per spec, what we're supposed to do is take the original
|
||||
# Promise.resolve and call it with the original Promise as this
|
||||
# value to make a Promise out of whatever value we actually have
|
||||
# here. The question is which global we should use. There are
|
||||
# a couple cases to consider:
|
||||
#
|
||||
# 1) Normal call to API with a Promise argument. This is a case the
|
||||
# spec covers, and we should be using the current Realm's
|
||||
# Promise. That means the current compartment.
|
||||
# 2) Promise return value from a callback or callback interface.
|
||||
# This is in theory a case the spec covers but in practice it
|
||||
# really doesn't define behavior here because it doesn't define
|
||||
# what Realm we're in after the callback returns, which is when
|
||||
# the argument conversion happens. We will use the current
|
||||
# compartment, which is the compartment of the callable (which
|
||||
# may itself be a cross-compartment wrapper itself), which makes
|
||||
# as much sense as anything else. In practice, such an API would
|
||||
# once again be providing a Promise to signal completion of an
|
||||
# operation, which would then not be exposed to anyone other than
|
||||
# our own implementation code.
|
||||
templateBody = fill(
|
||||
"""
|
||||
{ // Scope for our JSAutoCompartment.
|
||||
|
||||
rooted!(in(cx) let globalObj = CurrentGlobalOrNull(cx));
|
||||
let promiseGlobal = GlobalScope::from_object_maybe_wrapped(globalObj.handle().get());
|
||||
|
||||
rooted!(in(cx) let mut valueToResolve = $${val}.get());
|
||||
if !JS_WrapValue(cx, valueToResolve.handle_mut()) {
|
||||
$*{exceptionCode}
|
||||
}
|
||||
match Promise::Resolve(&promiseGlobal, cx, valueToResolve.handle()) {
|
||||
Ok(value) => value,
|
||||
Err(error) => {
|
||||
throw_dom_exception(cx, &promiseGlobal, error);
|
||||
$*{exceptionCode}
|
||||
}
|
||||
}
|
||||
}
|
||||
""",
|
||||
exceptionCode=exceptionCode)
|
||||
|
||||
if isArgument:
|
||||
declType = CGGeneric("&Promise")
|
||||
else:
|
||||
declType = CGGeneric("Rc<Promise>")
|
||||
return handleOptional(templateBody, declType, handleDefaultNull("None"))
|
||||
|
||||
if type.isGeckoInterface():
|
||||
assert not isEnforceRange and not isClamp
|
||||
|
||||
|
@ -780,79 +831,34 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
|
|||
elif isArgument:
|
||||
descriptorType = descriptor.argumentType
|
||||
|
||||
templateBody = ""
|
||||
isPromise = descriptor.interface.identifier.name == "Promise"
|
||||
if isPromise:
|
||||
# Per spec, what we're supposed to do is take the original
|
||||
# Promise.resolve and call it with the original Promise as this
|
||||
# value to make a Promise out of whatever value we actually have
|
||||
# here. The question is which global we should use. There are
|
||||
# a couple cases to consider:
|
||||
#
|
||||
# 1) Normal call to API with a Promise argument. This is a case the
|
||||
# spec covers, and we should be using the current Realm's
|
||||
# Promise. That means the current compartment.
|
||||
# 2) Promise return value from a callback or callback interface.
|
||||
# This is in theory a case the spec covers but in practice it
|
||||
# really doesn't define behavior here because it doesn't define
|
||||
# what Realm we're in after the callback returns, which is when
|
||||
# the argument conversion happens. We will use the current
|
||||
# compartment, which is the compartment of the callable (which
|
||||
# may itself be a cross-compartment wrapper itself), which makes
|
||||
# as much sense as anything else. In practice, such an API would
|
||||
# once again be providing a Promise to signal completion of an
|
||||
# operation, which would then not be exposed to anyone other than
|
||||
# our own implementation code.
|
||||
templateBody = fill(
|
||||
"""
|
||||
{ // Scope for our JSAutoCompartment.
|
||||
if descriptor.interface.isConsequential():
|
||||
raise TypeError("Consequential interface %s being used as an "
|
||||
"argument" % descriptor.interface.identifier.name)
|
||||
|
||||
rooted!(in(cx) let globalObj = CurrentGlobalOrNull(cx));
|
||||
let promiseGlobal = GlobalScope::from_object_maybe_wrapped(globalObj.handle().get());
|
||||
|
||||
rooted!(in(cx) let mut valueToResolve = $${val}.get());
|
||||
if !JS_WrapValue(cx, valueToResolve.handle_mut()) {
|
||||
$*{exceptionCode}
|
||||
}
|
||||
match Promise::Resolve(&promiseGlobal, cx, valueToResolve.handle()) {
|
||||
Ok(value) => value,
|
||||
Err(error) => {
|
||||
throw_dom_exception(cx, &promiseGlobal, error);
|
||||
$*{exceptionCode}
|
||||
}
|
||||
}
|
||||
}
|
||||
""",
|
||||
exceptionCode=exceptionCode)
|
||||
if failureCode is None:
|
||||
substitutions = {
|
||||
"sourceDescription": sourceDescription,
|
||||
"interface": descriptor.interface.identifier.name,
|
||||
"exceptionCode": exceptionCode,
|
||||
}
|
||||
unwrapFailureCode = string.Template(
|
||||
'throw_type_error(cx, "${sourceDescription} does not '
|
||||
'implement interface ${interface}.");\n'
|
||||
'${exceptionCode}').substitute(substitutions)
|
||||
else:
|
||||
if descriptor.interface.isConsequential():
|
||||
raise TypeError("Consequential interface %s being used as an "
|
||||
"argument" % descriptor.interface.identifier.name)
|
||||
unwrapFailureCode = failureCode
|
||||
|
||||
if failureCode is None:
|
||||
substitutions = {
|
||||
"sourceDescription": sourceDescription,
|
||||
"interface": descriptor.interface.identifier.name,
|
||||
"exceptionCode": exceptionCode,
|
||||
templateBody = fill(
|
||||
"""
|
||||
match ${function}($${val}) {
|
||||
Ok(val) => val,
|
||||
Err(()) => {
|
||||
$*{failureCode}
|
||||
}
|
||||
unwrapFailureCode = string.Template(
|
||||
'throw_type_error(cx, "${sourceDescription} does not '
|
||||
'implement interface ${interface}.");\n'
|
||||
'${exceptionCode}').substitute(substitutions)
|
||||
else:
|
||||
unwrapFailureCode = failureCode
|
||||
|
||||
templateBody = fill(
|
||||
"""
|
||||
match ${function}($${val}) {
|
||||
Ok(val) => val,
|
||||
Err(()) => {
|
||||
$*{failureCode}
|
||||
}
|
||||
}
|
||||
""",
|
||||
failureCode=unwrapFailureCode + "\n",
|
||||
function=conversionFunction)
|
||||
}
|
||||
""",
|
||||
failureCode=unwrapFailureCode + "\n",
|
||||
function=conversionFunction)
|
||||
|
||||
declType = CGGeneric(descriptorType)
|
||||
if type.nullable():
|
||||
|
@ -1323,7 +1329,7 @@ def typeNeedsCx(type, retVal=False):
|
|||
|
||||
# Returns a conversion behavior suitable for a type
|
||||
def getConversionConfigForType(type, isEnforceRange, isClamp, treatNullAs):
|
||||
if type.isSequence() or type.isMozMap():
|
||||
if type.isSequence() or type.isRecord():
|
||||
return getConversionConfigForType(innerContainerType(type), isEnforceRange, isClamp, treatNullAs)
|
||||
if type.isDOMString():
|
||||
assert not isEnforceRange and not isClamp
|
||||
|
@ -1381,6 +1387,9 @@ def getRetvalDeclarationForType(returnType, descriptorProvider):
|
|||
if returnType.nullable():
|
||||
result = CGWrapper(result, pre="Option<", post=">")
|
||||
return result
|
||||
if returnType.isPromise():
|
||||
assert not returnType.nullable()
|
||||
return CGGeneric("Rc<Promise>")
|
||||
if returnType.isGeckoInterface():
|
||||
descriptor = descriptorProvider.getDescriptor(
|
||||
returnType.unroll().inner.identifier.name)
|
||||
|
@ -1408,7 +1417,7 @@ def getRetvalDeclarationForType(returnType, descriptorProvider):
|
|||
if returnType.nullable():
|
||||
result = CGWrapper(result, pre="Option<", post=">")
|
||||
return result
|
||||
if returnType.isSequence() or returnType.isMozMap():
|
||||
if returnType.isSequence() or returnType.isRecord():
|
||||
result = getRetvalDeclarationForType(innerContainerType(returnType), descriptorProvider)
|
||||
result = wrapInNativeContainerType(returnType, result)
|
||||
if returnType.nullable():
|
||||
|
@ -1946,8 +1955,10 @@ class CGImports(CGWrapper):
|
|||
if parentName:
|
||||
descriptor = descriptorProvider.getDescriptor(parentName)
|
||||
extras += [descriptor.path, descriptor.bindingPath]
|
||||
elif t.isType() and t.isMozMap():
|
||||
elif t.isType() and t.isRecord():
|
||||
extras += ['dom::bindings::mozmap::MozMap']
|
||||
elif isinstance(t, IDLPromiseType):
|
||||
extras += ["dom::promise::Promise"]
|
||||
else:
|
||||
if t.isEnum():
|
||||
extras += [getModuleFromObject(t) + '::' + getIdentifier(t).name + 'Values']
|
||||
|
@ -3819,7 +3830,9 @@ class CGMemberJITInfo(CGThing):
|
|||
return "JSVAL_TYPE_UNDEFINED"
|
||||
if t.isSequence():
|
||||
return "JSVAL_TYPE_OBJECT"
|
||||
if t.isMozMap():
|
||||
if t.isRecord():
|
||||
return "JSVAL_TYPE_OBJECT"
|
||||
if t.isPromise():
|
||||
return "JSVAL_TYPE_OBJECT"
|
||||
if t.isGeckoInterface():
|
||||
return "JSVAL_TYPE_OBJECT"
|
||||
|
@ -4055,7 +4068,7 @@ def getUnionTypeTemplateVars(type, descriptorProvider):
|
|||
elif type.isDictionary():
|
||||
name = type.name
|
||||
typeName = name
|
||||
elif type.isSequence() or type.isMozMap():
|
||||
elif type.isSequence() or type.isRecord():
|
||||
name = type.name
|
||||
inner = getUnionTypeTemplateVars(innerContainerType(type), descriptorProvider)
|
||||
typeName = wrapInNativeContainerType(type, CGGeneric(inner["typeName"])).define()
|
||||
|
@ -4208,7 +4221,7 @@ class CGUnionConversionStruct(CGThing):
|
|||
else:
|
||||
object = None
|
||||
|
||||
mozMapMemberTypes = filter(lambda t: t.isMozMap(), memberTypes)
|
||||
mozMapMemberTypes = filter(lambda t: t.isRecord(), memberTypes)
|
||||
if len(mozMapMemberTypes) > 0:
|
||||
assert len(mozMapMemberTypes) == 1
|
||||
typeName = mozMapMemberTypes[0].name
|
||||
|
@ -6870,6 +6883,8 @@ def process_arg(expr, arg):
|
|||
expr += ".r()"
|
||||
else:
|
||||
expr = "&" + expr
|
||||
elif isinstance(arg.type, IDLPromiseType):
|
||||
expr = "&" + expr
|
||||
return expr
|
||||
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,162 @@
|
|||
def WebIDLTest(parser, harness):
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
interface Foo {
|
||||
[CEReactions(DOMString a)] void foo(boolean arg2);
|
||||
};
|
||||
""")
|
||||
|
||||
results = parser.finish()
|
||||
except:
|
||||
threw = True
|
||||
|
||||
harness.ok(threw, "Should have thrown for [CEReactions] with an argument")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
interface Foo {
|
||||
[CEReactions(DOMString b)] readonly attribute boolean bar;
|
||||
};
|
||||
""")
|
||||
|
||||
results = parser.finish()
|
||||
except:
|
||||
threw = True
|
||||
|
||||
harness.ok(threw, "Should have thrown for [CEReactions] with an argument")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
interface Foo {
|
||||
[CEReactions] attribute boolean bar;
|
||||
};
|
||||
""")
|
||||
|
||||
results = parser.finish()
|
||||
except Exception, e:
|
||||
harness.ok(False, "Shouldn't have thrown for [CEReactions] used on writable attribute. %s" % e)
|
||||
threw = True
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
interface Foo {
|
||||
[CEReactions] void foo(boolean arg2);
|
||||
};
|
||||
""")
|
||||
|
||||
results = parser.finish()
|
||||
except Exception, e:
|
||||
harness.ok(False, "Shouldn't have thrown for [CEReactions] used on regular operations. %s" % e)
|
||||
threw = True
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
interface Foo {
|
||||
[CEReactions] readonly attribute boolean A;
|
||||
};
|
||||
""")
|
||||
|
||||
results = parser.finish()
|
||||
except:
|
||||
threw = True
|
||||
|
||||
harness.ok(threw, "Should have thrown for [CEReactions] used on a readonly attribute")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
[CEReactions]
|
||||
interface Foo {
|
||||
}
|
||||
""")
|
||||
|
||||
results = parser.finish()
|
||||
except:
|
||||
threw = True
|
||||
|
||||
harness.ok(threw, "Should have thrown for [CEReactions] used on a interface")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
interface Foo {
|
||||
[CEReactions] getter any(DOMString name);
|
||||
};
|
||||
""")
|
||||
results = parser.finish()
|
||||
except:
|
||||
threw = True
|
||||
|
||||
harness.ok(threw,
|
||||
"Should have thrown for [CEReactions] used on a named getter")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
interface Foo {
|
||||
[CEReactions] creator boolean (DOMString name, boolean value);
|
||||
};
|
||||
""")
|
||||
results = parser.finish()
|
||||
except:
|
||||
threw = True
|
||||
|
||||
harness.ok(threw,
|
||||
"Should have thrown for [CEReactions] used on a named creator")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
interface Foo {
|
||||
[CEReactions] legacycaller double compute(double x);
|
||||
};
|
||||
""")
|
||||
results = parser.finish()
|
||||
except:
|
||||
threw = True
|
||||
|
||||
harness.ok(threw,
|
||||
"Should have thrown for [CEReactions] used on a legacycaller")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
interface Foo {
|
||||
[CEReactions] stringifier DOMString ();
|
||||
};
|
||||
""")
|
||||
results = parser.finish()
|
||||
except:
|
||||
threw = True
|
||||
|
||||
harness.ok(threw,
|
||||
"Should have thrown for [CEReactions] used on a stringifier")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
interface Foo {
|
||||
[CEReactions] jsonifier;
|
||||
};
|
||||
""")
|
||||
|
||||
results = parser.finish()
|
||||
except:
|
||||
threw = True
|
||||
|
||||
harness.ok(threw, "Should have thrown for [CEReactions] used on a jsonifier")
|
|
@ -13,7 +13,7 @@ def WebIDLTest(parser, harness):
|
|||
def checkMethod(method, QName, name, signatures,
|
||||
static=True, getter=False, setter=False, creator=False,
|
||||
deleter=False, legacycaller=False, stringifier=False,
|
||||
chromeOnly=False):
|
||||
chromeOnly=False, htmlConstructor=False):
|
||||
harness.ok(isinstance(method, WebIDL.IDLMethod),
|
||||
"Should be an IDLMethod")
|
||||
harness.ok(method.isMethod(), "Method is a method")
|
||||
|
@ -29,6 +29,7 @@ def WebIDLTest(parser, harness):
|
|||
harness.check(method.isLegacycaller(), legacycaller, "Method has the correct legacycaller value")
|
||||
harness.check(method.isStringifier(), stringifier, "Method has the correct stringifier value")
|
||||
harness.check(method.getExtendedAttribute("ChromeOnly") is not None, chromeOnly, "Method has the correct value for ChromeOnly")
|
||||
harness.check(method.isHTMLConstructor(), htmlConstructor, "Method has the correct htmlConstructor value")
|
||||
harness.check(len(method.signatures()), len(signatures), "Method has the correct number of signatures")
|
||||
|
||||
sigpairs = zip(method.signatures(), signatures)
|
||||
|
@ -93,6 +94,21 @@ def WebIDLTest(parser, harness):
|
|||
"constructor", [("TestChromeConstructor (Wrapper)", [])],
|
||||
chromeOnly=True)
|
||||
|
||||
parser = parser.reset()
|
||||
parser.parse("""
|
||||
[HTMLConstructor]
|
||||
interface TestHTMLConstructor {
|
||||
};
|
||||
""")
|
||||
results = parser.finish()
|
||||
harness.check(len(results), 1, "Should be one production")
|
||||
harness.ok(isinstance(results[0], WebIDL.IDLInterface),
|
||||
"Should be an IDLInterface")
|
||||
|
||||
checkMethod(results[0].ctor(), "::TestHTMLConstructor::constructor",
|
||||
"constructor", [("TestHTMLConstructor (Wrapper)", [])],
|
||||
htmlConstructor=True)
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
|
@ -107,3 +123,151 @@ def WebIDLTest(parser, harness):
|
|||
threw = True
|
||||
|
||||
harness.ok(threw, "Can't have both a Constructor and a ChromeConstructor")
|
||||
|
||||
# Test HTMLConstructor with argument
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
[HTMLConstructor(DOMString a)]
|
||||
interface TestHTMLConstructorWithArgs {
|
||||
};
|
||||
""")
|
||||
results = parser.finish()
|
||||
except:
|
||||
threw = True
|
||||
|
||||
harness.ok(threw, "HTMLConstructor should take no argument")
|
||||
|
||||
# Test HTMLConstructor on a callback interface
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
[HTMLConstructor]
|
||||
callback interface TestHTMLConstructorOnCallbackInterface {
|
||||
};
|
||||
""")
|
||||
results = parser.finish()
|
||||
except:
|
||||
threw = True
|
||||
|
||||
harness.ok(threw, "HTMLConstructor can't be used on a callback interface")
|
||||
|
||||
# Test HTMLConstructor and Constructor
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
[Constructor,
|
||||
HTMLConstructor]
|
||||
interface TestHTMLConstructorAndConstructor {
|
||||
};
|
||||
""")
|
||||
results = parser.finish()
|
||||
except:
|
||||
threw = True
|
||||
|
||||
harness.ok(threw, "Can't have both a Constructor and a HTMLConstructor")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
[HTMLConstructor,
|
||||
Constructor]
|
||||
interface TestHTMLConstructorAndConstructor {
|
||||
};
|
||||
""")
|
||||
results = parser.finish()
|
||||
except:
|
||||
threw = True
|
||||
|
||||
harness.ok(threw, "Can't have both a HTMLConstructor and a Constructor")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
[HTMLConstructor,
|
||||
Constructor(DOMString a)]
|
||||
interface TestHTMLConstructorAndConstructor {
|
||||
};
|
||||
""")
|
||||
except:
|
||||
threw = True
|
||||
|
||||
harness.ok(threw, "Can't have both a HTMLConstructor and a Constructor")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
[Constructor(DOMString a),
|
||||
HTMLConstructor]
|
||||
interface TestHTMLConstructorAndConstructor {
|
||||
};
|
||||
""")
|
||||
except:
|
||||
threw = True
|
||||
|
||||
harness.ok(threw, "Can't have both a HTMLConstructor and a Constructor")
|
||||
|
||||
# Test HTMLConstructor and ChromeConstructor
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
[ChromeConstructor,
|
||||
HTMLConstructor]
|
||||
interface TestHTMLConstructorAndChromeConstructor {
|
||||
};
|
||||
""")
|
||||
results = parser.finish()
|
||||
except:
|
||||
threw = True
|
||||
|
||||
harness.ok(threw, "Can't have both a HTMLConstructor and a ChromeConstructor")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
[HTMLConstructor,
|
||||
ChromeConstructor]
|
||||
interface TestHTMLConstructorAndChromeConstructor {
|
||||
};
|
||||
""")
|
||||
results = parser.finish()
|
||||
except:
|
||||
threw = True
|
||||
|
||||
harness.ok(threw, "Can't have both a HTMLConstructor and a ChromeConstructor")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
[ChromeConstructor(DOMString a),
|
||||
HTMLConstructor]
|
||||
interface TestHTMLConstructorAndChromeConstructor {
|
||||
};
|
||||
""")
|
||||
results = parser.finish()
|
||||
except:
|
||||
threw = True
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
[HTMLConstructor,
|
||||
ChromeConstructor(DOMString a)]
|
||||
interface TestHTMLConstructorAndChromeConstructor {
|
||||
};
|
||||
""")
|
||||
results = parser.finish()
|
||||
except:
|
||||
threw = True
|
||||
|
||||
harness.ok(threw, "Can't have both a HTMLConstructor and a ChromeConstructor")
|
||||
|
|
|
@ -34,3 +34,36 @@ def WebIDLTest(parser, harness):
|
|||
interface TestNamedConstructorNoInterfaceObject {
|
||||
};
|
||||
""")
|
||||
|
||||
# Test HTMLConstructor and NoInterfaceObject
|
||||
parser = parser.reset()
|
||||
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
[NoInterfaceObject, HTMLConstructor]
|
||||
interface TestHTMLConstructorNoInterfaceObject {
|
||||
};
|
||||
""")
|
||||
|
||||
results = parser.finish()
|
||||
except:
|
||||
threw = True
|
||||
|
||||
harness.ok(threw, "Should have thrown.")
|
||||
|
||||
parser = parser.reset()
|
||||
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
[HTMLConstructor, NoInterfaceObject]
|
||||
interface TestHTMLConstructorNoInterfaceObject {
|
||||
};
|
||||
""")
|
||||
|
||||
results = parser.finish()
|
||||
except:
|
||||
threw = True
|
||||
|
||||
harness.ok(threw, "Should have thrown.")
|
||||
|
|
|
@ -158,21 +158,27 @@ def WebIDLTest(parser, harness):
|
|||
"CallbackInterface?", "CallbackInterface2",
|
||||
"object", "Callback", "Callback2", "optional Dict",
|
||||
"optional Dict2", "sequence<long>", "sequence<short>",
|
||||
"MozMap<object>", "MozMap<Dict>", "MozMap<long>",
|
||||
"record<DOMString, object>",
|
||||
"record<USVString, Dict>",
|
||||
"record<ByteString, long>",
|
||||
"Date", "Date?", "any",
|
||||
"Promise<any>", "Promise<any>?",
|
||||
"USVString", "ArrayBuffer", "ArrayBufferView", "SharedArrayBuffer",
|
||||
"Uint8Array", "Uint16Array" ]
|
||||
# When we can parse Date and RegExp, we need to add them here.
|
||||
"Uint8Array", "Uint16Array",
|
||||
"(long or Callback)", "optional (long or Dict)",
|
||||
]
|
||||
# When we can parse Date, we need to add it here.
|
||||
# XXXbz we can, and should really do that...
|
||||
|
||||
# Try to categorize things a bit to keep list lengths down
|
||||
def allBut(list1, list2):
|
||||
return [a for a in list1 if a not in list2 and
|
||||
(a != "any" and a != "Promise<any>" and a != "Promise<any>?")]
|
||||
unions = [ "(long or Callback)", "optional (long or Dict)" ]
|
||||
numerics = [ "long", "short", "long?", "short?" ]
|
||||
booleans = [ "boolean", "boolean?" ]
|
||||
primitives = numerics + booleans
|
||||
nonNumerics = allBut(argTypes, numerics)
|
||||
nonNumerics = allBut(argTypes, numerics + unions)
|
||||
nonBooleans = allBut(argTypes, booleans)
|
||||
strings = [ "DOMString", "ByteString", "Enum", "Enum2", "USVString" ]
|
||||
nonStrings = allBut(argTypes, strings)
|
||||
|
@ -182,16 +188,18 @@ def WebIDLTest(parser, harness):
|
|||
sharedBufferSourceTypes = ["SharedArrayBuffer"]
|
||||
interfaces = [ "Interface", "Interface?", "AncestorInterface",
|
||||
"UnrelatedInterface", "ImplementedInterface" ] + bufferSourceTypes + sharedBufferSourceTypes
|
||||
nullables = ["long?", "short?", "boolean?", "Interface?",
|
||||
"CallbackInterface?", "optional Dict", "optional Dict2",
|
||||
"Date?", "any", "Promise<any>?"]
|
||||
nullables = (["long?", "short?", "boolean?", "Interface?",
|
||||
"CallbackInterface?", "optional Dict", "optional Dict2",
|
||||
"Date?", "any", "Promise<any>?"] +
|
||||
allBut(unions, [ "(long or Callback)" ]))
|
||||
dates = [ "Date", "Date?" ]
|
||||
sequences = [ "sequence<long>", "sequence<short>" ]
|
||||
nonUserObjects = nonObjects + interfaces + dates + sequences
|
||||
otherObjects = allBut(argTypes, nonUserObjects + ["object"])
|
||||
notRelatedInterfaces = (nonObjects + ["UnrelatedInterface"] +
|
||||
otherObjects + dates + sequences + bufferSourceTypes + sharedBufferSourceTypes)
|
||||
mozMaps = [ "MozMap<object>", "MozMap<Dict>", "MozMap<long>" ]
|
||||
records = [ "record<DOMString, object>", "record<USVString, Dict>",
|
||||
"record<ByteString, long>" ]
|
||||
|
||||
# Build a representation of the distinguishability table as a dict
|
||||
# of dicts, holding True values where needed, holes elsewhere.
|
||||
|
@ -231,9 +239,9 @@ def WebIDLTest(parser, harness):
|
|||
allBut(argTypes, sequences + ["object"]))
|
||||
setDistinguishable("sequence<short>",
|
||||
allBut(argTypes, sequences + ["object"]))
|
||||
setDistinguishable("MozMap<object>", nonUserObjects)
|
||||
setDistinguishable("MozMap<Dict>", nonUserObjects)
|
||||
setDistinguishable("MozMap<long>", nonUserObjects)
|
||||
setDistinguishable("record<DOMString, object>", nonUserObjects)
|
||||
setDistinguishable("record<USVString, Dict>", nonUserObjects)
|
||||
setDistinguishable("record<ByteString, long>", nonUserObjects)
|
||||
setDistinguishable("Date", allBut(argTypes, dates + ["object"]))
|
||||
setDistinguishable("Date?", allBut(argTypes, dates + nullables + ["object"]))
|
||||
setDistinguishable("any", [])
|
||||
|
@ -244,6 +252,10 @@ def WebIDLTest(parser, harness):
|
|||
setDistinguishable("Uint8Array", allBut(argTypes, ["ArrayBufferView", "Uint8Array", "object"]))
|
||||
setDistinguishable("Uint16Array", allBut(argTypes, ["ArrayBufferView", "Uint16Array", "object"]))
|
||||
setDistinguishable("SharedArrayBuffer", allBut(argTypes, ["SharedArrayBuffer", "object"]))
|
||||
setDistinguishable("(long or Callback)",
|
||||
allBut(nonUserObjects, numerics))
|
||||
setDistinguishable("optional (long or Dict)",
|
||||
allBut(nonUserObjects, numerics + nullables))
|
||||
|
||||
def areDistinguishable(type1, type2):
|
||||
return data[type1].get(type2, False)
|
||||
|
@ -263,7 +275,6 @@ def WebIDLTest(parser, harness):
|
|||
callback Callback2 = long(short arg);
|
||||
dictionary Dict {};
|
||||
dictionary Dict2 {};
|
||||
interface _Promise {};
|
||||
interface TestInterface {%s
|
||||
};
|
||||
"""
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
# Import the WebIDL module, so we can do isinstance checks and whatnot
|
||||
import WebIDL
|
||||
|
||||
def WebIDLTest(parser, harness):
|
||||
try:
|
||||
parser.parse("""
|
||||
enum Foo { "a" };
|
||||
interface Foo;
|
||||
""")
|
||||
results = parser.finish()
|
||||
harness.ok(False, "Should fail to parse")
|
||||
except Exception, e:
|
||||
harness.ok("Name collision" in e.message,
|
||||
"Should have name collision for interface")
|
||||
|
||||
parser = parser.reset()
|
||||
try:
|
||||
parser.parse("""
|
||||
dictionary Foo { long x; };
|
||||
enum Foo { "a" };
|
||||
""")
|
||||
results = parser.finish()
|
||||
harness.ok(False, "Should fail to parse")
|
||||
except Exception, e:
|
||||
harness.ok("Name collision" in e.message,
|
||||
"Should have name collision for dictionary")
|
||||
|
||||
parser = parser.reset()
|
||||
try:
|
||||
parser.parse("""
|
||||
enum Foo { "a" };
|
||||
enum Foo { "b" };
|
||||
""")
|
||||
results = parser.finish()
|
||||
harness.ok(False, "Should fail to parse")
|
||||
except Exception, e:
|
||||
harness.ok("Multiple unresolvable definitions" in e.message,
|
||||
"Should have name collision for dictionary")
|
||||
|
|
@ -88,6 +88,8 @@ def WebIDLTest(parser, harness):
|
|||
disallowedNonMethodNames = ["clear", "delete"]
|
||||
mapDisallowedNonMethodNames = ["set"] + disallowedNonMethodNames
|
||||
setDisallowedNonMethodNames = ["add"] + disallowedNonMethodNames
|
||||
unrelatedMembers = [("unrelatedAttribute", WebIDL.IDLAttribute),
|
||||
("unrelatedMethod", WebIDL.IDLMethod)]
|
||||
|
||||
#
|
||||
# Simple Usage Tests
|
||||
|
@ -99,52 +101,147 @@ def WebIDLTest(parser, harness):
|
|||
iterable<long>;
|
||||
readonly attribute unsigned long length;
|
||||
getter long(unsigned long index);
|
||||
attribute long unrelatedAttribute;
|
||||
long unrelatedMethod();
|
||||
};
|
||||
""", valueIterableMembers)
|
||||
""", valueIterableMembers + unrelatedMembers)
|
||||
|
||||
shouldPass("Iterable (key only) inheriting from parent",
|
||||
"""
|
||||
interface Foo1 : Foo2 {
|
||||
iterable<long>;
|
||||
readonly attribute unsigned long length;
|
||||
getter long(unsigned long index);
|
||||
};
|
||||
interface Foo2 {
|
||||
attribute long unrelatedAttribute;
|
||||
long unrelatedMethod();
|
||||
};
|
||||
""", valueIterableMembers, numProductions=2)
|
||||
|
||||
shouldPass("Iterable (key and value)",
|
||||
"""
|
||||
interface Foo1 {
|
||||
iterable<long, long>;
|
||||
attribute long unrelatedAttribute;
|
||||
long unrelatedMethod();
|
||||
};
|
||||
""", iterableMembers,
|
||||
""", iterableMembers + unrelatedMembers,
|
||||
# numProductions == 2 because of the generated iterator iface,
|
||||
numProductions=2)
|
||||
|
||||
shouldPass("Maplike (readwrite)",
|
||||
shouldPass("Iterable (key and value) inheriting from parent",
|
||||
"""
|
||||
interface Foo1 {
|
||||
maplike<long, long>;
|
||||
interface Foo1 : Foo2 {
|
||||
iterable<long, long>;
|
||||
};
|
||||
""", mapRWMembers)
|
||||
interface Foo2 {
|
||||
attribute long unrelatedAttribute;
|
||||
long unrelatedMethod();
|
||||
};
|
||||
""", iterableMembers,
|
||||
# numProductions == 3 because of the generated iterator iface,
|
||||
numProductions=3)
|
||||
|
||||
shouldPass("Maplike (readwrite)",
|
||||
"""
|
||||
interface Foo1 {
|
||||
maplike<long, long>;
|
||||
attribute long unrelatedAttribute;
|
||||
long unrelatedMethod();
|
||||
};
|
||||
""", mapRWMembers)
|
||||
""", mapRWMembers + unrelatedMembers)
|
||||
|
||||
shouldPass("Maplike (readwrite) inheriting from parent",
|
||||
"""
|
||||
interface Foo1 : Foo2 {
|
||||
maplike<long, long>;
|
||||
};
|
||||
interface Foo2 {
|
||||
attribute long unrelatedAttribute;
|
||||
long unrelatedMethod();
|
||||
};
|
||||
""", mapRWMembers, numProductions=2)
|
||||
|
||||
shouldPass("Maplike (readwrite)",
|
||||
"""
|
||||
interface Foo1 {
|
||||
maplike<long, long>;
|
||||
attribute long unrelatedAttribute;
|
||||
long unrelatedMethod();
|
||||
};
|
||||
""", mapRWMembers + unrelatedMembers)
|
||||
|
||||
shouldPass("Maplike (readwrite) inheriting from parent",
|
||||
"""
|
||||
interface Foo1 : Foo2 {
|
||||
maplike<long, long>;
|
||||
};
|
||||
interface Foo2 {
|
||||
attribute long unrelatedAttribute;
|
||||
long unrelatedMethod();
|
||||
};
|
||||
""", mapRWMembers, numProductions=2)
|
||||
|
||||
shouldPass("Maplike (readonly)",
|
||||
"""
|
||||
interface Foo1 {
|
||||
readonly maplike<long, long>;
|
||||
attribute long unrelatedAttribute;
|
||||
long unrelatedMethod();
|
||||
};
|
||||
""", mapROMembers)
|
||||
""", mapROMembers + unrelatedMembers)
|
||||
|
||||
shouldPass("Maplike (readonly) inheriting from parent",
|
||||
"""
|
||||
interface Foo1 : Foo2 {
|
||||
readonly maplike<long, long>;
|
||||
};
|
||||
interface Foo2 {
|
||||
attribute long unrelatedAttribute;
|
||||
long unrelatedMethod();
|
||||
};
|
||||
""", mapROMembers, numProductions=2)
|
||||
|
||||
shouldPass("Setlike (readwrite)",
|
||||
"""
|
||||
interface Foo1 {
|
||||
setlike<long>;
|
||||
attribute long unrelatedAttribute;
|
||||
long unrelatedMethod();
|
||||
};
|
||||
""", setRWMembers)
|
||||
""", setRWMembers + unrelatedMembers)
|
||||
|
||||
shouldPass("Setlike (readwrite) inheriting from parent",
|
||||
"""
|
||||
interface Foo1 : Foo2 {
|
||||
setlike<long>;
|
||||
};
|
||||
interface Foo2 {
|
||||
attribute long unrelatedAttribute;
|
||||
long unrelatedMethod();
|
||||
};
|
||||
""", setRWMembers, numProductions=2)
|
||||
|
||||
shouldPass("Setlike (readonly)",
|
||||
"""
|
||||
interface Foo1 {
|
||||
readonly setlike<long>;
|
||||
attribute long unrelatedAttribute;
|
||||
long unrelatedMethod();
|
||||
};
|
||||
""", setROMembers)
|
||||
""", setROMembers + unrelatedMembers)
|
||||
|
||||
shouldPass("Setlike (readonly) inheriting from parent",
|
||||
"""
|
||||
interface Foo1 : Foo2 {
|
||||
readonly setlike<long>;
|
||||
};
|
||||
interface Foo2 {
|
||||
attribute long unrelatedAttribute;
|
||||
long unrelatedMethod();
|
||||
};
|
||||
""", setROMembers, numProductions=2)
|
||||
|
||||
shouldPass("Inheritance of maplike/setlike",
|
||||
"""
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
# Import the WebIDL module, so we can do isinstance checks and whatnot
|
||||
import WebIDL
|
||||
|
||||
def WebIDLTest(parser, harness):
|
||||
# Basic functionality
|
||||
parser.parse(
|
||||
"""
|
||||
interface Iface {
|
||||
[NewObject] readonly attribute Iface attr;
|
||||
[NewObject] Iface method();
|
||||
};
|
||||
""")
|
||||
results = parser.finish()
|
||||
harness.ok(results, "Should not have thrown on basic [NewObject] usage")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse(
|
||||
"""
|
||||
interface Iface {
|
||||
[Pure, NewObject] readonly attribute Iface attr;
|
||||
};
|
||||
""")
|
||||
results = parser.finish()
|
||||
except:
|
||||
threw = True
|
||||
harness.ok(threw, "[NewObject] attributes must depend on something")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse(
|
||||
"""
|
||||
interface Iface {
|
||||
[Pure, NewObject] Iface method();
|
||||
};
|
||||
""")
|
||||
results = parser.finish()
|
||||
except:
|
||||
threw = True
|
||||
harness.ok(threw, "[NewObject] methods must depend on something")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse(
|
||||
"""
|
||||
interface Iface {
|
||||
[Cached, NewObject, Affects=Nothing] readonly attribute Iface attr;
|
||||
};
|
||||
""")
|
||||
results = parser.finish()
|
||||
except:
|
||||
threw = True
|
||||
harness.ok(threw, "[NewObject] attributes must not be [Cached]")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse(
|
||||
"""
|
||||
interface Iface {
|
||||
[StoreInSlot, NewObject, Affects=Nothing] readonly attribute Iface attr;
|
||||
};
|
||||
""")
|
||||
results = parser.finish()
|
||||
except:
|
||||
threw = True
|
||||
harness.ok(threw, "[NewObject] attributes must not be [StoreInSlot]")
|
|
@ -2,7 +2,6 @@ def WebIDLTest(parser, harness):
|
|||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
interface _Promise {};
|
||||
interface A {
|
||||
legacycaller Promise<any> foo();
|
||||
};
|
||||
|
@ -18,7 +17,6 @@ def WebIDLTest(parser, harness):
|
|||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
interface _Promise {};
|
||||
interface A {
|
||||
Promise<any> foo();
|
||||
long foo(long arg);
|
||||
|
@ -35,7 +33,6 @@ def WebIDLTest(parser, harness):
|
|||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
interface _Promise {};
|
||||
interface A {
|
||||
long foo(long arg);
|
||||
Promise<any> foo();
|
||||
|
@ -48,9 +45,36 @@ def WebIDLTest(parser, harness):
|
|||
"Should not allow overloads which have both Promise and "
|
||||
"non-Promise return types.")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
interface A {
|
||||
Promise<any>? foo();
|
||||
};
|
||||
""")
|
||||
results = parser.finish();
|
||||
except:
|
||||
threw = True
|
||||
harness.ok(threw,
|
||||
"Should not allow nullable Promise return values.")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
interface A {
|
||||
void foo(Promise<any>? arg);
|
||||
};
|
||||
""")
|
||||
results = parser.finish();
|
||||
except:
|
||||
threw = True
|
||||
harness.ok(threw,
|
||||
"Should not allow nullable Promise arguments.")
|
||||
|
||||
parser = parser.reset()
|
||||
parser.parse("""
|
||||
interface _Promise {};
|
||||
interface A {
|
||||
Promise<any> foo();
|
||||
Promise<any> foo(long arg);
|
||||
|
@ -61,3 +85,73 @@ def WebIDLTest(parser, harness):
|
|||
harness.ok(True,
|
||||
"Should allow overloads which only have Promise and return "
|
||||
"types.")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
interface A {
|
||||
attribute Promise<any> attr;
|
||||
};
|
||||
""")
|
||||
results = parser.finish();
|
||||
except:
|
||||
threw = True
|
||||
harness.ok(threw,
|
||||
"Should not allow writable Promise-typed attributes.")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
interface A {
|
||||
[LenientSetter] readonly attribute Promise<any> attr;
|
||||
};
|
||||
""")
|
||||
results = parser.finish();
|
||||
except:
|
||||
threw = True
|
||||
harness.ok(threw,
|
||||
"Should not allow [LenientSetter] Promise-typed attributes.")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
interface A {
|
||||
[PutForwards=bar] readonly attribute Promise<any> attr;
|
||||
};
|
||||
""")
|
||||
results = parser.finish();
|
||||
except:
|
||||
threw = True
|
||||
harness.ok(threw,
|
||||
"Should not allow [PutForwards] Promise-typed attributes.")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
interface A {
|
||||
[Replaceable] readonly attribute Promise<any> attr;
|
||||
};
|
||||
""")
|
||||
results = parser.finish();
|
||||
except:
|
||||
threw = True
|
||||
harness.ok(threw,
|
||||
"Should not allow [Replaceable] Promise-typed attributes.")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
interface A {
|
||||
[SameObject] readonly attribute Promise<any> attr;
|
||||
};
|
||||
""")
|
||||
results = parser.finish();
|
||||
except:
|
||||
threw = True
|
||||
harness.ok(threw,
|
||||
"Should not allow [SameObject] Promise-typed attributes.")
|
||||
|
|
|
@ -3,8 +3,8 @@ import WebIDL
|
|||
def WebIDLTest(parser, harness):
|
||||
parser.parse("""
|
||||
dictionary Dict {};
|
||||
interface MozMapArg {
|
||||
void foo(MozMap<Dict> arg);
|
||||
interface RecordArg {
|
||||
void foo(record<DOMString, Dict> arg);
|
||||
};
|
||||
""")
|
||||
|
||||
|
@ -19,7 +19,7 @@ def WebIDLTest(parser, harness):
|
|||
signature = members[0].signatures()[0]
|
||||
args = signature[1]
|
||||
harness.check(len(args), 1, "Should have one arg")
|
||||
harness.ok(args[0].type.isMozMap(), "Should have a MozMap type here")
|
||||
harness.ok(args[0].type.isRecord(), "Should have a record type here")
|
||||
harness.ok(args[0].type.inner.isDictionary(),
|
||||
"Should have a dictionary inner type")
|
||||
|
||||
|
@ -27,13 +27,27 @@ def WebIDLTest(parser, harness):
|
|||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
interface MozMapVoidArg {
|
||||
void foo(MozMap<void> arg);
|
||||
interface RecordVoidArg {
|
||||
void foo(record<DOMString, void> arg);
|
||||
};
|
||||
""")
|
||||
|
||||
results = parser.finish()
|
||||
except Exception,x:
|
||||
threw = True
|
||||
harness.ok(threw, "Should have thrown because record can't have void as value type.")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
dictionary Dict {
|
||||
record<DOMString, Dict> val;
|
||||
};
|
||||
""")
|
||||
|
||||
harness.ok(threw, "Should have thrown.")
|
||||
results = parser.finish()
|
||||
except Exception,x:
|
||||
threw = True
|
||||
harness.ok(threw,
|
||||
"Should have thrown on dictionary containing itself via record.")
|
|
@ -6,15 +6,14 @@ def WebIDLTest(parser, harness):
|
|||
getter long long (unsigned long index);
|
||||
setter long long (unsigned long index, long long value);
|
||||
creator long long (unsigned long index, long long value);
|
||||
deleter long long (unsigned long index);
|
||||
getter boolean (DOMString name);
|
||||
setter boolean (DOMString name, boolean value);
|
||||
creator boolean (DOMString name, boolean value);
|
||||
deleter boolean (DOMString name);
|
||||
readonly attribute unsigned long length;
|
||||
};
|
||||
|
||||
interface SpecialMethodsCombination {
|
||||
getter deleter long long (unsigned long index);
|
||||
setter creator long long (unsigned long index, long long value);
|
||||
getter deleter boolean (DOMString name);
|
||||
setter creator boolean (DOMString name, boolean value);
|
||||
|
@ -49,25 +48,39 @@ def WebIDLTest(parser, harness):
|
|||
setter=True)
|
||||
checkMethod(iface.members[2], "::SpecialMethods::__indexedcreator", "__indexedcreator",
|
||||
creator=True)
|
||||
checkMethod(iface.members[3], "::SpecialMethods::__indexeddeleter", "__indexeddeleter",
|
||||
deleter=True)
|
||||
checkMethod(iface.members[4], "::SpecialMethods::__namedgetter", "__namedgetter",
|
||||
checkMethod(iface.members[3], "::SpecialMethods::__namedgetter", "__namedgetter",
|
||||
getter=True)
|
||||
checkMethod(iface.members[5], "::SpecialMethods::__namedsetter", "__namedsetter",
|
||||
checkMethod(iface.members[4], "::SpecialMethods::__namedsetter", "__namedsetter",
|
||||
setter=True)
|
||||
checkMethod(iface.members[6], "::SpecialMethods::__namedcreator", "__namedcreator",
|
||||
checkMethod(iface.members[5], "::SpecialMethods::__namedcreator", "__namedcreator",
|
||||
creator=True)
|
||||
checkMethod(iface.members[7], "::SpecialMethods::__nameddeleter", "__nameddeleter",
|
||||
checkMethod(iface.members[6], "::SpecialMethods::__nameddeleter", "__nameddeleter",
|
||||
deleter=True)
|
||||
|
||||
iface = results[1]
|
||||
harness.check(len(iface.members), 4, "Expect 4 members")
|
||||
harness.check(len(iface.members), 3, "Expect 3 members")
|
||||
|
||||
checkMethod(iface.members[0], "::SpecialMethodsCombination::__indexedgetterdeleter",
|
||||
"__indexedgetterdeleter", getter=True, deleter=True)
|
||||
checkMethod(iface.members[1], "::SpecialMethodsCombination::__indexedsettercreator",
|
||||
checkMethod(iface.members[0], "::SpecialMethodsCombination::__indexedsettercreator",
|
||||
"__indexedsettercreator", setter=True, creator=True)
|
||||
checkMethod(iface.members[2], "::SpecialMethodsCombination::__namedgetterdeleter",
|
||||
checkMethod(iface.members[1], "::SpecialMethodsCombination::__namedgetterdeleter",
|
||||
"__namedgetterdeleter", getter=True, deleter=True)
|
||||
checkMethod(iface.members[3], "::SpecialMethodsCombination::__namedsettercreator",
|
||||
checkMethod(iface.members[2], "::SpecialMethodsCombination::__namedsettercreator",
|
||||
"__namedsettercreator", setter=True, creator=True)
|
||||
|
||||
parser = parser.reset();
|
||||
|
||||
threw = False
|
||||
try:
|
||||
parser.parse(
|
||||
"""
|
||||
interface IndexedDeleter {
|
||||
deleter void(unsigned long index);
|
||||
};
|
||||
""")
|
||||
parser.finish()
|
||||
except:
|
||||
threw = True
|
||||
|
||||
harness.ok(threw, "There are no indexed deleters")
|
||||
|
||||
|
||||
|
|
|
@ -194,7 +194,7 @@ impl Headers {
|
|||
}
|
||||
Ok(())
|
||||
},
|
||||
Some(HeadersInit::ByteStringMozMap(m)) => {
|
||||
Some(HeadersInit::StringByteStringRecord(m)) => {
|
||||
for (key, value) in m.iter() {
|
||||
let key_vec = key.as_ref().to_string().into();
|
||||
let headers_key = ByteString::new(key_vec);
|
||||
|
|
|
@ -311,9 +311,9 @@ impl Request {
|
|||
try!(headers_copy.fill(Some(
|
||||
HeadersInit::ByteStringSequenceSequence(init_sequence.clone()))));
|
||||
},
|
||||
&HeadersInit::ByteStringMozMap(ref init_map) => {
|
||||
&HeadersInit::StringByteStringRecord(ref init_map) => {
|
||||
try!(headers_copy.fill(Some(
|
||||
HeadersInit::ByteStringMozMap(init_map.clone()))));
|
||||
HeadersInit::StringByteStringRecord(init_map.clone()))));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -880,8 +880,8 @@ impl Clone for HeadersInit {
|
|||
HeadersInit::Headers(h.clone()),
|
||||
&HeadersInit::ByteStringSequenceSequence(ref b) =>
|
||||
HeadersInit::ByteStringSequenceSequence(b.clone()),
|
||||
&HeadersInit::ByteStringMozMap(ref m) =>
|
||||
HeadersInit::ByteStringMozMap(m.clone()),
|
||||
&HeadersInit::StringByteStringRecord(ref m) =>
|
||||
HeadersInit::StringByteStringRecord(m.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -662,9 +662,9 @@ impl TestBindingMethods for TestBinding {
|
|||
fn PassStringMozMap(&self, _: MozMap<DOMString>) {}
|
||||
fn PassByteStringMozMap(&self, _: MozMap<ByteString>) {}
|
||||
fn PassMozMapOfMozMaps(&self, _: MozMap<MozMap<i32>>) {}
|
||||
fn PassMozMapUnion(&self, _: UnionTypes::LongOrByteStringMozMap) {}
|
||||
fn PassMozMapUnion2(&self, _: UnionTypes::TestBindingOrByteStringMozMap) {}
|
||||
fn PassMozMapUnion3(&self, _: UnionTypes::TestBindingOrByteStringSequenceSequenceOrByteStringMozMap) {}
|
||||
fn PassMozMapUnion(&self, _: UnionTypes::LongOrStringByteStringRecord) {}
|
||||
fn PassMozMapUnion2(&self, _: UnionTypes::TestBindingOrStringByteStringRecord) {}
|
||||
fn PassMozMapUnion3(&self, _: UnionTypes::TestBindingOrByteStringSequenceSequenceOrStringByteStringRecord) {}
|
||||
fn ReceiveMozMap(&self) -> MozMap<i32> { MozMap::new() }
|
||||
fn ReceiveNullableMozMap(&self) -> Option<MozMap<i32>> { Some(MozMap::new()) }
|
||||
fn ReceiveMozMapOfNullableInts(&self) -> MozMap<Option<i32>> { MozMap::new() }
|
||||
|
@ -750,9 +750,6 @@ impl TestBindingMethods for TestBinding {
|
|||
fn AcceptPromise(&self, _promise: &Promise) {
|
||||
}
|
||||
|
||||
fn AcceptNullablePromise(&self, _promise: Option<&Promise>) {
|
||||
}
|
||||
|
||||
fn PassSequenceSequence(&self, _seq: Vec<Vec<i32>>) {}
|
||||
fn ReturnSequenceSequence(&self) -> Vec<Vec<i32>> { vec![] }
|
||||
fn PassUnionSequenceSequence(&self, seq: LongOrLongSequenceSequence) {
|
||||
|
|
|
@ -16,9 +16,9 @@ dictionary BluetoothLEScanFilterInit {
|
|||
DOMString name;
|
||||
DOMString namePrefix;
|
||||
// Maps unsigned shorts to BluetoothDataFilters.
|
||||
MozMap<BluetoothDataFilterInit> manufacturerData;
|
||||
record<DOMString, BluetoothDataFilterInit> manufacturerData;
|
||||
// Maps BluetoothServiceUUIDs to BluetoothDataFilters.
|
||||
MozMap<BluetoothDataFilterInit> serviceData;
|
||||
record<DOMString, BluetoothDataFilterInit> serviceData;
|
||||
};
|
||||
|
||||
dictionary RequestDeviceOptions {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
// https://fetch.spec.whatwg.org/#headers-class
|
||||
|
||||
typedef (Headers or sequence<sequence<ByteString>> or MozMap<ByteString>) HeadersInit;
|
||||
typedef (Headers or sequence<sequence<ByteString>> or record<DOMString, ByteString>) HeadersInit;
|
||||
|
||||
[Constructor(optional HeadersInit init),
|
||||
Exposed=(Window,Worker)]
|
||||
|
|
|
@ -434,33 +434,33 @@ interface TestBinding {
|
|||
sequence<sequence<long>> returnSequenceSequence();
|
||||
void passUnionSequenceSequence((long or sequence<sequence<long>>) seq);
|
||||
|
||||
void passMozMap(MozMap<long> arg);
|
||||
void passNullableMozMap(MozMap<long>? arg);
|
||||
void passMozMapOfNullableInts(MozMap<long?> arg);
|
||||
void passOptionalMozMapOfNullableInts(optional MozMap<long?> arg);
|
||||
void passOptionalNullableMozMapOfNullableInts(optional MozMap<long?>? arg);
|
||||
void passCastableObjectMozMap(MozMap<TestBinding> arg);
|
||||
void passNullableCastableObjectMozMap(MozMap<TestBinding?> arg);
|
||||
void passCastableObjectNullableMozMap(MozMap<TestBinding>? arg);
|
||||
void passNullableCastableObjectNullableMozMap(MozMap<TestBinding?>? arg);
|
||||
void passOptionalMozMap(optional MozMap<long> arg);
|
||||
void passOptionalNullableMozMap(optional MozMap<long>? arg);
|
||||
void passOptionalNullableMozMapWithDefaultValue(optional MozMap<long>? arg = null);
|
||||
void passOptionalObjectMozMap(optional MozMap<TestBinding> arg);
|
||||
void passStringMozMap(MozMap<DOMString> arg);
|
||||
void passByteStringMozMap(MozMap<ByteString> arg);
|
||||
void passMozMapOfMozMaps(MozMap<MozMap<long>> arg);
|
||||
void passMozMap(record<DOMString, long> arg);
|
||||
void passNullableMozMap(record<DOMString, long>? arg);
|
||||
void passMozMapOfNullableInts(record<DOMString, long?> arg);
|
||||
void passOptionalMozMapOfNullableInts(optional record<DOMString, long?> arg);
|
||||
void passOptionalNullableMozMapOfNullableInts(optional record<DOMString, long?>? arg);
|
||||
void passCastableObjectMozMap(record<DOMString, TestBinding> arg);
|
||||
void passNullableCastableObjectMozMap(record<DOMString, TestBinding?> arg);
|
||||
void passCastableObjectNullableMozMap(record<DOMString, TestBinding>? arg);
|
||||
void passNullableCastableObjectNullableMozMap(record<DOMString, TestBinding?>? arg);
|
||||
void passOptionalMozMap(optional record<DOMString, long> arg);
|
||||
void passOptionalNullableMozMap(optional record<DOMString, long>? arg);
|
||||
void passOptionalNullableMozMapWithDefaultValue(optional record<DOMString, long>? arg = null);
|
||||
void passOptionalObjectMozMap(optional record<DOMString, TestBinding> arg);
|
||||
void passStringMozMap(record<DOMString, DOMString> arg);
|
||||
void passByteStringMozMap(record<DOMString, ByteString> arg);
|
||||
void passMozMapOfMozMaps(record<DOMString, record<DOMString, long>> arg);
|
||||
|
||||
void passMozMapUnion((long or MozMap<ByteString>) init);
|
||||
void passMozMapUnion2((TestBinding or MozMap<ByteString>) init);
|
||||
void passMozMapUnion3((TestBinding or sequence<sequence<ByteString>> or MozMap<ByteString>) init);
|
||||
void passMozMapUnion((long or record<DOMString, ByteString>) init);
|
||||
void passMozMapUnion2((TestBinding or record<DOMString, ByteString>) init);
|
||||
void passMozMapUnion3((TestBinding or sequence<sequence<ByteString>> or record<DOMString, ByteString>) init);
|
||||
|
||||
MozMap<long> receiveMozMap();
|
||||
MozMap<long>? receiveNullableMozMap();
|
||||
MozMap<long?> receiveMozMapOfNullableInts();
|
||||
MozMap<long?>? receiveNullableMozMapOfNullableInts();
|
||||
MozMap<MozMap<long>> receiveMozMapOfMozMaps();
|
||||
MozMap<any> receiveAnyMozMap();
|
||||
record<DOMString, long> receiveMozMap();
|
||||
record<DOMString, long>? receiveNullableMozMap();
|
||||
record<DOMString, long?> receiveMozMapOfNullableInts();
|
||||
record<DOMString, long?>? receiveNullableMozMapOfNullableInts();
|
||||
record<DOMString, record<DOMString, long>> receiveMozMapOfMozMaps();
|
||||
record<DOMString, any> receiveAnyMozMap();
|
||||
|
||||
static attribute boolean booleanAttributeStatic;
|
||||
static void receiveVoidStatic();
|
||||
|
@ -519,7 +519,6 @@ interface TestBinding {
|
|||
Promise<any> returnRejectedPromise(any value);
|
||||
readonly attribute Promise<boolean> promiseAttribute;
|
||||
void acceptPromise(Promise<DOMString> string);
|
||||
void acceptNullablePromise(Promise<DOMString>? string);
|
||||
Promise<any> promiseNativeHandler(SimpleCallback? resolve, SimpleCallback? reject);
|
||||
void promiseResolveNative(Promise<any> p, any value);
|
||||
void promiseRejectNative(Promise<any> p, any value);
|
||||
|
|
|
@ -201,23 +201,6 @@ impl<Impl: SelectorImpl> SelectorList<Impl> {
|
|||
pub fn from_vec(v: Vec<Selector<Impl>>) -> Self {
|
||||
SelectorList(v.into_iter().map(SelectorAndHashes::new).collect())
|
||||
}
|
||||
|
||||
pub fn to_css_from_index<W>(&self, from_index: usize, dest: &mut W)
|
||||
-> fmt::Result where W: fmt::Write {
|
||||
let mut iter = self.0.iter().skip(from_index);
|
||||
|
||||
let first = match iter.next() {
|
||||
Some(f) => f,
|
||||
None => return Ok(()),
|
||||
};
|
||||
|
||||
first.selector.to_css(dest)?;
|
||||
for selector_and_hashes in iter {
|
||||
dest.write_str(", ")?;
|
||||
selector_and_hashes.selector.to_css(dest)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Copied from Gecko, who copied it from WebKit. Note that increasing the
|
||||
|
@ -714,7 +697,15 @@ impl<Impl: SelectorImpl> Debug for LocalName<Impl> {
|
|||
|
||||
impl<Impl: SelectorImpl> ToCss for SelectorList<Impl> {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
self.to_css_from_index(/* from_index = */ 0, dest)
|
||||
let mut iter = self.0.iter();
|
||||
let first = iter.next()
|
||||
.expect("Empty SelectorList, should contain at least one selector");
|
||||
first.selector.to_css(dest)?;
|
||||
for selector_and_hashes in iter {
|
||||
dest.write_str(", ")?;
|
||||
selector_and_hashes.selector.to_css(dest)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2112,11 +2112,17 @@ extern "C" {
|
|||
result: *mut nsAString);
|
||||
}
|
||||
extern "C" {
|
||||
pub fn Servo_StyleRule_GetSelectorTextFromIndex(rule:
|
||||
pub fn Servo_StyleRule_GetSelectorTextAtIndex(rule:
|
||||
RawServoStyleRuleBorrowed,
|
||||
index: u32,
|
||||
result: *mut nsAString);
|
||||
}
|
||||
extern "C" {
|
||||
pub fn Servo_StyleRule_GetSpecificityAtIndex(rule:
|
||||
RawServoStyleRuleBorrowed,
|
||||
index: u32,
|
||||
specificity: *mut u64);
|
||||
}
|
||||
extern "C" {
|
||||
pub fn Servo_StyleRule_GetSelectorCount(rule: RawServoStyleRuleBorrowed,
|
||||
count: *mut u32);
|
||||
|
|
|
@ -1187,14 +1187,15 @@ pub extern "C" fn Servo_StyleRule_GetSelectorText(rule: RawServoStyleRuleBorrowe
|
|||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn Servo_StyleRule_GetSelectorTextFromIndex(rule: RawServoStyleRuleBorrowed,
|
||||
aSelectorIndex: u32,
|
||||
result: *mut nsAString) {
|
||||
pub extern "C" fn Servo_StyleRule_GetSelectorTextAtIndex(rule: RawServoStyleRuleBorrowed,
|
||||
index: u32,
|
||||
result: *mut nsAString) {
|
||||
read_locked_arc(rule, |rule: &StyleRule| {
|
||||
rule.selectors.to_css_from_index(
|
||||
aSelectorIndex as usize,
|
||||
unsafe { result.as_mut().unwrap() }
|
||||
).unwrap();
|
||||
let index = index as usize;
|
||||
if index >= rule.selectors.0.len() {
|
||||
return;
|
||||
}
|
||||
rule.selectors.0[index].selector.to_css(unsafe { result.as_mut().unwrap() }).unwrap();
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1205,6 +1206,21 @@ pub extern "C" fn Servo_StyleRule_GetSelectorCount(rule: RawServoStyleRuleBorrow
|
|||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn Servo_StyleRule_GetSpecificityAtIndex(rule: RawServoStyleRuleBorrowed,
|
||||
index: u32,
|
||||
specificity: *mut u64) {
|
||||
read_locked_arc(rule, |rule: &StyleRule| {
|
||||
let mut specificity = unsafe { specificity.as_mut().unwrap() };
|
||||
let index = index as usize;
|
||||
if index >= rule.selectors.0.len() {
|
||||
*specificity = 0;
|
||||
return;
|
||||
}
|
||||
*specificity = rule.selectors.0[index].selector.specificity() as u64;
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn Servo_ImportRule_GetHref(rule: RawServoImportRuleBorrowed, result: *mut nsAString) {
|
||||
read_locked_arc(rule, |rule: &ImportRule| {
|
||||
|
|
|
@ -247,6 +247,7 @@ class MachCommands(CommandBase):
|
|||
|
||||
if android:
|
||||
# Build OpenSSL for android
|
||||
env["OPENSSL_VERSION"] = "1.0.2k"
|
||||
make_cmd = ["make"]
|
||||
if jobs is not None:
|
||||
make_cmd += ["-j" + jobs]
|
||||
|
@ -264,7 +265,7 @@ class MachCommands(CommandBase):
|
|||
verbose=verbose)
|
||||
if status:
|
||||
return status
|
||||
openssl_dir = path.join(openssl_dir, "openssl-1.0.1t")
|
||||
openssl_dir = path.join(openssl_dir, "openssl-{}".format(env["OPENSSL_VERSION"]))
|
||||
env['OPENSSL_LIB_DIR'] = openssl_dir
|
||||
env['OPENSSL_INCLUDE_DIR'] = path.join(openssl_dir, "include")
|
||||
env['OPENSSL_STATIC'] = 'TRUE'
|
||||
|
|
|
@ -4,11 +4,11 @@ all: openssl
|
|||
|
||||
# From http://wiki.openssl.org/index.php/Android
|
||||
.PHONY: openssl
|
||||
openssl: openssl-1.0.1t/libssl.so
|
||||
openssl: openssl-${OPENSSL_VERSION}/libssl.so
|
||||
|
||||
openssl-1.0.1t/libssl.so: openssl-1.0.1t/Configure
|
||||
./openssl.sh ${ANDROID_NDK}
|
||||
openssl-${OPENSSL_VERSION}/libssl.so: openssl-${OPENSSL_VERSION}/Configure
|
||||
./openssl.sh ${ANDROID_NDK} ${OPENSSL_VERSION}
|
||||
|
||||
openssl-1.0.1t/Configure:
|
||||
wget https://www.openssl.org/source/old/1.0.1/openssl-1.0.1t.tar.gz
|
||||
tar -zxf openssl-1.0.1t.tar.gz
|
||||
openssl-${OPENSSL_VERSION}/Configure:
|
||||
URL=https://s3.amazonaws.com/rust-lang-ci/rust-ci-mirror/openssl-${OPENSSL_VERSION}.tar.gz; \
|
||||
curl $$URL | tar xzf -
|
||||
|
|
|
@ -181,7 +181,7 @@ if [ ! -z "$VERBOSE" ] && [ "$VERBOSE" != "0" ]; then
|
|||
echo "ANDROID_DEV: $ANDROID_DEV"
|
||||
fi
|
||||
|
||||
cd openssl-1.0.1t
|
||||
cd openssl-$2
|
||||
perl -pi -e 's/install: all install_docs install_sw/install: install_docs install_sw/g' Makefile.org
|
||||
|
||||
# The code being built isn't maintained by us, so we redirect stderr to stdout
|
||||
|
|
|
@ -123,6 +123,7 @@ win64-nightly/opt:
|
|||
- builds/taskcluster_firefox_windows_64_opt.py
|
||||
- disable_signing.py
|
||||
- taskcluster_nightly.py
|
||||
run-on-projects: []
|
||||
|
||||
win64/pgo:
|
||||
description: "Win64 Opt PGO"
|
||||
|
|
|
@ -244,6 +244,9 @@ def target_tasks_mozilla_beta(full_task_graph, parameters):
|
|||
if platform in ('linux64-pgo', 'linux-pgo', 'android-api-15-nightly',
|
||||
'android-x86-nightly'):
|
||||
return False
|
||||
if platform in ('macosx64-nightly', 'win64-nightly'):
|
||||
# Don't do some nightlies on-push until it's ready.
|
||||
return False
|
||||
if platform in ('linux64', 'linux', 'macosx64'):
|
||||
if task.attributes['build_type'] == 'opt':
|
||||
return False
|
||||
|
|
|
@ -39,13 +39,13 @@ and [specification](https://github.com/mozilla/geckodriver/issues?q=is%3Aissue+i
|
|||
problems in our
|
||||
[issue tracker](https://github.com/mozilla/geckodriver/issues).
|
||||
|
||||
Support is best in Firefox 52.0.3 and onwards,
|
||||
Support is best in Firefox 53 and greater,
|
||||
although generally the more recent the Firefox version,
|
||||
the better the experience as they have more bug fixes and features.
|
||||
Some features will only be available in the most recent Firefox versions,
|
||||
and we strongly advise using the [latest Firefox Nightly](https://nightly.mozilla.org/) with geckodriver.
|
||||
Since Windows XP support in Firefox will be dropped with Firefox 53,
|
||||
we do not support this platform.
|
||||
Since Windows XP support was dropped with Firefox 53,
|
||||
geckodriver is not supported on this platform.
|
||||
|
||||
## WebDriver capabilities
|
||||
|
||||
|
|
|
@ -114,6 +114,24 @@ assert.window = function (win, msg = "") {
|
|||
}, msg, NoSuchWindowError)(win);
|
||||
};
|
||||
|
||||
/**
|
||||
* Asserts that |context| is a valid browsing context.
|
||||
*
|
||||
* @param {browser.Context} context
|
||||
* Browsing context to test.
|
||||
* @param {string=} msg
|
||||
* Custom error message.
|
||||
*
|
||||
* @throws {NoSuchWindowError}
|
||||
* If |context| is invalid.
|
||||
*/
|
||||
assert.contentBrowser = function (context, msg = "") {
|
||||
msg = msg || "Current window does not have a content browser";
|
||||
assert.that(c => c && c.contentBrowser,
|
||||
msg,
|
||||
NoSuchWindowError)(context);
|
||||
};
|
||||
|
||||
/**
|
||||
* Asserts that there is no current user prompt.
|
||||
*
|
||||
|
|
|
@ -145,6 +145,21 @@ browser.Context = class {
|
|||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current URL of the content browser.
|
||||
* If no browser is available, null will be returned.
|
||||
*/
|
||||
get currentURL() {
|
||||
// Bug 1363368 - contentBrowser could be null until we wait for its
|
||||
// initialization been finished
|
||||
if (this.contentBrowser) {
|
||||
return this.contentBrowser.currentURI.spec;
|
||||
|
||||
} else {
|
||||
throw new NoSuchWindowError("Current window does not have a content browser");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current tabmodal UI object. According to the browser
|
||||
* associated with the currently selected tab.
|
||||
|
|
|
@ -145,6 +145,18 @@ Object.defineProperty(GeckoDriver.prototype, "a11yChecks", {
|
|||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(GeckoDriver.prototype, "currentURL", {
|
||||
get: function () {
|
||||
switch (this.context) {
|
||||
case Context.CHROME:
|
||||
return this.getCurrentWindow().location.href;
|
||||
|
||||
case Context.CONTENT:
|
||||
return this.curBrowser.currentURL;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(GeckoDriver.prototype, "proxy", {
|
||||
get: function () {
|
||||
return this.capabilities.get("proxy");
|
||||
|
@ -194,7 +206,7 @@ Object.defineProperty(GeckoDriver.prototype, "windowHandles", {
|
|||
});
|
||||
|
||||
Object.defineProperty(GeckoDriver.prototype, "chromeWindowHandles", {
|
||||
get : function () {
|
||||
get: function () {
|
||||
let hs = [];
|
||||
let winEn = Services.wm.getEnumerator(null);
|
||||
|
||||
|
@ -968,21 +980,10 @@ GeckoDriver.prototype.get = function* (cmd, resp) {
|
|||
* A modal dialog is open, blocking this operation.
|
||||
*/
|
||||
GeckoDriver.prototype.getCurrentUrl = function (cmd) {
|
||||
const win = assert.window(this.getCurrentWindow());
|
||||
assert.window(this.getCurrentWindow());
|
||||
assert.noUserPrompt(this.dialog);
|
||||
|
||||
switch (this.context) {
|
||||
case Context.CHROME:
|
||||
return win.location.href;
|
||||
|
||||
case Context.CONTENT:
|
||||
if (this.curBrowser.contentBrowser) {
|
||||
return this.curBrowser.contentBrowser.currentURI.spec;
|
||||
} else {
|
||||
throw new NoSuchWindowError(
|
||||
"Not a browser window, or no tab currently selected");
|
||||
}
|
||||
}
|
||||
return this.currentURL;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1059,19 +1060,15 @@ GeckoDriver.prototype.getPageSource = function* (cmd, resp) {
|
|||
*/
|
||||
GeckoDriver.prototype.goBack = function* (cmd, resp) {
|
||||
assert.content(this.context);
|
||||
assert.window(this.getCurrentWindow());
|
||||
assert.contentBrowser(this.curBrowser);
|
||||
assert.noUserPrompt(this.dialog);
|
||||
|
||||
if (!this.curBrowser.tab) {
|
||||
// Navigation does not work for non-browser windows
|
||||
return;
|
||||
}
|
||||
|
||||
// If there is no history, just return
|
||||
if (!this.curBrowser.contentBrowser.webNavigation.canGoBack) {
|
||||
return;
|
||||
}
|
||||
|
||||
let currentURL = this.getCurrentUrl();
|
||||
let currentURL = this.currentURL;
|
||||
let goBack = this.listener.goBack({pageTimeout: this.timeouts.pageLoad});
|
||||
|
||||
// If a remoteness update interrupts our page load, this will never return
|
||||
|
@ -1106,19 +1103,15 @@ GeckoDriver.prototype.goBack = function* (cmd, resp) {
|
|||
*/
|
||||
GeckoDriver.prototype.goForward = function* (cmd, resp) {
|
||||
assert.content(this.context);
|
||||
assert.window(this.getCurrentWindow());
|
||||
assert.contentBrowser(this.curBrowser);
|
||||
assert.noUserPrompt(this.dialog);
|
||||
|
||||
if (!this.curBrowser.tab) {
|
||||
// Navigation does not work for non-browser windows
|
||||
return;
|
||||
}
|
||||
|
||||
// If there is no history, just return
|
||||
if (!this.curBrowser.contentBrowser.webNavigation.canGoForward) {
|
||||
return;
|
||||
}
|
||||
|
||||
let currentURL = this.getCurrentUrl();
|
||||
let currentURL = this.currentURL;
|
||||
let goForward = this.listener.goForward({pageTimeout: this.timeouts.pageLoad});
|
||||
|
||||
// If a remoteness update interrupts our page load, this will never return
|
||||
|
|
|
@ -118,6 +118,16 @@ add_test(function test_window() {
|
|||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_contentBrowser() {
|
||||
assert.contentBrowser({contentBrowser: 42});
|
||||
|
||||
for (let typ of [null, undefined, {contentBrowser: null}]) {
|
||||
Assert.throws(() => assert.contentBrowser(typ), NoSuchWindowError);
|
||||
}
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_object() {
|
||||
assert.object({});
|
||||
assert.object(new Object());
|
||||
|
|
|
@ -67,6 +67,18 @@ test(function() {
|
|||
assert_equals(serialize(dt), '<!DOCTYPE html PUBLIC ""\'" "\'"">');
|
||||
}, "DocumentType: 'APOSTROPHE' (U+0027) and 'QUOTATION MARK' (U+0022)");
|
||||
|
||||
test(function() {
|
||||
var el = document.createElement("a");
|
||||
el.setAttribute("href", "\u3042\u3044\u3046 !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~");
|
||||
assert_equals(serialize(el), "<a xmlns=\"http://www.w3.org/1999/xhtml\" href=\"\u3042\u3044\u3046 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\"></a>");
|
||||
}, "Element: href attributes are not percent-encoded");
|
||||
|
||||
test(function() {
|
||||
var el = document.createElement("a");
|
||||
el.setAttribute("href", "?\u3042\u3044\u3046 !\"$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~");
|
||||
assert_equals(serialize(el), "<a xmlns=\"http://www.w3.org/1999/xhtml\" href=\"?\u3042\u3044\u3046 !"$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\"></a>");
|
||||
}, "Element: query parts in href attributes are not percent-encoded");
|
||||
|
||||
test(function() {
|
||||
var pi = document.createProcessingInstruction("a", "");
|
||||
assert_equals(serialize(pi), "<?a ?>");
|
||||
|
|
|
@ -11,9 +11,6 @@ namespace Telemetry {
|
|||
|
||||
using namespace HangMonitor;
|
||||
|
||||
/** The maximum number of stacks that we're keeping for hang reports. */
|
||||
const size_t kMaxHangStacksKept = 50;
|
||||
|
||||
// This utility function generates a string key that is used to index the annotations
|
||||
// in a hash map from |HangReports::AddHang|.
|
||||
nsresult
|
||||
|
@ -36,6 +33,9 @@ ComputeAnnotationsKey(const HangAnnotationsPtr& aAnnotations, nsAString& aKeyOut
|
|||
}
|
||||
|
||||
#if defined(MOZ_GECKO_PROFILER)
|
||||
/** The maximum number of stacks that we're keeping for hang reports. */
|
||||
const size_t kMaxHangStacksKept = 50;
|
||||
|
||||
void
|
||||
HangReports::AddHang(const Telemetry::ProcessedStack& aStack,
|
||||
uint32_t aDuration,
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
|
||||
namespace {
|
||||
|
||||
#if defined(MOZ_GECKO_PROFILER)
|
||||
|
||||
/** Defines the size of the keyed stack dictionary. */
|
||||
const uint8_t kMaxKeyLength = 50;
|
||||
|
||||
|
@ -161,7 +159,6 @@ KeyedStackCapturer::Clear()
|
|||
mStackInfos.Clear();
|
||||
mStacks.Clear();
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace Telemetry
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
#ifndef KeyedStackCapturer_h__
|
||||
#define KeyedStackCapturer_h__
|
||||
|
||||
#ifdef MOZ_GECKO_PROFILER
|
||||
|
||||
#include "Telemetry.h"
|
||||
#include "nsString.h"
|
||||
#include "nsClassHashtable.h"
|
||||
|
@ -74,6 +72,4 @@ private:
|
|||
} // namespace Telemetry
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // MOZ_GECKO_PROFILER
|
||||
|
||||
#endif // KeyedStackCapturer_h__
|
||||
|
|
|
@ -99,10 +99,13 @@ using Telemetry::Common::AutoHashtable;
|
|||
using mozilla::dom::Promise;
|
||||
using mozilla::dom::AutoJSAPI;
|
||||
using mozilla::Telemetry::HangReports;
|
||||
using mozilla::Telemetry::KeyedStackCapturer;
|
||||
using mozilla::Telemetry::CombinedStacks;
|
||||
using mozilla::Telemetry::ComputeAnnotationsKey;
|
||||
|
||||
#if defined(MOZ_GECKO_PROFILER)
|
||||
using mozilla::Telemetry::KeyedStackCapturer;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* IOInterposeObserver recording statistics of main-thread I/O during execution,
|
||||
* aimed at consumption by TelemetryImpl
|
||||
|
|
|
@ -57,7 +57,6 @@ SOURCES += [
|
|||
'HangReports.cpp',
|
||||
'ipc/TelemetryIPC.cpp',
|
||||
'ipc/TelemetryIPCAccumulator.cpp',
|
||||
'KeyedStackCapturer.cpp',
|
||||
'Telemetry.cpp',
|
||||
'TelemetryCommon.cpp',
|
||||
'TelemetryEvent.cpp',
|
||||
|
@ -66,6 +65,12 @@ SOURCES += [
|
|||
'WebrtcTelemetry.cpp',
|
||||
]
|
||||
|
||||
# KeyedStackCapturer entirely relies on profiler to be enabled.
|
||||
if CONFIG['MOZ_GECKO_PROFILER']:
|
||||
SOURCES += [
|
||||
'KeyedStackCapturer.cpp'
|
||||
]
|
||||
|
||||
EXTRA_COMPONENTS += [
|
||||
'TelemetryStartup.js',
|
||||
'TelemetryStartup.manifest'
|
||||
|
|
|
@ -244,34 +244,32 @@ def get_possible_node_paths_win():
|
|||
|
||||
|
||||
def simple_which(filename, path=None):
|
||||
try:
|
||||
return which.which(filename, path)
|
||||
except which.WhichError:
|
||||
return None
|
||||
exts = [".cmd", ".exe", ""] if platform.system() == "Windows" else [""]
|
||||
|
||||
for ext in exts:
|
||||
try:
|
||||
return which.which(filename + ext, path)
|
||||
except which.WhichError:
|
||||
pass
|
||||
|
||||
# If we got this far, we didn't find it with any of the extensions, so
|
||||
# just return.
|
||||
return None
|
||||
|
||||
|
||||
def which_path(filename):
|
||||
"""
|
||||
Return the nodejs or npm path.
|
||||
"""
|
||||
if platform.system() == "Windows":
|
||||
for ext in [".cmd", ".exe", ""]:
|
||||
# Look in the system path first.
|
||||
filepath = simple_which(filename + ext)
|
||||
if filepath is None:
|
||||
# If we don't find it there, fallback to the non-system paths.
|
||||
filepath = simple_which(filename + ext, get_possible_node_paths_win())
|
||||
|
||||
if filepath is not None:
|
||||
return filepath
|
||||
|
||||
# If we got this far, we didn't find it with any of the extensions, so
|
||||
# just return.
|
||||
return None
|
||||
|
||||
# Non-windows.
|
||||
# Look in the system path first.
|
||||
path = simple_which(filename)
|
||||
if path is None and filename == "node":
|
||||
if path is not None:
|
||||
return path
|
||||
|
||||
if platform.system() == "Windows":
|
||||
# If we didn't find it fallback to the non-system paths.
|
||||
path = simple_which(filename, get_possible_node_paths_win())
|
||||
elif filename == "node":
|
||||
path = simple_which("nodejs")
|
||||
|
||||
return path
|
||||
|
|
|
@ -537,6 +537,18 @@ public:
|
|||
return result;
|
||||
}
|
||||
|
||||
static nscolor GetColorUsingStandins(ColorID aID,
|
||||
nscolor aDefault = NS_RGB(0, 0, 0))
|
||||
{
|
||||
nscolor result = NS_RGB(0, 0, 0);
|
||||
if (NS_FAILED(GetColor(aID,
|
||||
true, // aUseStandinsForNativeColors
|
||||
&result))) {
|
||||
return aDefault;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int32_t GetInt(IntID aID, int32_t aDefault = 0)
|
||||
{
|
||||
int32_t result;
|
||||
|
|
Загрузка…
Ссылка в новой задаче