diff --git a/lib/browser/utils/open-external/directives/open-external.js b/lib/browser/utils/open-external/directives/open-external.js index 5f86d75f..b9ced94d 100644 --- a/lib/browser/utils/open-external/directives/open-external.js +++ b/lib/browser/utils/open-external/directives/open-external.js @@ -18,6 +18,8 @@ const electron = require('electron'); const shell = electron.remote.require('shell'); +const os = require('os'); +const nodeOpen = require('open'); /** * This directive provides an attribute to open an external @@ -41,6 +43,25 @@ module.exports = function() { element.css('cursor', 'pointer'); element.on('click', function() { + + // Electron's `shell.openExternal()` fails on GNU/Linux + // when Electron is ran with `sudo`. + // The issue was reported, and this is a workaround until + // its fixed on the Electron side. + // `node-open` is smart enough to check the `$SUDO_USER` + // environment variable and to prepend `sudo -u ` + // if needed. + // We keep `shell.openExternal()` for OSes other than + // Linux since we intend to fully rely on it when the + // issue is fixed, and since its closer integration with + // the operating system might lead to more accurate results + // than a third party NPM module. + // + // See https://github.com/electron/electron/issues/5039 + if (os.platform() === 'linux') { + return nodeOpen(scope.openExternal); + } + shell.openExternal(scope.openExternal); }); } diff --git a/package.json b/package.json index 8698474f..74e0a403 100644 --- a/package.json +++ b/package.json @@ -83,6 +83,7 @@ "is-elevated": "^1.0.0", "lodash": "^4.5.1", "ngstorage": "^0.3.10", + "open": "0.0.5", "resin-image-write": "^2.0.5", "resin-zip-image": "^1.1.2", "sudo-prompt": "^2.2.0", diff --git a/tests/browser/utils/open-external.spec.js b/tests/browser/utils/open-external.spec.js index ba6b8fe9..27c26556 100644 --- a/tests/browser/utils/open-external.spec.js +++ b/tests/browser/utils/open-external.spec.js @@ -17,6 +17,7 @@ 'use strict'; const m = require('mochainon'); +const os = require('os'); const angular = require('angular'); const electron = require('electron'); const shell = electron.remote.require('shell'); @@ -43,13 +44,26 @@ describe('Browser: OpenExternal', function() { m.chai.expect(element.css('cursor')).to.equal('pointer'); }); - it('should call Electron shell.openExternal with the attribute value', function() { - const shellExternalStub = m.sinon.stub(shell, 'openExternal'); - const element = $compile('Resin.io')($rootScope); - element.triggerHandler('click'); - $rootScope.$digest(); - m.chai.expect(shellExternalStub).to.have.been.calledWith('https://resin.io'); - shellExternalStub.restore(); + describe('given non linux', function() { + + beforeEach(function() { + this.osPlatformStub = m.sinon.stub(os, 'platform'); + this.osPlatformStub.returns('darwin'); + }); + + afterEach(function() { + this.osPlatformStub.restore(); + }); + + it('should call Electron shell.openExternal with the attribute value', function() { + const shellExternalStub = m.sinon.stub(shell, 'openExternal'); + const element = $compile('Resin.io')($rootScope); + element.triggerHandler('click'); + $rootScope.$digest(); + m.chai.expect(shellExternalStub).to.have.been.calledWith('https://resin.io'); + shellExternalStub.restore(); + }); + }); });