Расширение Chrome - получение содержимого DOM

116

Я пытаюсь получить доступ к содержимому ActiveTab DOM из своего всплывающего окна. Вот мой манифест:

{
  "manifest_version": 2,

  "name": "Test",
  "description": "Test script",
  "version": "0.1",

  "permissions": [
    "activeTab",
    "https://api.domain.com/"
  ],

  "background": {
    "scripts": ["background.js"],
    "persistent": false
  },
  "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",

  "browser_action": {
    "default_icon": "icon.png",
    "default_title": "Chrome Extension test",
    "default_popup": "index.html"
  }
}

Я действительно не понимаю, подходят ли фоновые сценарии (страницы событий с persistence: false) или content_scripts. Я прочитал всю документацию и другие сообщения SO, но для меня это все еще не имеет смысла.

Может кто-нибудь объяснить, почему я могу использовать одно вместо другого.

Вот файл background.js, который я пробовал:

chrome.extension.onMessage.addListener(
  function(request, sender, sendResponse) {
    // LOG THE CONTENTS HERE
    console.log(request.content);
  }
);

И я просто выполняю это из всплывающей консоли:

chrome.tabs.getSelected(null, function(tab) {
  chrome.tabs.sendMessage(tab.id, { }, function(response) {
    console.log(response);
  });
});

Я собираюсь:

Port: Could not establish connection. Receiving end does not exist. 

ОБНОВИТЬ:

{
  "manifest_version": 2,

  "name": "test",
  "description": "test",
  "version": "0.1",

  "permissions": [
    "tabs",
    "activeTab",
    "https://api.domain.com/"
  ],

  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["content.js"]
    }
  ],

  "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",

  "browser_action": {
    "default_icon": "icon.png",
    "default_title": "Test",
    "default_popup": "index.html"
  }
}

content.js

chrome.extension.onMessage.addListener(
  function(request, sender, sendResponse) {
    if (request.text && (request.text == "getDOM")) {
      sendResponse({ dom: document.body.innerHTML });
    }
  }
);

popup.html

chrome.tabs.getSelected(null, function(tab) {
  chrome.tabs.sendMessage(tab.id, { action: "getDOM" }, function(response) {
    console.log(response);
  });
});

Когда я его запускаю, я все равно получаю ту же ошибку:

undefined
Port: Could not establish connection. Receiving end does not exist. lastError:30
undefined
Brandonhilkert
источник

Ответы:

184

Термины «фоновая страница», «всплывающее окно», «сценарий содержимого» все еще сбивают вас с толку; Я настоятельно рекомендую более подробно ознакомиться с документацией по расширениям Google Chrome .

Что касается вашего вопроса, подходят ли сценарии контента или фоновые страницы:

Скрипты контента : определенно
скрипты контента - единственный компонент расширения, имеющий доступ к DOM веб-страницы.

Фоновая страница / всплывающее окно : возможно (возможно, максимум 1 из двух).
Возможно, вам потребуется, чтобы сценарий содержимого передавал содержимое DOM либо на фоновую страницу, либо во всплывающее окно для дальнейшей обработки.


Повторюсь, настоятельно рекомендую более внимательно изучить имеющуюся документацию!
Тем не менее, вот пример расширения, которое извлекает содержимое DOM на страницах StackOverflow и отправляет его на фоновую страницу, которая, в свою очередь, печатает его в консоли:

background.js:

// Regex-pattern to check URLs against. 
// It matches URLs like: http[s]://[...]stackoverflow.com[...]
var urlRegex = /^https?:\/\/(?:[^./?#]+\.)?stackoverflow\.com/;

// A function to use as callback
function doStuffWithDom(domContent) {
    console.log('I received the following DOM content:\n' + domContent);
}

// When the browser-action button is clicked...
chrome.browserAction.onClicked.addListener(function (tab) {
    // ...check the URL of the active tab against our pattern and...
    if (urlRegex.test(tab.url)) {
        // ...if it matches, send a message specifying a callback too
        chrome.tabs.sendMessage(tab.id, {text: 'report_back'}, doStuffWithDom);
    }
});

content.js:

// Listen for messages
chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
    // If the received message has the expected format...
    if (msg.text === 'report_back') {
        // Call the specified callback, passing
        // the web-page's DOM content as argument
        sendResponse(document.all[0].outerHTML);
    }
});

