Audio element losing custom properties

A content script I’ve sets custom properties on an Audio element. The property is later used in a key event handler. However, I notice that the element loses the said property when injected no a BBC website that has video content. Here’s a distilled down version of the content script:

function keyHandler(ev) {
  var str = new Array();
  if (ev.altKey)
    str.push("alt");
  str.push(ev.key);

  switch (str.join("+").toLowerCase()) {
    case "alt+x":
      if (audio.name)
        console.log("audio has name property");
      else
        console.log("audio is missing name property  that was set earlier");
      break;
  }
}

var audio = new Audio();

audio.name = "foobar";

// Register keyboard handler
document.addEventListener("keydown", keyHandler);

Press alt+x to trigger the handler. On most web pages, I get the message “audio has name property” on the console. On this BBC page, for example, I get “audio is missing name property that was set earlier”: http://www.bbc.co.uk/news/uk-england-leeds-42225040.

Can someone point me what’s gonig on please? Thanks.

Are you sure you add the .name property to exactly that element? You could add all processed audio elements in a WeakSet and check the presence in that set to check that.

Does this work with property manes that defensively won’t be interpreted as attributes?

The snippet I shared is where the behaviour can be observed - nothing more needed. As to your question, yes, there’s only one object of that name, and there are no default attributes called ‘name’. I mean, it works on almost everwhere else!

Would someone like to try the snippet?

I’ve played around with it a little. I think this is a side-effect of X-Ray wrappers (the system that leads to https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Content_scripts#Sharing_objects_with_page_scripts and so on). If you instead set and read it on the wrappedJSObject property it works as expected. I think the base object is somehow protected from tampering by X-Rays.

I haven’t found a website where it works as you are expecting in Firefox.

Not sure if that’s exactly what’s going on, but it seems very much X-Ray related.

Actually, this is https://bugzilla.mozilla.org/show_bug.cgi?id=1408996

X-Ray is the reason why I asked about attribute accessors. If you were to set or get the .name property on an input element, you’d set/get its name attribute, which is shared state with the website context and can thus be changed by the page scripts. This should not be the case for <audio> elements, as they don’t have name as an attribute, but to be safe, you should still use a different name or place your data in a WeakMap.

Using wrappedJSObject is generally risky, because the page can change the properties of that (and the prototypes and properties of properties of the properties …) in unexpected ways.

You could still try to use WeakMaps. using the input.wrappedJSObject as a WeakMap key should be safe.

Ah, thanks. That bug report does seem to describe my issue. I’ll subscribe to it.

In any case, my argugent – and the same that was made in the bug report – was that content scripts are to be executed in a sandbox, so at least it should see what it had set before, regardless of what had gone on in the rest of the world.

1 Like