Electron の Webview で新規タブを開く場合もページ遷移させる
- カテゴリ:
- JavaScript
- コメント数:
- Comments: 0
◆ new-window イベントが起きない
◆ 最近ではウィンドウ関係は main プロセスでまとめて制御するのが良いらしい
◆ remote は推奨されなくなってるので ipc 通信で処理を書いていく必要あり
◆ 実現はできたけどすごく面倒
◆ 最近ではウィンドウ関係は main プロセスでまとめて制御するのが良いらしい
◆ remote は推奨されなくなってるので ipc 通信で処理を書いていく必要あり
◆ 実現はできたけどすごく面倒
Electron の更新が辛い
Electron ってアップデートが早い上に破壊的変更が多すぎるので古いバージョンで作ったものを最新版に更新するのはとてもつらいですセキュリティを理由に 以前は簡単にできたことができなくなってるケースもあります
main プロセスで実行しないといけなくなっていたりです
さらに破壊的変更は繰り返されています
指示通りに書き換えたけど動かない → remote が使われていたけど 推奨されなくなったので手動で有効にするオプションが必要
オプションを有効にしたけど動かない → 別パッケージが必要になってるらしい
と言った感じです
ドキュメント上で破壊的変更を順にたどれるならいいのですが 検索しても出たり出なかったりですし 動かなくてググるの繰り返しです
それがあちこちの機能で発生するのでとてもつらいです
Webview
webview では 以前は target="_blank" や window.open で新しいタブを開こうとすると 開かれませんでしたが代わりに new-window イベントが起きていましたタブのような機能をサポートするなら webview を複数作って並べたりもできますが ひとつの webview だけならこのイベント時に webview.src を書き換えればページを移動できます
最新版にするとこの動きができなくなってました
ドキュメント上は new-window というイベントは残っているのに webview からそのイベントは発生しません
allowpopups 属性をつけてポップアップを許可すればイベントは発生しましたが ウィンドウが開かれてしまいます
ウィンドウが開かれると困ります
調べていると main プロセスで webContents を使って制御が必要らしいです
renderer プロセスだけで完結したいのに とても面倒です
やりたいことは新しいタブで開かず ひとつの webview でページを移動するだけなのに
方法
最終的にこういうものになりました[index.js] (main)
const { app, BrowserWindow, ipcMain, webContents } = require("electron")
const createWindow = () => {
const main_window = new BrowserWindow({
width: 1280,
height: 700,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
webviewTag: true,
},
})
ipcMain.on("webview-init", async (event, id) => {
const webview_webcontents = webContents.fromId(id)
webview_webcontents.setWindowOpenHandler(details => {
main_window.webContents.send("webview-new-window", details.url)
return { action: "deny" }
})
})
main_window.loadFile("page.html")
}
// ...
[page.html] (renderer)
<script type="module" src="page.js"></script>
<webview src="http://var.blog.jp/" allowpopups></webview>
[page.js] (renderer)
const { ipcRenderer } = require("electron")
const webview = document.querySelector("webview")
ipcRenderer.send("webview-init", webview.getWebContentsId())
ipcRenderer.on("webview-new-window", (event, url) => {
webview.src = url
})
remote は推奨されず簡単には使えなくなってるので 推奨される ipc 通信にしました
最初に webview の web contents id というものを取得して main プロセスに送っておきます
main プロセスで id を受け取ったら その id から webContents を取得します
その webContents に setWindowOpenHandler でウィンドウが開かれるときの動作を設定します
new-window イベントの代わりにウィンドウを開こうとしたことを伝えるため webview-new-window というメッセージを renderer プロセスに送っています
このときにウィンドウを開くアクションは全部拒否します
renderer プロセスでは webview-new-window というメッセージを受け取ったら url を webview に設定してページを移動します
webview タグに allowpopups をつけていないと main プロセスで setWindowOpenHandler を設定しても関数が呼び出されないので必要です
allow なのに全拒否されてポップアップは開かない とわかりづらいことになってますが仕方ないです
代替?
単純に新しいタブを使わずにページを移動するだけなのにすごく面倒ですこれまで new-window イベントが起きなくなって とりあえずの対処としてやっていた方法の方が楽で良かったんじゃないかと思いました
その方法は単純に target="_blank" を外すというものです
preload を使い webview のページ内で JavaScript を実行します
for (const a of document.querySelectorAll("a[target]")) {
a.removeAttribute("target")
}
動的に DOM を構築してるページに対応するなら MutationObserver を使います
click や mousedown イベント時に都度削除や preventDefault して JavaScript でページ移動させるということもできます
window.open を使うケースはもっと単純で関数を置き換えておきます
window.open = (url) => location = url
目的が new-window イベントの検知ではなく 新しいタブで開こうとせず同じ webview 内で開くことなのでこれでも十分です