LoginSignup
3
3

More than 5 years have passed since last update.

Content Script内でwindow contextで実行した関数の結果を取得する

Last updated at Posted at 2016-02-08

Background

Chrome ExtensionのContent Scriptではcontextがページ上で実行されているJSとは違うためwindow経由で変数取得や関数実行ができません。

windowコンテキストで関数を実行する方法は

http://qiita.com/suin/items/5e1aa942e654bce442f7

http://stackoverflow.com/questions/20499994/access-window-variable-from-content-script

などいろいろありますが、結果を取得する例は見つからなかったので自分で作ってみました。

Solution

Gist: https://gist.github.com/dtaniwaki/b2b29a95b98f593ffd57

関数を実行した後でその結果を挿入したscriptタグのdatasetにJSONにシリアライズして格納し、content script側から同じIDのscriptを探してデシリアライズしてコールバックに結果を渡しています。

windowContextRunner.js
'use strict'

let waitScriptResult = function (id, timeout) {
  return new Promise(function (resolve, reject) {
    let timerId
    timerId = setInterval(function () {
      let elem = document.getElementById(id)
      if (typeof elem !== 'undefined' && typeof elem.dataset.done !== 'undefined') {
        clearInterval(timerId)
        timerId = null
        if (typeof elem.dataset.error !== 'undefined' && elem.dataset.error !== '') {
          reject(Error(elem.dataset.error))
        } else {
          resolve(JSON.parse(elem.dataset.result))
        }
        document.body.removeChild(elem)
      }
    }, 100)
    setTimeout(function () {
      if (timerId) {
        clearInterval(timerId)
        reject(Error('The injected script was timeout'))
      }
    }, timeout || 5000)
  })
}

let injectScript = function (promise) {
  let n = Math.floor(Math.random() * 1000000)
  let id = `kzaex_run_in_page_${n}`
  let s = document.createElement('script')
  s.id = id
  s.text = `
    (function() {
      var promise = ${promise.toString()};
      var script = document.getElementById("${id}");
      promise.then(function(result, reject) {
        script.dataset.result = JSON.stringify(result);
        script.dataset.done = 'true';
      }).catch(function(e) {
        script.dataset.error = e.message;
        script.dataset.done = 'true';
      })
    })();
    `
  document.body.appendChild(s)
  return id
}

export function run (script, timeout) {
  let id = injectScript(`
    new Promise(function(resolve, reject) {
      try {
        resolve((${script.toString()})());
      } catch(e) {
        reject(e);
      }
    });
    `)
  return waitScriptResult(id, timeout)
}

export function runWithCallback (script, timeout) {
  let id = injectScript(`
    new Promise(function(resolve, reject) {
      try {
        (${script.toString()})(function(result) {
          resolve(result);
        });
      } catch(e) {
        reject(e);
      }
    });
  `)
  return waitScriptResult(id, timeout)
}

使い方は以下のようになります。

import * as windowContextRunner from './windowContextRunner.js'

windowContextRunner.run(function() {
  // Do something and return the result
  var result = 1;
  return result;
}).then(function(result) {
  // Use the result of the function above
  result == 1
});

エラーハンドリングなどは特にここでは深く実装していませんが、簡単に追加できると思うので用途に応じて追加してください!

3
3
0

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
3
3