Electron で <webview> を使って UI をつくっていると,一般的なデスクトップアプリのようにリンクをクリックした時に外部ブラウザでそれを開きたいことがあります.
<webview> 内でリンクをクリックして遷移しようとしたときにフックして外部ブラウザで代わりに開く処理を実装したのでそのメモです.
Electron の <webview> のドキュメントはここ にすべてあります.(これだけしかないとも言う)
なお,renderer プロセスからでも require('shell').openExternal(url) で外部ページを直接開くことができます.(実際には url をプロセス間通信でブラウザプロセス側に送り,そちらで外部ブラウザに渡しているようです)
'new-window' イベント
<webview> にいくつかあるイベントのうち,'new-window' イベントを受けると,<webview> 内のゲストページが新しいウィンドウを開こうとしたときにそれをフックすることができます.
let webview = document.getElementById('my-cool-webview');
webview.addEventListener('new-window', function(e){
console.log('Guest window is openning new window: ' + e.url);
require('shell').openExternal(e.url);
});
しかしこれだけでは充分ではありません.このイベントはゲストページが新しいウィンドウ(タブ)をつくろうとしたときに発生するため,ページ内で別の URL に遷移した場合には発火しません.
'did-start-loading' イベント
そこで,dirty hack 気味ですが 'did-start-loading' イベントを使います.このイベントはゲストページがページをロードし始めた時に呼ばれます.よって,「ゲストページが別ページをロードしようとした時にそれを妨害してブラウザでウィンドウを開いてやる」という処理でやりたいことが実現できます.ゲストページの読み込みを止めるには webview.stop() メソッドを,ゲストページが開いている URL を取るには webview.getUrl() メソッドがそれぞれ使えます.
let webview = document.getElementById('my-cool-webview');
webview.addEventListener('did-start-loading', function(e){
const url = webview.getUrl();
console.log('Guest window is loading new page: ' + url);
webview.stop();
require('shell').openExternal(url);
});
まとめ
まとめると次のような処理でやりたいことが実現できます.webview がページをロードする前に 'did-start-loading' をフックしてしまうと最初の読み込みも邪魔してしまうと思うので,'dom-ready' で読み込みが完了したときにフックを仕掛けるようにします.また,'did-start-loading' 中は読み込みを中止する前に getUrl() で開こうとしていたページを取得しておきます.
const shell = require('shell');
let webview = document.getElementById('my-cool-webview');
webview.addEventListener('new-window', function(e){
shell.openExternal(e.url);
});
webview.addEventListener('dom-ready', function(e){
webview.addEventListener('did-start-loading', function(e){
const url = webview.getUrl();
webview.stop();
shell.openExternal(url);
});
});
これでアプリの中のページ遷移を抑えつつリンクは外部のブラウザで開くようにできました.