The officially official Devuan Forum!

You are not logged in.

#1 Re: Freedom Hacks » The Absurdist Comedy » Yesterday 19:28:41

about:support

Name 	Firefox Developer Edition
Version 	154.0a1 	
Update Channel 	aurora
User Agent 	Mozilla/5.0 (X11; Linux x86_64; rv:154.0) Gecko/20100101 Firefox/154.0
Profile Directory 	~/.firefox-dev/qxls80vg.dev-edition-default-1

If User Agent is not set correctly with:

imply_option("MOZ_APP_UA_NAME", "Firefox")

You can set it with override in about:config

general.useragent.override      Mozilla/5.0 (X11; Linux x86_64; rv:154.0) Gecko/20100101 Firefox/154.0	
imply_option("MOZ_APP_PROFILE", "firefox-dev")

MOZ_APP_PROFILE=firefox-dev means User Profile Directory     

$HOME/.firefox-dev

for user settings.

firefox/browser/moz.configure is a secret configuration file.

#2 Re: Freedom Hacks » The Absurdist Comedy » Yesterday 18:27:56

does the firefox develper auto-update?

Yes, it does (by default). You can disable/enable this feature is about:config, or disable and lock in a secret config.

$ cat /etc/firefox-dev/defaults/pref/firefox-dev.cfg3-2.js
// Debian system-wide preferences for Firefox Developer Edition
// This file is intended for system administrators to configure default settings.
// Use 'pref("preference.name", value);' for defaults.
// Use 'pref("preference.name", value, locked);' to enforce immutable settings.

// Enable Remote Improvements - Nimbus rollouts system
pref("nimbus.rollouts.enabled", true);
pref("services.sync.prefs.sync.nimbus.rollouts.enabled", false, locked);

// Enable automatic updates  
pref("app.update.auto", true);  
pref("app.update.enabled", true);
pref("app.update.background.enabled", true);

// Disable Remote Improvements - Nimbus rollouts system
// pref("nimbus.rollouts.enabled", false, locked);
// pref("services.sync.prefs.sync.nimbus.rollouts.enabled", false, locked);

// Disable automatic updates  
// pref("app.update.auto", false, locked);  
// pref("app.update.enabled", false, locked);
// pref("app.update.background.enabled", false, locked); 

#3 Freedom Hacks » The exclusive mode of ALSA » 2026-06-21 01:45:27

igorzwx
Replies: 0
The exclusive mode of ALSA

With plughw:0,0 or default, the audio path is:

Application → ALSA Library → Plugin Layer → Kernel PCM Core → Hardware Driver → Hardware 

The ALSA path with hw:0,0 is:

Application → ALSA Library → Kernel PCM Core → Hardware Driver → Hardware 

When using hw:0,0, the ALSA library bypasses the plugin layer (software format/rate/channel conversion) but still goes through the kernel PCM core for buffer management, period handling, and synchronization before reaching the hardware driver.

Usually, "exclusive mode" refers to exclusive device access control by one applications (this is what the word exclusive is supposed to mean). Such exclusive device access prevents multiple applications from simultaneously using the same hardware resource. In the Linux kernel's technical documentation, "exclusive" has a specific meaning: preventing multiple applications from simultaneously using the same hardware resource.

ALSA permits "exclusive full duplex":

$ arecord -f cd -D hw:0,0 | aplay -V mono -D hw:0,0  
Recording WAVE 'stdin' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
Playing WAVE 'stdin' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
#####################   +                          | 46%

$ arecord -f cd -D hw:1,0 | aplay -V mono -D hw:1,0 
Recording WAVE 'stdin' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
Playing WAVE 'stdin' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
##################################################+| MAX

$ arecord -f cd -D hw:PCH,0 | aplay -V mono -D hw:PCH,0 
Recording WAVE 'stdin' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
Playing WAVE 'stdin' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
####################                             + | 97% 

arecord opens hw:0,0 for capture
aplay opens hw:0,0 for playback
Both processes have the the same device open simultaneously in "exclusive mode".

The active meter indicates that the full duplex works and audio data is actively flowing through the pipeline in real-time.

Thus, two applications, arecord and aplay, have exclusive access to the same device simultaneously. It works with Intel HDA codecs, USB soundcards, etc.

OSS4 with software mixer vmix disabled does not support "exclusive full duplex" for Intel HDA codecs

$ ./fulldup

Using audio engine 0=HD Audio play front for duplex

/dev/dsp doesn't support one device based full duplex scheme
Please use the two device scheme.

$ file /dev/dsp
/dev/dsp: symbolic link to /dev/oss/oss_hdaudio0/pcm0 

ALSA is more advanced in this sense. In advanced Monty Python reality, the word exclusive has a flexible meaning. It may mean anything you want.

#4 Re: Freedom Hacks » The Absurdist Comedy » 2026-06-19 16:54:19

Patch 1 & 2
Sourcedir: firefox
FILE: firefox/browser/moz.configure

 imply_option("MOZ_DEVTOOLS", "all")
 imply_option("BROWSER_CHROME_URL", "chrome://browser/content/browser.xhtml")
+imply_option("MOZ_APP_UA_NAME", "Firefox")
+imply_option("MOZ_APP_PROFILE", "firefox-dev") 

You should simply add two lines:

 imply_option("MOZ_APP_UA_NAME", "Firefox")
 imply_option("MOZ_APP_PROFILE", "firefox-dev")

after other "imply_option" lines.

$ cat firefox/browser/moz.configure 
# 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/.

imply_option("MOZ_PLACES", True)
imply_option("MOZ_SERVICES_HEALTHREPORT", True)
imply_option("MOZ_SERVICES_SYNC", True)
imply_option("MOZ_DEDICATED_PROFILES", True)
imply_option("MOZ_BLOCK_PROFILE_DOWNGRADE", True)
imply_option("MOZ_NORMANDY", True)
imply_option("MOZ_PROFILE_MIGRATOR", True)

imply_option("MOZ_APP_VENDOR", "Mozilla")
imply_option("MOZ_APP_ID", "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}")
# Include the DevTools client, not just the server (which is the default)
imply_option("MOZ_DEVTOOLS", "all")
imply_option("BROWSER_CHROME_URL", "chrome://browser/content/browser.xhtml")
imply_option("MOZ_APP_UA_NAME", "Firefox")
imply_option("MOZ_APP_PROFILE", "firefox-dev")

...

It works with Zoom.

#5 Re: Packaging for Devuan » Packaging the fftrate ALSA plugin » 2026-06-19 10:55:28

Package Details: libasound2-plugin-fftrate 1.6.3-1
_https://aur.archlinux.org/packages/libasound2-plugin-fftrate

The Arch Linux patch for fftrate (gcc-12+):

prepare() {
	cd "$srcdir/$pkgbase-$pkgver"
	find . -type f \( -name "*.cpp" -o -name "*.c" -o -name "*.h" \) -exec sed -i -e 's/min(/MIN(/g' -e 's/max(/MAX(/g' {} +
}  

It does fix the problem, but creates a new one: a sort of inconsistency.

For example:

The upstream: PetrovSE fftrate/src/lib/mathmac.h

//================================================================

//  Min & max computing

//----------------------------------------------------------------

#ifndef min

#define min( _x, _y )					( (_x) < (_y) ? (_x) : (_y) )

#endif

#ifndef max

#define max( _x, _y )					( (_x) > (_y) ? (_x) : (_y) )

#endif

Patched file: src/lib/mathmac.h

//================================================================

//  Min & max computing

//----------------------------------------------------------------

#ifndef min  
#define MIN( _x, _y )					( (_x) < (_y) ? (_x) : (_y) )  
#endif  
  
#ifndef max  
#define MAX( _x, _y )					( (_x) > (_y) ? (_x) : (_y) )  
#endif

It should be:

//================================================================
//  Min & max computing
//----------------------------------------------------------------
#ifndef MIN
#define MIN( _x, _y )					( (_x) < (_y) ? (_x) : (_y) )
#endif

#ifndef MAX
#define MAX( _x, _y )					( (_x) > (_y) ? (_x) : (_y) )
#endif

Notice that matlab files should not be patched,
only -name "*.cpp" -o -name "*.c" -o -name "*.h"

#6 Freedom Hacks » Do ALSA users really need fftrate? » 2026-06-18 18:58:54

igorzwx
Replies: 0

Bypassing the hardware resampler in Intel HDA codecs with a software real-time fftrate codec to resample everything to 192kHz 32-bit is a solid architectural strategy. It works at the userspace level on top of ALSA and avoids potential quality issues in codec hardware SRC implementations.

HDA Codec Resampling in the Kernel

The Linux kernel's HDA subsystem does have hardware sample rate converter (SRC) capabilities managed by codec drivers. For example, the Creative CA0132 driver documentation shows that HDA codecs have dedicated SRC ports (0x80–0xbf) for sample rate conversion within the codec's internal routing.

Kernel-Level Format Handling

The kernel handles PCM format conversion through the snd_hdac_stream_format() function, which converts channel count, sample format, and sample rate into the HDA format value used by the hardware.

Hardware-Specific Workarounds

Some codecs have hardware-specific sample rate limitations that require kernel-level workarounds. For instance, the ALC269 driver has a fixup for the Lenovo Ideapad that forces analog I/O to 44.1kHz due to a hardware problem.

The fftrate project

The software resampling approach of the fftrate project bypasses these kernel-level hardware resamplers entirely by:
1.  Accepting audio at any rate from the application.
2.  Resampling to 192kHz 32-bit in userspace.
3.  Sending the already-resampled audio to ALSA at a fixed rate.

This avoids potential quality issues or bugs in the codec's hardware SRC implementation. The kernel ALSA driver simply sees a constant 192kHz 32-bit stream and passes it through without additional resampling.

Dialectical conclusion

There is a belief that fftrate may cure deafness, though it hasn't been verified by scientific methods. Give it a chance. It may (or may not) work.

NOTE: The claim that fftrate can "cure deafness" is a metaphorical exaggeration common in audiophile circles, not a medical fact. There is no scientific evidence that software resampling can restore hearing loss or cure physiological deafness.

#7 Re: Freedom Hacks » The Absurdist Comedy » 2026-06-13 22:17:16

Why should they accept a patch that has not been tested by ALSA users?

#8 Re: Freedom Hacks » The Absurdist Comedy » 2026-06-13 19:51:57

Updated the guide on building Firefox with the ALSA backend patch:
"Firefox: Dialectics of Antagonistic Security Bugs"
_https://dev1galaxy.org/viewtopic.php?id=8012

NOTE: The Debian package is created using the make_deb.sh script.

If the build is successful, you can test it with Zoom.

#9 Re: Freedom Hacks » The Absurdist Comedy » 2026-06-13 11:41:09

Guide on building Firefox:
_https://dev1galaxy.org/viewtopic.php?id=8012

Since applying patches can be confusing, simply replace the existing file with the new one:

1. Locate the original file in your Firefox source code:
firefox/media/libcubeb/src/cubeb_alsa.c
2. Delete or backup the old file.
3. Copy the new cubeb_alsa.c file (the fully functional version) into the same directory.
4. Proceed with the build process as usual.
This avoids the need for patch commands entirely.

Firefox/cubeb ALSA backend
firefox/media/libcubeb/src/cubeb_alsa.c

/*
 * Copyright © 2011 Mozilla Foundation
 *
 * This program is made available under an ISC-style license.  See the
 * accompanying file LICENSE for details.
 */
#undef NDEBUG
#define _DEFAULT_SOURCE
#define _BSD_SOURCE
#if defined(__NetBSD__)
#define _NETBSD_SOURCE /* timersub() */
#endif
#define _XOPEN_SOURCE 500
#include "cubeb-internal.h"
#include "cubeb/cubeb.h"
#include "cubeb_tracing.h"
#include <alsa/asoundlib.h>
#include <assert.h>
#include <dlfcn.h>
#include <limits.h>
#include <poll.h>
#include <pthread.h>
#include <sys/time.h>
#include <unistd.h>

#ifdef DISABLE_LIBASOUND_DLOPEN
#define WRAP(x) x
#else
#define WRAP(x) (*cubeb_##x)
#define LIBASOUND_API_VISIT(X)                                                 \
  X(snd_config)                                                                \
  X(snd_config_add)                                                            \
  X(snd_config_copy)                                                           \
  X(snd_config_delete)                                                         \
  X(snd_config_get_id)                                                         \
  X(snd_config_get_string)                                                     \
  X(snd_config_imake_integer)                                                  \
  X(snd_config_search)                                                         \
  X(snd_config_search_definition)                                              \
  X(snd_lib_error_set_handler)                                                 \
  X(snd_pcm_avail_update)                                                      \
  X(snd_pcm_close)                                                             \
  X(snd_pcm_delay)                                                             \
  X(snd_pcm_drain)                                                             \
  X(snd_pcm_frames_to_bytes)                                                   \
  X(snd_pcm_get_params)                                                        \
  X(snd_pcm_hw_params_any)                                                     \
  X(snd_pcm_hw_params_get_channels_max)                                        \
  X(snd_pcm_hw_params_get_rate)                                                \
  X(snd_pcm_hw_params_set_rate_near)                                           \
  X(snd_pcm_hw_params_sizeof)                                                  \
  X(snd_pcm_nonblock)                                                          \
  X(snd_pcm_open)                                                              \
  X(snd_pcm_open_lconf)                                                        \
  X(snd_pcm_pause)                                                             \
  X(snd_pcm_poll_descriptors)                                                  \
  X(snd_pcm_poll_descriptors_count)                                            \
  X(snd_pcm_poll_descriptors_revents)                                          \
  X(snd_pcm_readi)                                                             \
  X(snd_pcm_recover)                                                           \
  X(snd_pcm_set_params)                                                        \
  X(snd_pcm_start)                                                             \
  X(snd_pcm_state)                                                             \
  X(snd_pcm_writei)

#define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x;
LIBASOUND_API_VISIT(MAKE_TYPEDEF);
#undef MAKE_TYPEDEF
/* snd_pcm_hw_params_alloca is actually a macro */
#define snd_pcm_hw_params_sizeof cubeb_snd_pcm_hw_params_sizeof
#endif

#define CUBEB_STREAM_MAX 16
#define CUBEB_WATCHDOG_MS 10000

#define CUBEB_ALSA_PCM_NAME "default"

#define ALSA_PA_PLUGIN "ALSA <-> PulseAudio PCM I/O Plugin"

/* ALSA is not thread-safe.  snd_pcm_t instances are individually protected
   by the owning cubeb_stream's mutex.  snd_pcm_t creation and destruction
   is not thread-safe until ALSA 1.0.24 (see alsa-lib.git commit 91c9c8f1),
   so those calls must be wrapped in the following mutex. */
static pthread_mutex_t cubeb_alsa_mutex = PTHREAD_MUTEX_INITIALIZER;
static int cubeb_alsa_error_handler_set = 0;

static struct cubeb_ops const alsa_ops;

struct cubeb {
  struct cubeb_ops const * ops;
  void * libasound;

  pthread_t thread;

  /* Mutex for streams array, must not be held while blocked in poll(2). */
  pthread_mutex_t mutex;

  /* Sparse array of streams managed by this context. */
  cubeb_stream * streams[CUBEB_STREAM_MAX];

  /* fds and nfds are only updated by alsa_run when rebuild is set. */
  struct pollfd * fds;
  nfds_t nfds;
  int rebuild;

  int shutdown;

  /* Control pipe for forcing poll to wake and rebuild fds or recalculate the
   * timeout. */
  int control_fd_read;
  int control_fd_write;

  /* Track number of active streams.  This is limited to CUBEB_STREAM_MAX
     due to resource contraints. */
  unsigned int active_streams;

  /* Local configuration with handle_underrun workaround set for PulseAudio
     ALSA plugin.  Will be NULL if the PA ALSA plugin is not in use or the
     workaround is not required. */
  snd_config_t * local_config;
  int is_pa;
};

enum stream_state { INACTIVE, RUNNING, DRAINING, PROCESSING, ERROR };

struct cubeb_stream {
  /* Note: Must match cubeb_stream layout in cubeb.c. */
  cubeb * context;
  void * user_ptr;
  /**/
  pthread_mutex_t mutex;
  snd_pcm_t * pcm;
  cubeb_data_callback data_callback;
  cubeb_state_callback state_callback;
  snd_pcm_uframes_t stream_position;
  snd_pcm_uframes_t last_position;
  snd_pcm_uframes_t buffer_size;
  cubeb_stream_params params;

  /* Every member after this comment is protected by the owning context's
     mutex rather than the stream's mutex, or is only used on the context's
     run thread. */
  pthread_cond_t cond; /* Signaled when the stream's state is changed. */

  enum stream_state state;

  struct pollfd * saved_fds; /* A copy of the pollfds passed in at init time. */
  struct pollfd *
      fds; /* Pointer to this waitable's pollfds within struct cubeb's fds. */
  nfds_t nfds;

  struct timeval drain_timeout;

  /* XXX: Horrible hack -- if an active stream has been idle for
     CUBEB_WATCHDOG_MS it will be disabled and the error callback will be
     called.  This works around a bug seen with older versions of ALSA and
     PulseAudio where streams would stop requesting new data despite still
     being logically active and playing. */
  struct timeval last_activity;
  float volume;

  char * buffer;
  snd_pcm_uframes_t bufframes;
  snd_pcm_stream_t stream_type;

  struct cubeb_stream * other_stream;
};

static int
any_revents(struct pollfd * fds, nfds_t nfds)
{
  nfds_t i;

  for (i = 0; i < nfds; ++i) {
    if (fds[i].revents) {
      return 1;
    }
  }

  return 0;
}

static int
cmp_timeval(struct timeval * a, struct timeval * b)
{
  if (a->tv_sec == b->tv_sec) {
    if (a->tv_usec == b->tv_usec) {
      return 0;
    }
    return a->tv_usec > b->tv_usec ? 1 : -1;
  }
  return a->tv_sec > b->tv_sec ? 1 : -1;
}

static int
timeval_to_relative_ms(struct timeval * tv)
{
  struct timeval now;
  struct timeval dt;
  long long t;
  int r;

  gettimeofday(&now, NULL);
  r = cmp_timeval(tv, &now);
  if (r >= 0) {
    timersub(tv, &now, &dt);
  } else {
    timersub(&now, tv, &dt);
  }
  t = dt.tv_sec;
  t *= 1000;
  t += (dt.tv_usec + 500) / 1000;

  if (t > INT_MAX) {
    t = INT_MAX;
  } else if (t < INT_MIN) {
    t = INT_MIN;
  }

  return r >= 0 ? t : -t;
}

static int
ms_until(struct timeval * tv)
{
  return timeval_to_relative_ms(tv);
}

static int
ms_since(struct timeval * tv)
{
  return -timeval_to_relative_ms(tv);
}

static void
rebuild(cubeb * ctx)
{
  nfds_t nfds;
  int i;
  nfds_t j;
  cubeb_stream * stm;

  assert(ctx->rebuild);

  /* Always count context's control pipe fd. */
  nfds = 1;
  for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
    stm = ctx->streams[i];
    if (stm) {
      stm->fds = NULL;
      if (stm->state == RUNNING) {
        nfds += stm->nfds;
      }
    }
  }

  free(ctx->fds);
  ctx->fds = calloc(nfds, sizeof(struct pollfd));
  assert(ctx->fds);
  ctx->nfds = nfds;

  /* Include context's control pipe fd. */
  ctx->fds[0].fd = ctx->control_fd_read;
  ctx->fds[0].events = POLLIN | POLLERR;

  for (i = 0, j = 1; i < CUBEB_STREAM_MAX; ++i) {
    stm = ctx->streams[i];
    if (stm && stm->state == RUNNING) {
      memcpy(&ctx->fds[j], stm->saved_fds, stm->nfds * sizeof(struct pollfd));
      stm->fds = &ctx->fds[j];
      j += stm->nfds;
    }
  }

  ctx->rebuild = 0;
}

static void
poll_wake(cubeb * ctx)
{
  if (write(ctx->control_fd_write, "x", 1) < 0) {
    /* ignore write error */
  }
}

