Edited at

ElectronのWebViewで遊ぶ

More than 1 year has passed since last update.

ElectronのWebviewで、特定のwebコンテンツを扱いたい。専ブラっぽいものの原型を調べる。


webview


  • electronの標準ライブラリ

  • ちょっとiframeっぽい


    • jsの実行空間がElectronレンダラーと別(あたりまえか)

    • iframeはwindow.parent.document.hogeなどでやり取りができるけど、webviewはプロセス間通信が必要(後述)

    • プロセス間通信はwebworkerっぽい感じ。arg/returnではなく、メッセージのやり取りでオブジェクトを運ぶ。



  • Cookieが使えたり色々便利


使い方


基本


  • htmlで<webview>を書くだけ。


index.html

<webview

id="wv"
src="https://electron.atom.io/">
</webview>

これだけ。


webview内のdocumentでjsを実行


  • ChromeExtensionなどのようにロードしたドキュメント上でJSが実行できる。

  • 外からJSをできるので、ちょっとXSSっぽい感じがする

preload属性に実行したいjsを渡しておくと、


index.html

<webview

id="wv"
preload="inject.js"
src="https://electron.atom.io/">
</webview>

実行される。


inject.js

var body = document.querySelector(body);

console.log(body);

これでindex.htmlのconsoleを開いてみると、なにも出力されていないはず。これはmain > window > webviewの階層で別プロセス(別のJS空間)だから。index.htmlに以下を追記するとwebviewの中のdeveloperToolが開いて、console.logがへの出力が観察できます。


index.html

<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を用意します


index.html

<script src="ipc-host.js"></script>

<webview
id="wv"
preload="inject.js"
src="https://electron.atom.io/">
</webview>


ipc-host.js


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を呼び出すことで、返信ができます。


inject.js

// 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?