Source code

Revision control

Copy as Markdown

Other Tools

/* 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";
const QRCodeDialog = {
_url: null,
_qrCodeDataURI: null,
/** Initializes the dialog from window arguments. */
init() {
if (window.arguments && window.arguments[0]) {
const params = window.arguments[0];
this._url = params.url;
this._qrCodeDataURI = params.qrCodeDataURI;
}
document.mozSubdialogReady = this.setupDialog();
document
.getElementById("copy-button")
.addEventListener("click", () => this.copyImage());
document
.getElementById("save-button")
.addEventListener("click", () => this.saveImage());
document
.getElementById("close-button")
.addEventListener("click", () => window.close());
},
/**
* Populates the dialog with the QR code image and URL, or shows an error
* if the QR code data URI is missing.
*/
async setupDialog() {
const successContainer = document.getElementById("success-container");
if (!this._qrCodeDataURI) {
this.showFeedback("error", "qrcode-panel-error");
return;
}
const imageElement = document.getElementById("qrcode-image");
imageElement.src = this._qrCodeDataURI;
successContainer.hidden = false;
const urlElement = document.getElementById("qrcode-url");
urlElement.textContent = this._url;
urlElement.title = this._url;
},
/**
* Shows a feedback message bar in the dialog and resizes the dialog to fit.
*
* @param {string} type - The message bar type ("success" or "error").
* @param {string} l10nId - The Fluent localization ID for the message.
*/
async showFeedback(type, l10nId) {
let bar = document.getElementById("feedback-bar");
if (!bar) {
bar = document.createElement("moz-message-bar");
bar.id = "feedback-bar";
bar.setAttribute("role", "alert");
bar.setAttribute("data-l10n-attrs", "message");
bar.setAttribute("dismissable", "");
bar.addEventListener("message-bar:user-dismissed", () => {
requestAnimationFrame(() => window.resizeDialog?.());
});
let content = document.getElementById("qrcode-dialog-content");
content.appendChild(bar);
}
bar.type = type;
document.l10n.setAttributes(bar, l10nId);
await bar.updateComplete;
window.resizeDialog?.();
},
/**
* Strips the data URI prefix from the stored QR code data URI, base64-decodes
* the payload, and returns the raw PNG bytes as a Uint8Array.
*
* @returns {Uint8Array} The decoded PNG image bytes.
* @throws {Error} If the data URI is missing or not a base64-encoded PNG.
*/
decodeDataURI() {
const dataPrefix = "data:image/png;base64,";
if (!this._qrCodeDataURI?.startsWith(dataPrefix)) {
throw new Error("Invalid QR code image data");
}
return Uint8Array.fromBase64(this._qrCodeDataURI.slice(dataPrefix.length));
},
/** Copies the QR code image to the clipboard as a PNG. */
async copyImage() {
try {
const qrCodeBytes = this.decodeDataURI();
const item = new ClipboardItem({
"image/png": new Blob([qrCodeBytes], { type: "image/png" }),
});
await navigator.clipboard.write([item]);
this.showFeedback("success", "qrcode-copy-success");
} catch (error) {
console.error("Failed to copy QR code:", error);
this.showFeedback("error", "qrcode-copy-error");
}
},
/** Opens a file picker and saves the QR code image as a PNG file. */
async saveImage() {
const nsIFilePicker = Ci.nsIFilePicker;
const fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
const [title, pngFilterTitle, defaultFilename] =
await document.l10n.formatValues([
"qrcode-save-title",
"qrcode-save-filter-png",
"qrcode-save-filename",
]);
fp.init(window.browsingContext, title, nsIFilePicker.modeSave);
fp.appendFilter(pngFilterTitle, "*.png");
fp.defaultString = defaultFilename;
fp.defaultExtension = "png";
const result = await new Promise(resolve => fp.open(resolve));
if (
result === nsIFilePicker.returnOK ||
result === nsIFilePicker.returnReplace
) {
try {
const qrCodeBytes = this.decodeDataURI();
await IOUtils.write(fp.file.path, qrCodeBytes);
this.showFeedback("success", "qrcode-save-success");
} catch (error) {
console.error("Failed to save QR code:", error);
this.showFeedback("error", "qrcode-save-error");
}
}
},
};
window.addEventListener("DOMContentLoaded", () => {
QRCodeDialog.init();
});