Bug 1345077 - Part 2 - Add telemetry on permission requests from third party origins, handling user input and insecure contexts. r=florian,liuche draft
authorJohann Hofmann <jhofmann@mozilla.com>
Wed, 10 Jan 2018 21:07:59 +0100
changeset 723593 52935b560f771214a3e47aa9d27ca90d25077718
parent 723592 18e49399247ea5b69dc603b70ef9981f97b618ba
child 746904 2f02d9781d2437f65885cfe55d65182b0219f031
push id96479
push userjhofmann@mozilla.com
push dateTue, 23 Jan 2018 15:50:23 +0000
reviewersflorian, liuche
bugs1345077
milestone60.0a1
Bug 1345077 - Part 2 - Add telemetry on permission requests from third party origins, handling user input and insecure contexts. r=florian,liuche MozReview-Commit-ID: 4ARIxa6gwCg
browser/components/nsBrowserGlue.js
browser/components/tests/browser/browser_contentpermissionprompt.js
browser/modules/ContentWebRTC.jsm
browser/modules/webrtcUI.jsm
toolkit/components/telemetry/Histograms.json
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -2851,16 +2851,42 @@ ContentPermissionPrompt.prototype = {
         combinedIntegration.createPermissionPrompt(type, request);
       if (!permissionPrompt) {
         throw Components.Exception(
           `Failed to handle permission of type ${type}`,
           Cr.NS_ERROR_FAILURE);
       }
 
       permissionPrompt.prompt();
+
+      let schemeHistogram = Services.telemetry.getKeyedHistogramById("PERMISSION_REQUEST_ORIGIN_SCHEME");
+      let scheme = 0;
+      // URI is null for system principals.
+      if (request.principal.URI) {
+        switch (request.principal.URI.scheme) {
+          case "http":
+            scheme = 1;
+            break;
+          case "https":
+            scheme = 2;
+            break;
+        }
+      }
+      schemeHistogram.add(type, scheme);
+
+      // request.element should be the browser element in e10s.
+      if (request.element && request.element.contentPrincipal) {
+        let thirdPartyHistogram = Services.telemetry.getKeyedHistogramById("PERMISSION_REQUEST_THIRD_PARTY_ORIGIN");
+        let isThirdParty = request.principal.origin != request.element.contentPrincipal.origin;
+        thirdPartyHistogram.add(type, isThirdParty);
+      }
+
+      let userInputHistogram = Services.telemetry.getKeyedHistogramById("PERMISSION_REQUEST_HANDLING_USER_INPUT");
+      userInputHistogram.add(type, request.isHandlingUserInput);
+
     } catch (ex) {
       Cu.reportError(ex);
       request.cancel();
       throw ex;
     }
   },
 };
 
--- a/browser/components/tests/browser/browser_contentpermissionprompt.js
+++ b/browser/components/tests/browser/browser_contentpermissionprompt.js
@@ -56,16 +56,17 @@ MockContentPermissionRequest.prototype =
   wrappedJSObject: this,
   // For some of our tests, we want to make sure that the
   // request is cancelled, so we add some instrumentation here
   // to check that cancel() is called.
   cancel() {
     this.cancelled = true;
   },
   cancelled: false,
