Search code examples
javascriptgoogle-chromefirefoxhttpsgetusermedia

Test if navigator.getUserMedia requires https without browser user agent sniffing


I would like to use Javascript to determine if a browser supports webcam access (navigator.getUserMedia) over http, in the event that it doesn't I want to redirect the user to https. HOWEVER, I do not want to redirect the user to https at all if the browser supports webcam access without requiring https (firefox, for example) I want to serve the site using exclusively http. My current solution is to shiff a browser's user agent a la:

var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;

My current solution will break down in theory as Firefox is likely to patch http support for webcam access out some time in the next few years.

Theres a reason for my reluctance to push https on all browsers at this point in time that I wont get into, but yes I realize that the browser world is moving in a direction that will someday require https everywhere.

!!navigator.getUserMedia

This returns true wether I'm running chrome in http or https, I would like to produce a falsey value when running http on current versions of chrome but not firefox, The reason I want to do it this way rather than user agent sniffing is because as browsers patch support for webcam access on http I want my code to adapt without needing to be patched.

In short, How can I detect the browsers that support video features in http without looking for specific browser user agents?


Solution

  • The following will detect Chrome's https behavior today, without a prompt:

    (Use fiddle in Chrome since getUserMedia in SO snippets don't work in Chrome for some reason)

    if ('https:' == document.location.protocol) {
      console.log("Page is https.");
    } else {
      navigator.mediaDevices.getUserMedia({video: {width: {min: 2, max: 1}}})
      .then(stream => {
        log("Detection failed!");
        stream.getTracks().forEach(t => t.stop());
      })
      .catch(e => {
        switch (e.name) {
          case "NotSupportedError":
          case "NotAllowedError":
          case "SecurityError":
            console.log("getUserMedia in http is disallowed");
            break;
          case "OverconstrainedError":
          default:
            console.log("getUserMedia in http is allowed");
            break;
        }
      });
    }

    This works because Chrome today fails with "NotSupportedError" in https before it considers constraints. We're passing in impossible constraints, so in the case where http is allowed, we instead fail with an "OverconstrainedError", so either way no prompt is ever shown to the user.

    You'll note I'm hedging a bit, because "NotSupportedError" is not actually a valid error according to the spec. I think once Chrome updates to follow the spec, it should fail with "SecurityError" instead of "NotSupportedError", but I'm not certain. There's a chance it would instead consider constraints first, in which case this test may stop working.