flypig.co.uk

List items

Items from the current list are shown below.

Blog

10 Jul 2024 : Day 284 #
Yesterday I applied the final two WebRTC patches, transferred over from ESR 78, with mixed results. I left a build running overnight after having manually applied the changes to the moz.build file after I failed to get the changes autogenerated from changes to the BUILD.gn file.

When I test the new version I do get slightly different results compared to the previous install. On Mozilla's getUserMedia example page I now get the same results whether I'm trying to use the camera or the microphone:
library "libandroidicu.so" needed or dlopened by "/system/lib64/
    libmedia.so" is not accessible for the namespace "(default)"
JavaScript error: file:///usr/lib64/mozembedlite/components/
    EmbedLiteWebrtcUI.js, line 293: TypeError: 
    contentWindow.navigator.mozGetUserMediaDevices is not a function
I'm not sure about that libandroidicu.so error. I've not had a chance to find the code that's attempting to load this in dynamically — it doesn't look like the sort of library we should be needing — so I'll have to add this task to my queue.

The fact that both the microphone and camera produce this error is I think a sign that the changes to the moz.build file are useful, but it brings us to the same conclusion, which is that we need to fix the process for requesting permissions from the user. So the obvious thing to do is to follow that error and try to find out why it's happening. But when I check the EmbedLiteWebrtcUI.js file the reason for the error is immediately evident: the mozGetUserMediaDevices() method is no longer part of the Navigator interface, as defined in Navigator.webidl.

I can use git blame to find out when it was removed and why:
$ git log -1 -S "mozGetUserMediaDevices" dom/webidl/Navigator.webidl
commit 9f60769705be72d6057feb073c87aca32e0baf27
Author: Karl Tomlinson <karlt+@karlt.net>
Date:   Thu May 6 05:16:49 2021 +0000

    Bug 1709474 move mozGetUserMediaDevices from Navigator to 
    GetUserMediaRequest r=jib,webidl,geckoview-reviewers,smaug,agi
    
    Differential Revision: https://phabricator.services.mozilla.com/D111565
Referring to the upstream D111565 changeset, not only has the method been removed from the interface definition, it's also been removed from the source file, in this case Navigator.cpp. This is the method that could be found there in ESR 78:
void Navigator::MozGetUserMediaDevices(
    MozGetUserMediaDevicesSuccessCallback& aOnSuccess,
    NavigatorUserMediaErrorCallback& aOnError, uint64_t aInnerWindowID,
    const nsAString& aCallID, ErrorResult& aRv) {
  if (!mWindow || !mWindow->GetOuterWindow() ||
      mWindow->GetOuterWindow()->GetCurrentInnerWindow() != mWindow) {
    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
    return;
  }
Again, referring to the same changeset it looks like the method has been moved to the GetUserMediaRequest.cpp file, or at least that there's a new equivalent to be found there. This is what this looks like in ESR 91:
void GetUserMediaRequest::GetDevices(
    nsTArray<RefPtr<nsIMediaDevice>>& retval) const {
  MOZ_ASSERT(retval.Length() == 0);
  if (!mMediaDeviceSet) {
    return;
  }
  for (const auto& device : *mMediaDeviceSet) {
    retval.AppendElement(device);
  }
}
These are clearly not identical methods, but with a bit of adjustment to the calling code it may be that we'll need to make use of this as an alternative to MozGetUserMediaDevices(). I'm not sure how this will pan out, so this will require some more investigation. First up, this is the code in EmbedLiteWebrtcUI.js where this is called, which is currently present in both ESR 78 and ESR 91 because it's part of the embedlite-components package:
      case &quot;getUserMedia:request&quot;:
        let constraints = aSubject.getConstraints();
        let contentWindow = Services.wm.getOuterWindowWithId(aSubject.windowID);

        contentWindow.navigator.mozGetUserMediaDevices(
          constraints,
          function(devices) {
            if (!contentWindow.closed) {
              EmbedLiteWebrtcUI.prototype._prompt(
                contentWindow,
                aSubject.callID,
                constraints,
                devices,
                aSubject.isSecure);
            }
          },
          function(error) {
            Services.obs.notifyObservers(null, &quot;getUserMedia:request:
    deny&quot;, aSubject.callID);
            Cu.reportError(error);
          },
          aSubject.innerWindowID,
          aSubject.callID
        );
        break;
Note that this is JavaScript code, as are the following snippets as well. The equivalent upstream call as used by Firefox happening in the ESR 78 code is in the WebRTCChild.jsm file:
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,
        isHandlingUserInput
      );
    },
    function(error) {
      // Device enumeration is done ahead of handleGUMRequest, so we're not
      // responsible for handling the NotFoundError spec case.
      denyGUMRequest({ callID: aSubject.callID });
    },
    aSubject.innerWindowID,
    aSubject.callID
  );
}
And here's the equivalent code, as it would run on Firefox, on ESR 91. As we can see, this no longer uses the mozGetUserMediaDevices() method:
function handleGUMRequest(aSubject, aTopic, aData) {
  // Now that a getUserMedia request has been created, we should check
  // to see if we're supposed to have any devices muted. This needs
  // to occur after the getUserMedia request is made, since the global
  // mute state is associated with the GetUserMediaWindowListener, which
  // is only created after a getUserMedia request.
  GlobalMuteListener.init();

  let constraints = aSubject.getConstraints();
  let contentWindow = Services.wm.getOuterWindowWithId(aSubject.windowID);

  prompt(
    aSubject.type,
    contentWindow,
    aSubject.windowID,
    aSubject.callID,
    constraints,
    aSubject.devices,
    aSubject.isSecure,
    aSubject.isHandlingUserInput
  );
}
I'm going to need to compare these three methods further, but it's late here already, so that will have to wait until tomorrow. I'm not anticipating having much time to look at this tomorrow, but will try to get something done and posted about it nonetheless.

If you'd like to read any of my other gecko diary entries, they're all available on my Gecko-dev Diary page.

Comments

Uncover Disqus comments