Bug 1639283 - Add Telemetry to some of our WebRTC UI. r=pbz, data-review=mmccorquodale
authorMike Conley <mconley@mozilla.com>
Wed, 20 May 2020 22:51:31 +0000
changeset 531359 fd7802694d8f5255871f0da15e8057530ed36e41
parent 531358 46aa9dd6e711d83fd222cdf9d67260b2bdc84db0
child 531360 cf1939b293d954d1539818daeb2a2553dcf1d15d
push id37438
push userabutkovits@mozilla.com
push dateThu, 21 May 2020 09:36:57 +0000
treeherdermozilla-central@2d00a1a6495c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspbz
bugs1639283
milestone78.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1639283 - Add Telemetry to some of our WebRTC UI. r=pbz, data-review=mmccorquodale Differential Revision: https://phabricator.services.mozilla.com/D76213
browser/modules/webrtcUI.jsm
toolkit/components/telemetry/Events.yaml
--- a/browser/modules/webrtcUI.jsm
+++ b/browser/modules/webrtcUI.jsm
@@ -44,16 +44,18 @@ var webrtcUI = {
       this.initialized = true;
 
       XPCOMUtils.defineLazyPreferenceGetter(
         this,
         "useLegacyGlobalIndicator",
         "privacy.webrtc.legacyGlobalIndicator",
         true
       );
+
+      Services.telemetry.setEventRecordingEnabled("webrtc.ui", true);
     }
   },
 
   uninit() {
     if (this.initialized) {
       Services.obs.removeObserver(this, "browser-delayed-startup-finished");
       this.initialized = false;
     }
@@ -67,22 +69,34 @@ var webrtcUI = {
     }
   },
 
   SHARING_NONE: 0,
   SHARING_WINDOW: 1,
   SHARING_SCREEN: 2,
 
   // Set of browser windows that are being shared over WebRTC.
-  sharedWindows: new WeakSet(),
+  sharedBrowserWindows: new WeakSet(),
+
+  // True if one or more screens is being shared.
   sharingScreen: false,
+
   allowedSharedBrowsers: new WeakSet(),
   allowTabSwitchesForSession: false,
   tabSwitchCountForSession: 0,
 
