Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How do I open a url from <a> on default OS browser? #1344

Closed
luicaps opened this issue Apr 1, 2015 · 35 comments
Closed

How do I open a url from <a> on default OS browser? #1344

luicaps opened this issue Apr 1, 2015 · 35 comments

Comments

@luicaps
Copy link

@luicaps luicaps commented Apr 1, 2015

I want to take an to an external website, but I do not want it to happen inside atom-shell, I want it to be in the default browser. How can I do that?

Thanks!

@maxogden
Copy link
Contributor

@maxogden maxogden commented Apr 2, 2015

Loading

@zcbenz zcbenz closed this Apr 2, 2015
@AlicanC
Copy link

@AlicanC AlicanC commented Sep 7, 2015

On my setup, I tried both shell.openExternal('http://example.com') and shell.openItem('http://example.com') and both opened the website in the background.

I ended up using child_process.execSync('start http://example.com') on Win32 and child_process.execSync('open http://example.com') on Darwin so the browser actually pops up and gets focus.

Loading

@suchipi
Copy link

@suchipi suchipi commented Oct 21, 2015

On Linux, you can use xdg-open:
child_process.execSync('xdg-open http://example.com')

Loading

@AlicanC
Copy link

@AlicanC AlicanC commented Oct 21, 2015

I have actually decided to use node-open. You should give it a try if you don't want to mess with escaping and such.

Loading

@havenchyk
Copy link

@havenchyk havenchyk commented Jan 4, 2016

Guys, but your solutions are great, but I'm wondering how to intercept attempt to open url like "http://someurl.com" and then open in with shell.openExternal? Service worker allows to catch only requests to the files on the same domain. Is there ability to do this? /cc @maxogden @AlicanC

Loading

@rubencodes
Copy link

@rubencodes rubencodes commented Jan 14, 2016

Same question here as @havenchyk, is there a way to tell electron to open links external by default?

Loading

@suchipi
Copy link

@suchipi suchipi commented Jan 14, 2016

If your app only uses one window, and you can guarantee that every external link in your app opens in a new window (via eg. target="_blank"), you can do something like:

webContents.on('new-window', function(event, url){
  event.preventDefault();
  open(url);
});

Where webContents is your main BrowserWindow's webContents and open is a function that opens the url in your browser (I use node-open as recommended by AlicanC).

It'd be nice if there was an event fired when any link is clicked, so the app could decide if it should open in the browser, but I haven't found such an event if it exists.

Loading

@rubencodes
Copy link

@rubencodes rubencodes commented Jan 14, 2016

I found this code snippet on S.O.:

    var shell = require('electron').shell;
    //open links externally by default
    $(document).on('click', 'a[href^="http"]', function(event) {
        event.preventDefault();
        shell.openExternal(this.href);
    });

Dropped it in my main index file, it seems to be working as far as I can tell, even for dynamically generated links. I'm too noob at electron to know if there are any drawbacks to this I should watch out for. Thoughts?

Loading

@pravdomil
Copy link
Contributor

@pravdomil pravdomil commented Apr 12, 2016

I'm using this piece of code:

var handleRedirect = (e, url) => {
  if(url != webContents.getURL()) {
    e.preventDefault()
    require('electron').shell.openExternal(url)
  }
}

webContents.on('will-navigate', handleRedirect)
webContents.on('new-window', handleRedirect)

Loading

@peterlavey
Copy link

@peterlavey peterlavey commented May 11, 2016

First declare a spawn proccess

`app.controller('GuideCtrl', ['$scope', ($scope)=>{
const spawn = require('child_process').spawn;

  $scope.openBrowser=(url)=>{
     let exec = spawn('explore', [url], {});
     exec.stdout.on('data', (data)=> {
        console.log('stdout: ' + data)
     });
  }

}])`

and before call the method

<a ng-click="openBrowser('https://google.com')">Goto google</a>

Loading

@greggman
Copy link
Contributor

@greggman greggman commented Nov 23, 2016

Does shell.openExternal have any security issues? For example if links come off the net then raw net data is being passed to shell.openExternal or any of the other functions above. Does shell.openExternal make sure nothing bad's going to happen? Do I need to filter for schemas?

Loading

@stevenaubertin
Copy link

@stevenaubertin stevenaubertin commented Dec 3, 2016

Base on the code from @rubencodes , i used :

   const shell = require('electron').shell;
   $('.open-in-browser').click((event) => {
           event.preventDefault();
           shell.openExternal(event.target.href);
   });

Then you just have to drop the 'open-in-browser' class to each elements you want to open in the browser.

Loading

@alangrainger
Copy link

@alangrainger alangrainger commented Oct 26, 2017

Here's one that doesn't require JQuery, in case anyone else is hunting for it. It will automatically open any link that starts with 'http' in the external browser.