static void
set_timeout(struct timeval * timeout, unsigned int ms)
{
  gettimeofday(timeout, NULL);
  timeout->tv_sec += ms / 1000;
  timeout->tv_usec += (ms % 1000) * 1000;
}

static void
stream_buffer_decrement(cubeb_stream * stm, long count)
{
  if (count < 0 || (snd_pcm_uframes_t)count > stm->bufframes) {
    count = stm->bufframes;
  }
  char * bufremains =
      stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, count);
  memmove(stm->buffer, bufremains,
          WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes - count));
  stm->bufframes -= count;
}

static void
alsa_set_stream_state(cubeb_stream * stm, enum stream_state state)
{
  cubeb * ctx;
  int r;

  ctx = stm->context;
  stm->state = state;
  r = pthread_cond_broadcast(&stm->cond);
  assert(r == 0);
  ctx->rebuild = 1;
  poll_wake(ctx);
}

static enum stream_state
alsa_process_stream(cubeb_stream * stm)
{
  unsigned short revents;
  snd_pcm_sframes_t avail;
  int draining;

  draining = 0;

  pthread_mutex_lock(&stm->mutex);

  /* Call _poll_descriptors_revents() even if we don't use it
     to let underlying plugins clear null events.  Otherwise poll()
     may wake up again and again, producing unnecessary CPU usage. */
  WRAP(snd_pcm_poll_descriptors_revents)
  (stm->pcm, stm->fds, stm->nfds, &revents);

  avail = WRAP(snd_pcm_avail_update)(stm->pcm);

  /* Got null event? Bail and wait for another wakeup. */
  if (avail == 0) {
    pthread_mutex_unlock(&stm->mutex);
    return RUNNING;
  }

  /* This could happen if we were suspended with SIGSTOP/Ctrl+Z for a long time.
   */
  if ((unsigned int)avail > stm->buffer_size) {
    avail = stm->buffer_size;
  }

  /* Capture: Read available frames */
  if (stm->stream_type == SND_PCM_STREAM_CAPTURE && avail > 0) {
    snd_pcm_sframes_t got;

    if (avail + stm->bufframes > stm->buffer_size) {
      /* Buffer overflow. Skip and overwrite with new data. */
      stm->bufframes = 0;
      // TODO: should it be marked as DRAINING?
    }

    got = WRAP(snd_pcm_readi)(stm->pcm, stm->buffer + stm->bufframes, avail);

    if (got < 0) {
      avail = got; // the error handler below will recover us
    } else {
      stm->bufframes += got;
      stm->stream_position += got;

      gettimeofday(&stm->last_activity, NULL);
    }
  }

  /* Capture: Pass read frames to callback function */
  if (stm->stream_type == SND_PCM_STREAM_CAPTURE && stm->bufframes > 0 &&
      (!stm->other_stream ||
       stm->other_stream->bufframes < stm->other_stream->buffer_size)) {
    snd_pcm_sframes_t wrote = stm->bufframes;
    struct cubeb_stream * mainstm = stm->other_stream ? stm->other_stream : stm;
    void * other_buffer = stm->other_stream ? stm->other_stream->buffer +
                                                  stm->other_stream->bufframes
                                            : NULL;

    /* Correct write size to the other stream available space */
    if (stm->other_stream &&
        wrote > (snd_pcm_sframes_t)(stm->other_stream->buffer_size -
                                    stm->other_stream->bufframes)) {
      wrote = stm->other_stream->buffer_size - stm->other_stream->bufframes;
    }

    pthread_mutex_unlock(&stm->mutex);
    wrote = stm->data_callback(mainstm, stm->user_ptr, stm->buffer,
                               other_buffer, wrote);
    pthread_mutex_lock(&stm->mutex);

    if (wrote < 0) {
      avail = wrote; // the error handler below will recover us
    } else {
      stream_buffer_decrement(stm, wrote);

      if (stm->other_stream) {
        stm->other_stream->bufframes += wrote;
      }
    }
  }

  /* Playback: Don't have enough data? Let's ask for more. */
  if (stm->stream_type == SND_PCM_STREAM_PLAYBACK &&
      avail > (snd_pcm_sframes_t)stm->bufframes &&
      (!stm->other_stream || stm->other_stream->bufframes > 0)) {
    long got = avail - stm->bufframes;
    void * other_buffer = stm->other_stream ? stm->other_stream->buffer : NULL;
    char * buftail =
        stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes);

    /* Correct read size to the other stream available frames */
    if (stm->other_stream &&
        got > (snd_pcm_sframes_t)stm->other_stream->bufframes) {
      got = stm->other_stream->bufframes;
    }

    pthread_mutex_unlock(&stm->mutex);
    got = stm->data_callback(stm, stm->user_ptr, other_buffer, buftail, got);
    pthread_mutex_lock(&stm->mutex);

    if (got < 0) {
      avail = got; // the error handler below will recover us
    } else {
      stm->bufframes += got;

      if (stm->other_stream) {
        stream_buffer_decrement(stm->other_stream, got);
      }
    }
  }

  /* Playback: Still don't have enough data? Add some silence. */
  if (stm->stream_type == SND_PCM_STREAM_PLAYBACK &&
      avail > (snd_pcm_sframes_t)stm->bufframes) {
    long drain_frames = avail - stm->bufframes;
    double drain_time = (double)drain_frames / stm->params.rate;

    char * buftail =
        stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes);
    memset(buftail, 0, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, drain_frames));
    stm->bufframes = avail;

    /* Mark as draining, unless we're waiting for capture */
    if (!stm->other_stream || stm->other_stream->bufframes > 0) {
      set_timeout(&stm->drain_timeout, drain_time * 1000);

      draining = 1;
    }
  }

  /* Playback: Have enough data and no errors. Let's write it out. */
  if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && avail > 0) {
    snd_pcm_sframes_t wrote;

    if (stm->params.format == CUBEB_SAMPLE_FLOAT32NE) {
      float * b = (float *)stm->buffer;
      for (uint32_t i = 0; i < avail * stm->params.channels; i++) {
        b[i] *= stm->volume;
      }
    } else {
      short * b = (short *)stm->buffer;
      for (uint32_t i = 0; i < avail * stm->params.channels; i++) {
        b[i] *= stm->volume;
      }
    }

    wrote = WRAP(snd_pcm_writei)(stm->pcm, stm->buffer, avail);
    if (wrote < 0) {
      avail = wrote; // the error handler below will recover us
    } else {
      stream_buffer_decrement(stm, wrote);

      stm->stream_position += wrote;
      gettimeofday(&stm->last_activity, NULL);
    }
  }

  /* Got some error? Let's try to recover the stream. */
  if (avail < 0) {
    avail = WRAP(snd_pcm_recover)(stm->pcm, avail, 0);

    /* Capture pcm must be started after initial setup/recover */
    if (avail >= 0 && stm->stream_type == SND_PCM_STREAM_CAPTURE &&
        WRAP(snd_pcm_state)(stm->pcm) == SND_PCM_STATE_PREPARED) {
      avail = WRAP(snd_pcm_start)(stm->pcm);
    }
  }

  /* Failed to recover, this stream must be broken. */
  if (avail < 0) {
    pthread_mutex_unlock(&stm->mutex);
    stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
    return ERROR;
  }

  pthread_mutex_unlock(&stm->mutex);
  return draining ? DRAINING : RUNNING;
}

static int
alsa_run(cubeb * ctx)
{
  int r;
  int timeout;
  int i;
  char dummy;
  cubeb_stream * stm;
  enum stream_state state;

  pthread_mutex_lock(&ctx->mutex);

  if (ctx->rebuild) {
    rebuild(ctx);
  }

  /* Wake up at least once per second for the watchdog. */
  timeout = 1000;
  for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
    stm = ctx->streams[i];
    if (stm && stm->state == DRAINING) {
      r = ms_until(&stm->drain_timeout);
      if (r >= 0 && timeout > r) {
        timeout = r;
      }
    }
  }

  pthread_mutex_unlock(&ctx->mutex);
  r = poll(ctx->fds, ctx->nfds, timeout);
  pthread_mutex_lock(&ctx->mutex);

  if (r > 0) {
    if (ctx->fds[0].revents & POLLIN) {
      if (read(ctx->control_fd_read, &dummy, 1) < 0) {
        /* ignore read error */
      }

      if (ctx->shutdown) {
        pthread_mutex_unlock(&ctx->mutex);
        return -1;
      }
    }

    for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
      stm = ctx->streams[i];
      /* We can't use snd_pcm_poll_descriptors_revents here because of
         https://github.com/kinetiknz/cubeb/issues/135. */
      if (stm && stm->state == RUNNING && stm->fds &&
          any_revents(stm->fds, stm->nfds)) {
        alsa_set_stream_state(stm, PROCESSING);
        pthread_mutex_unlock(&ctx->mutex);
        state = alsa_process_stream(stm);
        pthread_mutex_lock(&ctx->mutex);
        alsa_set_stream_state(stm, state);
      }
    }
  } else if (r == 0) {
    for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
      stm = ctx->streams[i];
      if (stm) {
        if (stm->state == DRAINING && ms_since(&stm->drain_timeout) >= 0) {
          alsa_set_stream_state(stm, INACTIVE);
          stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
        } else if (stm->state == RUNNING &&
                   ms_since(&stm->last_activity) > CUBEB_WATCHDOG_MS) {
          alsa_set_stream_state(stm, ERROR);
          stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
        }
      }
    }
  }

  pthread_mutex_unlock(&ctx->mutex);

  return 0;
}

static void *
alsa_run_thread(void * context)
{
  cubeb * ctx = context;
  int r;

  CUBEB_REGISTER_THREAD("cubeb rendering thread");

  do {
    r = alsa_run(ctx);
  } while (r >= 0);

  CUBEB_UNREGISTER_THREAD();

  return NULL;
}

static snd_config_t *
get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm)
{
  int r;
  snd_config_t * slave_pcm;
  snd_config_t * slave_def;
  snd_config_t * pcm;
  char const * string;
  char node_name[64];

  slave_def = NULL;

  r = WRAP(snd_config_search)(root_pcm, "slave", &slave_pcm);
  if (r < 0) {
    return NULL;
  }

  r = WRAP(snd_config_get_string)(slave_pcm, &string);
  if (r >= 0) {
    r = WRAP(snd_config_search_definition)(lconf, "pcm_slave", string,
                                           &slave_def);
    if (r < 0) {
      return NULL;
    }
  }

  do {
    r = WRAP(snd_config_search)(slave_def ? slave_def : slave_pcm, "pcm", &pcm);
    if (r < 0) {
      break;
    }

    r = WRAP(snd_config_get_string)(slave_def ? slave_def : slave_pcm, &string);
    if (r < 0) {
      break;
    }

    r = snprintf(node_name, sizeof(node_name), "pcm.%s", string);
    if (r < 0 || r > (int)sizeof(node_name)) {
      break;
    }
    r = WRAP(snd_config_search)(lconf, node_name, &pcm);
    if (r < 0) {
      break;
    }

    return pcm;
  } while (0);

  if (slave_def) {
    WRAP(snd_config_delete)(slave_def);
  }

  return NULL;
}

/* Work around PulseAudio ALSA plugin bug where the PA server forces a
   higher than requested latency, but the plugin does not update its (and
   ALSA's) internal state to reflect that, leading to an immediate underrun
   situation.  Inspired by WINE's make_handle_underrun_config.
   Reference: http://mailman.alsa-project.org/pipermail/alsa-devel/2012-July/05
 */
static snd_config_t *
init_local_config_with_workaround(char const * pcm_name)
{
  int r;
  snd_config_t * lconf;
  snd_config_t * pcm_node;
  snd_config_t * node;
  char const * string;
  char node_name[64];

  lconf = NULL;

  if (WRAP(snd_config) == NULL) {
    return NULL;
  }

  r = WRAP(snd_config_copy)(&lconf, WRAP(snd_config));
  if (r < 0) {
    return NULL;
  }

  do {
    r = WRAP(snd_config_search_definition)(lconf, "pcm", pcm_name, &pcm_node);
    if (r < 0) {
      break;
    }

    r = WRAP(snd_config_get_id)(pcm_node, &string);
    if (r < 0) {
      break;
    }

    r = snprintf(node_name, sizeof(node_name), "pcm.%s", string);
    if (r < 0 || r > (int)sizeof(node_name)) {
      break;
    }
    r = WRAP(snd_config_search)(lconf, node_name, &pcm_node);
    if (r < 0) {
      break;
    }

    /* If this PCM has a slave, walk the slave configurations until we reach the
     * bottom. */
    while ((node = get_slave_pcm_node(lconf, pcm_node)) != NULL) {
      pcm_node = node;
    }

    /* Fetch the PCM node's type, and bail out if it's not the PulseAudio
     * plugin. */
    r = WRAP(snd_config_search)(pcm_node, "type", &node);
    if (r < 0) {
      break;
    }

    r = WRAP(snd_config_get_string)(node, &string);
    if (r < 0) {
      break;
    }

    if (strcmp(string, "pulse") != 0) {
      break;
    }

    /* Don't clobber an explicit existing handle_underrun value, set it only
       if it doesn't already exist. */
    r = WRAP(snd_config_search)(pcm_node, "handle_underrun", &node);
    if (r != -ENOENT) {
      break;
    }

    /* Disable pcm_pulse's asynchronous underrun handling. */
    r = WRAP(snd_config_imake_integer)(&node, "handle_underrun", 0);
    if (r < 0) {
      break;
    }

    r = WRAP(snd_config_add)(pcm_node, node);
    if (r < 0) {
      break;
    }

    return lconf;
  } while (0);

  WRAP(snd_config_delete)(lconf);

  return NULL;
}

static int
alsa_locked_pcm_open(snd_pcm_t ** pcm, char const * pcm_name,
                     snd_pcm_stream_t stream, snd_config_t * local_config)
{
  int r;

  pthread_mutex_lock(&cubeb_alsa_mutex);
  if (local_config) {
    r = WRAP(snd_pcm_open_lconf)(pcm, pcm_name, stream, SND_PCM_NONBLOCK,
                                 local_config);
  } else {
    r = WRAP(snd_pcm_open)(pcm, pcm_name, stream, SND_PCM_NONBLOCK);
  }
  pthread_mutex_unlock(&cubeb_alsa_mutex);

  return r;
}

static int
alsa_locked_pcm_close(snd_pcm_t * pcm)
{
  int r;

  pthread_mutex_lock(&cubeb_alsa_mutex);
  r = WRAP(snd_pcm_close)(pcm);
  pthread_mutex_unlock(&cubeb_alsa_mutex);

  return r;
}

static int
alsa_register_stream(cubeb * ctx, cubeb_stream * stm)
{
  int i;

  pthread_mutex_lock(&ctx->mutex);
  for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
    if (!ctx->streams[i]) {
      ctx->streams[i] = stm;
      break;
    }
  }
  pthread_mutex_unlock(&ctx->mutex);

  return i == CUBEB_STREAM_MAX;
}

static void
alsa_unregister_stream(cubeb_stream * stm)
{
  cubeb * ctx;
  int i;

  ctx = stm->context;

  pthread_mutex_lock(&ctx->mutex);
  for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
    if (ctx->streams[i] == stm) {
      ctx->streams[i] = NULL;
      break;
    }
  }
  pthread_mutex_unlock(&ctx->mutex);
}

static void
silent_error_handler(char const * file, int line, char const * function,
                     int err, char const * fmt, ...)
{
  (void)file;
  (void)line;
  (void)function;
  (void)err;
  (void)fmt;
}

/*static*/ int
alsa_init(cubeb ** context, char const * context_name)
{
  (void)context_name;
  void * libasound = NULL;
  cubeb * ctx;
  int r;
  int i;
  int fd[2];
  pthread_attr_t attr;
  snd_pcm_t * dummy;

  assert(context);
  *context = NULL;

#ifndef DISABLE_LIBASOUND_DLOPEN
  libasound = dlopen("libasound.so.2", RTLD_LAZY);
  if (!libasound) {
    libasound = dlopen("libasound.so", RTLD_LAZY);
    if (!libasound) {
      return CUBEB_ERROR;
    }
  }

#define LOAD(x)                                                                \
  {                                                                            \
    cubeb_##x = dlsym(libasound, #x);                                          \
    if (!cubeb_##x) {                                                          \
      dlclose(libasound);                                                      \
      return CUBEB_ERROR;                                                      \
    }                                                                          \
  }

  LIBASOUND_API_VISIT(LOAD);
#undef LOAD
#endif

  pthread_mutex_lock(&cubeb_alsa_mutex);
  if (!cubeb_alsa_error_handler_set) {
    WRAP(snd_lib_error_set_handler)(silent_error_handler);
    cubeb_alsa_error_handler_set = 1;
  }
  pthread_mutex_unlock(&cubeb_alsa_mutex);

  ctx = calloc(1, sizeof(*ctx));
  assert(ctx);

  ctx->ops = &alsa_ops;
  ctx->libasound = libasound;

  r = pthread_mutex_init(&ctx->mutex, NULL);
  assert(r == 0);

  r = pipe(fd);
  assert(r == 0);

  for (i = 0; i < 2; ++i) {
    fcntl(fd[i], F_SETFD, fcntl(fd[i], F_GETFD) | FD_CLOEXEC);
    fcntl(fd[i], F_SETFL, fcntl(fd[i], F_GETFL) | O_NONBLOCK);
  }

  ctx->control_fd_read = fd[0];
  ctx->control_fd_write = fd[1];

  /* Force an early rebuild when alsa_run is first called to ensure fds and
     nfds have been initialized. */
  ctx->rebuild = 1;

  r = pthread_attr_init(&attr);
  assert(r == 0);

  r = pthread_attr_setstacksize(&attr, 256 * 1024);
  assert(r == 0);

  r = pthread_create(&ctx->thread, &attr, alsa_run_thread, ctx);
  assert(r == 0);

  r = pthread_attr_destroy(&attr);
  assert(r == 0);

  /* Open a dummy PCM to force the configuration space to be evaluated so that
     init_local_config_with_workaround can find and modify the default node. */
  r = alsa_locked_pcm_open(&dummy, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK,
                           NULL);
  if (r >= 0) {
    alsa_locked_pcm_close(dummy);
  }
  ctx->is_pa = 0;
  pthread_mutex_lock(&cubeb_alsa_mutex);
  ctx->local_config = init_local_config_with_workaround(CUBEB_ALSA_PCM_NAME);
  pthread_mutex_unlock(&cubeb_alsa_mutex);
  if (ctx->local_config) {
    ctx->is_pa = 1;
    r = alsa_locked_pcm_open(&dummy, CUBEB_ALSA_PCM_NAME,
                             SND_PCM_STREAM_PLAYBACK, ctx->local_config);
    /* If we got a local_config, we found a PA PCM.  If opening a PCM with that
       config fails with EINVAL, the PA PCM is too old for this workaround. */
    if (r == -EINVAL) {
      pthread_mutex_lock(&cubeb_alsa_mutex);
      WRAP(snd_config_delete)(ctx->local_config);
      pthread_mutex_unlock(&cubeb_alsa_mutex);
      ctx->local_config = NULL;
    } else if (r >= 0) {
      alsa_locked_pcm_close(dummy);
    }
  }

  *context = ctx;

  return CUBEB_OK;
}

static char const *
alsa_get_backend_id(cubeb * ctx)
{
  (void)ctx;
  return "alsa";
}

static void
alsa_destroy(cubeb * ctx)
{
  int r;

  assert(ctx);

  pthread_mutex_lock(&ctx->mutex);
  ctx->shutdown = 1;
  poll_wake(ctx);
  pthread_mutex_unlock(&ctx->mutex);

  r = pthread_join(ctx->thread, NULL);
  assert(r == 0);

  close(ctx->control_fd_read);
  close(ctx->control_fd_write);
  pthread_mutex_destroy(&ctx->mutex);
  free(ctx->fds);

  if (ctx->local_config) {
    pthread_mutex_lock(&cubeb_alsa_mutex);
    WRAP(snd_config_delete)(ctx->local_config);
    pthread_mutex_unlock(&cubeb_alsa_mutex);
  }
#ifndef DISABLE_LIBASOUND_DLOPEN
  if (ctx->libasound) {
    dlclose(ctx->libasound);
  }
#endif
  free(ctx);
}

