41
55

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ElectronのWebViewで遊ぶ

Last updated at Posted at 2017-04-06

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?
41
55
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
41
55

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?