A little "background story":
You want to let the user choose and upload a file from your popup. But in OSX, as soon as the file-chooser dialog opens, the popup loses focus and closes, causing its JS context to get destroyed as well. Thus, the dialog opens and closes immediately.
This is a known bug on MAC for quite some time.
The solution:
You can move the dialog opening logic to the background-page, which is not affected by loss of focus. From the popup, you can send a message to the background-page, requesting to initiate the browse-and-upload process (see sample code below).
manifest.json
{ ... "background": { "persistent": false, "scripts": ["background.js"] }, "browser_action": { "default_title": "Test Extension", // "default_icon": { // "19": "img/icon19.png", // "38": "img/icon38.png" // }, "default_popup": "popup.html" }, "permissions": [ "https://www.example.com/uploads" // The above permission is needed for cross-domain XHR ] }
popup.html
... <script src="popup.js"></script> </head> <body> <input type="button" id="button" value="Browse and Upload" /> ...
popup.js
document.addEventListener('DOMContentLoaded', function () { document.getElementById('button').addEventListener('click', function () { chrome.runtime.sendMessage({ action: 'browseAndUpload' }); window.close(); }); });
background.js
var uploadUrl = 'https://www.example.com/uploads'; /* Creates an `input[type="file]` */ var fileChooser = document.createElement('input'); fileChooser.type = 'file'; fileChooser.addEventListener('change', function () { var file = fileChooser.files[0]; var formData = new FormData(); formData.append(file.name, file); var xhr = new XMLHttpRequest(); xhr.open('POST', uploadUrl, true); xhr.addEventListener('readystatechange', function (evt) { console.log('ReadyState: ' + xhr.readyState, 'Status: ' + xhr.status); }); xhr.send(formData); form.reset(); // <-- Resets the input so we do get a `change` event, // even if the user chooses the same file }); /* Wrap it in a form for resetting */ var form = document.createElement('form'); form.appendChild(fileChooser); /* Listen for messages from popup */ chrome.runtime.onMessage.addListener(function (msg) { if (msg.action === 'browseAndUpload') { fileChooser.click(); } });
Heads up:
As a security precaution, Chrome will execute fileChooser.click() only if it is a result of user interaction.
In the above example, the user clicks the button in the popup, which sends a message to the background-page, which calls fileChooser.click();. If you try to call it programmatically it won't work. (E.g. calling it on document load won't have any effect.)