static void
alsa_stream_destroy(cubeb_stream * stm);

static int
alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream,
                        char const * stream_name, snd_pcm_stream_t stream_type,
                        cubeb_devid deviceid,
                        cubeb_stream_params * stream_params,
                        unsigned int latency_frames,
                        cubeb_data_callback data_callback,
                        cubeb_state_callback state_callback, void * user_ptr)
{
  (void)stream_name;
  cubeb_stream * stm;
  int r;
  snd_pcm_format_t format;
  snd_pcm_uframes_t period_size;
  int latency_us = 0;
  char const * pcm_name =
      deviceid ? (char const *)deviceid : CUBEB_ALSA_PCM_NAME;

  assert(ctx && stream);

  *stream = NULL;

  if (stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
    return CUBEB_ERROR_NOT_SUPPORTED;
  }

  switch (stream_params->format) {
  case CUBEB_SAMPLE_S16LE:
    format = SND_PCM_FORMAT_S16_LE;
    break;
  case CUBEB_SAMPLE_S16BE:
    format = SND_PCM_FORMAT_S16_BE;
    break;
  case CUBEB_SAMPLE_FLOAT32LE:
    format = SND_PCM_FORMAT_FLOAT_LE;
    break;
  case CUBEB_SAMPLE_FLOAT32BE:
    format = SND_PCM_FORMAT_FLOAT_BE;
    break;
  default:
    return CUBEB_ERROR_INVALID_FORMAT;
  }

  pthread_mutex_lock(&ctx->mutex);
  if (ctx->active_streams >= CUBEB_STREAM_MAX) {
    pthread_mutex_unlock(&ctx->mutex);
    return CUBEB_ERROR;
  }
  ctx->active_streams += 1;
  pthread_mutex_unlock(&ctx->mutex);

  stm = calloc(1, sizeof(*stm));
  assert(stm);

  stm->context = ctx;
  stm->data_callback = data_callback;
  stm->state_callback = state_callback;
  stm->user_ptr = user_ptr;
  stm->params = *stream_params;
  stm->state = INACTIVE;
  stm->volume = 1.0;
  stm->buffer = NULL;
  stm->bufframes = 0;
  stm->stream_type = stream_type;
  stm->other_stream = NULL;

  r = pthread_mutex_init(&stm->mutex, NULL);
  assert(r == 0);

  r = pthread_cond_init(&stm->cond, NULL);
  assert(r == 0);

  r = alsa_locked_pcm_open(&stm->pcm, pcm_name, stm->stream_type,
                           ctx->local_config);
  if (r < 0) {
    alsa_stream_destroy(stm);
    return CUBEB_ERROR;
  }

  r = WRAP(snd_pcm_nonblock)(stm->pcm, 1);
  assert(r == 0);

  latency_us = latency_frames * 1e6 / stm->params.rate;

  /* Ugly hack: the PA ALSA plugin allows buffer configurations that can't
     possibly work.  See https://bugzilla.mozilla.org/show_bug.cgi?id=761274.
     Only resort to this hack if the handle_underrun workaround failed. */
  if (!ctx->local_config && ctx->is_pa) {
    const int min_latency = 5e5;
    latency_us = latency_us < min_latency ? min_latency : latency_us;
  }

  r = WRAP(snd_pcm_set_params)(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED,
                               stm->params.channels, stm->params.rate, 1,
                               latency_us);
  if (r < 0) {
    alsa_stream_destroy(stm);
    return CUBEB_ERROR_INVALID_FORMAT;
  }

  r = WRAP(snd_pcm_get_params)(stm->pcm, &stm->buffer_size, &period_size);
  assert(r == 0);

  /* Double internal buffer size to have enough space when waiting for the other
   * side of duplex connection */
  stm->buffer_size *= 2;
  stm->buffer =
      calloc(1, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->buffer_size));
  assert(stm->buffer);

  stm->nfds = WRAP(snd_pcm_poll_descriptors_count)(stm->pcm);
  assert(stm->nfds > 0);

  stm->saved_fds = calloc(stm->nfds, sizeof(struct pollfd));
  assert(stm->saved_fds);
  r = WRAP(snd_pcm_poll_descriptors)(stm->pcm, stm->saved_fds, stm->nfds);
  assert((nfds_t)r == stm->nfds);

  if (alsa_register_stream(ctx, stm) != 0) {
    alsa_stream_destroy(stm);
    return CUBEB_ERROR;
  }

  *stream = stm;

  return CUBEB_OK;
}

static int
alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
                 cubeb_devid input_device,
                 cubeb_stream_params * input_stream_params,
                 cubeb_devid output_device,
                 cubeb_stream_params * output_stream_params,
                 unsigned int latency_frames, cubeb_data_callback data_callback,
                 cubeb_state_callback state_callback, void * user_ptr)
{
  int result = CUBEB_OK;
  cubeb_stream *instm = NULL, *outstm = NULL;

  if (result == CUBEB_OK && input_stream_params) {
    result = alsa_stream_init_single(ctx, &instm, stream_name,
                                     SND_PCM_STREAM_CAPTURE, input_device,
                                     input_stream_params, latency_frames,
                                     data_callback, state_callback, user_ptr);
  }

  if (result == CUBEB_OK && output_stream_params) {
    result = alsa_stream_init_single(ctx, &outstm, stream_name,
                                     SND_PCM_STREAM_PLAYBACK, output_device,
                                     output_stream_params, latency_frames,
                                     data_callback, state_callback, user_ptr);
  }

  if (result == CUBEB_OK && input_stream_params && output_stream_params) {
    instm->other_stream = outstm;
    outstm->other_stream = instm;
  }

  if (result != CUBEB_OK && instm) {
    alsa_stream_destroy(instm);
  }

  *stream = outstm ? outstm : instm;

  return result;
}

static void
alsa_stream_destroy(cubeb_stream * stm)
{
  int r;
  cubeb * ctx;

  assert(stm && (stm->state == INACTIVE || stm->state == ERROR ||
                 stm->state == DRAINING));

  ctx = stm->context;

  if (stm->other_stream) {
    stm->other_stream->other_stream = NULL; // to stop infinite recursion
    alsa_stream_destroy(stm->other_stream);
  }

  pthread_mutex_lock(&stm->mutex);
  if (stm->pcm) {
    if (stm->state == DRAINING) {
      WRAP(snd_pcm_drain)(stm->pcm);
    }
    alsa_locked_pcm_close(stm->pcm);
    stm->pcm = NULL;
  }
  free(stm->saved_fds);
  pthread_mutex_unlock(&stm->mutex);
  pthread_mutex_destroy(&stm->mutex);

  r = pthread_cond_destroy(&stm->cond);
  assert(r == 0);

  alsa_unregister_stream(stm);

  pthread_mutex_lock(&ctx->mutex);
  assert(ctx->active_streams >= 1);
  ctx->active_streams -= 1;
  pthread_mutex_unlock(&ctx->mutex);

  free(stm->buffer);

  free(stm);
}

static int
alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
{
  int r;
  cubeb_stream * stm;
  snd_pcm_hw_params_t * hw_params;
  cubeb_stream_params params;
  params.rate = 44100;
  params.format = CUBEB_SAMPLE_FLOAT32NE;
  params.channels = 2;

  snd_pcm_hw_params_alloca(&hw_params);

  assert(ctx);

  r = alsa_stream_init(ctx, &stm, "", NULL, NULL, NULL, &params, 100, NULL,
                       NULL, NULL);
  if (r != CUBEB_OK) {
    return CUBEB_ERROR;
  }

  assert(stm);

  r = WRAP(snd_pcm_hw_params_any)(stm->pcm, hw_params);
  if (r < 0) {
    return CUBEB_ERROR;
  }

  r = WRAP(snd_pcm_hw_params_get_channels_max)(hw_params, max_channels);
  if (r < 0) {
    return CUBEB_ERROR;
  }

  /* Cap at reasonable maximum to filter driver placeholder values */  
  if (*max_channels > 64) {  
    *max_channels = 64;  
  }  

  alsa_stream_destroy(stm);

  return CUBEB_OK;
}

static int
alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
{
  (void)ctx;
  int r, dir;
  snd_pcm_t * pcm;
  snd_pcm_hw_params_t * hw_params;

  snd_pcm_hw_params_alloca(&hw_params);

  /* get a pcm, disabling resampling, so we get a rate the
   * hardware/dmix/pulse/etc. supports. */
  r = WRAP(snd_pcm_open)(&pcm, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK,
                         SND_PCM_NO_AUTO_RESAMPLE);
  if (r < 0) {
    return CUBEB_ERROR;
  }

  r = WRAP(snd_pcm_hw_params_any)(pcm, hw_params);
  if (r < 0) {
    WRAP(snd_pcm_close)(pcm);
    return CUBEB_ERROR;
  }

  r = WRAP(snd_pcm_hw_params_get_rate)(hw_params, rate, &dir);
  if (r >= 0) {
    /* There is a default rate: use it. */
    WRAP(snd_pcm_close)(pcm);
    return CUBEB_OK;
  }

  /* Use a common rate, alsa may adjust it based on hw/etc. capabilities. */
  *rate = 44100;

  r = WRAP(snd_pcm_hw_params_set_rate_near)(pcm, hw_params, rate, NULL);
  if (r < 0) {
    WRAP(snd_pcm_close)(pcm);
    return CUBEB_ERROR;
  }

  WRAP(snd_pcm_close)(pcm);

  return CUBEB_OK;
}

static int
alsa_get_min_latency(cubeb * ctx, cubeb_stream_params params,
                     uint32_t * latency_frames)
{
  (void)ctx;
  /* 40ms is found to be an acceptable minimum, even on a super low-end
   * machine. */
  *latency_frames = 40 * params.rate / 1000;

  return CUBEB_OK;
}

static int
alsa_stream_start(cubeb_stream * stm)
{
  cubeb * ctx;

  assert(stm);
  ctx = stm->context;

  if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && stm->other_stream) {
    int r = alsa_stream_start(stm->other_stream);
    if (r != CUBEB_OK)
      return r;
  }

  pthread_mutex_lock(&stm->mutex);
  /* Capture pcm must be started after initial setup/recover */
  if (stm->stream_type == SND_PCM_STREAM_CAPTURE &&
      WRAP(snd_pcm_state)(stm->pcm) == SND_PCM_STATE_PREPARED) {
    WRAP(snd_pcm_start)(stm->pcm);
  }
  WRAP(snd_pcm_pause)(stm->pcm, 0);
  gettimeofday(&stm->last_activity, NULL);
  pthread_mutex_unlock(&stm->mutex);

  pthread_mutex_lock(&ctx->mutex);
  if (stm->state != INACTIVE) {
    pthread_mutex_unlock(&ctx->mutex);
    return CUBEB_ERROR;
  }
  alsa_set_stream_state(stm, RUNNING);
  pthread_mutex_unlock(&ctx->mutex);

  return CUBEB_OK;
}

static int
alsa_stream_stop(cubeb_stream * stm)
{
  cubeb * ctx;
  int r;

  assert(stm);
  ctx = stm->context;

  if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && stm->other_stream) {
    int r = alsa_stream_stop(stm->other_stream);
    if (r != CUBEB_OK)
      return r;
  }

  pthread_mutex_lock(&ctx->mutex);
  while (stm->state == PROCESSING) {
    r = pthread_cond_wait(&stm->cond, &ctx->mutex);
    assert(r == 0);
  }

  alsa_set_stream_state(stm, INACTIVE);
  pthread_mutex_unlock(&ctx->mutex);

  pthread_mutex_lock(&stm->mutex);
  WRAP(snd_pcm_pause)(stm->pcm, 1);
  pthread_mutex_unlock(&stm->mutex);

  return CUBEB_OK;
}

static int
alsa_stream_get_position(cubeb_stream * stm, uint64_t * position)
{
  snd_pcm_sframes_t delay;

  assert(stm && position);

  pthread_mutex_lock(&stm->mutex);

  delay = -1;
  if (WRAP(snd_pcm_state)(stm->pcm) != SND_PCM_STATE_RUNNING ||
      WRAP(snd_pcm_delay)(stm->pcm, &delay) != 0) {
    *position = stm->last_position;
    pthread_mutex_unlock(&stm->mutex);
    return CUBEB_OK;
  }

  assert(delay >= 0);

  *position = 0;
  if (stm->stream_position >= (snd_pcm_uframes_t)delay) {
    *position = stm->stream_position - delay;
  }

  stm->last_position = *position;

  pthread_mutex_unlock(&stm->mutex);
  return CUBEB_OK;
}

static int
alsa_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
{
  snd_pcm_sframes_t delay;
  /* This function returns the delay in frames until a frame written using
     snd_pcm_writei is sent to the DAC. The DAC delay should be < 1ms anyways.
   */
  if (WRAP(snd_pcm_delay)(stm->pcm, &delay)) {
    return CUBEB_ERROR;
  }

  *latency = delay;

  return CUBEB_OK;
}

static int
alsa_stream_set_volume(cubeb_stream * stm, float volume)
{
  /* setting the volume using an API call does not seem very stable/supported */
  pthread_mutex_lock(&stm->mutex);
  stm->volume = volume;
  pthread_mutex_unlock(&stm->mutex);

  return CUBEB_OK;
}

static int
alsa_enumerate_devices(cubeb * context, cubeb_device_type type,
                       cubeb_device_collection * collection)
{
  cubeb_device_info * device;
  snd_pcm_info_t * pcminfo;
  snd_ctl_t * ctl;
  snd_ctl_card_info_t * cardinfo;
  int card = -1;
  char card_name[32];
  int err;
  int dev;
  snd_pcm_stream_t stream;

  snd_pcm_info_alloca(&pcminfo);
  snd_ctl_card_info_alloca(&cardinfo);

  collection->count = 0;
  collection->device = NULL;

  if (snd_card_next(&card) < 0 || card < 0)
    return CUBEB_OK;

  while (card >= 0) {
    sprintf(card_name, "hw:%d", card);
    err = snd_ctl_open(&ctl, card_name, 0);
    if (err < 0) {
      snd_card_next(&card);
      continue;
    }

    err = snd_ctl_card_info(ctl, cardinfo);
    if (err < 0) {
      snd_ctl_close(ctl);
      snd_card_next(&card);
      continue;
    }

    dev = -1;
    while (1) {
      if (snd_ctl_pcm_next_device(ctl, &dev) < 0)
        break;
      if (dev < 0)
        break;

      for (stream = 0; stream < 2; stream++) {
        if ((type & CUBEB_DEVICE_TYPE_OUTPUT) && stream == SND_PCM_STREAM_CAPTURE)
          continue;
        if ((type & CUBEB_DEVICE_TYPE_INPUT) && stream == SND_PCM_STREAM_PLAYBACK)
          continue;

        snd_pcm_info_set_device(pcminfo, dev);
        snd_pcm_info_set_subdevice(pcminfo, 0);
        snd_pcm_info_set_stream(pcminfo, stream);
        err = snd_ctl_pcm_info(ctl, pcminfo);
        if (err < 0)
          continue;

        device = (cubeb_device_info *)calloc(1, sizeof(cubeb_device_info));
        if (!device) {
          snd_ctl_close(ctl);
          return CUBEB_ERROR;
        }

        device->device_id = strdup(snd_pcm_info_get_name(pcminfo));
        device->friendly_name = strdup(snd_pcm_info_get_name(pcminfo));
        device->group_id = strdup(snd_ctl_card_info_get_id(cardinfo));
        device->vendor_name = strdup(snd_ctl_card_info_get_name(cardinfo));

        device->type = (stream == SND_PCM_STREAM_PLAYBACK) ? 
                       CUBEB_DEVICE_TYPE_OUTPUT : CUBEB_DEVICE_TYPE_INPUT;
        device->devid = (void *)(intptr_t)(card << 16 | dev);
        device->state = CUBEB_DEVICE_STATE_ENABLED;
        device->preferred = (dev == 0);
        
        device->max_channels = 8;  
        
        device->min_rate = 8000;   /* Conservative minimum */ 
        device->max_rate = 192000; /* Conservative maximum */
        device->default_format = (cubeb_device_fmt)CUBEB_SAMPLE_FLOAT32NE;
        device->latency_lo = 0;
        device->latency_hi = 0;

        collection->count++;
        collection->device = (cubeb_device_info *)realloc(collection->device,
                                                         collection->count * sizeof(cubeb_device_info));
        if (!collection->device) {
          free((void *)device->device_id);  
          free((void *)device->friendly_name);  
          free((void *)device->group_id);  
          free((void *)device->vendor_name);
          free(device);
          snd_ctl_close(ctl);
          return CUBEB_ERROR;
        }
        collection->device[collection->count - 1] = *device;
        free(device);
      }
    }
    snd_ctl_close(ctl);
    snd_card_next(&card);
  }

  return CUBEB_OK;
}

static int  
alsa_device_collection_destroy(cubeb * context,  
                               cubeb_device_collection * collection)  
{  
  size_t i;  
  
  (void)context;  
  
  for (i = 0; i < collection->count; ++i) {  
    free((void *)collection->device[i].device_id);  
    free((void *)collection->device[i].friendly_name);  
    free((void *)collection->device[i].group_id);  
    free((void *)collection->device[i].vendor_name);  
  }  
  free(collection->device);  
  return CUBEB_OK;  
}

static struct cubeb_ops const alsa_ops = {
    .init = alsa_init,
    .get_backend_id = alsa_get_backend_id,
    .get_max_channel_count = alsa_get_max_channel_count,
    .get_min_latency = alsa_get_min_latency,
    .get_preferred_sample_rate = alsa_get_preferred_sample_rate,
    .get_supported_input_processing_params = NULL,
    .enumerate_devices = alsa_enumerate_devices,
    .device_collection_destroy = alsa_device_collection_destroy,
    .destroy = alsa_destroy,
    .stream_init = alsa_stream_init,
    .stream_destroy = alsa_stream_destroy,
    .stream_start = alsa_stream_start,
    .stream_stop = alsa_stream_stop,
    .stream_get_position = alsa_stream_get_position,
    .stream_get_latency = alsa_stream_get_latency,
    .stream_get_input_latency = NULL,
    .stream_set_volume = alsa_stream_set_volume,
    .stream_set_name = NULL,
    .stream_get_current_device = NULL,
    .stream_set_input_mute = NULL,
    .stream_set_input_processing_params = NULL,
    .stream_device_destroy = NULL,
    .stream_register_device_changed_callback = NULL,
    .register_device_collection_changed = NULL};

#10 Re: Freedom Hacks » The Absurdist Comedy » 2026-06-12 13:24:52

PedroReina wrote:

Any chance to be accepted upstream?

Any chance for it to be tested by ALSA users?

The OSS4 backend is officially unmaintained. The ALSA backend seems to be orphaned. Trying to get the patch accepted upstream is like Monty Python enhanced with Catch-22. A workaround is a dialectical deconstruction of Firefox.

Summary of the Patch

The ALSA backend patch for cubeb fixes two critical issues:

Device enumeration: Replaces the placeholder implementation that returned a single fictional "default" device with proper ALSA device discovery using snd_card_next() and snd_ctl_pcm_next_device() to report actual audio devices with their real capabilities

Max channel count: Caps the maximum channel count at 64 to sanitize unrealistic values (like 10000) returned by snd_pcm_hw_params_get_channels_max() from some drivers

These fixes ensure that Firefox and web applications like Zoom receive accurate device information instead of incorrect placeholder values that were causing failures.

#11 Re: Freedom Hacks » The Absurdist Comedy » 2026-06-11 21:16:38

Patch to fix Firefox ALSA backend src/cubeb_alsa.c

$ cat patches/0002-ALSA-backend-for-Firefox-Fix-device-enumeration-and-.patch
From 69df295f5cf8d80f594110f9576a7bed153295b5 Mon Sep 17 00:00:00 2001
From: Devuan
Date: Thu, 11 Jun 2026 21:58:59 +0200
Subject: [PATCH 2/3] ALSA backend for Firefox: Fix device enumeration and max
 channel count

- Replace placeholder device enumeration with proper ALSA device
  discovery using snd_card_next() and snd_ctl_pcm_next_device() to
  report actual audio devices with their real capabilities instead of
  a single fictional default device.

