tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

QRCodeGenerator.sys.mjs (4596B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 /**
      6 * QR Code Generator with Firefox logo overlay
      7 * This module generates QR codes with the Firefox logo in the center
      8 * Uses a worker thread for QR generation to avoid blocking the main thread
      9 */
     10 
     11 const lazy = {};
     12 
     13 ChromeUtils.defineLazyGetter(lazy, "logConsole", function () {
     14  return console.createInstance({
     15    prefix: "QRCodeGenerator",
     16    maxLogLevel: Services.prefs.getBoolPref("browser.qrcode.log", false)
     17      ? "Debug"
     18      : "Warn",
     19  });
     20 });
     21 
     22 ChromeUtils.defineESModuleGetters(lazy, {
     23  QRCodeWorker: "moz-src:///browser/components/qrcode/QRCodeWorker.sys.mjs",
     24 });
     25 
     26 export const QRCodeGenerator = {
     27  /**
     28   * Generate a QR code for the given URL with Firefox logo overlay
     29   *
     30   * @param {string} url - The URL to encode
     31   * @param {Document} document - The document to use for creating elements
     32   * @returns {Promise<string>} - Data URI of the QR code with logo
     33   */
     34  async generateQRCode(url, document) {
     35    // Create a fresh worker for this generation
     36    // Worker will be terminated after use to free resources
     37    const worker = new lazy.QRCodeWorker();
     38 
     39    try {
     40      // Generate the base QR code with high error correction to allow for logo overlay
     41      // Use worker thread to avoid blocking main thread
     42      const qrData = await worker.generateQRCode(url, "H");
     43 
     44      // Use a higher resolution for better quality (scale up 4x)
     45      const scale = 4;
     46      const canvas = document.createElementNS(
     47        "http://www.w3.org/1999/xhtml",
     48        "canvas"
     49      );
     50      canvas.width = qrData.width * scale;
     51      canvas.height = qrData.height * scale;
     52      const ctx = canvas.getContext("2d");
     53 
     54      // Disable image smoothing for crisp QR code rendering
     55      ctx.imageSmoothingEnabled = false;
     56 
     57      // Load and draw the base QR code at high resolution
     58      const qrImage = await this._loadImage(document, qrData.src);
     59      ctx.drawImage(qrImage, 0, 0, qrData.width * scale, qrData.height * scale);
     60 
     61      // Calculate logo size and position (center of QR code)
     62      // Use 18% of QR code size (reduced from 25%) to stay within error correction limits
     63      const logoSize = Math.floor(qrData.width * 0.18) * scale;
     64      const centerX = Math.floor((qrData.width * scale) / 2);
     65      const centerY = Math.floor((qrData.height * scale) / 2);
     66 
     67      // Draw circular white background for logo with minimal padding
     68      const padding = 4 * scale;
     69      const radius = (logoSize + padding * 2) / 2;
     70      ctx.fillStyle = "white";
     71      ctx.beginPath();
     72      ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI);
     73      ctx.fill();
     74 
     75      // Load and draw the Firefox logo at high resolution
     76      try {
     77        const logoImage = await this._loadFirefoxLogo(document);
     78        // Re-enable smoothing for the logo to avoid pixelation
     79        ctx.imageSmoothingEnabled = true;
     80        ctx.imageSmoothingQuality = "high";
     81 
     82        // Draw logo centered
     83        const logoX = centerX - logoSize / 2;
     84        const logoY = centerY - logoSize / 2;
     85        ctx.drawImage(logoImage, logoX, logoY, logoSize, logoSize);
     86      } catch (e) {
     87        lazy.logConsole.warn("Could not load Firefox logo for QR code:", e);
     88      }
     89 
     90      // Convert canvas to data URI
     91      return canvas.toDataURL("image/png");
     92    } finally {
     93      // Always terminate the worker to free resources
     94      try {
     95        await worker.terminate();
     96        lazy.logConsole.debug("QRCode worker terminated successfully");
     97      } catch (e) {
     98        lazy.logConsole.warn("Failed to terminate QRCode worker:", e);
     99      }
    100    }
    101  },
    102 
    103  /**
    104   * Load an image from a URL/data URI
    105   *
    106   * @param {Document} document - The document to use for creating the image
    107   * @param {string} src - The image source
    108   * @returns {Promise<HTMLImageElement>}
    109   */
    110  _loadImage(document, src) {
    111    return new Promise((resolve, reject) => {
    112      const img = document.createElementNS(
    113        "http://www.w3.org/1999/xhtml",
    114        "img"
    115      );
    116      img.onload = () => resolve(img);
    117      img.onerror = reject;
    118      img.src = src;
    119    });
    120  },
    121 
    122  /**
    123   * Load the Firefox logo
    124   *
    125   * @param {Document} document - The document to use for creating the image
    126   * @returns {Promise<HTMLImageElement>}
    127   */
    128  async _loadFirefoxLogo(document) {
    129    // Use the Firefox branding logo
    130    return this._loadImage(
    131      document,
    132      "chrome://branding/content/about-logo.svg"
    133    );
    134  },
    135 };