| |

I. Notification Permission Telemetry

My first Firefox patch: adding Glean telemetry to track how users interact with notification permission prompts.

Bug 1998053 | D274203 | Reviewer: timhuang


The Problem

Firefox had limited visibility into how users interact with notification permission requests. We knew when permissions were granted or denied, but not:

  • How often the permission icon appears vs. the full prompt
  • Whether users click the icon or get the prompt automatically
  • What percentage of requests are auto-blocked due to lack of user gesture
  • How behavior differs across site categories (social media vs. productivity apps)

The Architecture

Firefoxโ€™s notification permission system has several layers:

1. DOM Layer (dom/notification/)

  • Handles Notification.requestPermission() API calls
  • Validates security context (HTTPS required)
  • Checks for user gesture activation

2. Permission UI Layer (browser/modules/PermissionUI.sys.mjs)

  • DesktopNotificationPermissionPrompt class
  • Manages the permission dialog lifecycle
  • Handles the โ€œpost-promptโ€ (shaking icon) flow
  • This is where I added the new telemetry

3. Popup Notification System (browser/modules/PopupNotifications.jsm)

  • Generic notification panel infrastructure
  • Provides callbacks: onShown, onAfterShow, event callbacks

What I Built

Five Glean Event Metrics

web_notification_permission:
  icon_shown:
    description: Permission icon appears and animates in URL bar
  icon_clicked:
    description: User clicks the permission icon
  prompt_shown:
    description: Permission dialog displayed (script vs icon_click)
  prompt_blocked:
    description: Permission request auto-denied (no user gesture)
  prompt_interaction:
    description: User allows/blocks permission (with persistence)

Site Categorization System

Added a hardcoded map of 86 high-traffic domains across 8 categories:

const SITE_CATEGORIES = new Map([
  ["facebook.com", "social"],
  ["slack.com", "chat_communication"],
  ["mail.google.com", "email"],
  ["youtube.com", "media_streaming"],
  // ... 86 total domains
]);

function getSiteCategory(principal) {
  let host = principal.URI.host;
  if (SITE_CATEGORIES.has(host)) {
    return SITE_CATEGORIES.get(host);
  }
  // Check subdomain match
  for (let [domain, category] of SITE_CATEGORIES) {
    if (host.endsWith("." + domain)) {
      return category;
    }
  }
  return "other";
}

The Tricky Parts

Detecting Icon Click vs. Automatic Prompt

The challenge was distinguishing:

  • Automatic: User clicked a button โ†’ site calls requestPermission() โ†’ prompt appears immediately
  • Icon click: Site calls requestPermission() without gesture โ†’ icon appears โ†’ user clicks icon โ†’ prompt appears

Solution: check both the preference and the requestโ€™s hasValidTransientUserGestureActivation:

let trigger = "automatic";
if (
  this.requiresUserInput &&
  !this.request.hasValidTransientUserGestureActivation
) {
  trigger = "icon_click";
}

Finding the Right Callback Point

The telemetry had to go in the base PermissionPrompt classโ€™s prompt() method, which wraps all actionsโ€”not in DesktopNotificationPermissionPrompt directly.


What I Learned

  1. Firefoxโ€™s Permission System is Layered โ€” DOM validates, UI presents, permission manager stores
  2. Glean Telemetry is Code, Not Configuration โ€” Metrics are defined in YAML but recorded in code
  3. User Gestures Matter โ€” hasValidTransientUserGestureActivation determines immediate vs. deferred prompts
  4. The Permission Prompt is a State Machine โ€” Not just โ€œshown โ†’ allow/blockโ€ but a full lifecycle