- Cap max channel count at 64 to sanitize unrealistic values returned
  by snd_pcm_hw_params_get_channels_max() which can report placeholder
  values like 10000 from some drivers.
---
 src/cubeb_alsa.c | 160 +++++++++++++++++++++++++++++++++--------------
 1 file changed, 114 insertions(+), 46 deletions(-)

diff --git a/src/cubeb_alsa.c b/src/cubeb_alsa.c
index be9faa4..b76ec4f 100644
--- a/src/cubeb_alsa.c
+++ b/src/cubeb_alsa.c
@@ -1223,6 +1223,11 @@ alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
     return CUBEB_ERROR;
   }
 
+  /* Cap at reasonable maximum to filter driver placeholder values */  
+  if (*max_channels > 64) {  
+    *max_channels = 64;  
+  }  
+
   alsa_stream_destroy(stm);
 
   return CUBEB_OK;
@@ -1412,61 +1417,124 @@ static int
 alsa_enumerate_devices(cubeb * context, cubeb_device_type type,
                        cubeb_device_collection * collection)
 {
-  cubeb_device_info * device = NULL;
+  cubeb_device_info * device;
+  snd_pcm_info_t * pcminfo;
+  snd_ctl_t * ctl;
+  snd_ctl_card_info_t * cardinfo;
+  int card = -1;
+  char card_name[32];
+  int err;
+  int dev;
+  snd_pcm_stream_t stream;
+
+  snd_pcm_info_alloca(&pcminfo);
+  snd_ctl_card_info_alloca(&cardinfo);
+
+  collection->count = 0;
+  collection->device = NULL;
+
+  if (snd_card_next(&card) < 0 || card < 0)
+    return CUBEB_OK;
 
-  if (!context)
-    return CUBEB_ERROR;
+  while (card >= 0) {
+    sprintf(card_name, "hw:%d", card);
+    err = snd_ctl_open(&ctl, card_name, 0);
+    if (err < 0) {
+      snd_card_next(&card);
+      continue;
+    }
 
-  uint32_t rate, max_channels;
-  int r;
+    err = snd_ctl_card_info(ctl, cardinfo);
+    if (err < 0) {
+      snd_ctl_close(ctl);
+      snd_card_next(&card);
+      continue;
+    }
 
-  r = alsa_get_preferred_sample_rate(context, &rate);
-  if (r != CUBEB_OK) {
-    return CUBEB_ERROR;
-  }
+    dev = -1;
+    while (1) {
+      if (snd_ctl_pcm_next_device(ctl, &dev) < 0)
+        break;
+      if (dev < 0)
+        break;
+
+      for (stream = 0; stream < 2; stream++) {
+        if ((type & CUBEB_DEVICE_TYPE_OUTPUT) && stream == SND_PCM_STREAM_CAPTURE)
+          continue;
+        if ((type & CUBEB_DEVICE_TYPE_INPUT) && stream == SND_PCM_STREAM_PLAYBACK)
+          continue;
+
+        snd_pcm_info_set_device(pcminfo, dev);
+        snd_pcm_info_set_subdevice(pcminfo, 0);
+        snd_pcm_info_set_stream(pcminfo, stream);
+        err = snd_ctl_pcm_info(ctl, pcminfo);
+        if (err < 0)
+          continue;
+
+        device = (cubeb_device_info *)calloc(1, sizeof(cubeb_device_info));
+        if (!device) {
+          snd_ctl_close(ctl);
+          return CUBEB_ERROR;
+        }
 
-  r = alsa_get_max_channel_count(context, &max_channels);
-  if (r != CUBEB_OK) {
-    return CUBEB_ERROR;
+        device->device_id = strdup(snd_pcm_info_get_name(pcminfo));
+        device->friendly_name = strdup(snd_pcm_info_get_name(pcminfo));
+        device->group_id = strdup(snd_ctl_card_info_get_id(cardinfo));
+        device->vendor_name = strdup(snd_ctl_card_info_get_name(cardinfo));
+
+        device->type = (stream == SND_PCM_STREAM_PLAYBACK) ? 
+                       CUBEB_DEVICE_TYPE_OUTPUT : CUBEB_DEVICE_TYPE_INPUT;
+        device->devid = (void *)(intptr_t)(card << 16 | dev);
+        device->state = CUBEB_DEVICE_STATE_ENABLED;
+        device->preferred = (dev == 0);
+        
+        device->max_channels = 8;  
+        
+        device->min_rate = 8000;   /* Conservative minimum */ 
+        device->max_rate = 192000; /* Conservative maximum */
+        device->default_format = (cubeb_device_fmt)CUBEB_SAMPLE_FLOAT32NE;
+        device->latency_lo = 0;
+        device->latency_hi = 0;
+
+        collection->count++;
+        collection->device = (cubeb_device_info *)realloc(collection->device,
+                                                         collection->count * sizeof(cubeb_device_info));
+        if (!collection->device) {
+          free((void *)device->device_id);  
+          free((void *)device->friendly_name);  
+          free((void *)device->group_id);  
+          free((void *)device->vendor_name);
+          free(device);
+          snd_ctl_close(ctl);
+          return CUBEB_ERROR;
+        }
+        collection->device[collection->count - 1] = *device;
+        free(device);
+      }
+    }
+    snd_ctl_close(ctl);
+    snd_card_next(&card);
   }
 
-  char const * a_name = "default";
-  device = (cubeb_device_info *)calloc(1, sizeof(cubeb_device_info));
-  assert(device);
-  if (!device)
-    return CUBEB_ERROR;
-
-  device->device_id = a_name;
-  device->devid = (cubeb_devid)device->device_id;
-  device->friendly_name = a_name;
-  device->group_id = a_name;
-  device->vendor_name = a_name;
-  device->type = type;
-  device->state = CUBEB_DEVICE_STATE_ENABLED;
-  device->preferred = CUBEB_DEVICE_PREF_ALL;
-  device->format = CUBEB_DEVICE_FMT_S16NE;
-  device->default_format = CUBEB_DEVICE_FMT_S16NE;
-  device->max_channels = max_channels;
-  device->min_rate = rate;
-  device->max_rate = rate;
-  device->default_rate = rate;
-  device->latency_lo = 0;
-  device->latency_hi = 0;
-
-  collection->device = device;
-  collection->count = 1;
-
   return CUBEB_OK;
 }
 
-static int
-alsa_device_collection_destroy(cubeb * context,
-                               cubeb_device_collection * collection)
-{
-  assert(collection->count == 1);
-  (void)context;
-  free(collection->device);
-  return CUBEB_OK;
+static int  
+alsa_device_collection_destroy(cubeb * context,  
+                               cubeb_device_collection * collection)  
+{  
+  size_t i;  
+  
+  (void)context;  
+  
+  for (i = 0; i < collection->count; ++i) {  
+    free((void *)collection->device[i].device_id);  
+    free((void *)collection->device[i].friendly_name);  
+    free((void *)collection->device[i].group_id);  
+    free((void *)collection->device[i].vendor_name);  
+  }  
+  free(collection->device);  
+  return CUBEB_OK;  
 }
 
 static struct cubeb_ops const alsa_ops = {
-- 
2.39.5

#12 Freedom Hacks » Firefox: Dialectics of Antagonistic Security Bugs » 2026-05-30 18:19:11

igorzwx
Replies: 0
Firefox: Dialectics of Antagonistic Security Bugs

Firefox with unlocked preferences is an attractive target for attacks. Unprotected security preferences can be exploited as backdoors. To lock down these backdoors, you must fix antagonistic bugs with dialectical patches.

Don't panic! Debian's Firefox ESR leverages a system-wide preference lockdown that allows administrators to enforce security policies. Ordinary users, of course, may dwell in ignorance and fear. Devuan Wiki might be difficult to consume for TikTok users.

NOTE: This guide is a work in progress. Please do not post in this topic. Since the community has complained about "spoon-feeding", this guide is intended for experienced Linux users. If you have any questions, please consult the Devuan Wiki, or other knowledge repositories of the sort. If you have problems with dialectical patches, study Hegel or Monty Python.

Firefox Developer Edition
The browser made for developers
All the latest developer tools in beta in addition to features like the Multi-line Console Editor and WebSocket Inspector.
A separate profile and path so you can easily run it alongside Release or Beta Firefox.
Preferences tailored for web developers: Browser and remote debugging are enabled by default, as are the dark theme and developer toolbar button.
_https://www.firefox.com/en-US/channel/desktop/developer

It means:

about:config 
devtools.chrome.enabled = true 
devtools.theme = dark 
browser.toolbars.bookmarks.visibility = always 

Why not close backdoors with a key?

// Disable remote debugging
pref("devtools.debugger.remote-enabled", false, locked);
// Restrict connections to localhost only (default: true)
pref("devtools.debugger.force-local", true, locked);
// Disable browser chrome debugging (debugging Firefox itself). 
pref("devtools.chrome.enabled", false, locked);
// Block access to all developer tools functionality
pref("devtools.policy.disabled", true, locked); 

Security Implications of Unlocked Preferences:
1. Malicious extensions could modify security-critical preferences (like cookie behavior, remote debugging, or TLS settings)
2. Compromised user accounts could weaken protections through about:config
4. Malware could disable security features to facilitate further attacks
5. Social engineering could trick users into changing critical settings

Official Documentation: Building Firefox On Linux

_https://firefox-source-docs.mozilla.org/setup/linux_build.html
_https://firefox-source-docs.mozilla.org/build/buildsystem/mozconfigs.html

PATCHES:

$ cat PATCHES/0001-Set-MOZ_APP_UA_NAME-to-Firefox-for-clean-UA-strings.patch
From 8bd5afd0265356ddc323cffd8397208b5750227a Mon Sep 17 00:00:00 2001
From: Devuan <devuan@devuan.cargo-cult.org>
Date: Fri, 15 May 2026 23:40:28 +0200
Subject: [PATCH 1/3] Set MOZ_APP_UA_NAME to Firefox for clean UA strings

---
 browser/moz.configure | 1 +
 1 file changed, 1 insertion(+)

diff --git a/browser/moz.configure b/browser/moz.configure
index 3ea3d88b9360..0a95edc31354 100644
--- a/browser/moz.configure
+++ b/browser/moz.configure
@@ -16,6 +16,7 @@ imply_option("MOZ_APP_ID", "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}")
 # Include the DevTools client, not just the server (which is the default)
 imply_option("MOZ_DEVTOOLS", "all")
 imply_option("BROWSER_CHROME_URL", "chrome://browser/content/browser.xhtml")
+imply_option("MOZ_APP_UA_NAME", "Firefox")

 with only_when(target_has_linux_kernel & compile_environment):
-- 
2.39.5
$ cat PATCHES/0002-Set-MOZ_APP_PROFILE-to-Firefox-for-clean-APP_PROFILE.patch
From 252efd229f1d9dac21e053d05a5ef9ced1ca14f3 Mon Sep 17 00:00:00 2001
From: Devuan <devuan@devuan.cargo-cult.org>
Date: Sat, 16 May 2026 18:02:08 +0200
Subject: [PATCH 2/3] Set MOZ_APP_PROFILE to Firefox for clean APP_PROFILE
 strings

---
 browser/moz.configure | 1 +
 1 file changed, 1 insertion(+)

diff --git a/browser/moz.configure b/browser/moz.configure
index 0a95edc31354..a4a4945ddbdd 100644
--- a/browser/moz.configure
+++ b/browser/moz.configure
@@ -17,6 +17,7 @@ imply_option("MOZ_APP_ID", "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}")
 imply_option("MOZ_DEVTOOLS", "all")
 imply_option("BROWSER_CHROME_URL", "chrome://browser/content/browser.xhtml")
 imply_option("MOZ_APP_UA_NAME", "Firefox")
+imply_option("MOZ_APP_PROFILE", "firefox-dev")

 with only_when(target_has_linux_kernel & compile_environment):
-- 
2.39.5
$ cat PATCHES/0003-Fix-system-preferences-for-custom-firefox-dev-builds.patch
From 9f4226e27c3b85506873bea8e30645fb066709ae Mon Sep 17 00:00:00 2001
From: Devuan <devuan@devuan.cargo-cult.org>
Date: Thu, 28 May 2026 23:41:26 +0200
Subject: [PATCH 3/3] Fix system preferences for custom firefox-dev builds
 (security-critical)

This fixes two upstream bugs that prevent the system preferences security
feature from working, which is designed to lock down potential backdoors
by allowing administrators to enforce system-wide preference settings.

Bug 1: Configure option contradiction in toolkit/moz.configure
- Original code defines --disable-system-preferences but requires
  --enable-system-preferences in the when condition, creating a
  semantic contradiction that prevents the feature from being enabled
- Fixed with inline lambda pattern: when=depends("--disable-system-preferences")(lambda x: not x)

Bug 2: Dynamic app name resolution in xpcom/io/SpecialSystemDirectory.cpp
- Runtime code dynamically constructs /etc/{appname}/defaults/pref/ path
- This breaks system-wide configuration for custom builds with non-standard
  app names like firefox-dev
- Fixed by hardcoding "firefox-dev" to match the existing directory structure

REQUIREMENT: Users must add this line to their .mozconfig:
  ac_add_options --disable-system-preferences

This is a dialectical workaround: the --disable option actually enables
the feature due to the inverted lambda logic. Without this in .mozconfig,
the configure system will not set MOZ_SYSTEM_PREFERENCES.

Security Impact: System preferences load LAST, overriding application defaults.
This allows administrators to lock down preferences that could be exploited
as backdoors (telemetry, proxy settings, extensions, etc.). The upstream bugs
prevent this security mechanism from functioning.
---
 toolkit/moz.configure               |  4 ++--
 xpcom/io/SpecialSystemDirectory.cpp | 31 ++++++++++++++++-------------
 2 files changed, 19 insertions(+), 16 deletions(-)

diff --git a/toolkit/moz.configure b/toolkit/moz.configure
index 2412f33b4ef5..cd230f461282 100644
--- a/toolkit/moz.configure
+++ b/toolkit/moz.configure
@@ -4232,8 +4232,8 @@ option(
     help="Disable reading preferences from /etc/firefox",
 )
 
-set_config("MOZ_SYSTEM_PREFERENCES", True, when="--enable-system-preferences")
-set_define("MOZ_SYSTEM_PREFERENCES", True, when="--enable-system-preferences")
+set_config("MOZ_SYSTEM_PREFERENCES", True, when=depends("--disable-system-preferences")(lambda x: not x))
+set_define("MOZ_SYSTEM_PREFERENCES", True, when=depends("--disable-system-preferences")(lambda x: not x))
 
 # Allow disabling the creation a legacy profile
 # ==============================================================
diff --git a/xpcom/io/SpecialSystemDirectory.cpp b/xpcom/io/SpecialSystemDirectory.cpp
index 5e80ca881c27..3d1cdf7b3fc1 100644
--- a/xpcom/io/SpecialSystemDirectory.cpp
+++ b/xpcom/io/SpecialSystemDirectory.cpp
@@ -155,20 +155,22 @@ static nsresult GetUnixHomeDir(nsIFile** aFile) {
 #  endif
 }
 
-static nsresult GetUnixSystemConfigDir(nsIFile** aFile) {
-#  if defined(ANDROID)
-  return NS_ERROR_FAILURE;
-#  else
-  nsAutoCString appName;
-  if (nsCOMPtr<nsIXULAppInfo> appInfo =
-          do_GetService("@mozilla.org/xre/app-info;1")) {
-    MOZ_TRY(appInfo->GetName(appName));
-  } else {
-    appName.AssignLiteral(MOZ_APP_BASENAME);
-  }
-
-  ToLowerCase(appName);
-
+static nsresult GetUnixSystemConfigDir(nsIFile** aFile) {  
+#  if defined(ANDROID)  
+  return NS_ERROR_FAILURE;  
+#  else  
+  // DIALECTICAL FIX: Hardcode firefox-dev for custom build  
+  nsAutoCString appName("firefox-dev");  
+    
+  // Original dynamic code (commented out):  
+  // if (nsCOMPtr<nsIXULAppInfo> appInfo =  
+  //         do_GetService("@mozilla.org/xre/app-info;1")) {  
+  //   MOZ_TRY(appInfo->GetName(appName));  
+  // } else {  
+  //   appName.AssignLiteral(MOZ_APP_BASENAME);  
+  // }  
+  // ToLowerCase(appName);  
+  
   nsDependentCString sysConfigDir;
   if (PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR")) {
     const char* mozSystemConfigDir = PR_GetEnv("MOZ_SYSTEM_CONFIG_DIR");
@@ -181,6 +183,7 @@ static nsresult GetUnixSystemConfigDir(nsIFile** aFile) {
     sysConfigDir.Assign(nsLiteralCString("/app/etc"));
   }
 #    endif
+
   if (sysConfigDir.IsEmpty()) {
     sysConfigDir.Assign(nsLiteralCString("/etc"));
   }
-- 
2.39.5
$ cat PATCHES/0004-ALSA-backend-for-Firefox-Fix-device-enumeration-and-.patch
From 3af4931724f64c30ad3a4cf9175016ca9844ca84 Mon Sep 17 00:00:00 2001
From: Devuan  <devuan@devuan.cargo-cult.org>
Date: Sat, 13 Jun 2026 19:58:40 +0200
Subject: [PATCH 4/4] ALSA backend for Firefox: Fix device enumeration and max 
 channel count

- Replace placeholder device enumeration with proper ALSA device
  discovery using snd_card_next() and snd_ctl_pcm_next_device() to
  report actual audio devices with their real capabilities instead of
  a single fictional default device.

- Cap max channel count at 64 to sanitize unrealistic values returned
  by snd_pcm_hw_params_get_channels_max() which can report placeholder
  values like 10000 from some drivers.

These fixes ensure Firefox and web applications receive accurate device
information instead of incorrect placeholder values.
---
 media/libcubeb/src/cubeb_alsa.c | 160 +++++++++++++++++++++++---------
 1 file changed, 114 insertions(+), 46 deletions(-)

diff --git a/media/libcubeb/src/cubeb_alsa.c b/media/libcubeb/src/cubeb_alsa.c
index be9faa490cbd..b76ec4f74b61 100644
--- a/media/libcubeb/src/cubeb_alsa.c
+++ b/media/libcubeb/src/cubeb_alsa.c
@@ -1223,6 +1223,11 @@ alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
     return CUBEB_ERROR;
   }
 
+  /* Cap at reasonable maximum to filter driver placeholder values */  
+  if (*max_channels > 64) {  
+    *max_channels = 64;  
+  }  
+
   alsa_stream_destroy(stm);
 
   return CUBEB_OK;
@@ -1412,61 +1417,124 @@ static int
 alsa_enumerate_devices(cubeb * context, cubeb_device_type type,
                        cubeb_device_collection * collection)
 {
-  cubeb_device_info * device = NULL;
+  cubeb_device_info * device;
+  snd_pcm_info_t * pcminfo;
+  snd_ctl_t * ctl;
+  snd_ctl_card_info_t * cardinfo;
+  int card = -1;
+  char card_name[32];
+  int err;
+  int dev;
+  snd_pcm_stream_t stream;
+
+  snd_pcm_info_alloca(&pcminfo);
+  snd_ctl_card_info_alloca(&cardinfo);
+
+  collection->count = 0;
+  collection->device = NULL;
+
+  if (snd_card_next(&card) < 0 || card < 0)
+    return CUBEB_OK;
 
-  if (!context)
-    return CUBEB_ERROR;
+  while (card >= 0) {
+    sprintf(card_name, "hw:%d", card);
+    err = snd_ctl_open(&ctl, card_name, 0);
+    if (err < 0) {
+      snd_card_next(&card);
+      continue;
+    }
 
-  uint32_t rate, max_channels;
-  int r;
+    err = snd_ctl_card_info(ctl, cardinfo);
+    if (err < 0) {
+      snd_ctl_close(ctl);
+      snd_card_next(&card);
+      continue;
+    }
 
-  r = alsa_get_preferred_sample_rate(context, &rate);
-  if (r != CUBEB_OK) {
-    return CUBEB_ERROR;
-  }
+    dev = -1;
+    while (1) {
+      if (snd_ctl_pcm_next_device(ctl, &dev) < 0)
+        break;
+      if (dev < 0)
+        break;
+
+      for (stream = 0; stream < 2; stream++) {
+        if ((type & CUBEB_DEVICE_TYPE_OUTPUT) && stream == SND_PCM_STREAM_CAPTURE)
+          continue;
+        if ((type & CUBEB_DEVICE_TYPE_INPUT) && stream == SND_PCM_STREAM_PLAYBACK)
+          continue;
+
+        snd_pcm_info_set_device(pcminfo, dev);
+        snd_pcm_info_set_subdevice(pcminfo, 0);
+        snd_pcm_info_set_stream(pcminfo, stream);
+        err = snd_ctl_pcm_info(ctl, pcminfo);
+        if (err < 0)
+          continue;
+
+        device = (cubeb_device_info *)calloc(1, sizeof(cubeb_device_info));
+        if (!device) {
+          snd_ctl_close(ctl);
+          return CUBEB_ERROR;
+        }
 
-  r = alsa_get_max_channel_count(context, &max_channels);
-  if (r != CUBEB_OK) {
-    return CUBEB_ERROR;
+        device->device_id = strdup(snd_pcm_info_get_name(pcminfo));
+        device->friendly_name = strdup(snd_pcm_info_get_name(pcminfo));
+        device->group_id = strdup(snd_ctl_card_info_get_id(cardinfo));
+        device->vendor_name = strdup(snd_ctl_card_info_get_name(cardinfo));
+
+        device->type = (stream == SND_PCM_STREAM_PLAYBACK) ? 
+                       CUBEB_DEVICE_TYPE_OUTPUT : CUBEB_DEVICE_TYPE_INPUT;
+        device->devid = (void *)(intptr_t)(card << 16 | dev);
+        device->state = CUBEB_DEVICE_STATE_ENABLED;
+        device->preferred = (dev == 0);
+        
+        device->max_channels = 8;  
+        
+        device->min_rate = 8000;   /* Conservative minimum */ 
+        device->max_rate = 192000; /* Conservative maximum */
+        device->default_format = (cubeb_device_fmt)CUBEB_SAMPLE_FLOAT32NE;
+        device->latency_lo = 0;
+        device->latency_hi = 0;
+
+        collection->count++;
+        collection->device = (cubeb_device_info *)realloc(collection->device,
+                                                         collection->count * sizeof(cubeb_device_info));
+        if (!collection->device) {
+          free((void *)device->device_id);  
+          free((void *)device->friendly_name);  
+          free((void *)device->group_id);  
+          free((void *)device->vendor_name);
+          free(device);
+          snd_ctl_close(ctl);
+          return CUBEB_ERROR;
+        }
+        collection->device[collection->count - 1] = *device;
+        free(device);
+      }
+    }
+    snd_ctl_close(ctl);
+    snd_card_next(&card);
   }
 
-  char const * a_name = "default";
-  device = (cubeb_device_info *)calloc(1, sizeof(cubeb_device_info));
-  assert(device);
-  if (!device)
-    return CUBEB_ERROR;
-
-  device->device_id = a_name;
-  device->devid = (cubeb_devid)device->device_id;
-  device->friendly_name = a_name;
-  device->group_id = a_name;
-  device->vendor_name = a_name;
-  device->type = type;
-  device->state = CUBEB_DEVICE_STATE_ENABLED;
-  device->preferred = CUBEB_DEVICE_PREF_ALL;
-  device->format = CUBEB_DEVICE_FMT_S16NE;
-  device->default_format = CUBEB_DEVICE_FMT_S16NE;
-  device->max_channels = max_channels;
-  device->min_rate = rate;
-  device->max_rate = rate;
-  device->default_rate = rate;
-  device->latency_lo = 0;
-  device->latency_hi = 0;
-
-  collection->device = device;
-  collection->count = 1;
-
   return CUBEB_OK;
 }
 
-static int
-alsa_device_collection_destroy(cubeb * context,
-                               cubeb_device_collection * collection)
-{
-  assert(collection->count == 1);
-  (void)context;
-  free(collection->device);
-  return CUBEB_OK;
+static int  
+alsa_device_collection_destroy(cubeb * context,  
+                               cubeb_device_collection * collection)  
+{  
+  size_t i;  
+  
+  (void)context;  
+  
+  for (i = 0; i < collection->count; ++i) {  
+    free((void *)collection->device[i].device_id);  
+    free((void *)collection->device[i].friendly_name);  
+    free((void *)collection->device[i].group_id);  
+    free((void *)collection->device[i].vendor_name);  
+  }  
+  free(collection->device);  
+  return CUBEB_OK;  
 }
 
 static struct cubeb_ops const alsa_ops = {
-- 
2.39.5
# Project tree:
#   BUILD/
#   ├── build_dir_ALSA-dev/     # created with ./mach build 
#   ├── debdir_ALSA-dev/        # created with ./mach install
#   ├── DEB_templates/          # 
#   │   └── make_deb.sh         # Bash script for Firefox packaging
#   ├── firefox/                # Firefox source code
#   └── PATCHES
#       ├── 0001-Set-MOZ_APP_UA_NAME-to-Firefox-for-clean-UA-strings.patch
#       ├── 0002-Set-MOZ_APP_PROFILE-to-Firefox-for-clean-APP_PROFILE.patch
#       ├── 0003-Fix-system-preferences-for-custom-firefox-dev-builds.patch 
#       └── 0004-ALSA-backend-for-Firefox-Fix-device-enumeration-and-.patch

Create firefox/.mozconfig with a text editor

nano firefox/.mozconfig 
$ cat firefox/.mozconfig
# The default mozconfig is located here: sourcedir/browser/config/mozconfig
. $topsrcdir/browser/config/mozconfig  
mk_add_options MOZ_MAKE_FLAGS="-j$(expr $(nproc) + 2)" 
mk_add_options MOZ_OBJDIR="$(dirname $topsrcdir)"/build_dir_ALSA-dev  
mk_add_options MOZ_APP_DISPLAYNAME="Firefox Developer Edition"  
mk_add_options MOZ_SIMPLE_PACKAGE_NAME=firefox-dev  
ac_add_options --with-app-basename="Firefox Developer Edition" 
ac_add_options --with-app-name=firefox-dev  
ac_add_options --prefix=/usr  
ac_add_options --without-sysroot  
ac_add_options --enable-audio-backends=alsa
mk_add_options MOZ_DEV_EDITION=1
mk_add_options MOZ_APP_REMOTINGNAME=firefox-dev
ac_add_options --with-branding=browser/branding/aurora   #  Firefox Developer Edition
ac_add_options --enable-update-channel=aurora
ac_add_options --disable-crashreporter   # Optional
ac_add_options --disable-system-preferences  # dialectical workaround to enable system preferences
export LDFLAGS="-Wl,--no-keep-memory" 

Update the sorce code:

cd firefox && git pull 

Apply pathes:

$ ls -1 ../PATCHES
0001-Set-MOZ_APP_UA_NAME-to-Firefox-for-clean-UA-strings.patch
0002-Set-MOZ_APP_PROFILE-to-Firefox-for-clean-APP_PROFILE.patch
0003-Fix-system-preferences-for-custom-firefox-dev-builds.patch
0004-ALSA-backend-for-Firefox-Fix-device-enumeration-and-.patch
patch -Np1 -i ../PATCHES/0001*
patch -Np1 -i ../PATCHES/0002*
patch -Np1 -i ../PATCHES/0003* 
patch -Np1 -i ../PATCHES/0004*

Build firefox-dev

./mach clobber
./mach configure
$ grep "MOZ_SYSTEM_PREFERENCES" ../build_dir_ALSA-dev/config.status.json
    "MOZ_SYSTEM_PREFERENCES": "1",
    "MOZ_SYSTEM_PREFERENCES": "1", 
./mach build -v --priority normal 
$ grep "MOZ_SYSTEM_PREFERENCES" ../build_dir_ALSA-dev/mozilla-config.h
#define MOZ_SYSTEM_PREFERENCES 1 

Install to debdir

DESTDIR="$(dirname $(pwd))"/debdir_ALSA-dev ./mach install 
$ tree -L 3 ../debdir_ALSA-dev
../debdir_ALSA-dev
└── usr
    ├── bin
    │   └── firefox-dev -> /usr/lib/firefox-dev/firefox-dev
    └── lib
        └── firefox-dev 
$ ../debdir_ALSA-dev/usr/lib/firefox-dev/firefox-dev
Mozilla Firefox Developer Edition 153.0a1 

Packaging

sudo apt install fakeroot
mkdir "$(dirname $(pwd))"/DEB_templates && cd "$(dirname $(pwd))"/DEB_templates 

Create a script for Firefox packaging with a text editor

nano make_deb.sh

and make it executable.

$ cat make_deb.sh
#!/bin/bash
#
# Firefox Developer Edition Debian Package Builder
# ================================================
#
# This script creates a Debian package for Firefox Developer Edition 
# with security-hardened configuration. 
#
# NOTE: Alternatively, one may try the official Mozilla ./mach repackage deb tool, 
#       though it might be an exercise in masochism.
#
# Usage: ./make_deb.sh
# Location: Run from DEB_templates directory
#
# Project structure:
#   BUILD/
#   ├── build_dir_ALSA-dev/     # Compiled Firefox binaries created with ./mach build 
#   ├── debdir_ALSA-dev/        # Package staging directory created with ./mach install
#   ├── DEB_templates/          # This directory
#   │   └── make_deb.sh         # This script
#   └── firefox/                # Firefox source tree
#
# Output: firefox-dev-ed_<version>_<arch>.deb
#
# ==============================================================================

# Bash Strict Mode
set -euo pipefail

# -----------------------------------------------------------------------------
# Configuration
# -----------------------------------------------------------------------------
DEB_ROOT="$(dirname "$(pwd)")"/debdir_ALSA-dev
SHARE_DIR="$DEB_ROOT/usr/share"
ETC_DIR="$DEB_ROOT/etc"
LIB_DIR="$DEB_ROOT/usr/lib"
PACKAGE_NAME="firefox-dev"
DEB_PACKAGE_NAME="firefox-dev-ed"
VERSION="153.0a1-1"
ARCH="amd64"

echo "Creating Debian package for $PACKAGE_NAME $VERSION..."

# -----------------------------------------------------------------------------
# 1. Create directory structure (FHS compliant)
# -----------------------------------------------------------------------------
install -dm755 "$SHARE_DIR/$PACKAGE_NAME"/{browser/{chrome/icons/default,defaults/preferences},distribution/searchplugins/common}
install -dm755 "$SHARE_DIR/applications"
install -dm755 "$SHARE_DIR/icons/hicolor"/{16x16,32x32,48x48,64x64,128x128,symbolic}/apps
install -dm755 "$SHARE_DIR/doc/$PACKAGE_NAME"
install -dm755 "$SHARE_DIR/lintian/overrides"
install -dm755 "$SHARE_DIR/man/man1"
install -dm755 "$SHARE_DIR/mozilla/extensions"
install -dm755 "$ETC_DIR/$PACKAGE_NAME"/defaults/pref
install -dm755 "$DEB_ROOT/DEBIAN"

# -----------------------------------------------------------------------------
# 2. Create system-wide configuration files
# -----------------------------------------------------------------------------
# These files contain security-hardened Firefox preferences.
# They are placed in /etc/firefox-dev/ and symlinked to the defaults/pref directory.
# This allows system administrators to easily modify defaults.

# Primary configuration file with security preferences
install -m644 <(cat << 'EOF'
// Debian system-wide preferences for Firefox Developer Edition
// ============================================================
//
// This file contains security-hardened default settings for Firefox.
// System administrators can modify these values to change defaults.
//
// Syntax:
//   pref("preference.name", value);           // Default (user can override)
//   pref("preference.name", value, locked);   // Locked (user cannot override)
//
//   String values must be enclosed in double quotes.
//
// Security Configuration:
// ------------------------

// Extension updates
pref("extensions.update.enabled", true);

// Browser behavior
pref("browser.shell.checkDefaultBrowser", false);

// Media settings (disable GMP OpenH264 for privacy)
pref("media.gmp-gmpopenh264.enabled", false);

// Enhanced privacy: disable enhanced new tab page
pref("browser.newtabpage.enhanced", false, locked);

// Telemetry and data reporting (DISABLED for privacy)
pref("datareporting.healthreport.uploadEnabled", false, locked);

// URL bar: disable search suggestions for privacy
pref("browser.urlbar.suggest.searches", false, locked);

// Telemetry (DISABLED for privacy)
pref("toolkit.telemetry.enabled", false, locked);

// Media configuration for better sound quality
pref("media.webm.enabled", false, locked);
pref("media.resampling.enabled", false, locked);
pref("media.cubeb_latency_playback_ms", 160, locked);

// Region and localization (force US/English to prevent fingerprinting)
pref("browser.region.network.url", "", locked);
pref("browser.region.update.enabled", false, locked);
pref("browser.region.network.scan", false, locked);
pref("privacy.spoof_english", 2, locked);
pref("intl.accept_languages", "en-US, en, en-GB", locked);
pref("browser.search.region", "US", locked);
pref("browser.search.geoip.url", "", locked);
pref("distribution.searchplugins.defaultLocale", "en-US", locked);
EOF
) "$ETC_DIR/$PACKAGE_NAME/defaults/pref/firefox-dev.cfg1.js"

# -----------------------------------------------------------------------------
# 4. Create desktop entry (freedesktop.org compliant)
# -----------------------------------------------------------------------------
install -m644 <(cat << 'EOF'
[Desktop Entry]
Name=Firefox Developer Edition
Comment=Web Browser
Exec=/usr/lib/firefox-dev/firefox-dev %u
Icon=firefox-dev
Terminal=false
Type=Application
Categories=Network;WebBrowser;
MimeType=text/html;text/xml;application/xhtml+xml;application/vnd.mozilla.xul+xml;text/mml;x-scheme-handler/http;x-scheme-handler/https;x-scheme-handler/ftp;
StartupNotify=true
EOF
) "$SHARE_DIR/applications/$PACKAGE_NAME.desktop"

# -----------------------------------------------------------------------------
# 5. Create copyright file (Debian Policy compliant)
# -----------------------------------------------------------------------------
install -m644 <(cat << 'EOF'
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: Firefox Developer Edition
Source: https://github.com/mozilla/firefox

License: MPL-2.0
 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/.
EOF
) "$SHARE_DIR/doc/$PACKAGE_NAME/copyright"

# -----------------------------------------------------------------------------
# 6. Create lintian override (suppress expected warnings)
# -----------------------------------------------------------------------------
install -m644 <(echo "$PACKAGE_NAME: binary-without-manpage") \
    "$SHARE_DIR/lintian/overrides/$PACKAGE_NAME"

# -----------------------------------------------------------------------------
# 7. Create man page
# -----------------------------------------------------------------------------
install -m644 <(cat << 'EOF'
.TH FIREFOX-DEV 1 "User Commands"
.SH NAME
firefox-dev \- Mozilla Firefox Developer Edition
.SH DESCRIPTION
Firefox Developer Edition with custom ALSA support.
This build uses ALSA directly for audio output instead of PulseAudio.
.SH FILES
.I /etc/firefox-dev/firefox-dev.cfg*.js
System-wide preference files.
.SH SEE ALSO
Firefox documentation: https://developer.mozilla.org/
EOF
) "$SHARE_DIR/man/man1/$PACKAGE_NAME.1"
gzip -9f "$SHARE_DIR/man/man1/$PACKAGE_NAME.1"

# -----------------------------------------------------------------------------
# 8. Copy icons from Firefox build directory
# -----------------------------------------------------------------------------
ICON_SOURCE_DIR="$LIB_DIR/$PACKAGE_NAME/browser/chrome/icons/default"
if [ -d "$ICON_SOURCE_DIR" ]; then
    for size in 16 32 48 64 128; do
        if [ -f "$ICON_SOURCE_DIR/default${size}.png" ]; then
            install -m644 "$ICON_SOURCE_DIR/default${size}.png" \
                "$SHARE_DIR/icons/hicolor/${size}x${size}/apps/$PACKAGE_NAME.png"
        fi
    done
fi

# -----------------------------------------------------------------------------
# 9. Calculate dependencies and Installed-Size
# -----------------------------------------------------------------------------
# This section uses dpkg-shlibdeps to automatically calculate library dependencies.
# A temporary debian directory is created for dependency calculation.

# Create control file template for dpkg-shlibdeps
mkdir -p debian
cat > debian/control << EOF
Source: ${DEB_PACKAGE_NAME}
Package: ${DEB_PACKAGE_NAME}
Depends: \${shlibs:Depends}
EOF

# Calculate dependencies using dpkg-shlibdeps
dpkg-shlibdeps -x"$PACKAGE_NAME" -l"$DEB_ROOT"/usr/lib \
    --ignore-missing-info -e $(find "$DEB_ROOT" -type f 2>/dev/null) 2>/dev/null

# Extract dependencies or use fallback
if [ -f debian/substvars ] && grep -q "shlibs:Depends" debian/substvars; then
    DEPS=$(grep "shlibs:Depends" debian/substvars | sed 's/shlibs:Depends=//')
else
    DEPS="libc6"
fi

# Add changelog (required by dpkg-gencontrol)
cat > debian/changelog << EOF
${DEB_PACKAGE_NAME} (${VERSION}) unstable; urgency=medium
  * Custom build with security enhancements and ALSA support
 -- Devuan Packaging <devuan@devuan.org>  $(date -R)
EOF

# Add misc dependencies (required by dpkg-gencontrol)
echo "misc:Depends=" >> debian/substvars
echo "misc:Pre-Depends=" >> debian/substvars

# Create control file template for dpkg-gencontrol
cat > debian/control << EOF
Source: ${DEB_PACKAGE_NAME}

Package: ${DEB_PACKAGE_NAME}
Architecture: ${ARCH}
EOF

# Run dpkg-gencontrol to calculate Installed-Size
dpkg-gencontrol -p${DEB_PACKAGE_NAME} \
    -ldebian/changelog \
    -Tdebian/substvars \
    -P"$DEB_ROOT" \
    2>/dev/null

# Extract Installed-Size
SIZE=$(sed -n 's/Installed-Size: //p' "$DEB_ROOT/DEBIAN/control")

# Clean up temporary directory
rm -rf debian

# -----------------------------------------------------------------------------
# 10. Create DEBIAN/control file with all fields in correct Debian order
# -----------------------------------------------------------------------------

cat > "$DEB_ROOT/DEBIAN/control" << EOF
Package: ${DEB_PACKAGE_NAME}
Version: ${VERSION}
Priority: optional
Section: web
Architecture: ${ARCH}
Maintainer: Devuan Packaging <devuan@devuan.org>
Installed-Size: ${SIZE}
Provides: gnome-www-browser, www-browser
Depends: ${DEPS}
Conflicts: firefox-dev
Recommends: libavcodec61 | libavcodec-extra61 | libavcodec60 | libavcodec-extra60 | libavcodec59 | libavcodec-extra59 | libavcodec58 | libavcodec-extra58 | libavcodec57 | libavcodec-extra57 | libavcodec56 | libavcodec-extra56 | libavcodec55 | libavcodec-extra55 | libavcodec54 | libavcodec-extra54 | libavcodec53 | libavcodec-extra53
Suggests: fonts-stix | otf-stix, fonts-lmodern, libgssapi-krb5-2 | libkrb53, libcanberra0
Description: Firefox Developer Edition (ALSA build)
 Firefox Developer Edition is a high-performance browser for web developers,
 featuring Multi-line Console Editor and WebSocket Inspector. It runs
 side-by-side with Release, Beta, or Nightly builds using a separate Profile
 Directory and installation path.
 .
 This custom build includes:
  * Security-hardened default preferences
  * ALSA audio support (no PulseAudio dependency)
  * Pre-configured developer defaults: remote debugging enabled, dark theme,
    and developer toolbar
EOF

# -----------------------------------------------------------------------------
# 11. Generate md5sums for all installed files
# -----------------------------------------------------------------------------
cd "$DEB_ROOT"
install -m644 <(find . -type f -not -path './DEBIAN/*' -printf '%P\0' | \
    xargs -0 md5sum | sort -k 2) DEBIAN/md5sums

# Display package structure
echo "Package structure:"
tree -L 3 .

# Return to script directory
cd "$(dirname "$(pwd)")"/DEB_templates

# -----------------------------------------------------------------------------
# 12. Build the package using dpkg-deb
# -----------------------------------------------------------------------------
echo "Building Debian package..."
fakeroot -- dpkg-deb -b "$DEB_ROOT" "${DEB_PACKAGE_NAME}_${VERSION}_${ARCH}.deb"

echo "Package built successfully: ${DEB_PACKAGE_NAME}_${VERSION}_${ARCH}.deb"

Run make_deb.sh to build Debian package

$ ./make_deb.sh
Creating Debian package for firefox-dev 153.0a1-1...
Package structure:
.
├── DEBIAN
│   ├── control
│   └── md5sums
├── etc
│   └── firefox-dev
│       └── defaults
└── usr
    ├── bin
    │   └── firefox-dev -> /usr/lib/firefox-dev/firefox-dev
    ├── lib
    │   └── firefox-dev
    └── share
        ├── applications
        ├── doc
        ├── firefox-dev
        ├── icons
        ├── lintian
        ├── man
        └── mozilla

17 directories, 3 files
Building Debian package...
dpkg-deb: building package 'firefox-dev-ed' in 'firefox-dev-ed_153.0a1-1_amd64.deb'.
Package built successfully: firefox-dev-ed_153.0a1-1_amd64.deb 
$ ls -1 *deb
firefox-dev-ed_153.0a1-1_amd64.deb

Install

sudo dpkg -i firefox-dev-ed_153.0a1-1_amd64.deb 
$ firefox-dev --version
Mozilla Firefox Developer Edition 153.0a1 

Hacking dependencies

The DEBIAN/control file is not included in DEBIAN/md5sums.
You can edit it freely without regenerating checksums.
To rebuild the package, run:

fakeroot -- dpkg-deb -b ../debdir_ALSA-dev firefox-dev-ed_153.0a1-1_amd64.deb

Repackaging

fakeroot -u -- dpkg-repack firefox-dev-ed
fakeroot -u dpkg-repack --generate <package-name> 

Example of Firefox hardened configuration:

$ cat /etc/firefox-dev/defaults/pref/firefox-dev.cfg5.js
// ============================================================================
// Firefox Security-Hardened Configuration
// ============================================================================
// This file locks all security-critical preferences to prevent 
// modification by users, extensions, or malicious code. 
// Place in /etc/firefox-dev/defaults/pref/
// ============================================================================

// -----------------------------------------------------------------------------
// Sandbox
// -----------------------------------------------------------------------------
// Linux Content Process Sandbox
// Level 6 = default-deny for ioctl (most restrictive)
pref("security.sandbox.content.level", 6, locked);
// Whitelist paths (empty string = no whitelist)
pref("security.sandbox.content.write_path_whitelist", "", locked);
pref("security.sandbox.content.read_path_whitelist", "", locked);
pref("security.sandbox.content.syscall_whitelist", "", locked);

// Socket Process Sandbox
// Level 2 = default-deny for ioctl
pref("security.sandbox.socket.process.level", 2, locked);

// Sandbox Logging (disable for security)
pref("security.sandbox.logging.enabled", false, locked);

// -----------------------------------------------------------------------------
// Remote Debugging and Developer Tools
// -----------------------------------------------------------------------------
// Disable remote debugging
pref("devtools.debugger.remote-enabled", false, locked);
// Port number for the debugging server (default: 6000)
pref("devtools.debugger.remote-port", 6000, locked);
// Restrict connections to localhost only (default: true)
pref("devtools.debugger.force-local", true, locked);
// Disable browser chrome debugging (debugging Firefox itself). Debian's default: false
pref("devtools.chrome.enabled", false, locked);
// Block access to all developer tools functionality
pref("devtools.policy.disabled", true, locked);

// -----------------------------------------------------------------------------
// Cookie and Privacy Settings
// -----------------------------------------------------------------------------
// Strongest cookie privacy setting (reject trackers, partition third-party cookies)
pref("network.cookie.cookieBehavior", 5, locked);
pref("network.cookie.cookieBehavior.pbmode", 5, locked);
// Block-by-default with opt-in partitioning (more restrictive than dFPI)
pref("network.cookie.cookieBehavior.optInPartitioning", true, locked);
pref("network.cookie.cookieBehavior.optInPartitioning.pbmode", true, locked);
// Block third-party cookies from tracking protection list
pref("network.cookie.cookieBehavior.trackerCookieBlocking", true, locked);
// Prevent sync from propagating weaker settings
pref("services.sync.prefs.sync.network.cookie.cookieBehavior", false, locked);

// Global Privacy Control
pref("privacy.globalprivacycontrol.enabled", true, locked);
pref("privacy.globalprivacycontrol.functionality.enabled", true, locked);
pref("privacy.globalprivacycontrol.pbmode.enabled", true, locked);

// Disable First Party Isolation (incompatible with behavior 5)
pref("privacy.firstparty.isolate", false, locked);
pref("privacy.firstparty.isolate.block_post_message", false, locked);
pref("privacy.firstparty.isolate.restrict_opener_access", true, locked);
pref("privacy.firstparty.isolate.use_site", false, locked);

// -----------------------------------------------------------------------------
// TLS/SSL Configuration
// -----------------------------------------------------------------------------
// Minimum TLS version (1=tls1, 2=tls1.1, 3=tls1.2, 4=tls1.3)
pref("security.tls.version.min", 3, locked);
// Maximum TLS version
pref("security.tls.version.max", 4, locked);
// Disable deprecated TLS versions
pref("security.tls.version.enable-deprecated", false, locked);

// TLS Security Features
pref("security.ssl.require_safe_negotiation", true, locked);
pref("security.tls.hello_downgrade_check", true, locked);
pref("security.ssl.enable_ocsp_stapling", true, locked);
pref("security.OCSP.require", true, locked);
pref("security.OCSP.enabled", 1, locked);

// TLS 1.3 Features
pref("security.tls.enable_0rtt_data", false, locked);
pref("security.tls.enable_post_handshake_auth", true, locked);
pref("security.tls.enable_delegated_credentials", true, locked);

// Encrypted Client Hello (ECH)
pref("network.dns.echconfig.enabled", true, locked);
pref("network.dns.http3_echconfig.enabled", true, locked);

// Disable weak cipher suites
pref("security.ssl3.rsa_aes_128_sha", false, locked);
pref("security.ssl3.rsa_aes_256_sha", false, locked);
pref("security.ssl3.rsa_aes_128_gcm_sha256", false, locked);
pref("security.ssl3.rsa_aes_256_gcm_sha384", false, locked);
pref("security.ssl3.deprecated.rsa_des_ede3_sha", false, locked);

// -----------------------------------------------------------------------------
// Content Security
// -----------------------------------------------------------------------------
pref("security.block_fileuri_script_with_wrong_mime", true, locked);
pref("security.mixed_content.block_active_content", true, locked);
pref("security.mixed_content.block_display_content", true, locked);
pref("security.mixed_content.upgrade_display_content", true, locked);
pref("security.insecure_connection_text.enabled", true, locked);
pref("security.insecure_connection_text.pbmode.enabled", true, locked);
pref("security.warn_submit_secure_to_insecure", true, locked);

// HTTPS-Only Mode
//pref("dom.security.https_only_mode", true, locked);
//pref("dom.security.https_only_mode_pbm", true, locked);
//pref("dom.security.https_first", true, locked);
//pref("dom.security.https_first_pbm", true, locked);

// HTTPS-First (less aggressive than HTTPS-Only)
pref("dom.security.https_first", true, locked);
pref("dom.security.https_first_pbm", true, locked);

// -----------------------------------------------------------------------------
// Certificate and PKI Settings
// -----------------------------------------------------------------------------
pref("security.default_personal_cert", "Ask Every Time", locked);
pref("security.pki.certificate_transparency.mode", 1, locked);
pref("security.ssl.errorReporting.enabled", true, locked);
pref("security.enterprise_roots.enabled", true, locked);

// -----------------------------------------------------------------------------
// Safe Browsing
// -----------------------------------------------------------------------------
pref("browser.safebrowsing.malware.enabled", true, locked);
pref("browser.safebrowsing.phishing.enabled", true, locked);
pref("browser.safebrowsing.downloads.enabled", true, locked);
pref("browser.safebrowsing.downloads.remote.block_potentially_unwanted", true, locked);
pref("browser.safebrowsing.downloads.remote.block_uncommon", true, locked);

// -----------------------------------------------------------------------------
// WebAuthn
// -----------------------------------------------------------------------------
pref("security.webauthn.always_allow_direct_attestation", false, locked);

// -----------------------------------------------------------------------------
// CSP Reporting
// -----------------------------------------------------------------------------
pref("security.csp.reporting.enabled", true, locked);

// -----------------------------------------------------------------------------
// Extension Security
// -----------------------------------------------------------------------------
//pref("xpinstall.whitelist.required", true, locked);
//pref("xpinstall.enabled", false, locked);
//pref("extensions.update.enabled", false, locked);

// -----------------------------------------------------------------------------
// Telemetry and Data Collection
// -----------------------------------------------------------------------------
pref("datareporting.healthreport.uploadEnabled", false, locked);
pref("toolkit.telemetry.enabled", false, locked);
pref("browser.newtabpage.activity-stream.feeds.telemetry", false, locked);
pref("browser.newtabpage.activity-stream.telemetry", false, locked);

// -----------------------------------------------------------------------------
// Network Security
// -----------------------------------------------------------------------------
pref("network.http.sendRefererHeader", 2, locked);
pref("privacy.resistFingerprinting", true, locked);
pref("privacy.trackingprotection.enabled", true, locked);
pref("privacy.trackingprotection.pbmode.enabled", true, locked);

// -----------------------------------------------------------------------------
// DNS over HTTPS
// -----------------------------------------------------------------------------
// pref("network.trr.mode", 3, locked);

// DoH with fallback (mode 2 instead of 3)
pref("network.trr.mode", 2, locked);

// -----------------------------------------------------------------------------
// Local Network Access
// -----------------------------------------------------------------------------
pref("network.lna.blocking", true, locked);

// -----------------------------------------------------------------------------
// Post-Quantum Cryptography
// -----------------------------------------------------------------------------
pref("security.tls.post_quantum_key_agreement.enabled", true, locked);

#13 Re: Freedom Hacks » Chasing the Fox (Caccia alla volpe) » 2026-05-05 14:17:10

greenjeans wrote:

That would be a real service to the community

A real service to the community might be to avoid annoying others with your comments and advice. Please do not post in my topics.

_https://en.wikipedia.org/wiki/Sndio
sndio is the software layer of the OpenBSD operating system that manages sound cards and MIDI ports. It provides an optional sound server and a documented application programming interface to access either the server or the audio and MIDI hardware in a uniform way.

_https://man.openbsd.org/sndiod.8
BUGS
Resampling is low quality; down-sampling especially should be avoided when recording.

If -a off is used, sndiod creates sub-devices to expose first and then opens the audio hardware on demand. Technically, this allows sndiod to attempt to use one of the sub-devices it exposes as an audio device, creating a deadlock. There's nothing to prevent the user from shooting themselves in the foot by creating such a deadlock.

sndio might be perfectly suitable for semi-deaf and half-demented users.

If someone really needs sndio, he might consider forking it to integrate the fftrate resampler for higher-quality audio processing
_https://github.com/PetrovSE/fftrate

_https://man.openbsd.org/sndiod.8
BUGS
Resampling is low quality; down-sampling especially should be avoided when recording.

Perhaps sndio developers engage in post-modern humor. Linux users are unlikely to notice a Monty Python situation where a "bug" is documented rather than fixed.

If you don’t get this kind of humor, think of post-truth, post-philosophy, and post-documentation. It’s a post-real world where absurdity is plainly documented — in man pages, wikis, and official notes — without irony. The joke isn’t hidden; it’s right there, labeled "BUG". The word "bug" has become a post-word with a post-meaning: not a flaw to fix, but a punchline accepted as fact.

What is special about post-reality is that it can be consumed innocently — just like myth, it appears factual, not constructed.

The myth consumer takes the signification for a system of facts: myth is read as a factual system whereas it is but a semiological system.
Roland Barthes, Myth Today.

#14 Re: Freedom Hacks » Chasing the Fox (Caccia alla volpe) » 2026-05-05 12:37:54

To get sndio working with Firefox (Cubeb) you have to build it with sndio support

# Install sndio development libraries  
sudo apt-get install libsndio-dev  
  
# Add to your mozconfig  
ac_add_options --enable-sndio 

# Alternatively, you may try to disable ALSA
ac_add_options --enable-audio-backends=sndio

# Firefox about:config
media.cubeb.backend      sndio
sndio Backend   cubeb_sndio.c:314-360

Dynamic library loading
Basic stream operations
48kHz preferred sample rate   cubeb_sndio.c:535-536
2048 frame minimum latency    cubeb_sndio.c:546-547

#15 Re: Freedom Hacks » Chasing the Fox (Caccia alla volpe) » 2026-05-04 18:47:08

[Because of epidemic of deafness] Sonova is now exiting the consumer audio market to refocus on its core hearing care business (hearing aids and cochlear implants). The professional division remains with the Sennheiser family.

After the Fox - Gold Robbery of Cairo
_https://youtu.be/zgcGyt6qOLg
_https://en.wikipedia.org/wiki/After_the_Fox

#16 Re: Freedom Hacks » Chasing the Fox (Caccia alla volpe) » 2026-05-04 18:02:04

Do you want to amplify audio volume in Firefox? It will reduce sound quality. At high levels, it may damage hearing or speakers.

#17 Re: Freedom Hacks » Chasing the Fox (Caccia alla volpe) » 2026-05-04 15:56:48

Firefox:
Does not resample audio by default.
WebM can be easily disabled.
PulseAudio backend can be easily disabled.

Chrome (and all Chrome-based browsers):
Resampling cannot be disabled.
WebM cannot be disabled.
PulseAudio backend cannot be disabled.

This is because, perhaps, Chrome developers do not hear the difference.

#18 Re: Freedom Hacks » Chasing the Fox (Caccia alla volpe) » 2026-05-04 13:42:04

Firefox Developer Edition

The browser made for developers
All the latest developer tools in beta in addition to features like the Multi-line Console Editor and WebSocket Inspector.
A separate profile and path so you can easily run it alongside Release or Beta Firefox.
Preferences tailored for web developers: Browser and remote debugging are enabled by default, as are the dark theme and developer toolbar button.
_https://www.firefox.com/en-US/channel/desktop/developer

Building Firefox On Linux
_https://firefox-source-docs.mozilla.org/setup/linux_build.html
_https://firefox-source-docs.mozilla.org/build/buildsystem/mozconfigs.html

mozconfig for Firefox Developer Edition (ALSA only, without pulse-rust backend):

$ cat .mozconfig
# The default mozconfig is located here: sourcedir/browser/config/mozconfig
. $topsrcdir/browser/config/mozconfig  
mk_add_options MOZ_MAKE_FLAGS="-j$(expr $(nproc) + 2)" 
mk_add_options MOZ_OBJDIR="$(dirname $topsrcdir)"/build_dir_ALSA-dev  
mk_add_options MOZ_APP_DISPLAYNAME="Firefox Developer Edition"  
mk_add_options MOZ_SIMPLE_PACKAGE_NAME=firefox-dev  
ac_add_options --with-app-basename="Firefox Developer Edition"  
ac_add_options --with-app-name=firefox-dev  
ac_add_options --prefix=/usr  
ac_add_options --without-sysroot  # classified
ac_add_options --enable-audio-backends=alsa
mk_add_options MOZ_DEV_EDITION=1
ac_add_options --with-branding=browser/branding/aurora   #  Firefox Developer Edition
export LDFLAGS="-Wl,--no-keep-memory"
cd firefox
git pull
./mach clobber
./mach configure
./mach build -v --priority normal
$ ./mach run --version
Mozilla Firefox Developer Edition 152.0a1 
DESTDIR="$(dirname $(pwd))"/debdir_ALSA-dev ./mach install 
$ ls -1 ../debdir_ALSA-dev/usr
bin
lib

$ file ../debdir_ALSA-dev/usr/bin/firefox-dev
../debdir_ALSA-dev/usr/bin/firefox-dev: broken symbolic link to /usr/lib/firefox-dev/firefox-dev 

A bash script to create the /usr/share directory structure for Firefox Developer Edition
(modify it to suit your needs)

$ cat make_share.sh
#!/bin/bash

# Create Firefox Developer Edition /usr/share structure
SHARE_DIR="$(dirname $(pwd))"/debdir_ALSA-dev/usr/share
PACKAGE_NAME="firefox-dev"
APP_NAME="Firefox Developer Edition"

echo "Creating /usr/share structure for $APP_NAME..."

# Create main directories
mkdir -p "$SHARE_DIR/$PACKAGE_NAME"/{browser/{chrome/icons/default,defaults/preferences},distribution/searchplugins/common}
mkdir -p "$SHARE_DIR/applications"
mkdir -p "$SHARE_DIR/icons/hicolor"/{16x16,32x32,48x48,64x64,128x128,symbolic}/apps
mkdir -p "$SHARE_DIR/doc/$PACKAGE_NAME"
mkdir -p "$SHARE_DIR/man/man1"
mkdir -p "$SHARE_DIR/lintian/overrides"
mkdir -p "$SHARE_DIR/mozilla/extensions/{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"

# Create desktop entry file
cat > "$SHARE_DIR/applications/$PACKAGE_NAME.desktop" << EOF
[Desktop Entry]
Version=1.0
Type=Application
Name=$APP_NAME
Comment=The browser made for developers
Exec=/usr/bin/$PACKAGE_NAME %U
Icon=$PACKAGE_NAME
Terminal=false
Categories=Network;WebBrowser;
StartupNotify=true
MimeType=text/html;text/xml;application/xhtml+xml;application/vnd.mozilla.xul+xml;text/mml;x-scheme-handler/http;x-scheme-handler/https;x-scheme-handler/ftp;
EOF

# Create copyright file
cat > "$SHARE_DIR/doc/$PACKAGE_NAME/copyright" << EOF
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: $PACKAGE_NAME
Source: https://hg.mozilla.org/mozilla-central/

Files: *
Copyright: 2024 Mozilla Foundation
License: MPL-2.0
 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/.

License: MPL-2.0
EOF

# Create lintian overrides
cat > "$SHARE_DIR/lintian/overrides/$PACKAGE_NAME" << EOF
$PACKAGE_NAME: package-name-doesnt-match-sonames
$PACKAGE_NAME: binary-without-manpage
$PACKAGE_NAME: missing-dep-for-interpreter /usr/bin/perl (perl >= 5.6)
$PACKAGE_NAME: script-not-executable ./usr/share/$PACKAGE_NAME/defaults/preferences/syspref.js
EOF

# Create man page
cat > "$SHARE_DIR/man/man1/$PACKAGE_NAME.1" << EOF
.TH $PACKAGE_NAME 1 "2024" "Mozilla Foundation" "User Commands"
.SH NAME
$PACKAGE_NAME \- Mozilla Firefox Developer Edition web browser
.SH DESCRIPTION
Firefox Developer Edition is the blazing fast browser that offers cutting edge developer tools and latest features like CSS Grid support and framework debugging
.SH OPTIONS
.TP
\fB\--help\fR
Prints the command line options.
.TP
\fB\--version\fR
Prints the version information.
.SH FILES
.I /usr/lib/$PACKAGE_NAME/firefox-dev
\- The main executable
.SH SEE ALSO
.BR firefox(1)
EOF

gzip -9 "$SHARE_DIR/man/man1/$PACKAGE_NAME.1"

# Copy icons from build if available
BUILD_LIB_DIR="$(dirname $(pwd))"/debdir_ALSA-dev/usr/lib/firefox-dev
if [ -f "$BUILD_LIB_DIR/browser/chrome/icons/default/default16.png" ]; then
    for size in 16 32 48 64 128; do
        if [ -f "$BUILD_LIB_DIR/browser/chrome/icons/default/default${size}.png" ]; then
            cp "$BUILD_LIB_DIR/browser/chrome/icons/default/default${size}.png" \
               "$SHARE_DIR/icons/hicolor/${size}x${size}/apps/$PACKAGE_NAME.png"
        fi
    done
else
    # Create placeholder icons
    for size in 16 32 48 64 128; do
        convert -size ${size}x${size} xc:transparent "$SHARE_DIR/icons/hicolor/${size}x${size}/apps/$PACKAGE_NAME.png" 2>/dev/null || \
        touch "$SHARE_DIR/icons/hicolor/${size}x${size}/apps/$PACKAGE_NAME.png"
    done
fi

# Set proper permissions
chmod 644 "$SHARE_DIR/applications/$PACKAGE_NAME.desktop"
chmod 644 "$SHARE_DIR/doc/$PACKAGE_NAME"/*
chmod 644 "$SHARE_DIR/lintian/overrides/$PACKAGE_NAME"
find "$SHARE_DIR" -type d -exec chmod 755 {} \;

echo "Created /usr/share structure in $SHARE_DIR"
echo "Directory tree:"
find "$SHARE_DIR" -type d | sort

echo "Files created:"
find "$SHARE_DIR" -type f | sort
./make_share.sh
$ ls -1 ../debdir_ALSA-dev/usr
bin
lib
share

How to calculate dependencies

Make a template

mkdir debian

echo -e "Source: firefox-dev\nPackage: firefox-dev\nDepends: \${shlibs:Depends}" >> debian/control 
$ cat debian/control
Source: firefox-dev
Package: firefox-dev
Depends: ${shlibs:Depends}

Run "dpkg-shlibdeps" to calculate dependencies

dpkg-shlibdeps -v -xfirefox-dev -l"$(dirname $(pwd))"/debdir_ALSA-dev/usr/lib --ignore-missing-info -e $(find "$(dirname $(pwd))"/debdir_ALSA-dev/usr -type f 2>/dev/null) 
$ cat debian/substvars
shlibs:Depends=libasound2 (>= 1.1.0), libatk1.0-0 (>= 1.12.4), libc6 (>= 2.36), libcairo-gobject2 (>= 1.10.0), libcairo2 (>= 1.10.0), libdbus-1-3 (>= 1.9.14), libfontconfig1 (>= 2.12.6), libfreetype6 (>= 2.11.1), libgcc-s1 (>= 4.2), libgdk-pixbuf-2.0-0 (>= 2.22.0), libglib2.0-0 (>= 2.37.3), libgtk-3-0 (>= 3.13.7), libharfbuzz0b (>= 0.6.0), libnspr4 (>= 2:4.12), libnss3 (>= 2:3.82), libpango-1.0-0 (>= 1.14.0), libpangocairo-1.0-0 (>= 1.14.0), libstdc++6 (>= 12), libx11-6, libx11-xcb1 (>= 2:1.8.4), libxcb-shm0, libxcb1, libxcomposite1 (>= 1:0.4.5), libxcursor1 (>> 1.1.2), libxdamage1 (>= 1:1.1), libxext6, libxfixes3, libxi6, libxrandr2 (>= 2:1.4.0), libxrender1, zlib1g (>= 1:1.1.4)
install -vm0755 -d "$(dirname $(pwd))"/debdir_ALSA-dev/DEBIAN
$ cat "$(dirname $(pwd))"/debdir_ALSA-dev/DEBIAN/control
Package: firefox-dev
Version: 152.0a1
Priority: optional
Section: web
Architecture: amd64
Maintainer: Devuan
Installed-Size: 433 MB
Provides: gnome-www-browser, www-browser
Depends: libc6 
Recommends: libavcodec61 | libavcodec-extra61 | libavcodec60 | libavcodec-extra60 | libavcodec59 | libavcodec-extra59 | libavcodec58 | libavcodec-extra58 | libavcodec57 | libavcodec-extra57 | libavcodec56 | libavcodec-extra56 | libavcodec55 | libavcodec-extra55 | libavcodec54 | libavcodec-extra54 | libavcodec53 | libavcodec-extra53
Suggests: fonts-stix | otf-stix, fonts-lmodern, libgssapi-krb5-2 | libkrb53, libcanberra0
Description: Mozilla Firefox Developer Edition. ALSA only.
 The browser made for developers
 .
 All the latest developer tools in beta in addition to features like the Multi-line Console Editor and WebSocket Inspector.
 .
 A separate profile and path so you can easily run it alongside Release or Beta Firefox.
 .
 Preferences tailored for web developers: Browser and remote debugging are enabled by default, as are the dark theme and developer toolbar button.
 .
 Mozilla Firefox Developer Edition. ALSA only, without pulse-rust backend.

Generate DEBIAN/md5sums

cd "$(dirname $(pwd))"/debdir_ALSA-dev

find . -type f -not -path "./DEBIAN/*" -exec md5sum {} + | sort -k 2 | sed 's/\.\/\(.*\)/\1/' > DEBIAN/md5sums

cd ..

chmod 0644 -- debdir_ALSA-dev/DEBIAN/md5sums 

Make Debian package

$ fakeroot -- dpkg-deb -b debdir_ALSA-dev firefox-dev_152.0a1_amd64.deb
dpkg-deb: building package 'firefox-dev' in 'firefox-dev_152.0a1_amd64.deb'.

$ ls -1 *deb
firefox-dev_152.0a1_amd64.deb

Install

sudo dpkg -i firefox-dev_152.0a1_amd64.deb
$ firefox-dev --version
Mozilla Firefox Developer Edition 152.0a1

Firefox 152.0a1 is not on YouTube’s allowlist. To enable YouTube live chat, use a User Agent override to spoof Firefox 150.0. This prevents YouTube from incorrectly flagging your browser as an 'older version' and blocking the chat feature.

about:config
general.useragent.override      Mozilla/5.0 (X11; Linux x86_64; rv:150.0) Gecko/20100101 Firefox/150.0	
privacy.resistFingerprinting    false   # default

NOTE: This guide is for experienced Linux users. If you have need help, please start a new topic on "Desktop and Multimedia".

#19 Re: Freedom Hacks » Chasing the Fox (Caccia alla volpe) » 2026-04-30 21:05:13

To summare "secret knowledge":

Configuration Editor for Firefox
_https://support.mozilla.org/en-US/kb/about-config-editor-firefox

Firefox settings for better sound quality

about:config

media.resampling.enabled            false
media.webm.enabled                  false
media.mediasource.webm.enabled	   false  # it might be deprecated
media.cubeb.backend		   alsa   # if ALSA backend  is available
media.cubeb_latency_playback_ms		160 

NOTE: Firefox's default value (for all platforms):

media.cubeb_latency_playback_ms		100 

This is because Firefox is optimized for macOS, not for Linux with ALSA.

media.encoder.webm.enabled       false   # Disable WebM recording
media.mediasource.vp9.enabled    false   # Disable WebM in MSE 

Media Source Extensions (MSE)
_https://en.wikipedia.org/wiki/Media_Source_Extensions

Verification:

1. about:support  - search for "Audio Backend"

2. MOZ_LOG="MediaDecoder:4,cubeb:5"

On both Linux and macOS, you need Firefox logs to detect unwanted resampling, or to verify that Firefox does not resample.

Linux logs:

MOZ_LOG="MediaDecoder:4,cubeb:5" stdbuf -oL firefox 2>&1 https://www.youtube.com/watch?v=X0lwWwJJfXk | grep  --line-buffered -E "MetadataLoaded.*rate=|FirstFrameLoaded.*rate=|CubebStreamInit output stream rate|target rate|Output hardware|Input|Output|Rates" | grep -vE "hasVideo=0|hasAudio=0" 
$ MOZ_LOG="MediaDecoder:4,cubeb:5" stdbuf -oL firefox 2>&1 https://www.youtube.com/watch?v=X0lwWwJJfXk | grep  --line-buffered -E "MetadataLoaded.*rate=|FirstFrameLoaded.*rate=|CubebStreamInit output stream rate|target rate|Output hardware|Input|Output|Rates" | grep -vE "hasVideo=0|hasAudio=0" 
[Child 25647: Main Thread]: D/MediaDecoder MediaDecoder[7f2a416c2e00] MetadataLoaded, channels=2 rate=44100 hasAudio=1 hasVideo=1
[Child 25647: Main Thread]: D/MediaDecoder MediaDecoder[7f2a416c2e00] FirstFrameLoaded, channels=2 rate=44100 hasAudio=1 hasVideo=1 mPlayState=PLAY_STATE_LOADING transportSeekable=1
[Child 25647: Main Thread]: D/MediaDecoder MediaDecoder[7f2a2c7d7d00] MetadataLoaded, channels=2 rate=44100 hasAudio=1 hasVideo=1
[Child 25647: Main Thread]: D/MediaDecoder MediaDecoder[7f2a2c7d7d00] FirstFrameLoaded, channels=2 rate=44100 hasAudio=1 hasVideo=1 mPlayState=PLAY_STATE_LOADING transportSeekable=1
[Child 25647: MediaDecoderStateMachine #1]: I/cubeb CubebStreamInit output stream rate 44100
[fftrate ALSA plugin log: 44100 --> 48000 Hz]
Input:  44100 Hz, 2 ch, 's32_le' (0xa): dummy = 0, period = 1764
Output: 48000 Hz, 2 ch, 's16_le' (0x2): dummy = 0, period = 1920
Rates:  44100 --> 48000 (J: 0.00%, T: FFT, W: Vorbis) 

macOS Firefox logs:

about:support
Name 	Firefox
Version 	151.0b3
Audio Backend	audiounit-rust
Max Channels	2
Preferred Sample Rate	44100

about:config
media.resampling.enabled     false
media.webm.enabled           false   # Disable WebM playback 

[grep = GNU grep]

MOZ_LOG="MediaDecoder:4,cubeb:5" stdbuf -oL /Applications/Firefox\ Developer\ Edition.app/Contents/MacOS/firefox 2>&1 https://youtu.be/qeUcGD4rRRc | ggrep  --line-buffered -E "MetadataLoaded.*rate=|FirstFrameLoaded.*rate=|CubebStreamInit output stream rate|target rate|Output hardware" | ggrep -vE "hasVideo=0|hasAudio=0"
➤ MOZ_LOG="MediaDecoder:4,cubeb:5" stdbuf -oL /Applications/Firefox\ Developer\ Edition.app/Contents/MacOS/firefox 2>&1 https://youtu.be/qeUcGD4rRRc | ggrep  --line-buffered -E "MetadataLoaded.*rate=|FirstFrameLoaded.*rate=|CubebStreamInit output stream rate|target rate|Output hardware" | ggrep -vE "hasVideo=0|hasAudio=0"
[Child 2125: Main Thread]: D/MediaDecoder MediaDecoder[133ffa100] MetadataLoaded, channels=2 rate=44100 hasAudio=1 hasVideo=1
[Child 2125: Main Thread]: D/MediaDecoder MediaDecoder[133ffa100] FirstFrameLoaded, channels=2 rate=44100 hasAudio=1 hasVideo=1 mPlayState=PLAY_STATE_LOADING transportSeekable=1
[Child 2125: MediaDecoderStateMachine #1]: I/cubeb CubebStreamInit output stream rate 44100
[Parent 2109: AudioIPC Server RPC]: E/cubeb mod.rs:4077: (0x14d932800) Output hardware description: AudioStreamBasicDescription { mSampleRate: 44100.0, mFormatID: 1819304813, mFormatFlags: 9, mBytesPerPacket: 8, mFramesPerPacket: 1, mBytesPerFrame: 8, mChannelsPerFrame: 2, mBitsPerChannel: 32, mReserved: 0 }
[Parent 2109: AudioIPC Server RPC]: E/cubeb cubeb_resampler_internal.h:555:Input and output sample-rate match, target rate of 44100Hz

Explanation:

Input Source Media Rate: 44100 Hz (from YouTube AAC metadata)

[Child 2125: Main Thread]: D/MediaDecoder MediaDecoder[133ffa100] MetadataLoaded, channels=2 rate=44100 hasAudio=1 hasVideo=1  
[Child 2125: Main Thread]: D/MediaDecoder MediaDecoder[133ffa100] FirstFrameLoaded, channels=2 rate=44100 hasAudio=1 hasVideo=1 mPlayState=PLAY_STATE_LOADING transportSeekable=1 

Output Stream Rate: 44100 Hz (Cubeb initialization)

[Child 2125: MediaDecoderStateMachine #1]: I/cubeb CubebStreamInit output stream rate 44100 

Conclusion: Since input source rate (44100 Hz) = output stream rate (44100 Hz), no resampling is occurring in Firefox's audio pipeline.

NOTE: To prevent software resampling by the macOS software mixer, set the sample rate to 44100Hz using the Audio MIDI Setup utility:

open -a Audio\ MIDI\ Setup.app 

Reference media files:
Robert de Visée Prélude et Allemande, Jonas Nordberg, theorbo
_https://youtu.be/qeUcGD4rRRc
The 10 Questions Everyone Asks About My 6-Foot, 14-String Lute [Theorbo]
_https://www.youtube.com/watch?v=X0lwWwJJfXk
The coolest LUTE I've ever seen!
_https://www.youtube.com/watch?v=4YmELV5p6ZY

Building Firefox On Linux
_https://firefox-source-docs.mozilla.org/setup/linux_build.html
_https://firefox-source-docs.mozilla.org/build/buildsystem/mozconfigs.html

A secret mozconfig to disable pulse-rust backend

$ cat .mozconfig
# Default: sourcedir/browser/config/mozconfig
. $topsrcdir/browser/config/mozconfig  
mk_add_options MOZ_MAKE_FLAGS="-j$(expr $(nproc) + 2)" 
mk_add_options MOZ_OBJDIR="$(dirname $topsrcdir)"/build_dir_ALSA  
mk_add_options MOZ_APP_DISPLAYNAME="Firefox ALSA"  
mk_add_options MOZ_SIMPLE_PACKAGE_NAME=firefox-alsa  
ac_add_options --with-app-basename="Firefox ALSA"  
ac_add_options --with-app-name=firefox-alsa  
ac_add_options --prefix=/usr  
ac_add_options --without-sysroot  # classified
ac_add_options --enable-audio-backends=alsa 
export LDFLAGS="-Wl,--no-keep-memory" 
cd firefox
git pull
./mach clobber
./mach configure 
./mach build -v --priority normal
./mach run --version
./mach run
DESTDIR="$(dirname $(pwd))"/debdir_ALSA ./mach install 

NOTE: The documented configure options can be listed with ./configure --help, but some may not work depending on undocumented options. To understand which options are actually functional, you need to study the source code.

$ ./configure --help

Usage: configure.py [options]

Options: [defaults in brackets after descriptions]
  Help options:
    --help                    Print this message

  Options from build/moz.configure/init.configure:
    --enable-application      Application to build. Same as --enable-project
    --enable-project          Project to build [browser]
    --enable-artifact-builds  Download and use prebuilt binary artifacts
    --host                    Define the system type performing the build
    --target                  Define the system type where the resulting executables will be used
    --with-version-file-path  Specify a custom path to app version files instead of auto-detecting
    --as-milestone={early-beta,late-beta,release}
                              Build with another milestone configuration (e.g., as release)
    --enable-update-channel   Select application update channel [default]
    --with-app-basename       Typically stays consistent for multiple branded versions of a given application (e.g. Aurora and Firefox both use "Firefox"), but may vary for full rebrandings (e.g. Iceweasel). Used for application.ini's "Name" field, which controls profile location in the absence of a "Profile" field (see below), and various system integration hooks (Unix remoting, Windows MessageWindow name, etc
    --prefix=PREFIX           Install files using PREFIX as root directory [/usr/local]
    --includedir=DIR          C header files in DIR [/usr/include]
    --libdir=DIR              Object code libraries in DIR [/usr/lib]

  Options from moz.configure:
    --enable-artifact-build-symbols[={full}]
                              Download symbols when artifact builds are enabled
    --disable-compile-environment
                              Disable compiler/library checks
    --disable-tests           Do not build test libraries & programs
    --enable-debug            Enable building with developer debug info (using the given compiler flags)
    --with-debug-label        Debug DEBUG_<value> for each comma-separated value given
    --enable-release          Build with more conservative, release engineering-oriented options. This may slow down builds.
    --disable-unified-build   Enable building modules in non unified context
    --enable-valgrind         Enable Valgrind integration hooks
    --enable-build-backend={Clangd,ChromeMap,CompileDB,CppEclipse,FasterMake,FasterMake+RecursiveMake,RecursiveMake,StaticAnalysis,TestManifest,VisualStudio},...
                              Deprecated
    --build-backends={Clangd,ChromeMap,CompileDB,CppEclipse,FasterMake,FasterMake+RecursiveMake,RecursiveMake,StaticAnalysis,TestManifest,VisualStudio},...
                              Build backends to generate [RecursiveMake,FasterMake,Clangd]
    --enable-gtest-in-build   Enable building the gtest libxul during the build
    --enable-ui-locale        Select the user interface locale (default: en-US) [en-US]
    --enable-strip            Enable stripping of libs & executables
    --disable-install-strip   Enable stripping of libs & executables when packaging
    --with-system-zlib        Use system libz

  Options from build/moz.configure/bootstrap.configure:
    --disable-bootstrap       Disable bootstrap or update of toolchains

  Options from build/moz.configure/toolchain.configure:
    --disable-optimize        Disable optimizations via compiler flags
    --with-toolchain-prefix   Prefix for the target toolchain
    --with-compiler-wrapper   Enable compiling with wrappers such as distcc and ccache
    --with-ccache             Enable compiling with ccache
    --enable-gold             Deprecated
    --enable-linker           Select the linker {bfd, gold, ld64, lld, lld-*, mold}
    --disable-debug-symbols   Disable debug symbols using the given compiler flags
    --enable-address-sanitizer
                              Enable Address Sanitizer
    --enable-memory-sanitizer
                              Enable Memory Sanitizer
    --enable-thread-sanitizer
                              Enable Thread Sanitizer
    --enable-undefined-sanitizer
                              Enable UndefinedBehavior Sanitizer
    --enable-signed-overflow-sanitizer
                              Enable UndefinedBehavior Sanitizer (Signed Integer Overflow Parts)
    --enable-unsigned-overflow-sanitizer
                              Enable UndefinedBehavior Sanitizer (Unsigned Integer Overflow Parts)
    --enable-hardening        Enables security hardening compiler options
    --enable-stl-hardening    Enable C++ STL hardening
    --enable-frame-pointers   Enable frame pointers
    --enable-coverage         Enable code coverage
    --enable-clang-plugin     Enable building with the Clang plugin (gecko specific static analyzers)
    --enable-fuzzing          Enable fuzzing support
    --enable-snapshot-fuzzing
                              Enable experimental snapshot fuzzing support
    --enable-cpp-rtti         Enable C++ RTTI
    --enable-path-remapping[={c,rust},...]
                              Enable remapping source and object paths in compiled outputs
    --enable-dtrace           Build with dtrace support

  Options from build/moz.configure/memory.configure:
    --enable-jemalloc         Replace memory allocator with jemalloc

  Options from build/moz.configure/warnings.configure:
    --enable-warnings-as-errors
                              Enable treating warnings as errors

  Options from build/moz.configure/flags.configure:
    --enable-icf              Enable Identical Code Folding
    --disable-new-pass-manager
                              Use the legacy LLVM pass manager in clang builds

  Options from build/moz.configure/lto-pgo.configure:
    --enable-profile-generate[={cross}]
                              Build a PGO instrumented binary
    --enable-profile-use[={cross}]
                              Use a generated profile during the build
    --with-pgo-profile-path   Path to the directory with unmerged profile data to use during the build, or to a merged profdata file
    --with-pgo-jarlog         Use the provided jarlog file when packaging during a profile-use build
    --enable-lto[={full,thin,cross},...]
                              Enable LTO

  Options from browser/moz.configure:
    --disable-browser-newtab-as-addon
                              Disable bundling newtab as a built-in addon

  Options from toolkit/moz.configure:
    --with-distribution-id    Set distribution-specific id [org.mozilla]
    --disable-gecko-profiler  Disable the Gecko profiler
    --enable-dmd              Enable Dark Matter Detector (heap profiler). Also enables jemalloc, replace-malloc and profiling
    --enable-audio-backends={aaudio,alsa,audiounit,jack,opensl,oss,pulseaudio,sndio,sunaudio,wasapi},...
                              Enable various cubeb backends [pulseaudio]
    --enable-alsa             Enable ALSA audio backend
    --enable-jack             Enable JACK audio backend
    --enable-pulseaudio       Enable PulseAudio audio backend
    --enable-sndio            Enable sndio audio backend
    --with-l10n-base          Path to l10n repositories
    --enable-default-toolkit={cairo-gtk3,cairo-gtk3-wayland,cairo-gtk3-x11-wayland,cairo-gtk3-wayland-only,cairo-gtk3-x11-only}
                              Select default toolkit [cairo-gtk3]
    --with-system-pipewire    Use system PipeWire
    --with-system-gbm         Use system gbm
    --with-system-libdrm      Use system libdrm
    --with-gl-provider        Set GL provider backend type
    --disable-wmf             Disable support for Windows Media Foundation
    --disable-ffmpeg          Disable FFmpeg for fragmented H264/AAC decoding
    --disable-av1             Disable av1 video support
    --with-system-av1         Use system av1 (located with pkg-config)
    --disable-jxl             Disable jxl image support
    --disable-real-time-tracing
                              Disable tracing of real-time audio callbacks
    --enable-openmax          Enable OpenMAX IL for video/audio decoding
    --enable-chrome-format={omni,jar,flat}
                              Select FORMAT of chrome files during packaging [omni]
    --enable-minify[={properties,js},...]
                              Select types of files to minify during packaging [properties]
    --with-mozilla-api-keyfile
                              Use the secret key contained in the given keyfile for Mozilla API requests
    --with-google-location-service-api-keyfile
                              Use the secret key contained in the given keyfile for Google Location Service API requests
    --with-google-safebrowsing-api-keyfile
                              Use the secret key contained in the given keyfile for Google Safebrowsing API requests
    --with-bing-api-keyfile   Use the client id and secret key contained in the given keyfile for Bing API requests
    --with-adjust-sdk-keyfile
                              Use the secret key contained in the given keyfile for Adjust SDK requests
    --with-leanplum-sdk-keyfile
                              Use the client id and secret key contained in the given keyfile for Leanplum SDK requests
    --with-pocket-api-keyfile
                              Use the secret key contained in the given keyfile for Pocket API requests
    --enable-webrender-debugger
                              Build the websocket debug server in WebRender
    --enable-app-system-headers
                              Use additional system headers defined in $MOZ_BUILD_APP/app-system-headers.mozbuild
    --disable-printing        Disable printing support
    --disable-synth-speechd   Disable speech-dispatcher support
    --disable-webspeech       Disable support for HTML Speech API
    --disable-webspeechtestbackend
                              Disable support for HTML Speech API Test Backend
    --disable-skia-pdf        Disable Skia PDF
    --with-system-webp        Use system libwebp (located with pkgconfig)
    --disable-webdriver       Disable support for WebDriver remote protocols
    --disable-geckodriver     Do not build geckodriver
    --enable-webrtc           Enable support for WebRTC
    --enable-raw              Enable support for RAW media
    --enable-address-sanitizer-reporter
                              Enable Address Sanitizer Reporter Extension
    --enable-proxy-bypass-protection
                              Prevent suspected or confirmed proxy bypasses
    --disable-proxy-direct-failover
                              Disable direct failover for system requests
    --disable-accessibility   Disable accessibility support
    --with-unsigned-addon-scopes={app,system},...
                              Addon scopes where signature is not required
    --allow-addon-sideload    Addon sideloading is allowed
    --disable-extensions-webidl-bindings
                              Disable building experimental WebExtensions WebIDL bindings
    --enable-launcher-process
                              Enable launcher process by default
    --enable-bundled-fonts    Enable support for bundled fonts on desktop platforms
    --enable-reflow-perf      Enable reflow performance tracing
    --enable-layout-debugger  Enable layout debugger
    --with-system-libvpx      Use system libvpx (located with pkgconfig)
    --with-system-jpeg        Use system libjpeg (installed at given prefix)
    --with-system-png         Use system libpng
    --with-wasm-sandboxed-libraries={graphite,ogg,hunspell,expat,woff2,soundtouch},...
                              Enable wasm sandboxing for the selected libraries
    --enable-disk-remnant-avoidance
                              Prevent persistence of auxiliary files on application close
    --enable-forkserver       Enable fork server
    --disable-backgroundtasks
                              Disable running in background task mode
    --enable-mobile-optimize  Enable mobile optimizations
    --disable-pref-extensions
                              Disable pref extensions such as autoconfig
    --disable-startupcache    Disable startup cache
    --enable-official-branding
                              Enable Official mozilla.org Branding. Do not distribute builds with --enable-official-branding unless you have permission to use trademarks per http://www.mozilla.org/foundation/trademarks/
    --with-branding=DIR       Use branding from directory DIR
    --with-crashreporter-url  Set an alternative crashreporter url [https://crash-reports.mozilla.com/]
    --with-system-libevent    Use system libevent
    --enable-crashreporter    Enable crash reporting
    --disable-dbus            Disable dbus support
    --enable-debug-js-modules
                              Enable debug mode for frontend JS libraries
    --enable-dump-painting    Enable paint debugging
    --enable-libproxy         Enable libproxy support
    --enable-logrefcnt        Enable logging of refcounts
    --disable-negotiateauth   Disable GSS-API negotiation
    --disable-parental-controls
                              Do not build parental controls
    --enable-sandbox          Enable sandboxing support
    --disable-system-extension-dirs
                              Disable searching system- and account-global directories for extensions of any kind; use only profile-specific extension directories
    --with-system-pixman      Use system pixman (located with pkgconfig)
    --disable-universalchardet
                              Disable universal encoding detection
    --disable-zipwriter       Disable zipwriter component
    --with-user-appdir        Set user-specific appdir [.mozilla]
    --enable-uniffi-fixtures  Enable UniFFI Fixtures/Examples
    --disable-system-policies
                              Disable reading policies from Windows registry, macOS's file system attributes, and /etc/firefox
    --disable-legacy-profile-creation
                              Disable the creation a legacy profile, to be used by old versions of Firefox, when no profiles exist
    --with-onnx-runtime       Location of the ONNX Runtime

  Options from js/moz.configure:
    --with-app-name           Used for e.g. the binary program file name. If not set, defaults to a lowercase form of MOZ_APP_BASENAME
    --enable-js-shell         Build the JS shell
    --enable-decorators       Enable experimental JS Decorators support
    --disable-explicit-resource-management
                              Disable explicit resource management
    --enable-portable-baseline-interp
                              Enable the portable baseline interpreter
    --enable-portable-baseline-interp-force
                              Enable forcing use of the portable baseline interpreter
    --enable-aot-ics          Enable including ahead-of-time corpus of CacheIR IC bodies
    --enable-aot-ics-force    Enable forcing the AOT ICs option on without additional configuration
    --enable-aot-ics-enforce  Enable enforcing that only AOT IC corpus is used, crashing otherwise (TEST ONLY)
    --enable-jit              Enable use of the JITs
    --enable-ion              Deprecated
    --enable-simulator={arm,arm64,mips64,loong64,riscv64}
                              Enable a JIT code simulator for the specified architecture
    --enable-instruments      Enable instruments remote profiling
    --enable-callgrind        Enable callgrind profiling
    --disable-profiling       Do not set compile flags necessary for using sampling profilers (e.g. shark, perf)
    --disable-execution-tracing
                              Do not set compile flags necessary for running the JS execution tracer
    --enable-vtune            Enable VTune profiling
    --enable-gc-probes        Turn on probes for allocation and finalization
    --enable-gczeal           Enable zealous GCing
    --enable-oom-breakpoint   Enable a breakpoint function for artificial OOMs
    --disable-jitdump         Disable perf jitdump integration
    --enable-jitspew          Enable the Jit spew and IONFLAGS environment variable
    --enable-masm-verbose     Enable MacroAssembler verbosity of generated code
    --disable-ctypes          Disable js-ctypes
    --enable-rust-simd        Enable explicit SIMD in Rust code
    --disable-spidermonkey-telemetry
                              Disable performance telemetry for SpiderMonkey (e.g. compile and run times)
    --enable-wasm-codegen-debug
                              Enable debugging for wasm codegen
    --wasm-no-experimental    Force disable all wasm experimental features for testing
    --enable-wasm-jspi        Enable WebAssembly JS PI
    --disable-shared-memory   Disable JS/WebAssembly shared memory and atomics
    --enable-wasm-simd        Enable WebAssembly SIMD
    --enable-wasm-avx         Enable AVX support for WebAssembly SIMD
    --enable-wasm-relaxed-simd
                              Enable WebAssembly relaxed SIMD
    --enable-wasm-moz-intgemm
                              Enable WebAssembly intgemm private intrinsics
    --disable-wasm-memory-control
                              Disable WebAssembly fine-grained memory control instructions
    --disable-wasm-branch-hinting
                              Disable WebAssembly Branch hints
    --with-sixgill            Enable static checking of code using sixgill
    --with-jitreport-granularity[={0,1,2,3}]
                              Default granularity at which to report JIT code to external tools (0 - no info, 1 - code ranges for while functions only, 2 - per-line information, 3 - per-op information) [3]
    --with-system-icu         Use system ICU
    --without-intl-api        Disable ECMAScript Internationalization API
    --disable-icu4x           Disable using ICU4X
    --disable-wasm-type-reflections
                              Disable type reflection in WASM JS-API
    --disable-wasm-resizable-arraybuffer
                              Disable resizable ArrayBuffer in WASM

  Options from build/moz.configure/nspr.configure:
    --with-system-nspr        Use system NSPR

  Options from build/moz.configure/rust.configure:
    --enable-rust-tests       Enable building and running of Rust tests during `make check`
    --enable-rust-debug       Build Rust code with debug assertions turned on
    --disable-cargo-incremental
                              Disable incremental rust compilation

  Options from build/moz.configure/bindgen.configure:
    --with-libclang-path      Absolute path to a directory containing Clang/LLVM libraries for bindgen (version 3.9.x or above)
    --with-clang-path         Absolute path to a Clang binary for bindgen (version 3.9.x or above)

  Options from js/ffi.configure:
    --with-system-ffi         Use system libffi (located with pkgconfig)

  Options from build/moz.configure/node.configure:
    --disable-nodejs          Require Node.js to build

  Options from build/moz.configure/nss.configure:
    --with-system-nss         Use system NSS

  Options from build/moz.configure/update-programs.configure:
    --disable-updater         Disable building the updater
    --enable-unverified-updates
                              Enable application update without verifying MAR or updater binary signatures
    --enable-default-browser-agent
                              Enable building the default browser agent

Environment variables:
  Options from build/moz.configure/init.configure:
    MOZ_AUTOMATION            Enable options for automated builds
    MOZCONFIG                 Mozconfig location
    MOZILLABUILD              Path to Mozilla Build (Windows-only)
    CONFIG_SHELL              Path to a POSIX shell
    GIT                       Path to the git program
    MOZILLA_OFFICIAL          Build an official release
    MOZBUILD_STATE_PATH       Path to a persistent state directory for the build system and related tools

  Options from moz.configure:
    MOZ_BUILD_HOOK            Path to the moz.build file that will be executed as if it were appended to every moz.build in the tree
    MOZ_COPY_PDBS             For builds that do not support symbols in the normal fashion, generate and copy them into the resulting build archive
    MOZ_PGO                   Build with profile guided optimizations
    READELF                   Path to the readelf program
    OBJCOPY                   Path to the objcopy program
    AWK                       Path to the awk program
    MAKE                      Path to GNU make
    GMAKE                     Path to the gmake program
    WATCHMAN                  Path to the watchman program
    XARGS                     Path to the xargs program
    MKFSHFS                   Path to the mkfshfs program
    HFS_TOOL                  Path to the hfs_tool program
    STRIP_FLAGS               Flags for the strip command
    STRIP                     Path to the strip program
    USE_LIBZ_RS               Use libz-rs-sys instead of zlib

  Options from build/moz.configure/toolchain.configure:
    HOST_CPPFLAGS             Extra flags for Preprocessing host sources []
    HOST_CFLAGS               Extra flags for compiling host C sources []
    HOST_CXXFLAGS             Extra flags for compiling host C++ sources []
    HOST_LDFLAGS              Extra flags for linking host object files []
    CPPFLAGS                  Extra flags for preprocessing sources []
    CFLAGS                    Extra flags for compiling C sources []
    CXXFLAGS                  Extra flags for compiling C++ sources []
    ASFLAGS                   Extra flags for assembling sources []
    LDFLAGS                   Extra flags for linking object files []
    LIBS                      Extra libraries for linking object files []
    MOZ_OPTIMIZE_FLAGS        Extra optimization flags
    MOZ_HAZARD                Build for the GC rooting hazard analysis
    CCACHE_PREFIX             Compiler prefix to use when using ccache
    RUSTC_WRAPPER             Wrap rust compilation with given tool
    SCCACHE_VERBOSE_STATS     Print verbose sccache stats after build
    CC                        Path to the target C compiler
    LD                        Deprecated
    CXX                       Path to the target C++ compiler
    HOST_CC                   Path to the host C compiler
    HOST_LD                   Deprecated
    HOST_CXX                  Path to the host C++ compiler
    MOZ_DEBUG_FLAGS           Debug compiler flags
    AS                        Path to the assembler
    LLVM_OBJDUMP              Path to llvm-objdump
    AR                        Path to the ar program
    HOST_AR                   Path to the host_ar program

  Options from build/moz.configure/pkg.configure:
    PKG_CONFIG                Path to the pkg_config program

  Options from build/moz.configure/lto-pgo.configure:
    LLVM_PROFDATA             Path to the llvm_profdata program
    MOZ_LD64_KNOWN_GOOD       Indicate that ld64 is free of symbol aliasing bugs

  Options from toolkit/moz.configure:
    MOZ_STUB_INSTALLER        Produce a stub installer
    MOZ_SOURCE_REPO           Project source repository
    MOZ_SOURCE_CHANGESET      Source changeset
    MOZ_INCLUDE_SOURCE_INFO   Include build repository informations
    USE_FC_FREETYPE           Force-enable the use of fontconfig freetype
    MOZ_TELEMETRY_REPORTING   Enable telemetry reporting
    TAR                       Path to the tar program
    UNZIP                     Path to the unzip program
    MIDL_FLAGS                Extra flags to pass to MIDL
    MOZ_REQUIRE_SIGNING       Enforce that add-ons are signed by the trusted root
    DUMP_SYMS                 Path to the dump_syms program
    MOZ_BRANDING_DIRECTORY    Path to the directory used for branding resources
    MOZ_OFFICIAL_BRANDING_DIRECTORY
                              Path to the directory used for official branding resources
    MOZ_APP_DISPLAYNAME       Branded application name
    MOZ_DEV_EDITION           Whether this a dev edition build
    MOZ_MACBUNDLE_ID          ID of the associated mac bundle
    MOZ_APP_REMOTINGNAME      Used for the internal program name, which affects profile name and remoting. If not set, defaults to MOZ_APP_NAME if the update channel is release, and MOZ_APP_NAME-MOZ_UPDATE_CHANNEL otherwise
    MOZ_WINCONSOLE            Whether we can create a console window
    MOZ_CRASHREPORTER_MOCK    Mock the crashreporter to test native GUIs
    MOZ_SIMPLE_PACKAGE_NAME   Package name override
    MOZ_PKG_SPECIAL           Name of special moz flavor
    MOZ_PACKAGE_JSSHELL       Whether the installer bundles the JS shell

  Options from build/moz.configure/rust.configure:
    RUSTC                     Path to the rust compiler
    CARGO                     Path to the Cargo package manager
    RUSTDOC                   Path to the rustdoc program
    RUSTDOCFLAGS              Extra options for the rustdoc program
    RUSTFLAGS                 Rust compiler flags
    RUSTC_OPT_LEVEL           Rust compiler optimization level (-C opt-level=%s) [2]

  Options from build/moz.configure/bindgen.configure:
    CBINDGEN                  Path to cbindgen
    RUSTFMT                   Path to the rustfmt program
    BINDGEN_CFLAGS            Options bindgen should pass to the C/C++ parser

  Options from build/moz.configure/node.configure:
    NODEJS                    Path to nodejs

  Options from build/moz.configure/update-programs.configure:
    MAR_CHANNEL_ID            MAR channel identifier
    ACCEPTED_MAR_CHANNEL_IDS  Accepted MAR channel identifiers

#20 Re: Freedom Hacks » UDisks2: Security Considerations » 2026-02-15 20:27:35

Devarch wrote:

It's possible that it was just propaganda to make West to believe in it.

If you like conspiracy theories... maybe anti-AI propaganda is sponsored by Russians. Kremlin is not a charity organization.

#21 Re: Freedom Hacks » UDisks2: Security Considerations » 2026-02-15 13:02:06

The problem is not censorship, but mob censorship.

#22 Re: Freedom Hacks » UDisks2: Security Considerations » 2026-02-15 00:45:14

There was an old Polish joke from the 1930s.  A group of hares tried to cross the Polish border. They said they needed refuge because the Soviet secret police were arresting camels. But you’re not camels! Yes, but how can we prove it?

#23 Re: Freedom Hacks » UDisks2: Security Considerations » 2026-02-15 00:29:59

The skepticism toward AI today parallels the Soviet-era rejection of cybernetics.

From 1950 to 1954, the reception of cybernetics by the Soviet Union establishment was exclusively negative. The Soviet Department for Agitation and Propaganda had called for anti-Americanism to be intensified throughout Soviet media, and in an attempt to fill the Department's quotas, Soviet journalists latched on to cybernetics as an American "reactionary pseudoscience" to denounce and mock. These attacks were interpreted as a signal of an official attitude to cybernetics, Soviet writers thus portraying cybernetics as "a full embodiment of imperialist ideology” during Joseph Stalin's premiership.
_https://en.wikipedia.org/wiki/Cybernetics_in_the_Soviet_Union

Nikita Khrushchev called genetics and cybernetics "prostitutes of capitalism."
Nikolai Vavilov, a geneticist, was arrested in 1940 and died in prison in 1943.
He was a fellow of the Royal Society, that is why, perhaps, we know what happened to him.
The fates of many other Soviet scientists remain obscure.
_https://en.wikipedia.org/wiki/Nikolai_Vavilov

Victor Glushkov: Insights to Remember

... In 1952, he earned his doctorate by proving the Fifth Generalized Problem of Hilbert and continued his academic career. In 1956, he headed the modeling and computing technology laboratory at the Mechanical Institute in Kyiv, where Sergey Lebedev and his team assembled the first European computer MESM.
In 1957, Glushkov transformed his lab into the Computing Center of the Academy of Sciences. In five years, it evolved into the separate Institute of Cybernetics.

..It is fascinating to delve into the intriguing narrative of how Glushkov navigated resistance to defend his mindset against Soviet bureaucrats, dragging talented managers into the swamp of the communist party's internal struggle.
_https://glushkov.dataart.com

For some strange reason, Victor Glushkov did not hate AI. In 1970, he developed the Evidence Algorithm (EA) as a research program in artificial intelligence focused on automated theorem proving. He considered AI as a part of cybernetics.

The Soviet rejection of cybernetics and repression of scientists contributed to its problems with technological innovations and failure in the Cold War arms and computing race.

#24 Re: Freedom Hacks » UDisks2: Security Considerations » 2026-02-14 16:35:07

Censorship doesn't necessarily mean editing.  There are many ways to silence someone.

There are many subtle ways to discourage someone from posting on a forum — beyond outright censorship. Constant accusations, excessive moderation, personal attacks, or repeatedly questioning someone's intent can create a hostile environment that pushes people away.

#25 Re: Freedom Hacks » UDisks2: Security Considerations » 2026-02-14 16:19:43

You seem to insist on your right to censor my posts.

Board footer

Forum Software