Переменные страницы в скрипте контента - PullRequest
30 голосов
/ 18 октября 2010

Есть ли способ получить переменные javascript страницы из скрипта контента Google Chrome?

Ответы [ 8 ]

64 голосов
/ 19 октября 2010

Если вам действительно нужно, вы можете вставить элемент <script> в DOM страницы;код внутри вашего <script> элемента будет выполнен, и этот код будет иметь доступ к переменным JavaScript в области видимости окна.Затем вы можете передать их обратно в скрипт контента, используя атрибуты data- и запускать пользовательские события.Почему да, и намеренно по всем причинам в документации, которую цитировал serg.Но если вам действительно нужно это сделать, это можно сделать.См. здесь и здесь для получения дополнительной информации.И удачи!

19 голосов
/ 21 июня 2014

Я создал небольшой вспомогательный метод, получайте удовольствие:)

, чтобы получить переменные окна "lannister", "Always", "Pays", "his", "долгов" , вы выполняете следующее:

var windowVariables = retrieveWindowVariables(["lannister", "always", "pays", "his", "debts"]);
console.log(windowVariables.lannister);
console.log(windowVariables.always);

мой код:

function retrieveWindowVariables(variables) {
    var ret = {};

    var scriptContent = "";
    for (var i = 0; i < variables.length; i++) {
        var currVariable = variables[i];
        scriptContent += "if (typeof " + currVariable + " !== 'undefined') $('body').attr('tmp_" + currVariable + "', " + currVariable + ");\n"
    }

    var script = document.createElement('script');
    script.id = 'tmpScript';
    script.appendChild(document.createTextNode(scriptContent));
    (document.body || document.head || document.documentElement).appendChild(script);

    for (var i = 0; i < variables.length; i++) {
        var currVariable = variables[i];
        ret[currVariable] = $("body").attr("tmp_" + currVariable);
        $("body").removeAttr("tmp_" + currVariable);
    }

    $("#tmpScript").remove();

    return ret;
}

обратите внимание, что я использовал jQuery .. вы можете легко использовать нативный js "removeAttribute" и вместо "removeChild" .

14 голосов
/ 06 мая 2016

Используя решение Лиран, я добавляю исправление для Objects, вот правильное решение:

function retrieveWindowVariables(variables) {
    var ret = {};

    var scriptContent = "";
    for (var i = 0; i < variables.length; i++) {
        var currVariable = variables[i];
        scriptContent += "if (typeof " + currVariable + " !== 'undefined') $('body').attr('tmp_" + currVariable + "', JSON.stringify(" + currVariable + "));\n"
    }

    var script = document.createElement('script');
    script.id = 'tmpScript';
    script.appendChild(document.createTextNode(scriptContent));
    (document.body || document.head || document.documentElement).appendChild(script);

    for (var i = 0; i < variables.length; i++) {
        var currVariable = variables[i];
        ret[currVariable] = $.parseJSON($("body").attr("tmp_" + currVariable));
        $("body").removeAttr("tmp_" + currVariable);
    }

     $("#tmpScript").remove();

    return ret;
}
2 голосов
/ 30 марта 2019

Документация Chrome дает вам хорошую отправную точку: https://developer.chrome.com/extensions/content_scripts#host-page-communication

Этот метод позволяет вам извлекать глобальную переменную страницы в ваш скрипт контента.Он также использует идею, чтобы принимать только входящие сообщения, которые вы узнаете, учитывая ваше рукопожатие.Вы также можете просто использовать Math.random() для рукопожатия, но мне было весело.

Объяснение

  1. Этот метод создает тег сценария
  2. Он переводит функцию в строкуpropagateVariable и передает текущее handShake и имя целевой переменной в строку для сохранения, так как функция не будет иметь доступа к нашей области содержимого скрипта.
  3. Затем она внедряет этот тег сценария на страницу.
  4. Затем мы создаем слушателя в нашем скрипте контента, ожидающего ответа от страницы, чтобы передать обратно переменную, которую мы ищем.
  5. К настоящему моменту введенный скрипт попал на страницу.
  6. Внедренный код был обернут в IIFE , поэтому он сам запускает передачу данных слушателю.
  7. Необязательно: слушатель проверяет, что у него было правильное рукопожатие и вуаля, мы можем доверять источникуданных (это на самом деле не безопасно, но это помогает создать идентификатор в этом случае, что дает нам некоторый уровень доверия).

