Created attachment 6854 [details] окно создания учетной записи При попытке создания второй учетной записи в thunderbird ничего не происходит (см. вложение). В консоли ошибок появляется сообщение: Ошибка: SecurityError: The operation is insecure. Источник: chrome://messenger/content/newmailaccount/accountProvisioner.js Строка: 254 " let name = EmailAccountProvisioner.storage.getItem("name") || nameElement.value; if (!name) { try { let userInfo = Cc["@mozilla.org/userinfo;1"].getService(Ci.nsIUserInfo); name = userInfo.fullname; } catch(e) { // nsIUserInfo may not be implemented on all platforms, and name might // not be avaialble even if it is. } } nameElement.value = name; EmailAccountProvisioner.saveName(); // Pretend like we've typed something into the search input to set the // initial enabled/disabled state of the search button. EmailAccountProvisioner.onSearchInputOrProvidersChanged(); document.getElementById("window").style.height = window.innerHeight - 1; document.querySelector("button.existing").addEventListener("click", function() { EmailAccountProvisioner.saveName(); EmailAccountProvisioner.NewMailAccount(EmailAccountProvisioner.msgWindow, null, window.arguments[0]); window.close(); }); // Handle Ctrl-W and Esc window.addEventListener("keypress", function(event) { if ((event.which == "119" && isAccel(event)) || event.keyCode == 27) { window.close(); } }); document.getElementById("search").addEventListener("submit", EmailAccountProvisioner.onSearchSubmit); let notifications = document.getElementById("notifications"); notifications.addEventListener("click", function(event) { if (event.target.tagName == "button" && event.target.classList.contains("create")) { EmailAccountProvisioner.onAddressSelected(event.target); } }); // Handle clicking on both email address suggestions, as well // as the headers for the providers of those suggestions. let results = document.getElementById("results"); results.addEventListener("click", event => { // Find the resultsGroup this click was in. let resultsGroup = event.target; while (resultsGroup) { if (resultsGroup.classList.contains("resultsGroup")) { break; } resultsGroup = resultsGroup.parentElement; } if (!resultsGroup) throw("Unexpected error finding resultsGroup."); // Return if we're already expanded if (resultsGroup.classList.contains("expanded")) return; for (let child of resultsGroup.parentElement.children) { if (child != resultsGroup) { child.classList.remove("expanded"); // Hide the other boxes. for (let node of child.querySelectorAll(".extra")) { node.classList.add("slideUp"); for (let address of node.querySelectorAll(".address")) { address.classList.remove("showWithFade"); address.classList.add("hideWithFade"); } } let more = child.querySelector(".more"); let makeListener = (aNode, aMore) => { let listener = () => { if (aMore) aMore.style.display = "block"; aNode.querySelector("button").disabled = true; aNode.removeEventListener("transitionend", listener); }; return listener; }; for (let node of child.querySelectorAll(".pricing")) { node.classList.remove("showWithFade"); // Disable the pricing button and show the "more" text // after the transition is complete. node.addEventListener("transitionend", makeListener(node, more)); node.classList.add("hideWithFade"); } for (let node of child.querySelectorAll(".price")) { node.classList.remove("hideWithFade"); node.classList.add("showWithFade"); } } else { child.classList.add("expanded"); // And show this box. let more = child.querySelector(".more"); if (more) more.style.display = "none"; for (let node of child.querySelectorAll(".pricing")) { node.classList.remove("hideWithFade"); node.classList.add("showWithFade"); node.querySelector("button").disabled = false; } for (let node of child.querySelectorAll(".price")) { node.classList.remove("showWithFade"); node.classList.add("hideWithFade"); } for (let node of child.querySelectorAll(".extra")) { node.classList.remove("slideUp"); for (let address of node.querySelectorAll(".address")) { address.classList.remove("hideWithFade"); address.classList.add("showWithFade"); } } } } }); for (let node of document.querySelectorAll("button.close")) { node.addEventListener("click", () => window.close()); } window.addEventListener("unload", function() { let searchEngineCheck = document.getElementById("search_engine_check"); if (window.arguments[0].search_engine && searchEngineCheck.checked) { let engine = Services.search.getEngineByName(window.arguments[0].search_engine); Services.search.currentEngine = engine; } }); if (window.arguments[0].success) { // Show the success page which lets a user compose mail, find add-ons, // set a signature, etc. gLog.info("Looks like we just finished ordering an address - showing the success page..."); EmailAccountProvisioner.showSuccessPage(); } else { // The default mode, where we display the search input, providers, etc document.getElementById("window").style.display = "block"; document.getElementById("successful_account").style.display = "none"; } gLog.info("Email Account Provisioner init complete."); EmailAccountProvisioner._inited = true; }, /** * Event handler for when the user submits the search request for their * name to the suggestFromName service. */ onSearchSubmit: function EAP_onSearchSubmit() { for (let node of document.getElementById("notifications").children) { node.style.display = "none"; } document.getElementById("instructions").classList.add("hide"); EmailAccountProvisioner.saveName(); // Here's where we do some kind of hack-y client-side sanitization. // Believe it or not, this is how you sanitize stuff to HTML elements // via jQuery. // let name = String.trim($("<div></div>").text($("#name").val()).html()); // Not quite sure what this was for, but here's the hack converted // to vanilla JS. let nameElement = document.getElementById("name"); let div = document.createElement("div"); div.textContent = nameElement.value; let name = div.innerHTML.trim(); if (!name) { nameElement.select(); nameElement.focus(); return; } EmailAccountProvisioner.searchEnabled(false); EmailAccountProvisioner.spinning(true); let [firstname, lastname] = splitName(name); let selectedProviderList = [...document.querySelectorAll(".provider input:checked")]; let providerList = selectedProviderList.map(node => node.value).join(','); let request = new XMLHttpRequest(); request.open("GET", EmailAccountProvisioner.suggestFromName + "?first_name=" + encodeURIComponent(firstname) + "&last_name=" + encodeURIComponent(lastname) + "&providers=" + encodeURIComponent(providerList) + "&version=2"); request.onload = function () { let data; try { data = JSON.parse(request.responseText); } catch(e) {}; EmailAccountProvisioner.onSearchResults(data); }; request.onerror = () => { gLog.info("Error response of XMLHttpRequest fetching address data."); EmailAccountProvisioner.showSearchError(); }; request.ontimeout = () => { gLog.info("Timeout of XMLHttpRequest fetching address data."); EmailAccountProvisioner.showSearchError(); } request.onloadend = function() { // Also called if we timeout. let firstAndLastName = document.getElementById("FirstAndLastName"); firstAndLastName.innerHTML = String.trim(firstname + " " + lastname); EmailAccountProvisioner.searchEnabled(true); EmailAccountProvisioner.spinning(false); }; request.timeout = CONNECTION_TIMEOUT; request.send(null); }, /** * Event handler for when the user selects an address by clicking on * the price button for that address. This function spawns the content * tab for the address order form, and then closes the Account Provisioner * window. */ onAddressSelected: function EAP_onAddressSelected(aTarget) { gLog.info("An address was selected by the user."); let provider = EmailAccountProvisioner.providers[aTarget.dataset["provider"]]; // Replace the variables in the url. let url = provider.api; let [firstName, lastName] = splitName(document.getElementById("name").value.trim()); let email = aTarget.getAttribute("address"); url = url.replace("{firstname}", firstName); url = url.replace("{lastname}", lastName); url = url.replace("{email}", email); // And add the extra data. let data = storedData[provider.id]; delete data.provider; for (let name in data) { url += (url.indexOf("?") == -1 ? "?" : "&") + name + "=" + encodeURIComponent(data[name]); } gLog.info("Opening up a contentTab with the order form."); // Then open a content tab. let mail3Pane = Services.wm.getMostRecentWindow("mail:3pane"); let tabmail = mail3Pane.document.getElementById("tabmail"); tabmail.openTab("accountProvisionerTab", { contentPage: url, realName: String.trim(firstName + " " + lastName), email: email, searchEngine: provider.search_engine, onLoad: function (aEvent, aBrowser) { window.close(); }, }); // Wait for the handler to close us. EmailAccountProvisioner.spinning(true); EmailAccountProvisioner.searchEnabled(false); for (let node of document.querySelectorAll("#notifications > :not(.spinner)")) { node.style.display = "none"; } }, /** * Attempt to fetch the provider list from the server. If it fails, * display an error message, and queue for retry. */ tryToPopulateProviderList: function EAP_tryToPopulateProviderList() { // If we're already in the middle of getting the provider list, or // we already got it before, bail out. if (this._loadingProviders || this._loadedProviders) return; gLog.info("Trying to populate provider list..."); // If there's a timeout ID for waking the account provisioner, clear it. if (this._loadProviderRetryId) { window.clearTimeout(this._loadProviderRetryId) this._loadProviderRetryId = null; } this.searchEnabled(false); this.spinning(true); let providerListUrl = Services.prefs.getCharPref("mail.provider.providerList"); let request = new XMLHttpRequest(); request.open("GET", providerListUrl); request.onload = function() { let data; try { data = JSON.parse(request.responseText); } catch(e) {}; EmailAccountProvisioner.populateProviderList(data); }; request.onerror = () => { // Ugh, we couldn't get the JSON file. Maybe we're not online. Or maybe // the server is down, or the file isn't being served. Regardless, if // we get here, none of this stuff is going to work. EmailAccountProvisioner._loadProviderRetryId = window.setTimeout(() => EmailAccountProvisioner.tryToPopulateProviderList(), RETRY_TIMEOUT); EmailAccountProvisioner._loadingProviders = false; EmailAccountProvisioner.beOffline(); gLog.error("Something went wrong loading the provider list JSON file. " + "Going into offline mode."); }; request.onloadend = function() { EmailAccountProvisioner._loadingProviders = false; EmailAccountProvisioner.spinning(false); gLog.info("Got provider list JSON."); }; request.timeout = CONNECTION_TIMEOUT; request.ontimeout = () => { glog.info("Timeout of XMLHttpRequest fetching provider list."); request.onError(); }; request.send(null); EmailAccountProvisioner._loadingProviders = true; gLog.info("We've kicked off a request for the provider list JSON file..."); }, providerHasCorrectFields: function EAP_providerHasCorrectFields(provider) { let result = true; let required = ["id", "label", "paid", "languages", "api", "tos_url", "privacy_url"]; for (let [index, aField] in Iterator(required)) { let fieldExists = (aField in provider); result &= fieldExists; if (!fieldExists) gLog.error("A provider did not have the field " + aField + ", and will be skipped."); }; return result; }, /** * Take the fetched providers, create checkboxes, icons and labels, * and insert them below the search input. */ populateProviderList: function EAP_populateProviderList(data) { gLog.info("Populating the provider list"); if (!data || !data.length) { gLog.error("The provider list we got back from the server was empty!"); EmailAccountProvisioner.beOffline(); return; } let providerList = document.getElementById("providerList"); let otherLangProviders = []; EmailAccountProvisioner.providers = {}; data.forEach(function(provider) { if (!(EmailAccountProvisioner.providerHasCorrectFields(provider))) { gLog.error("A provider had incorrect fields, and has been skipped"); return; } EmailAccountProvisioner.providers[provider.id] = provider; // Let's go through the array of languages for this provider, and // check to see if at least one of them matches general.useragent.locale. // If so, we'll show / select this provider by default. let supportsSomeUserLang = provider.languages.some(function (x) { return x == "*" || x == EmailAccountProvisioner.userLanguage; }); let checkboxId = provider.id + "-check"; let providerCheckbox = document.createElement("input"); providerCheckbox.setAttribute("type", "checkbox"); providerCheckbox.setAttribute("value", provider.id); providerCheckbox.className = "providerCheckbox"; providerCheckbox.setAttribute("id", checkboxId); let providerEntry = document.createElement("li"); providerEntry.className = "provider"; providerEntry.appendChild(providerCheckbox); let icon = document.createElement("img"); icon.className = "icon"; // We add this even if there is no icon, so that the alignment with // providers without icons isn't broken. providerEntry.appendChild(icon); if (provider.icon) { // Note this favicon must be fetched, which takes a noticeable // time the first time it happens. icon.setAttribute("src", provider.icon); } let labelSpan = document.createElement("label"); labelSpan.className = "providerLabel"; labelSpan.setAttribute("for", checkboxId); labelSpan.innerHTML = provider.label; providerEntry.appendChild(labelSpan); providerCheckbox.addEventListener("change", EmailAccountProvisioner.populateTermsAndPrivacyLinks); if (supportsSomeUserLang) { providerCheckbox.setAttribute("checked", "true"); providerEntry.style.display = "inline-block"; providerList.appendChild(providerEntry); } else { providerEntry.classList.add("otherLanguage"); otherLangProviders.push(providerEntry); } }); if (otherLangProviders.length) { for (let provider of otherLangProviders) { providerList.appendChild(provider); } let otherLangDesc = document.getElementById("otherLangDesc"); otherLangDesc.classList.remove("fadeOut"); otherLangDesc.classList.add("fadeIn"); otherLangDesc.addEventListener("click", function() { otherLangDesc.classList.remove("fadeIn"); otherLangDesc.classList.add("fadeOut"); for (let node of document.querySelectorAll(".otherLanguage")) { node.style.display = "inline-block"; node.classList.add("showWithFade"); } }); } EmailAccountProvisioner.populateTermsAndPrivacyLinks(); EmailAccountProvisioner.beOnline(); EmailAccountProvisioner._loadedProviders = true; EmailAccountProvisioner.onSearchInputOrProvidersChanged(); }, /** * Go through each of the checked providers, and add the appropriate * ToS and privacy links to the disclaimer. */ populateTermsAndPrivacyLinks: function EAP_populateTOSandPrivacyLinks() { gLog.info("Refreshing terms and privacy links"); // Empty the Terms of Service and Privacy links placeholder. let placeholder = document.querySelector(".commentary .placeholder"); placeholder.innerHTML = ""; let selectedProviders = [...document.querySelectorAll(".provider input:checked")]; let len = selectedProviders.length; EmailAccountProvisioner.someProvidersChecked = len > 0; if (!len) { // Something went really wrong - we shouldn't have gotten here. Bail out. return; } let innerHTML = ""; selectedProviders.forEach((checkbox, i) => { let providerId = checkbox.value; let provider = EmailAccountProvisioner.providers[providerId]; innerHTML += '<span>' + provider.label + ' (</span>'; innerHTML += '<a href="' + provider.privacy_url + '" '; innerHTML += 'class="privary external ' + provider.id + '">'; innerHTML += stringBundle.get("privacyPolicy") + '</a>'; innerHTML += '<span>' + stringBundle.get("sepComma") + '</span>'; innerHTML += '<a href="' + provider.tos_url + '" '; innerHTML += 'class="tos external ' + provider.id + '">'; innerHTML += stringBundle.get('tos') + '</a>'; innerHTML += '<span>)</span>'; if (len != 1) { if (i < len - 2) { innerHTML += '<span>' + stringBundle.get("sepComma") + '</span>'; } else if (i == len - 2) { innerHTML += '<span>' + stringBundle.get("sepAnd") + '</span>'; } } }); placeholder.innerHTML = innerHTML; }, /** * Something went wrong during search. Show a generic error. In the future, * we might want to show something a bit more descriptive. */ showSearchError: function() { for (let node of document.getElementById("notifications").children) { node.style.display = "none"; } for (let node of document.querySelectorAll("#notifications .error")) { node.style.display = "block" node.getBoundingClientRect(); node.classList.add("showWithFade"); } }, /** * Once we've received search results from the server, create some * elements to display those results, and inject them into the DOM. */ onSearchResults: function(data) { gLog.info("Got back search results"); // Empty any old results. let results = document.getElementById("results"); results.innerHTML = ""; if (!data || !data.length) { // If we've gotten back nonsense, display the generic // error message, and bail out. gLog.error("We got nothing back from the server for search results!"); EmailAccountProvisioner.showSearchError(); return; } // Get a list of the providers that the user checked - we'll // check against these to make sure the server didn't send any // back from a provider that the user did not select. let selectedProviderList = [...document.querySelectorAll(".provider input:checked")]; let selectedProviders = selectedProviderList.map(node => node.value); gLog.info(selectedProviders.length + " selected providers."); // Filter out any results that don't match our requirements... let returnedProviders = data.filter(function(aResult) { // We require that the search succeeded for a provider, that we // got at least one result, and that the provider is actually in // the list of providers that we care about. let providerInList = (aResult.provider in EmailAccountProvisioner.providers); if (!providerInList) gLog.error("Got a result back for a provider that was not " + "in the original providerList: " + aResult.provider); let providerSelected = selectedProviders.indexOf(aResult.provider) != -1; if (!providerSelected) gLog.error("Got a result back for a provider that the user did " + "not select: " + aResult.provider); return (aResult.succeeded && aResult.addresses.length > 0 && providerInList && providerSelected); }); if (returnedProviders.length == 0) { gLog.info("There weren't any results for the selected providers."); // Display the generic error message, and bail out. EmailAccountProvisioner.showSearchError(); return; } for (let provider of returnedProviders) { let group = document.createElement("div"); group.className = "resultsGroup"; let header = document.getElementById("resultsHeader").cloneNode(true); header.classList.remove("displayNone"); header.classList.add("selection"); let providerLabel = document.createTextNode(EmailAccountProvisioner.providers[provider.provider].label); header.querySelector(".provider").appendChild(providerLabel); let providerPrice; if (provider.price && provider.price != "0") providerPrice = document.createTextNode(provider.price); else providerPrice = document.createTextNode(stringBundle.get("free")); header.querySelector(".price").appendChild(providerPrice); group.appendChild(header); let renderedAddresses = 0; let addrIndex = 0; for (let address of provider.addresses) { addrIndex++; // Figure out the price to display on the address button, as so: // If there is a per-address price of > 0, use that. // Otherwise, if there is a per-address price of 0, use "Free", // Otherwise, there's no per-address price, // so if the provider's price is > 0, use that. // Or if the provider's price is 0, use "Free". let priceStr; if (address.price && address.price != "0") priceStr = stringBundle.get("price", [address.price]) else if (address.price && address.price == "0") priceStr = stringBundle.get("free"); else if (provider.price && provider.price != "0") priceStr = stringBundle.get("price", [provider.price]) else priceStr = stringBundle.get("free"); let templateElement = document.querySelector("#result_tmpl"); let result = document.importNode(templateElement.content, true).children[0]; result.innerHTML = result.innerHTML.replace(/\${address}/g, address.address ? address.address : address) .replace(/\${priceStr}/g, priceStr); group.appendChild(result); // Keep a count of the rendered addresses for the "More" buttons, etc. renderedAddresses++; if (addrIndex > MAX_SMALL_ADDRESSES) { result.classList.add("extra"); for (let address of result.querySelectorAll(".address")) { address.classList.add("hideWithFade"); } result.classList.add("slideUp"); } } gLog.info("Added " + renderedAddresses + " addresses, showing at most " + MAX_SMALL_ADDRESSES + "."); if (renderedAddresses > MAX_SMALL_ADDRESSES) { let more = renderedAddresses - MAX_SMALL_ADDRESSES; let moreStr = PluralForm.get(more, stringBundle.get("moreOptions")).replace("#1", more); let last = group.querySelector(".row:nth-child(" + (MAX_SMALL_ADDRESSES + 1) + ")"); last.innerHTML += '<div class="more">' + moreStr + '</div>'; } for (let node of group.querySelectorAll("button.create")) { node.dataset.provider = provider.provider; } // There doesn't seem to be a #resultsFooter anywhere. // let footer = document.getElementById("resultsFooter").cloneNode(true); // footer.classList.remove("displayNone"); // group.append(footer); results.appendChild(group); } for (let node of document.getElementById("notifications").children) { if (node.classList.contains("success")) { node.style.display = "block"; } else { node.style.display = "none"; } } for (let provider of data) { delete provider.succeeded; delete provider.addresses; delete provider.price; storedData[provider.provider] = provider; } }, /** * If we cannot retrieve the provider list from the server, display a * message about connection problems, and disable the search fields. */ beOffline: function EAP_beOffline() { let offlineMsg = stringBundle.get("cannotConnect"); let element = document.getElementById("cannotConnectMessage"); element.appendChild(document.createTextNode(offlineMsg)); element.style.display = "block"; element.style.opacity = 1; this.searchEnabled(false); gLog.info("Email Account Provisioner is in offline mode."); }, /** * If we're suddenly able to get the provider list, hide the connection * error message and re-enable the search fields. */ beOnline: function EAP_beOnline() { let element = document.getElementById("cannotConnectMessage"); element.style.display = "none"; element.innerHTML = ""; this.searchEnabled(true); gLog.info("Email Account Provisioner is in online mode."); } } XPCOMUtils.defineLazyGetter(EmailAccountProvisioner, "storage", function() { return getLocalStorage("accountProvisioner"); }); window.addEventListener("online", EmailAccountProvisioner.tryToPopulateProviderList); document.addEventListener("DOMContentLoaded", EmailAccountProvisioner.init); "
Версия thunderbird: 91.3.2-alt1 - Ошибка исправлена