+  // True if a window or screen is being shared.
+  sharingDisplay: false,
+
+  // The session ID is used to try to differentiate between instances
+  // where the user is sharing their display somehow. If the user
+  // transitions from a state of not sharing their display, to sharing a
+  // display, we bump the ID.
+  sharingDisplaySessionId: 0,
+
   // Map of browser elements to indicator data.
   perTabIndicators: new Map(),
   activePerms: new Map(),
 
   get showGlobalIndicator() {
     for (let [, indicators] of this.perTabIndicators) {
       if (indicators.showGlobalIndicator) {
         return true;
@@ -296,35 +310,45 @@ var webrtcUI = {
     } else {
       this._streams[index] = {
         browsingContext: aBrowsingContext,
         topBrowsingContext: aBrowsingContext.top,
         state: aData,
       };
     }
 
+    let wasSharingDisplay = this.sharingDisplay;
+
     // Reset our internal notion of whether or not we're sharing
     // a screen or browser window. Now we'll go through the shared
     // devices and re-determine what's being shared.
     let sharingBrowserWindow = false;
     let sharedWindowRawDeviceIds = new Set();
+    this.sharingDisplay = false;
     this.sharingScreen = false;
     let suppressNotifications = false;
 
+    // First, go through the streams and collect the counts on things
+    // like the total number of shared windows, and whether or not we're
+    // sharing screens.
     for (let stream of this._streams) {
       let { state } = stream;
       suppressNotifications |= state.suppressNotifications;
 
       for (let device of state.devices) {
+        let mediaSource = device.mediaSource;
+
+        if (mediaSource == "window" || mediaSource == "screen") {
+          this.sharingDisplay = true;
+        }
+
         if (!device.scary) {
           continue;
         }
 
-        let mediaSource = device.mediaSource;
-
         if (mediaSource == "window") {
           sharedWindowRawDeviceIds.add(device.rawId);
         } else if (mediaSource == "screen") {
           this.sharingScreen = true;
         }
 
         // If the user has granted a particular site the ability
         // to get a stream from a window or screen, we will
@@ -332,41 +356,79 @@ var webrtcUI = {
         //
         // We use the permanentKey here so that the allowing of
         // the tab survives tab tear-in and tear-out.
         let browser = stream.topBrowsingContext.embedderElement;
         this.allowedSharedBrowsers.add(browser.permanentKey);
       }
     }
 
-    this.sharedWindows = new WeakSet();
+    // Next, go through the list of shared windows, and map them
+    // to our browser windows so that we know which ones are shared.
+    this.sharedBrowserWindows = new WeakSet();
 
     for (let win of BrowserWindowTracker.orderedWindows) {
       let rawDeviceId;
       try {
         rawDeviceId = win.windowUtils.webrtcRawDeviceId;
       } catch (e) {
         // This can theoretically throw if some of the underlying
         // window primitives don't exist. In that case, we can skip
         // to the next window.
         continue;
       }
       if (sharedWindowRawDeviceIds.has(rawDeviceId)) {
-        this.sharedWindows.add(win);
+        this.sharedBrowserWindows.add(win);
 
         // If we've shared a window, then the initially selected tab
         // in that window should be exempt from tab switch warnings,
         // since it's already been shared.
         let selectedBrowser = win.gBrowser.selectedBrowser;
         this.allowedSharedBrowsers.add(selectedBrowser.permanentKey);
 
         sharingBrowserWindow = true;
       }
     }
 
+    // If we weren't sharing a window or screen, and now are, bump
+    // the sharingDisplaySessionId. We use this ID for Event
+    // telemetry, and consider a transition from no shared displays
+    // to some shared displays as a new session.
+    if (!wasSharingDisplay && this.sharingDisplay) {
+      this.sharingDisplaySessionId++;
+    }
+
+    // If we were adding a new display stream, record some Telemetry for
+    // it with the most recent sharedDisplaySessionId. We do this separately
+    // from the loops above because those take into account the pre-existing
+    // streams that might already have been shared.
+    if (aData.devices) {
+      // The mixture of camelCase with under_score notation here is due to
+      // an unfortunate collision of conventions between this file and
+      // Event Telemetry.
+      let silence_notifs = suppressNotifications ? "true" : "false";
+      for (let device of aData.devices) {
+        if (device.mediaSource == "screen") {
+          this.recordEvent("share_display", "screen", {
+            silence_notifs,
+          });
+        } else if (device.mediaSource == "window") {
+          if (device.scary) {
+            this.recordEvent("share_display", "browser_window", {
+              silence_notifs,
+            });
+          } else {
+            this.recordEvent("share_display", "window", {
+              silence_notifs,
+            });
+          }
+        }
+      }
+    }
+
     // Since we're not sharing a screen or browser window,
     // we can clear these state variables, which are used
     // to warn users on tab switching when sharing. These
     // are safe to reset even if we hadn't been sharing
     // the screen or browser window already.
     if (!this.sharingScreen && !sharingBrowserWindow) {
       this.allowedSharedBrowsers = new WeakSet();
       this.allowTabSwitchesForSession = false;
@@ -582,17 +644,17 @@ var webrtcUI = {
       gIndicatorWindow.close();
       gIndicatorWindow = null;
     }
   },
 
   getWindowShareState(window) {
     if (this.sharingScreen) {
       return this.SHARING_SCREEN;
-    } else if (this.sharedWindows.has(window)) {
+    } else if (this.sharedBrowserWindows.has(window)) {
       return this.SHARING_WINDOW;
     }
     return this.SHARING_NONE;
   },
 
   tabAddedWhileSharing(tab) {
     this.allowedSharedBrowsers.add(tab.linkedBrowser.permanentKey);
   },
@@ -608,28 +670,47 @@ var webrtcUI = {
     // switch to immediately upon sharing. These presumptions are based
     // on research that our user research team did with users using
     // video conferencing web applications.
     if (!this.tabSwitchCountForSession) {
       this.allowedSharedBrowsers.add(browser.permanentKey);
     }
 
     this.tabSwitchCountForSession++;
-    return (
+    let shouldShow =
       !this.allowTabSwitchesForSession &&
-      !this.allowedSharedBrowsers.has(browser.permanentKey)
-    );
+      !this.allowedSharedBrowsers.has(browser.permanentKey);
+
+    if (shouldShow) {
+      this.recordEvent("tab_switch_warning", "tab_switch_warning");
+    }
+
+    return shouldShow;
   },
 
   allowSharedTabSwitch(tab, allowForSession) {
     let browser = tab.linkedBrowser;
     let gBrowser = browser.getTabBrowser();
     this.allowedSharedBrowsers.add(browser.permanentKey);
     gBrowser.selectedTab = tab;
     this.allowTabSwitchesForSession = allowForSession;
+
+    if (allowForSession) {
+      this.recordEvent("allow_all_tabs", "allow_all_tabs");
+    }
+  },
+
+  recordEvent(type, object, args = {}) {
+    Services.telemetry.recordEvent(
+      "webrtc.ui",
+      type,
+      object,
+      this.sharingDisplaySessionId.toString(),
+      args
+    );
   },
 };
 
 function getGlobalIndicator() {
   if (!webrtcUI.useLegacyGlobalIndicator) {
     const INDICATOR_CHROME_URI =
       "chrome://browser/content/webrtcIndicator.xhtml";
     let features = "chrome,titlebar=yes,alwaysontop,minimizable=yes";
--- a/toolkit/components/telemetry/Events.yaml
+++ b/toolkit/components/telemetry/Events.yaml
@@ -2099,8 +2099,70 @@ security.ui.certerror:
       - "firefox"
     record_in_processes: ["content"]
     products:
       - firefox
     extra_keys:
       is_frame: If the error page is loaded in an iframe.
       has_sts: If the error page is for a site with HSTS headers or with a pinned key.
       panel_open: If the advanced panel was open at the time of the interaction.
+
+webrtc.ui:
+  share_display:
+    objects:
+      - screen
+      - window
+      - browser_window
+    description: >
+      Recorded when a display is shared. The value for this event is a unique
+      ID that differentiates different sharing sessions. A new sharing session
+      is created when the user transitions from not sharing a display to
+      sharing at least one display.
+    extra_keys:
+      silence_notifs: True if the user opted in to silencing DOM notifications.
+    notification_emails:
+      - mconley@mozilla.com
+      - vchin@mozilla.com
+    products:
+      - "firefox"
+    record_in_processes:
+      - main
+    bug_numbers:
+      - 1639283
+    expiry_version: "82"
+    release_channel_collection: opt-out
+  tab_switch_warning:
+    objects: ["tab_switch_warning"]
+    description: >
+      Recorded when the tab switch warning is displayed. The value for this
+      event is a unique ID that differentiates different sharing sessions. A
+      new sharing session is created when the user transitions from not
+      sharing a display to sharing at least one display.
+    notification_emails:
+      - mconley@mozilla.com
+      - vchin@mozilla.com
+    products:
+      - "firefox"
+    record_in_processes:
+      - main
+    bug_numbers:
+      - 1639283
+    expiry_version: "82"
+    release_channel_collection: opt-out
+  allow_all_tabs:
+    objects: ["allow_all_tabs"]
+    description: >
+      Recorded when the user chooses to allow all tab switches and suppress the
+      warning for a sharing session. The value for this event is a unique ID
+      that differentiates different sharing sessions. A new sharing session is
+      created when the user transitions from not sharing a display to sharing
+      at least one display.
+    notification_emails:
+      - mconley@mozilla.com
+      - vchin@mozilla.com
+    products:
+      - "firefox"
+    record_in_processes:
+      - main
+    bug_numbers:
+      - 1639283
+    expiry_version: "82"
+    release_channel_collection: opt-out
\ No newline at end of file