Раунд 1

v1.0

const globalToExtract = 'someVariableName';
const array = new Uint32Array(5);
const handShake = window.crypto.getRandomValues(array).toString();

function propagateVariable(handShake, variableName) {
  const message = { handShake };
  message[variableName] = window[variableName];
  window.postMessage(message, "*");
}

(function injectPropagator() {
  const script = `( ${propagateVariable.toString()} )('${handShake}', '${globalToExtract}');`
  const scriptTag = document.createElement('script');
  const scriptBody = document.createTextNode(script);
  
  scriptTag.id = 'chromeExtensionDataPropagator';
  scriptTag.appendChild(scriptBody);
  document.body.append(scriptTag);
})();

window.addEventListener("message", function({data}) {
  console.log("INCOMINGGGG!", data);
  // We only accept messages from ourselves
  if (data.handShake != handShake) return;

  console.log("Content script received: ", data);
}, false);

v1.1 с обещанием!

function extractGlobal(variableName) {

  const array = new Uint32Array(5);
  const handShake = window.crypto.getRandomValues(array).toString();

  function propagateVariable(handShake, variableName) {
    const message = { handShake };
    message[variableName] = window[variableName];
    window.postMessage(message, "*");
  }

  (function injectPropagator() {
    const script = `( ${propagateVariable.toString()} )('${handShake}', '${variableName}');`
    const scriptTag = document.createElement('script');
    const scriptBody = document.createTextNode(script);

    scriptTag.id = 'chromeExtensionDataPropagator';
    scriptTag.appendChild(scriptBody);
    document.body.append(scriptTag);
  })();

  return new Promise(resolve => {
    window.addEventListener("message", function({data}) {
      // We only accept messages from ourselves
      if (data.handShake != handShake) return;
      resolve(data);
    }, false);
  });
}

extractGlobal('someVariableName').then(data => {
  // Do Work Here
});

Раунд 2 - Класс и обещания

v2.0

Я бы порекомендовал добавить класс в его собственный файл и экспортировать его какпо умолчанию, если используются модули ES.Тогда это просто становится:

ExtractPageVariable('someGlobalPageVariable').data.then(pageVar => {
  // Do work here ?
});

class ExtractPageVariable {
  constructor(variableName) {
    this._variableName = variableName;
    this._handShake = this._generateHandshake();
    this._inject();
    this._data = this._listen();
  }

  get data() {
    return this._data;
  }

  // Private

  _generateHandshake() {
    const array = new Uint32Array(5);
    return window.crypto.getRandomValues(array).toString();
  }

  _inject() {
    function propagateVariable(handShake, variableName) {
      const message = { handShake };
      message[variableName] = window[variableName];
      window.postMessage(message, "*");
    }

    const script = `( ${propagateVariable.toString()} )('${this._handShake}', '${this._variableName}');`
    const scriptTag = document.createElement('script');
    const scriptBody = document.createTextNode(script);

    scriptTag.id = 'chromeExtensionDataPropagator';
    scriptTag.appendChild(scriptBody);
    document.body.append(scriptTag);
  }

  _listen() {
    return new Promise(resolve => {
      window.addEventListener("message", ({data}) => {
        // We only accept messages from ourselves
        if (data.handShake != this._handShake) return;
        resolve(data);
      }, false);
    })
  }
}

const windowData = new ExtractPageVariable('somePageVariable').data;
windowData.then(console.log);
windowData.then(data => {
   // Do work here
});
1 голос
/ 19 мая 2017

Если вы знаете, к каким переменным вы хотите получить доступ, вы можете создать быстрый пользовательский скрипт для получения их значений.

В popup.js:

chrome.tabs.executeScript(null, {code: 'var name = "property"'}, function() {
    chrome.tabs.executeScript(null, {file: "retrieveValue.js"}, function(ret) {
        for (var i = 0; i < ret.length; i++) {
            console.log(ret[i]); //prints out each returned element in the array
        }
    });
});

В retrieveValue.js:

function returnValues() {
    return document.getElementById("element")[name];
    //return any variables you need to retrieve
}
returnValues();