+  principal: Services.scriptSecurityManager.getSystemPrincipal(),
 };
 
 /**
  * Tests that if the nsIContentPermissionRequest has an empty
  * types array, that NS_ERROR_UNEXPECTED is thrown, and the
  * request is cancelled.
  */
 add_task(async function test_empty_types() {
--- a/browser/modules/ContentWebRTC.jsm
+++ b/browser/modules/ContentWebRTC.jsm
@@ -130,39 +130,40 @@ function handleGUMStop(aSubject, aTopic,
   let mm = getMessageManagerForWindow(contentWindow);
   if (mm)
     mm.sendAsyncMessage("webrtc:StopRecording", request);
 }
 
 function handleGUMRequest(aSubject, aTopic, aData) {
   let constraints = aSubject.getConstraints();
   let secure = aSubject.isSecure;
+  let isHandlingUserInput = aSubject.isHandlingUserInput;
   let contentWindow = Services.wm.getOuterWindowWithId(aSubject.windowID);
 
   contentWindow.navigator.mozGetUserMediaDevices(
     constraints,
     function(devices) {
       // If the window has been closed while we were waiting for the list of
       // devices, there's nothing to do in the callback anymore.
       if (contentWindow.closed)
         return;
 
       prompt(contentWindow, aSubject.windowID, aSubject.callID,
-             constraints, devices, secure);
+             constraints, devices, secure, isHandlingUserInput);
     },
     function(error) {
       // bug 827146 -- In the future, the UI should catch NotFoundError
       // and allow the user to plug in a device, instead of immediately failing.
       denyGUMRequest({callID: aSubject.callID}, error);
     },
     aSubject.innerWindowID,
     aSubject.callID);
 }
 
-function prompt(aContentWindow, aWindowID, aCallID, aConstraints, aDevices, aSecure) {
+function prompt(aContentWindow, aWindowID, aCallID, aConstraints, aDevices, aSecure, aIsHandlingUserInput) {
   let audioDevices = [];
   let videoDevices = [];
   let devices = [];
 
   // MediaStreamConstraints defines video as 'boolean or MediaTrackConstraints'.
   let video = aConstraints.video || aConstraints.picture;
   let audio = aConstraints.audio;
   let sharingScreen = video && typeof(video) != "boolean" &&
@@ -208,22 +209,28 @@ function prompt(aContentWindow, aWindowI
     return;
   }
 
   if (!aContentWindow.pendingGetUserMediaRequests) {
     setupPendingListsInitially(aContentWindow);
   }
   aContentWindow.pendingGetUserMediaRequests.set(aCallID, devices);
 
+  // Record third party origins for telemetry.
+  let isThirdPartyOrigin =
+    aContentWindow.document.location.origin != aContentWindow.top.document.location.origin;
+
   let request = {
     callID: aCallID,
     windowID: aWindowID,
     origin: aContentWindow.origin,
     documentURI: aContentWindow.document.documentURI,
     secure: aSecure,
+    isHandlingUserInput: aIsHandlingUserInput,
+    isThirdPartyOrigin,
     requestTypes,
     sharingScreen,
     sharingAudio,
     audioDevices,
     videoDevices
   };
 
   let mm = getMessageManagerForWindow(aContentWindow);
--- a/browser/modules/webrtcUI.jsm
+++ b/browser/modules/webrtcUI.jsm
@@ -840,16 +840,39 @@ function prompt(aBrowser, aRequest) {
 
   notification =
     chromeDoc.defaultView
              .PopupNotifications
              .show(aBrowser, "webRTC-shareDevices", message,
                    anchorId, mainAction, secondaryActions,
                    options);
   notification.callID = aRequest.callID;
+
+  let schemeHistogram = Services.telemetry.getKeyedHistogramById("PERMISSION_REQUEST_ORIGIN_SCHEME");
+  let thirdPartyHistogram = Services.telemetry.getKeyedHistogramById("PERMISSION_REQUEST_THIRD_PARTY_ORIGIN");
+  let userInputHistogram = Services.telemetry.getKeyedHistogramById("PERMISSION_REQUEST_HANDLING_USER_INPUT");
+
+  let docURI = aRequest.documentURI;
+  let scheme = 0;
+  if (docURI.startsWith("https")) {
+    scheme = 2;
+  } else if (docURI.startsWith("http")) {
+    scheme = 1;
+  }
+
+  for (let requestType of requestTypes) {
+    if (requestType == "AudioCapture") {
+      requestType = "Microphone";
+    }
+    requestType = requestType.toLowerCase();
+
+    schemeHistogram.add(requestType, scheme);
+    thirdPartyHistogram.add(requestType, aRequest.isThirdPartyOrigin);
+    userInputHistogram.add(requestType, aRequest.isHandlingUserInput);
+  }
 }
 
 function removePrompt(aBrowser, aCallId) {
   let chromeWin = aBrowser.ownerGlobal;
   let notification =
     chromeWin.PopupNotifications.getNotification("webRTC-shareDevices", aBrowser);
   if (notification && notification.callID == aCallId)
     notification.remove();
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -13925,10 +13925,38 @@
     "kind": "exponential",
     "high": 10000,
     "n_buckets": 20,
     "keyed": true,
     "description": "The number of prefs in each prefs file loaded, keyed by filename.",
     "releaseChannelCollection": "opt-out",
     "bug_numbers": [1426270],
     "alert_emails": ["nnethercote@mozilla.com"]
+  },
+  "PERMISSION_REQUEST_ORIGIN_SCHEME": {
+    "record_in_processes": ["main"],
+    "alert_emails": ["jhofmann@mozilla.com"],
+    "bug_numbers": [1286118],
+    "expires_in_version": "64",
+    "kind": "enumerated",
+    "n_values": 10,
+    "keyed": true,
+    "description": "Permission requests (showing a permission prompt) by origin scheme (0=other,1=http,2=https)."
+  },
+  "PERMISSION_REQUEST_THIRD_PARTY_ORIGIN": {
+    "record_in_processes": ["main"],
+    "alert_emails": ["jhofmann@mozilla.com"],
+    "bug_numbers": [1286118],
+    "expires_in_version": "64",
+    "kind": "boolean",
+    "keyed": true,
+    "description": "Permission requests (showing a permission prompt) by whether they come from a third party origin."
+  },
+  "PERMISSION_REQUEST_HANDLING_USER_INPUT": {
+    "record_in_processes": ["main"],
+    "alert_emails": ["jhofmann@mozilla.com"],
+    "bug_numbers": [1286118],
+    "expires_in_version": "64",
+    "kind": "boolean",
+    "keyed": true,
+    "description": "Permission requests (showing a permission prompt) by whether they were requested from code handling a user input event."
   }
 }