ElectronのWebviewで、特定のwebコンテンツを扱いたい。専ブラっぽいものの原型を調べる。
webview
- electronの標準ライブラリ
- ちょっと
iframe
っぽい- jsの実行空間がElectronレンダラーと別(あたりまえか)
- iframeは
window.parent.document.hoge
などでやり取りができるけど、webview
はプロセス間通信が必要(後述) - プロセス間通信は
webworker
っぽい感じ。arg/returnではなく、メッセージのやり取りでオブジェクトを運ぶ。
- Cookieが使えたり色々便利
使い方
基本
- htmlで
<webview>
を書くだけ。
<webview
id="wv"
src="https://electron.atom.io/">
</webview>
これだけ。
webview内のdocumentでjsを実行
- ChromeExtensionなどのようにロードしたドキュメント上でJSが実行できる。
- 外からJSをできるので、ちょっとXSSっぽい感じがする
preload
属性に実行したいjsを渡しておくと、
<webview
id="wv"
preload="inject.js"
src="https://electron.atom.io/">
</webview>
実行される。
var body = document.querySelector(body);
console.log(body);
これでindex.html
のconsoleを開いてみると、なにも出力されていないはず。これはmain > window > webview
の階層で別プロセス(別のJS空間)だから。index.htmlに以下を追記するとwebviewの中のdeveloperToolが開いて、console.logがへの出力が観察できます。
<script>
var webview = document.getElementById('wv');
webview.openDevTools();
</script>
webview内とelectronのwindowでやりとりする
上の例だとjsを埋め込んだだけになってしまうので、webViewから情報を取得してwindowに渡してみる。webviewの素晴らしいところは、埋め込んだjsinject.js
でnode_moduleが使えるところ。ElectronのipcRendererというメッセージAPIを使うことで、windowとwebviewでお話ができます。このへんのメッセージ授受方式がwebworkerっぽい。細かいことはソースに。
window側にメッセージを送るjsを用意します
<script src="ipc-host.js"></script>
<webview
id="wv"
preload="inject.js"
src="https://electron.atom.io/">
</webview>
var webview = document.getElementById('wv');
// ipcメッセージのイベントがwebviewにある
webview.addEventListener('ipc-message', function(event) {
switch(event.channel){
case "getContent":
console.log(event.arg[0]); // content.innerTextが出力される
});
break;
}
});
// webviewのロード完了イベント。onloadみたいな感じ。
webview.addEventListener("did-finish-load", function(){
webview.send("getContent");
});
webview側でメッセージオブジェクトを用意して、on('channel_name', function)
で受信時の動作を書きます。この中でsendToHost
を呼び出すことで、返信ができます。
// node api
const {ipcRenderer} = require('electron')
ipcRenderer.on('getContent', function(){
var content = document.getElementsById("content");
ipcRenderer.sendToHost('getContent', content.innerText);
});
便利なところ、工夫が必要なところ
- webviewでアクセスしたサイトのクッキーを維持できる。Electronを閉じてもログイン状態などが保持された。
- webview自体のAPIが豊富なのでiframeなどよりも楽に作れる。痒いところにも手が届きやすい感じ
- 画面遷移時に一瞬だけ画面が空白になる。
- クライアントアプリっぽさを出すには通信時のUIは必須
- iOS appはロード時にインジケータやローディングのUIを表示することをアプリ審査の基準としてほぼ義務化している
まだ調べていないこと、よくわからないこと
-
webview
のUAやリファラを指定する属性について。- UAを指定していない場合はChromeのUA?
- リファラを指定していない場合に画面遷移したらリファラはつく?
- webviewに
preload
で渡したjsはDOM操作できない?- HTMLを足したり消したりしようとしてみたがうまくいかず。
- XSS対策のため、許されていない?
-
webworker
だとwindowオブジェクトに触れないという制約があるので、似たような機構があるのかも - もしくはreadonly?