Вы можете изменить код для возврата массивов или других объектов.

1 голос
/ 21 июня 2015

Я на самом деле работал над этим, используя API localStorge. Примечание: чтобы использовать это, наш contentcript должен быть в состоянии прочитать localStorage. В файле manifest.json просто добавьте строку «storage»:

"permissions": [...,"storage"]

Функция угона живет в скрипте контента:

function hijack(callback) {
    "use strict";
    var code = function() {
      //We have access to topframe - no longer a contentscript          
      var ourLocalStorageObject = {
        globalVar: window.globalVar,
        globalVar2: window.globalVar2
      };
      var dataString = JSON.stringify(ourLocalStorageObject);
      localStorage.setItem("ourLocalStorageObject", dataString);
    };
    var script = document.createElement('script');
    script.textContent = '(' + code + ')()';
    (document.head||document.documentElement).appendChild(script);
    script.parentNode.removeChild(script);
    callback();
  }

Теперь мы можем звонить из содержания

document.addEventListener("DOMContentLoaded", function(event) { 
    hijack(callback);
});

или, если вы используете jQuery в вашем файле содержания, как я:

$(document).ready(function() { 
    hijack(callback);
});

для извлечения содержимого:

function callback() {
    var localStorageString = localStorage.getItem("ourLocalStorageObject");
    var ourLocalStorageObject= JSON.parse(localStorageString);

    console.log("I can see now on content script", ourLocalStorageObject);
    //(optional cleanup):
    localStorage.removeItem("ourLocalStorageObject");
}

Это можно вызвать несколько раз, поэтому, если ваша страница изменяет элементы или внутренний код, вы можете добавить прослушиватели событий, чтобы обновить ваше расширение новыми данными.

Редактировать: я добавил обратные вызовы, чтобы вы могли быть уверены, что ваши данные не будут недействительными (была эта проблема сама)

0 голосов
/ 09 мая 2019

Как объяснено частично в других ответах, переменные JS со страницы изолированы от вашего скрипта содержимого расширения Chrome. Обычно нет доступа к ним.

Но если вы вставите тег JavaScript на страницу, у вас будет доступ к тем переменным, которые там определены.

Я использую служебную функцию для вставки моего скрипта на странице:

/**
 * inject - Inject some javascript in order to expose JS variables to our content JavaScript
 * @param {string} source - the JS source code to execute
 * Example: inject('(' + myFunction.toString() + ')()');
 */
function inject(source) {
  const j = document.createElement('script'),
    f = document.getElementsByTagName('script')[0];
  j.textContent = source;
  f.parentNode.insertBefore(j, f);
  f.parentNode.removeChild(j);
}

Тогда вы можете сделать:

function getJSvar(whichVar) {
   document.body.setAttribute('data-'+whichVar,whichVar);
}
inject('(' + getJSvar.toString() + ')("somePageVariable")');

var pageVar = document.body.getAttribute('data-somePageVariable');

Обратите внимание, что если переменная представляет собой сложный тип данных (объект, массив ...), вам нужно будет сохранить значение в виде строки JSON в getJSvar (), а JSON.parse снова в своем скрипте содержимого.

0 голосов
/ 18 октября 2010

Нет.

Сценарии содержимого выполняются в специальной среде, называемой изолированным миром.Они имеют доступ к DOM страницы, в которую они внедрены, но не к каким-либо переменным или функциям JavaScript, созданным этой страницей.Каждый скрипт содержимого выглядит так, как будто на странице, на которой он запущен, нет другого JavaScript-кода.То же самое верно и в обратном: JavaScript, работающий на странице, не может вызывать какие-либо функции или обращаться к любым переменным, определенным скриптами контента.

Изолированные миры позволяют каждому скрипту контента вносить изменения в свою среду JavaScript, не беспокоясь о конфликте сстраница или с другими скриптами контента.Например, скрипт контента может включать JQuery v1, а страница может включать JQuery v2, и они не будут конфликтовать друг с другом.

Другое важное преимущество изолированных миров состоит в том, что они полностью разделяют JavaScript на странице.из JavaScript в расширениях.Это позволяет нам предлагать дополнительные функции сценариям содержимого, которые не должны быть доступны с веб-страниц, не беспокоясь о доступе к ним веб-страниц.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...