summaryrefslogtreecommitdiffstats
path: root/toolkit/components/aboutwebauthn/content/aboutWebauthn.js
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /toolkit/components/aboutwebauthn/content/aboutWebauthn.js
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/aboutwebauthn/content/aboutWebauthn.js')
-rw-r--r--toolkit/components/aboutwebauthn/content/aboutWebauthn.js866
1 files changed, 866 insertions, 0 deletions
diff --git a/toolkit/components/aboutwebauthn/content/aboutWebauthn.js b/toolkit/components/aboutwebauthn/content/aboutWebauthn.js
new file mode 100644
index 0000000000..9142ed198a
--- /dev/null
+++ b/toolkit/components/aboutwebauthn/content/aboutWebauthn.js
@@ -0,0 +1,866 @@
+/* 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/. */
+
+"use strict";
+
+let AboutWebauthnService = null;
+
+var AboutWebauthnManagerJS = {
+ _topic: "about-webauthn-prompt",
+ _initialized: false,
+ _l10n: null,
+ _bio_l10n: null,
+ _curr_data: null,
+ _current_tab: "",
+ _previous_tab: "",
+
+ init() {
+ if (this._initialized) {
+ return;
+ }
+ this._l10n = new Localization(["toolkit/about/aboutWebauthn.ftl"], true);
+ this._bio_l10n = new Map();
+ this._bio_l10n.set(
+ "Ctap2EnrollFeedbackFpGood",
+ "about-webauthn-ctap2-enroll-feedback-good"
+ );
+ this._bio_l10n.set(
+ "Ctap2EnrollFeedbackFpTooHigh",
+ "about-webauthn-ctap2-enroll-feedback-too-high"
+ );
+ this._bio_l10n.set(
+ "Ctap2EnrollFeedbackFpTooLow",
+ "about-webauthn-ctap2-enroll-feedback-too-low"
+ );
+ this._bio_l10n.set(
+ "Ctap2EnrollFeedbackFpTooLeft",
+ "about-webauthn-ctap2-enroll-feedback-too-left"
+ );
+ this._bio_l10n.set(
+ "Ctap2EnrollFeedbackFpTooRight",
+ "about-webauthn-ctap2-enroll-feedback-too-right"
+ );
+ this._bio_l10n.set(
+ "Ctap2EnrollFeedbackFpTooFast",
+ "about-webauthn-ctap2-enroll-feedback-too-fast"
+ );
+ this._bio_l10n.set(
+ "Ctap2EnrollFeedbackFpTooSlow",
+ "about-webauthn-ctap2-enroll-feedback-too-slow"
+ );
+ this._bio_l10n.set(
+ "Ctap2EnrollFeedbackFpPoorQuality",
+ "about-webauthn-ctap2-enroll-feedback-poor-quality"
+ );
+ this._bio_l10n.set(
+ "Ctap2EnrollFeedbackFpTooSkewed",
+ "about-webauthn-ctap2-enroll-feedback-too-skewed"
+ );
+ this._bio_l10n.set(
+ "Ctap2EnrollFeedbackFpTooShort",
+ "about-webauthn-ctap2-enroll-feedback-too-short"
+ );
+ this._bio_l10n.set(
+ "Ctap2EnrollFeedbackFpMergeFailure",
+ "about-webauthn-ctap2-enroll-feedback-merge-failure"
+ );
+ this._bio_l10n.set(
+ "Ctap2EnrollFeedbackFpExists",
+ "about-webauthn-ctap2-enroll-feedback-exists"
+ );
+ this._bio_l10n.set(
+ "Ctap2EnrollFeedbackNoUserActivity",
+ "about-webauthn-ctap2-enroll-feedback-no-user-activity"
+ );
+ this._bio_l10n.set(
+ "Ctap2EnrollFeedbackNoUserPresenceTransition",
+ "about-webauthn-ctap2-enroll-feedback-no-user-presence-transition"
+ );
+ this._bio_l10n.set(
+ "Ctap2EnrollFeedbackOther",
+ "about-webauthn-ctap2-enroll-feedback-other"
+ );
+
+ Services.obs.addObserver(this, this._topic);
+ this._initialized = true;
+ reset_page();
+ },
+
+ uninit() {
+ Services.obs.removeObserver(this, this._topic);
+ this._initialized = false;
+ this._l10n = null;
+ this._current_tab = "";
+ this._previous_tab = "";
+ },
+
+ observe(aSubject, aTopic, aData) {
+ let data = JSON.parse(aData);
+
+ // We have token
+ if (data.type == "selected-device") {
+ this._curr_data = data.auth_info;
+ this.show_ui_based_on_authenticator_info(data);
+ fake_click_event_for_id("info-tab-button");
+ } else if (data.type == "select-device") {
+ set_info_text("about-webauthn-text-select-device");
+ } else if (data.type == "pin-required") {
+ open_pin_required_tab();
+ } else if (data.type == "pin-invalid") {
+ let retries = data.retries ? data.retries : 0;
+ show_results_banner(
+ "error",
+ "about-webauthn-results-pin-invalid-error",
+ JSON.stringify({ retriesLeft: retries })
+ );
+ open_pin_required_tab();
+ } else if (data.type == "bio-enrollment-update") {
+ if (data.result.EnrollmentList) {
+ show_results_banner("success", "about-webauthn-results-success");
+ this.show_enrollment_list(data.result.EnrollmentList);
+ bio_enrollment_in_progress(false);
+ } else if (data.result.DeleteSuccess || data.result.AddSuccess) {
+ show_results_banner("success", "about-webauthn-results-success");
+ clear_bio_enrollment_samples();
+ // Update AuthenticatorInfo
+ this._curr_data = data.result.DeleteSuccess ?? data.result.AddSuccess;
+ fake_click_event_for_id("bio-enrollments-tab-button");
+ bio_enrollment_in_progress(false);
+ // If we still have some enrollments to show, update the list
+ // otherwise, remove it.
+ if (
+ this._curr_data.options.bioEnroll === true ||
+ this._curr_data.options.userVerificationMgmtPreview === true
+ ) {
+ list_bio_enrollments();
+ } else {
+ // Hide the list, because it's empty
+ document.getElementById(
+ "bio-enrollment-list-subsection"
+ ).hidden = true;
+ }
+ } else if (data.result.UpdateSuccess) {
+ fake_click_event_for_id("bio-enrollments-tab-button");
+ list_bio_enrollments();
+ } else if (data.result.SampleStatus) {
+ show_add_bio_enrollment_section();
+ let up = document.getElementById("enrollment-update");
+ let sample_update = document.createElement("div");
+ let new_line = document.createElement("label");
+ new_line.classList.add("sample");
+ new_line.setAttribute(
+ "data-l10n-id",
+ this._bio_l10n.get(data.result.SampleStatus[0])
+ );
+ sample_update.appendChild(new_line);
+ let samples_needed_line = document.createElement("label");
+ samples_needed_line.setAttribute(
+ "data-l10n-id",
+ "about-webauthn-samples-still-needed"
+ );
+ samples_needed_line.setAttribute(
+ "data-l10n-args",
+ JSON.stringify({ repeatCount: data.result.SampleStatus[1] })
+ );
+ sample_update.classList.add("bio-enrollment-sample");
+ sample_update.appendChild(samples_needed_line);
+ up.appendChild(sample_update);
+ }
+ } else if (data.type == "credential-management-update") {
+ credential_management_in_progress(false);
+ if (data.result.CredentialList) {
+ show_results_banner("success", "about-webauthn-results-success");
+ this.show_credential_list(data.result.CredentialList.credential_list);
+ } else {
+ // DeleteSuccess or UpdateSuccess
+ show_results_banner("success", "about-webauthn-results-success");
+ list_credentials();
+ }
+ } else if (data.type == "listen-success") {
+ reset_page();
+ // Show results
+ show_results_banner("success", "about-webauthn-results-success");
+ this._reset_in_progress = "";
+ AboutWebauthnService.listen();
+ } else if (data.type == "listen-error") {
+ reset_page();
+
+ if (!data.error) {
+ show_results_banner("error", "about-webauthn-results-general-error");
+ } else if (data.error.type == "pin-auth-blocked") {
+ show_results_banner(
+ "error",
+ "about-webauthn-results-pin-auth-blocked-error"
+ );
+ } else if (data.error.type == "pin-not-set") {
+ show_results_banner(
+ "error",
+ "about-webauthn-results-pin-not-set-error"
+ );
+ } else if (data.error.type == "device-blocked") {
+ show_results_banner(
+ "error",
+ "about-webauthn-results-pin-blocked-error"
+ );
+ } else if (data.error.type == "pin-is-too-short") {
+ show_results_banner(
+ "error",
+ "about-webauthn-results-pin-too-short-error"
+ );
+ } else if (data.error.type == "pin-is-too-long") {
+ show_results_banner(
+ "error",
+ "about-webauthn-results-pin-too-long-error"
+ );
+ } else if (data.error.type == "pin-invalid") {
+ let retries = data.error.retries
+ ? JSON.stringify({ retriesLeft: data.error.retries })
+ : null;
+ show_results_banner(
+ "error",
+ "about-webauthn-results-pin-invalid-error",
+ retries
+ );
+ } else if (data.error.type == "cancel") {
+ show_results_banner(
+ "error",
+ "about-webauthn-results-cancelled-by-user-error"
+ );
+ } else {
+ show_results_banner("error", "about-webauthn-results-general-error");
+ }
+ AboutWebauthnService.listen();
+ }
+ },
+
+ show_authenticator_options(options, element, l10n_base) {
+ let table = document.getElementById(element);
+ var empty_table = document.createElement("table");
+ empty_table.id = element;
+ table.parentNode.replaceChild(empty_table, table);
+ table = document.getElementById(element);
+ for (let key in options) {
+ if (key == "options") {
+ continue;
+ }
+ // Create an empty <tr> element and add it to the 1st position of the table:
+ var row = table.insertRow(0);
+
+ // Insert new cells (<td> elements) at the 1st and 2nd position of the "new" <tr> element:
+ var cell1 = row.insertCell(0);
+ var cell2 = row.insertCell(1);
+
+ // Add some text to the new cells:
+ let key_text = this._l10n.formatValueSync(
+ l10n_base + "-" + key.toLowerCase().replace(/_/g, "-")
+ );
+ var key_node = document.createTextNode(key_text);
+ cell1.appendChild(key_node);
+ var raw_value = JSON.stringify(options[key]);
+ var value = raw_value;
+ if (["true", "false", "null"].includes(raw_value)) {
+ value = this._l10n.formatValueSync(l10n_base + "-" + raw_value);
+ }
+ var value_node = document.createTextNode(value);
+ cell2.appendChild(value_node);
+ }
+ },
+
+ show_ui_based_on_authenticator_info(data) {
+ // Hide the "Please plug in a token"-message
+ document.getElementById("info-text-div").hidden = true;
+ // Show options, based on what the token supports
+ if (data.auth_info) {
+ document.getElementById("ctap2-token-info").style.display = "flex";
+ this.show_authenticator_options(
+ data.auth_info.options,
+ "authenticator-options",
+ "about-webauthn-auth-option"
+ );
+ this.show_authenticator_options(
+ data.auth_info,
+ "authenticator-info",
+ "about-webauthn-auth-info"
+ );
+ // Check if token supports PINs
+ if (data.auth_info.options.clientPin != null) {
+ document.getElementById("pin-tab-button").style.display = "flex";
+ if (data.auth_info.options.clientPin === true) {
+ // It has a Pin set
+ document.getElementById("change-pin-button").style.display = "block";
+ document.getElementById("set-pin-button").style.display = "none";
+ document.getElementById("current-pin-div").hidden = false;
+ } else {
+ // It does not have a Pin set yet
+ document.getElementById("change-pin-button").style.display = "none";
+ document.getElementById("set-pin-button").style.display = "block";
+ document.getElementById("current-pin-div").hidden = true;
+ }
+ } else {
+ document.getElementById("pin-tab-button").style.display = "none";
+ }
+
+ if (
+ data.auth_info.options.credMgmt ||
+ data.auth_info.options.credentialMgmtPreview
+ ) {
+ document.getElementById("credentials-tab-button").style.display =
+ "flex";
+ } else {
+ document.getElementById("credentials-tab-button").style.display =
+ "none";
+ }
+
+ if (
+ data.auth_info.options.bioEnroll != null ||
+ data.auth_info.options.userVerificationMgmtPreview != null
+ ) {
+ document.getElementById("bio-enrollments-tab-button").style.display =
+ "flex";
+ } else {
+ document.getElementById("bio-enrollments-tab-button").style.display =
+ "none";
+ }
+ } else {
+ // Currently auth-rs doesn't send this, because it filters out ctap2-devices.
+ // U2F / CTAP1 tokens can't be managed
+ set_info_text("about-webauthn-text-non-ctap2-device");
+ }
+ },
+
+ show_credential_list(credential_list) {
+ // We may have temporarily hidden the tab when asking the user for a PIN
+ // so we have to show it again.
+ fake_click_event_for_id("credentials-tab-button");
+ document.getElementById("credential-list-subsection").hidden = false;
+ let table = document.getElementById("credential-list");
+ var empty_table = document.createElement("table");
+ empty_table.id = "credential-list";
+ table.parentNode.replaceChild(empty_table, table);
+ if (!credential_list.length) {
+ document.getElementById("credential-list-empty-label").hidden = false;
+ return;
+ }
+ document.getElementById("credential-list-empty-label").hidden = true;
+ table = document.getElementById("credential-list");
+ credential_list.forEach(rp => {
+ // Add some text to the new cells:
+ let key_text = rp.rp.id;
+ rp.credentials.forEach(cred => {
+ let value_text = cred.user.name;
+ // Create an empty <tr> element and add it to the 1st position of the table:
+ var row = table.insertRow(0);
+ var key_node = document.createTextNode(key_text);
+ var value_node = document.createTextNode(value_text);
+ row.insertCell(0).appendChild(key_node);
+ row.insertCell(1).appendChild(value_node);
+ var delete_button = document.createElement("button");
+ delete_button.classList.add("delete-button");
+ delete_button.classList.add("credentials-button");
+ let garbage_icon = document.createElement("img");
+ garbage_icon.setAttribute(
+ "src",
+ "chrome://global/skin/icons/delete.svg"
+ );
+ garbage_icon.classList.add("delete-icon");
+ delete_button.appendChild(garbage_icon);
+ let delete_text = document.createElement("span");
+ delete_text.setAttribute(
+ "data-l10n-id",
+ "about-webauthn-delete-button"
+ );
+ delete_button.appendChild(delete_text);
+ delete_button.addEventListener("click", function () {
+ let context = document.getElementById("confirmation-context");
+ context.textContent = key_text + " - " + value_text;
+ credential_management_in_progress(true);
+ let cmd = {
+ CredentialManagement: { DeleteCredential: cred.credential_id },
+ };
+ context.setAttribute("data-ctap-command", JSON.stringify(cmd));
+ open_delete_confirmation_tab();
+ });
+ row.insertCell(2).appendChild(delete_button);
+ });
+ });
+ },
+
+ show_enrollment_list(enrollment_list) {
+ // We may have temporarily hidden the tab when asking the user for a PIN
+ // so we have to show it again.
+ fake_click_event_for_id("bio-enrollments-tab-button");
+ document.getElementById("bio-enrollment-list-subsection").hidden = false;
+ let table = document.getElementById("bio-enrollment-list");
+ var empty_table = document.createElement("table");
+ empty_table.id = "bio-enrollment-list";
+ table.parentNode.replaceChild(empty_table, table);
+ if (!enrollment_list.length) {
+ document.getElementById("bio-enrollment-list-empty-label").hidden = false;
+ return;
+ }
+ document.getElementById("bio-enrollment-list-empty-label").hidden = true;
+ table = document.getElementById("bio-enrollment-list");
+ enrollment_list.forEach(enrollment => {
+ let key_text = enrollment.template_friendly_name ?? "<unnamed>";
+ var row = table.insertRow(0);
+ var key_node = document.createTextNode(key_text);
+ row.insertCell(0).appendChild(key_node);
+ var delete_button = document.createElement("button");
+ delete_button.classList.add("delete-button");
+ delete_button.classList.add("bio-enrollment-button");
+ let garbage_icon = document.createElement("img");
+ garbage_icon.setAttribute("src", "chrome://global/skin/icons/delete.svg");
+ garbage_icon.classList.add("delete-icon");
+ delete_button.appendChild(garbage_icon);
+ let delete_text = document.createElement("span");
+ delete_text.setAttribute("data-l10n-id", "about-webauthn-delete-button");
+ delete_button.appendChild(delete_text);
+ delete_button.addEventListener("click", function () {
+ let context = document.getElementById("confirmation-context");
+ context.textContent = key_text;
+ bio_enrollment_in_progress(true);
+ let cmd = {
+ BioEnrollment: { DeleteEnrollment: enrollment.template_id },
+ };
+ context.setAttribute("data-ctap-command", JSON.stringify(cmd));
+ open_delete_confirmation_tab();
+ });
+ row.insertCell(1).appendChild(delete_button);
+ });
+ },
+};
+
+function set_info_text(l10nId) {
+ document.getElementById("info-text-div").hidden = false;
+ let field = document.getElementById("info-text-field");
+ field.setAttribute("data-l10n-id", l10nId);
+ document.getElementById("ctap2-token-info").style.display = "none";
+}
+
+function show_results_banner(result, l10n, l10n_args) {
+ let ctap_result = document.getElementById("ctap-listen-result");
+ ctap_result.setAttribute("data-l10n-id", l10n);
+ ctap_result.classList.add(result);
+ if (l10n_args) {
+ ctap_result.setAttribute("data-l10n-args", l10n_args);
+ }
+}
+
+function hide_results_banner() {
+ let res_banner = document.getElementById("ctap-listen-result");
+ let res_div = document.getElementById("ctap-listen-div");
+ let empty_banner = document.createElement("label");
+ empty_banner.id = "ctap-listen-result";
+ res_div.replaceChild(empty_banner, res_banner);
+}
+
+function operation_in_progress(name, in_progress) {
+ let buttons = Array.from(document.getElementsByClassName(name));
+ buttons.forEach(button => {
+ button.disabled = in_progress;
+ });
+}
+
+function credential_management_in_progress(in_progress) {
+ operation_in_progress("credentials-button", in_progress);
+}
+
+function bio_enrollment_in_progress(in_progress) {
+ operation_in_progress("bio-enrollment-button", in_progress);
+}
+
+function clear_bio_enrollment_samples() {
+ // Remove all previous status updates
+ let up = document.getElementById("enrollment-update");
+ while (up.firstChild) {
+ up.removeChild(up.lastChild);
+ }
+ document.getElementById("enrollment-name").value = "";
+}
+
+function fake_click_event_for_id(id) {
+ // Not using document.getElementById(id).click();
+ // here, because we have to add additional data, so we don't
+ // hide the results-div here, if there is any. 'Normal' clicking
+ // by the user will hide it.
+ const evt = new CustomEvent("click", {
+ detail: { skip_results_clearing: true },
+ });
+ document.getElementById(id).dispatchEvent(evt);
+}
+
+function reset_page() {
+ // Hide everything that needs a device to know if it should be displayed
+ document.getElementById("ctap2-token-info").style.display = "none";
+ Array.from(document.getElementsByClassName("optional-category")).forEach(
+ div => {
+ div.style.display = "none";
+ }
+ );
+
+ // Only display the "please connect a device" - text
+ set_info_text("about-webauthn-text-connect-device");
+
+ // Clear results and input fields
+ hide_results_banner();
+ var divs = Array.from(document.getElementsByTagName("input"));
+ divs.forEach(div => {
+ div.value = "";
+ });
+
+ sidebar_set_disabled(false);
+
+ // ListCredentials
+ credential_management_in_progress(false);
+ document.getElementById("credential-list-subsection").hidden = true;
+
+ // BioEnrollment
+ clear_bio_enrollment_samples();
+ document.getElementById("bio-enrollment-list-subsection").hidden = true;
+ bio_enrollment_in_progress(false);
+
+ AboutWebauthnManagerJS._previous_tab = "";
+ AboutWebauthnManagerJS._current_tab = "";
+
+ // Not using `document.getElementById("info-tab-button").click();`
+ // here, because if we were focused on a category-button that got removed (e.g.
+ // when unplugging the device), we have to reset the ARIA-related attributes
+ // first, before we can click on the button, otherwise the a11y-tests
+ // will complain. So we fake the click again.
+ const evt = {
+ detail: {},
+ currentTarget: document.getElementById("info-tab-button"),
+ };
+ open_info_tab(evt);
+}
+
+function sidebar_set_disabled(disabled) {
+ var cats = Array.from(document.getElementsByClassName("category"));
+ cats.forEach(cat => {
+ if (disabled) {
+ cat.classList.add("disabled-category");
+ } else {
+ cat.classList.remove("disabled-category");
+ }
+ });
+}
+
+function check_pin_repeat_is_correct(button) {
+ let pin = document.getElementById("new-pin");
+ let pin_repeat = document.getElementById("new-pin-repeat");
+ let has_current_pin = !document.getElementById("current-pin-div").hidden;
+ let current_pin = document.getElementById("current-pin");
+ let can_enable_button =
+ pin.value != null && pin.value != "" && pin.value == pin_repeat.value;
+ if (has_current_pin && !current_pin.value) {
+ can_enable_button = false;
+ }
+ if (!can_enable_button) {
+ pin.classList.add("different");
+ pin_repeat.classList.add("different");
+ document.getElementById("set-pin-button").disabled = true;
+ document.getElementById("change-pin-button").disabled = true;
+ return false;
+ }
+ pin.classList.remove("different");
+ pin_repeat.classList.remove("different");
+ document.getElementById("set-pin-button").disabled = false;
+ document.getElementById("change-pin-button").disabled = false;
+ return true;
+}
+
+function send_pin() {
+ close_temporary_overlay_tab();
+ let pin = document.getElementById("pin-required").value;
+ AboutWebauthnService.pinCallback(0, pin);
+}
+
+function set_pin() {
+ let pin = document.getElementById("new-pin").value;
+ let cmd = { SetPIN: pin };
+ AboutWebauthnService.runCommand(JSON.stringify(cmd));
+}
+
+function change_pin() {
+ let curr_pin = document.getElementById("current-pin").value;
+ let new_pin = document.getElementById("new-pin").value;
+ let cmd = { ChangePIN: [curr_pin, new_pin] };
+ AboutWebauthnService.runCommand(JSON.stringify(cmd));
+}
+
+function list_credentials() {
+ credential_management_in_progress(true);
+ let cmd = { CredentialManagement: "GetCredentials" };
+ AboutWebauthnService.runCommand(JSON.stringify(cmd));
+}
+
+function list_bio_enrollments() {
+ bio_enrollment_in_progress(true);
+ let cmd = { BioEnrollment: "GetEnrollments" };
+ AboutWebauthnService.runCommand(JSON.stringify(cmd));
+}
+
+function show_add_bio_enrollment_section() {
+ const evt = new CustomEvent("click", {
+ detail: { temporary_overlay: true },
+ });
+ open_tab(evt, "add-bio-enrollment-section");
+ document.getElementById("enrollment-name").focus();
+}
+
+function start_bio_enrollment() {
+ bio_enrollment_in_progress(true);
+ let name = document.getElementById("enrollment-name").value;
+ if (!name) {
+ name = null; // Empty means "Not set"
+ }
+ let cmd = { BioEnrollment: { StartNewEnrollment: name } };
+ AboutWebauthnService.runCommand(JSON.stringify(cmd));
+}
+
+function cancel_transaction() {
+ credential_management_in_progress(false);
+ bio_enrollment_in_progress(false);
+ AboutWebauthnService.cancel(0);
+}
+
+function confirm_deletion() {
+ let context = document.getElementById("confirmation-context");
+ let cmd = context.getAttribute("data-ctap-command");
+ AboutWebauthnService.runCommand(cmd);
+}
+
+function cancel_confirmation() {
+ credential_management_in_progress(false);
+ bio_enrollment_in_progress(false);
+ close_temporary_overlay_tab();
+}
+
+async function onLoad() {
+ document.getElementById("set-pin-button").addEventListener("click", set_pin);
+ document
+ .getElementById("change-pin-button")
+ .addEventListener("click", change_pin);
+ document
+ .getElementById("list-credentials-button")
+ .addEventListener("click", list_credentials);
+ document
+ .getElementById("list-bio-enrollments-button")
+ .addEventListener("click", list_bio_enrollments);
+ document
+ .getElementById("add-bio-enrollment-button")
+ .addEventListener("click", show_add_bio_enrollment_section);
+ document
+ .getElementById("start-enrollment-button")
+ .addEventListener("click", start_bio_enrollment);
+ document
+ .getElementById("new-pin")
+ .addEventListener("input", check_pin_repeat_is_correct);
+ document
+ .getElementById("new-pin-repeat")
+ .addEventListener("input", check_pin_repeat_is_correct);
+ document
+ .getElementById("current-pin")
+ .addEventListener("input", check_pin_repeat_is_correct);
+ let info_button = document.getElementById("info-tab-button");
+ info_button.addEventListener("click", open_info_tab);
+ info_button.addEventListener("keydown", handle_keydowns);
+ let pin_button = document.getElementById("pin-tab-button");
+ pin_button.addEventListener("click", open_pin_tab);
+ pin_button.addEventListener("keydown", handle_keydowns);
+ let credentials_button = document.getElementById("credentials-tab-button");
+ credentials_button.addEventListener("click", open_credentials_tab);
+ credentials_button.addEventListener("keydown", handle_keydowns);
+ let bio_enrollments_button = document.getElementById(
+ "bio-enrollments-tab-button"
+ );
+ bio_enrollments_button.addEventListener("click", open_bio_enrollments_tab);
+ bio_enrollments_button.addEventListener("keydown", handle_keydowns);
+ document
+ .getElementById("send-pin-button")
+ .addEventListener("click", send_pin);
+ document
+ .getElementById("cancel-send-pin-button")
+ .addEventListener("click", cancel_transaction);
+ document
+ .getElementById("cancel-enrollment-button")
+ .addEventListener("click", cancel_transaction);
+ document
+ .getElementById("cancel-confirmation-button")
+ .addEventListener("click", cancel_confirmation);
+ document
+ .getElementById("confirm-deletion-button")
+ .addEventListener("click", confirm_deletion);
+ AboutWebauthnManagerJS.init();
+ try {
+ AboutWebauthnService.listen();
+ } catch (ex) {
+ set_info_text("about-webauthn-text-not-available");
+ AboutWebauthnManagerJS.uninit();
+ }
+}
+
+function handle_keydowns(event) {
+ let index;
+ let event_was_handled = true;
+ let tabs = Array.from(document.getElementsByClassName("category"));
+ if (tabs.length <= 0) {
+ return;
+ }
+
+ switch (event.key) {
+ case "ArrowLeft":
+ case "ArrowUp":
+ if (event.currentTarget === tabs[0]) {
+ event.currentTarget.focus();
+ } else {
+ index = tabs.indexOf(event.currentTarget);
+ tabs[index - 1].focus();
+ }
+ break;
+
+ case "ArrowRight":
+ case "ArrowDown":
+ if (event.currentTarget === tabs[tabs.length - 1]) {
+ event.currentTarget.focus();
+ } else {
+ index = tabs.indexOf(event.currentTarget);
+ tabs[index + 1].focus();
+ }
+ break;
+
+ case "Home":
+ tabs[0].focus();
+ break;
+
+ case "End":
+ tabs[tabs.length - 1].focus();
+ break;
+
+ case "Enter":
+ case " ":
+ event.currentTarget.click();
+ break;
+
+ default:
+ event_was_handled = false;
+ break;
+ }
+
+ if (event_was_handled) {
+ event.stopPropagation();
+ event.preventDefault();
+ }
+}
+
+function open_tab(evt, tabName) {
+ var tabcontent, tablinks;
+ // Hide all others
+ tabcontent = Array.from(document.getElementsByClassName("tabcontent"));
+ tabcontent.forEach(tab => {
+ tab.style.display = "none";
+ });
+ // Display the one we selected
+ document.getElementById(tabName).style.display = "block";
+
+ // If this is a temporary overlay, like pin-required, we don't
+ // touch the sidebar and which button is selected.
+ if (!evt.detail.temporary_overlay) {
+ tablinks = Array.from(document.getElementsByClassName("category"));
+ tablinks.forEach(tablink => {
+ tablink.removeAttribute("selected");
+ tablink.setAttribute("aria-selected", "false");
+ tablink.setAttribute("tabindex", "-1");
+ tablink.disabled = false;
+ });
+ evt.currentTarget.setAttribute("selected", "true");
+ evt.currentTarget.setAttribute("tabindex", "0");
+ evt.currentTarget.setAttribute("aria-selected", "true");
+ }
+
+ if (!evt.detail.skip_results_clearing) {
+ hide_results_banner();
+ }
+ sidebar_set_disabled(false);
+ AboutWebauthnManagerJS._previous_tab = AboutWebauthnManagerJS._current_tab;
+ AboutWebauthnManagerJS._current_tab = tabName;
+}
+
+function open_info_tab(evt) {
+ open_tab(evt, "token-info-section");
+}
+function open_pin_tab(evt) {
+ open_tab(evt, "set-change-pin-section");
+}
+function open_credentials_tab(evt) {
+ open_tab(evt, "credential-management-section");
+}
+function open_bio_enrollments_tab(evt) {
+ // We can only list, if there are any registered already
+ if (
+ AboutWebauthnManagerJS._curr_data.options.bioEnroll === true ||
+ AboutWebauthnManagerJS._curr_data.options.userVerificationMgmtPreview ===
+ true
+ ) {
+ document.getElementById("list-bio-enrollments-button").style.display =
+ "inline-block";
+ } else {
+ document.getElementById("list-bio-enrollments-button").style.display =
+ "none";
+ }
+ open_tab(evt, "bio-enrollment-section");
+}
+function open_reset_tab(evt) {
+ open_tab(evt, "reset-token-section");
+}
+function open_pin_required_tab() {
+ // Remove any old value we might have had
+ document.getElementById("pin-required").value = "";
+ const evt = new CustomEvent("click", {
+ detail: {
+ temporary_overlay: true,
+ skip_results_clearing: true, // We might be called multiple times, if PIN was invalid
+ },
+ });
+ open_tab(evt, "pin-required-section");
+ document.getElementById("pin-required").focus();
+ // This is a temporary overlay, so we don't want the
+ // user to click away from it, unless via the Cancel-button.
+ sidebar_set_disabled(true);
+}
+function close_temporary_overlay_tab() {
+ const evt = new CustomEvent("click", {
+ detail: { temporary_overlay: true },
+ });
+ open_tab(evt, AboutWebauthnManagerJS._previous_tab);
+ sidebar_set_disabled(false);
+}
+function open_delete_confirmation_tab() {
+ const evt = new CustomEvent("click", {
+ detail: {
+ temporary_overlay: true,
+ },
+ });
+ open_tab(evt, "confirm-deletion-section");
+ // This is a temporary overlay, so we don't want the
+ // user to click away from it, unless via the Cancel-button.
+ sidebar_set_disabled(true);
+}
+
+try {
+ AboutWebauthnService = Cc["@mozilla.org/webauthn/service;1"].getService(
+ Ci.nsIWebAuthnService
+ );
+ document.addEventListener("DOMContentLoaded", onLoad);
+ window.addEventListener("beforeunload", event => {
+ AboutWebauthnManagerJS.uninit();
+ if (AboutWebauthnService) {
+ AboutWebauthnService.cancel(0);
+ }
+ });
+} catch (ex) {
+ // Do nothing if we fail to create a singleton instance,
+ // showing the default no-module message.
+ console.error(ex);
+}