manifest.json:

{
  "manifest_version": 2,
  "name": "Test Extension",
  "version": "0.0",
  ...

  "background": {
    "persistent": false,
    "scripts": ["background.js"]
  },
  "content_scripts": [{
    "matches": ["*://*.stackoverflow.com/*"],
    "js": ["content.js"]
  }],
  "browser_action": {
    "default_title": "Test Extension"
  },

  "permissions": ["activeTab"]
}
гкалпак
источник
6
@solvingPuzzles: chrome.runtime.sendMessageотправляет сообщения на BackgroundPage и во всплывающие окна. chrome.tabs.sendMessageотправляет сообщения ContentScripts.
gkalpak
23
Проголосовали против, поскольку этот ответ не объясняет, как получить ФАКТИЧЕСКУЮ DOM с текущей вкладки.
Джон Пол Барбагалло
2
@JohnPaulBarbagallo: Вопрос касался получения содержимого DOM, а не доступа / манипулирования реальной DOM. Я думаю, что мой ответ делает это (и другие думают так же). Если у вас есть лучшее решение, опубликуйте его в качестве ответа. Если у вас есть другие требования, опубликуйте их как новый вопрос. В любом случае спасибо за отзыв :)
gkalpak
2
@zoltar: он печатается в консоли фоновой страницы.
gkalpak
2
Я скопировал / вставил этот ответ, но не могу получить console.log из сценария содержимого. Помогите, пожалуйста!
ClementWalter 03
72

Вам не нужно использовать передачу сообщений для получения или изменения DOM. Я использовал chrome.tabs.executeScriptвместо этого. В моем примере я использую только разрешение activeTab, поэтому скрипт выполняется только на активной вкладке.

часть manifest.json

"browser_action": {
    "default_title": "Test",
    "default_popup": "index.html"
},
"permissions": [
    "activeTab",
    "<all_urls>"
]

index.html

<!DOCTYPE html>
<html>
  <head></head>
  <body>
    <button id="test">TEST!</button>
    <script src="test.js"></script>
  </body>
</html>

test.js

document.getElementById("test").addEventListener('click', () => {
    console.log("Popup DOM fully loaded and parsed");

    function modifyDOM() {
        //You can play with your DOM here or check URL against your regex
        console.log('Tab script:');
        console.log(document.body);
        return document.body.innerHTML;
    }

    //We have permission to access the activeTab, so we can call chrome.tabs.executeScript:
    chrome.tabs.executeScript({
        code: '(' + modifyDOM + ')();' //argument here is a string but function.toString() returns function's code
    }, (results) => {
        //Here we have just the innerHTML and not DOM structure
        console.log('Popup script:')
        console.log(results[0]);
    });
});
Оскар
источник
1
Прекрасно работает! Спасибо. Не знаю почему, но я не мог заставить принятое решение работать на меня.
Гудфеллоу
Ваше заявление о том, что вы используете только activeTabразрешение, неточно. Вы явно получаете <all_urls>в дополнение к activeTab.
Макиен
1
test.js - это сценарий, который вы включили в HTML-код своей страницы, поэтому я не уверен, что вам нужны какие-либо разрешения.
Скотт Бейкер
11

Для тех, кто пробовал гкалпак, ответьте и он не сработал,

имейте в виду, что Chrome добавит сценарий содержимого на необходимую страницу, только если ваше расширение включено во время запуска Chrome, а также рекомендуется перезапустить браузер после внесения этих изменений

bxN5
источник
1
Это спасло мне день
Ромен Дери