Getting file from file chooser after extension popup closed


(jrie) #1

In the previous version of Firefox, it was possible to get and react at a ‘change’ event of a input field of the type ‘file’ from within a popup page, after the file chooser was open and a file was selected.

Since the update of Firefox, this seems to be not possible anymore.
Did anything related to this change?

This kind of functionality is used in one add-on, here:
FlagCookies.js - importing settings file


(Juraj Masiar) #2

I don’t see the code for getting the file name in there (not a single name property is being accessed).
In my code the name is accessed through e.target.files[0].name and it still works even in current Nightly 65. Maybe issue with onchange / change handler?


(Niklas Gollenstede) #3

The reason this is happening is that the panel closes on blur – and opening any kind of (non-HTML) dialog takes the focus. This behavior is … unfortunate, but there is no way to prevent it (as an extension developer).

Anyway, after the panel closes and emits its unload event, no other events or timers related to that window will fire.

What could work is to have the background page “own” the input element. Maybe the event will then sill fire even if the panel is gone. But I don’t think that the file picker can be opened while the input element is owned by the background page. So what you might want to try is to transfer the ownership of the element to the background page after the picker is opened.

That could look something like this:

const input = document.querySelector('input[type=file]');

const background = await browser.runtime.getBackgroundPage();
if (!background) { console.warn(`This doesn't work in private windows ...`); return; }

window.onunload = () => {
    background.document.adoptNode(input); // this may or may not help, but if it doesn't, I don't think there is a solution at all ...
};

input.onchange = () => {
    background.doSomethingWith(input.files);
};

Just be aware that your window is still dead, so doSomethingWith should probably be a function from a background script, to avoid weird things.


(jrie) #4

@juraj.masiar
Thank you for your answer. But there is no need to retrieve the file name as jsZip is can use the file object (blob) directly instead. But as you pointed out, there’s a issue with the change event/handler.

@NilkasG
Indeed that is the case as you describe, I added a fix on Github, which makes use of getBackgroundPage() and document.adoptNode().

This works very well, the only downside to your solution, getBackgroundPage() does not work in ‘Private Windows’.

Also, there was no direct need to make use of the unload event and the import function had to be added directly to the background script.

I committed the changes here:
File input change on Github

Thank you very much help!


(Niklas Gollenstede) #5

Interesting, and good to know that this works.

So to summarize what you are doing /what’s happening (in that order):

  • define the change event handler in a background script
  • the panel opens and has the file input in its DOM
  • input element be adopted by the background page, set the change event handler
  • the node is still in the panels DOM and will be clicked by the user
  • the panel closes but the event is still fired (in the background page)

Is that right? Please do correct me if I’m wrong!


(jrie) #6

@NilkasG
The steps are right.

For it to work, the function to be called has to be placed in the background script, otherwise, after the blur of the popup page, the function is removed prior and marked as “dead object”, even when anonymous - which is not accessible anymore then.

Just to mention, the trigger for the “background page handling” is called by click on the input. Not on unload, as the input node is not accessible for transfer to the background page at this point.

So yes, it is like you say.


(Niklas Gollenstede) #7

Ah, yes. It might be important to call adoptNode after the click that opens the popup, because I think that it removes the node from its original DOM.
And also that the “entire” event listener must be a background script function, to avoid that [dead object] business.

Thanks for your feedback!