From e8516b1727dcffbff58b1abf4e4341395cfeb544 Mon Sep 17 00:00:00 2001 From: Juan Cruz Viotti Date: Wed, 6 Apr 2016 11:12:46 -0400 Subject: [PATCH] Fix shell.openExternal() freezing GNU/Linux 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 Signed-off-by: Juan Cruz Viotti --- .../open-external/directives/open-external.js | 21 ++++++++++++++ package.json | 1 + tests/browser/utils/open-external.spec.js | 28 ++++++++++++++----- 3 files changed, 43 insertions(+), 7 deletions(-) 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(); + }); + }); });