Put this in your renderer process:

// Open all links in external browser
let shell = require('electron').shell
document.addEventListener('click', function (event) {
  if (event.target.tagName === 'A' && event.target.href.startsWith('http')) {
    event.preventDefault()
    shell.openExternal(event.target.href)
  }
})

Loading

@MrDrProfX
Copy link

@MrDrProfX MrDrProfX commented Jan 22, 2018

If you want ALL <a> tags to open in the default browser, try this in your main.ts:

const shell = require('electron').shell;

mainWin.webContents.on('will-navigate', (event, url) => {
  event.preventDefault()
  shell.openExternal(url)
});

This assumes your have a single page app like me. If not, you'll need to do some extra sanitizing.

Loading

@greggman
Copy link
Contributor

@greggman greggman commented Jan 22, 2018

I just want to reiterate that using this with user content without a whitelist is probably a gaping security hole. I don't know what all various schemes do and what their inputs are but just as a simple example a link like this

 <a href="imessage:hello">click me</a>

Will prompt the user in Chrome and Safari on macOS but with the code above Electron will just open the app.

It almost feels like Electron itself should default to being more secure here rather than leave it to every individual programmer to figure out how to make it secure on their own.

At a minimum you probably want something like

function isSafeishURL(url) {
  return url.startsWith('http:') || url.startsWith('https:');
}

mainWin.webContents.on('will-navigate', (event, url) => {
  event.preventDefault();
  if (isSafeishURL(url)) {
    shell.openExternal(url);
  }
});

Loading

@MarshallOfSound
Copy link
Member

@MarshallOfSound MarshallOfSound commented Jan 22, 2018

@greggman See the open external permission type in https://electronjs.org/docs/api/session#sessetpermissionrequesthandlerhandler

You can block those 👍

Loading

@tangliming
Copy link

@tangliming tangliming commented May 15, 2018

Hi, I am using vue.js and solve this problem inspired by above discussion, in case some one like me using vue also have the same problem, I paste my code here.

<template>
<div class="board-item" v-for="element in list" :key="element.id">
<span><a class='board-item-a' :href='element.url' target='_blank'>{{element.title}}</a></span>
</div>
</template>

<script>
mounted () {
this.$el.querySelectorAll('.board-item-a').forEach(a => {
a.addEventListener('click', (e) => {
e.preventDefault()
require('electron').shell.openExternal(e.target.href)
})
})
},
</script>

Loading

@rsemscom
Copy link

@rsemscom rsemscom commented May 29, 2018

Based on comment of @alangrainger - ts version:

const {app, shell, BrowserWindow} = require('electron');

...
mainWindow.webContents.on('new-window', function(event, url){
   event.preventDefault();
   shell.openExternal(url);
});

Loading

@GrantGryczan
Copy link

@GrantGryczan GrantGryczan commented Jun 30, 2018

What about data URLs?

Loading

@steve-todorov
Copy link

@steve-todorov steve-todorov commented Feb 17, 2019

Angular 7 version (with live reloads):

        const openExternalLinksInOSBrowser = (event, url) => {
            if (url.match(/.*localhost.*/gi) === null && (url.startsWith('http:') || url.startsWith('https:'))) {
                event.preventDefault();
                shell.openExternal(url);
            }
        };
        win.webContents.on('new-window', openExternalLinksInOSBrowser);
        win.webContents.on('will-navigate', openExternalLinksInOSBrowser);

The url.match(/.*localhost.*/gi) === null part is necessary because otherwise when you change something in your angular application it will open new window/tab in your OS browser instead of reloading it in the electron app.

Loading

@lefuturiste
Copy link

@lefuturiste lefuturiste commented Jul 4, 2019

All the methods work fine but only on non root electron app, what can I use to open a external url on default OS browser on a root elevated process?

Loading

@travis5491811
Copy link

@travis5491811 travis5491811 commented Oct 18, 2019

Just wanted to post an answer for other Vue.js users.

<template>
  <div>
    <a href="https://google.com" target="_blank" @click.prevent="openExternalBrowser">Google.com Status</a>
  </div>
</template>

<script>
const { remote } = require('electron');

export default {
  name: 'HelloWorld',
  methods: {
    openExternalBrowser(e) {
      remote.shell.openExternal(e.target.href);
    },
  },
};
</script>

Loading

@hamzamu
Copy link

@hamzamu hamzamu commented Oct 20, 2019

Just wanted to post an answer for other Vue.js users.

<template>
  <div>
    <a href="https://google.com" target="_blank" @click.prevent="openExternalBrowser">Google.com Status</a>
  </div>
</template>

<script>
const { remote } = require('electron');

