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 · 36 comments
Closed

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

luicaps opened this issue Apr 1, 2015 · 36 comments

Comments

@luicaps
Copy link

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!

@max-mapper
Copy link
Contributor

@luicaps docs are here https://github.com/atom/atom-shell/blob/master/docs/api/shell.md#shellopenexternalurl

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

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.

@suchipi
Copy link

suchipi commented Oct 21, 2015

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

@AlicanC
Copy link

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.

@havenchyk
Copy link

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

@rubencodes
Copy link

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

@suchipi
Copy link

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.

@rubencodes
Copy link

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?

@pravdomil
Copy link
Contributor

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)

@peterlavey
Copy link

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>

@greggman
Copy link
Contributor

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?

@stevenaubertin
Copy link

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.

@alangrainger
Copy link

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)
  }
})

@MrDrProfX
Copy link

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.

@greggman
Copy link
Contributor

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);
  }
});

@MarshallOfSound
Copy link
Member

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

You can block those 👍

@tangliming
Copy link

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>

@rsemscom
Copy link

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);
});

@GrantGryczan
Copy link

What about data URLs?

@steve-todorov
Copy link

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.

@lefuturiste
Copy link

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?

@travis5491811
Copy link

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>

@hamzamu
Copy link

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.

@iMrDJAi
Copy link

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

@baruchiro
Copy link

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?

@travis5491811
Copy link

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?

da-kami added a commit to comit-network/ambrosia that referenced this issue Oct 16, 2020
Apparently this is not that trivial due to operating system differences,
so I opted for using the `open` library.
Details here: electron/electron#1344
@Nik720
Copy link

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?

@SupertigerDev
Copy link

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?

@danielweck
Copy link

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

@SharpDevSa
Copy link

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' };
});

@hanayashiki
Copy link

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

@SimonBiggs
Copy link

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);
    }
  })
})

@Canuckaholic
Copy link

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' }
})

@brad-za
Copy link

brad-za 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...

@SReject
Copy link

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' };
});

@Fefedu973
Copy link

Fefedu973 commented Jun 30, 2022

i tried this BUT i doesen't work beacause my link is a div let me show you:
my js js :

document.addEventListener('click', function (event) {
  if (event.target.tagName === 'A' && event.target.href.startsWith('http')) {
    event.preventDefault()
    shell.openExternal(event.target.href)
  }
})

My html :
Work with :
<a style="display:block;color:white;text-decoration:none;" href="https://discord.com/">Test</a>
Does not work with :
<a style="display:block;color:white;text-decoration:none;" href="https://discord.com/"><div class="socialoutline"><img style="width:35px;position:relative;left:7px;top:9px;"src="assets/images/logo/social/discord.svg">&#8195;&#8196;Discord</div></a>

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

No branches or pull requests