export default {
  name: 'HelloWorld',
  methods: {
    openExternalBrowser(e) {
      remote.shell.openExternal(e.target.href);
    },
  },
};
</script>

Thank you.

Loading

@iMrDJAi
Copy link

@iMrDJAi iMrDJAi commented Dec 3, 2019

On main.js:

app.on('web-contents-created', (e, contents) => {
    contents.on('new-window', (e, url) => {
      e.preventDefault();
      require('open')(url);
    });
    contents.on('will-navigate', (e, url) => {
      if (url !== contents.getURL()) e.preventDefault(), require('open')(url);
    });
});

You need to install open package:

npm i open --save

Loading

@baruchiro
Copy link

@baruchiro baruchiro commented Dec 7, 2019

Just wanted to post an answer for other Vue.js users.

@travis5491811 This opens up another Electron window with the right page. Is this the expected behavior?

Loading

@travis5491811
Copy link

@travis5491811 travis5491811 commented Dec 9, 2019

No, when implemented properly, my post should answer the thread ticket "How do I open a url from on default OS browser", but for vue users. The answer I provided works in my app Electron v5.0.11, Vue v2.6.10, tested in production on multiple Windows 10 and Linux Desktop machines

Just wanted to post an answer for other Vue.js users.

@travis5491811 This opens up another Electron window with the right page. Is this the expected behavior?

Loading

@Nik720
Copy link

@Nik720 Nik720 commented Feb 3, 2021

Is there any way to set watcher for shell.openExternal(URL); I want to set watcher for opened page's specific URL and when I got that URL Need to close the browser window or tab and return?

Loading

@supertiger1234
Copy link

@supertiger1234 supertiger1234 commented Mar 24, 2021

appWindow.webContents.on('new-window', evt) is now deprecated and setWindowOpenHandler should be used. how can i prevent defaults like i used to with new-window?

Loading

@danielweck
Copy link

@danielweck danielweck commented Apr 8, 2021

appWindow.webContents.on('new-window', evt) is now deprecated and setWindowOpenHandler should be used. how can i prevent defaults like i used to with new-window?

https://www.electronjs.org/docs/api/window-open#browserwindowproxy-example

Loading

@SharpDevSa
Copy link

@SharpDevSa SharpDevSa commented Apr 15, 2021

My code snippet clue to deal with it accordingly to my Electron version ^12.0.0

const win = new BrowserWindow();
win.webContents.setWindowOpenHandler(({ url }) => {
    // config.fileProtocol is my custom file protocol
    if (url.startsWith(config.fileProtocol)) {
        return { action: 'allow' };
    }
    // open url in a browser and prevent default
    shell.openExternal(url);
    return { action: 'deny' };
});

Loading

@hanayashiki
Copy link

@hanayashiki hanayashiki commented May 7, 2021

shell.openExternal allows RCE if you don't trust the source. Hope the eletron can provide a secure open for that.

Loading

@SimonBiggs
Copy link

@SimonBiggs SimonBiggs commented Sep 17, 2021

To expand on @hanayashiki's answer, here is what was found after some google searching:

https://benjamin-altpeter.de/shell-openexternal-dangers/

Given the above, @hanazuki would you say that the following may be appropriate?

import url from "url";

app.on('web-contents-created', (event, contents) => {
  contents.on('will-navigate', (event, navigationUrl) => {
    event.preventDefault();

    const parsedUrl = new url.URL(navigationUrl);
    if (["https:", "http:", "mailto:"].includes(parsedUrl.protocol)) {
      shell.openExternal(navigationUrl);
    }
  })
})

Loading

@Canuckaholic
Copy link

@Canuckaholic Canuckaholic commented Nov 8, 2021

For anyone who stumbles upon this thread, I found the official Electron documentation to not be very clear. This achieved what I was looking for, which is simply to open an external link in the user's default browser and not open an Electron window:

this.win.webContents.setWindowOpenHandler(({ url }) => {
            // Only allow https external links
            if (url.startsWith('https:')) {
                shell.openExternal(url)
            }
            return { action: 'deny' }
})

Loading

@putintin420
Copy link

@putintin420 putintin420 commented Nov 16, 2021

@Canuckaholic Did you put that in your createWindow function? I have had no luck implementing any of the fixes above. Spent days on this now...

Loading

@SReject
Copy link

@SReject SReject commented Nov 18, 2021

@Canuckaholic Did you put that in your createWindow function? I have had no luck implementing any of the fixes above. Spent days on this now...

In your main process after creating a BrowserWindow instance you apply the hook

const { BrowserWindow, shell } = require('electron');

const win = new BrowserWindow({ /* options */ });

win.webContents.setWindowOpenHandler(({ url }) => {
    if (url.startsWith('https:')) {
        shell.openExternal(url);
    }
    return { action: 'deny' };
});

Loading

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet