Chrome-Erweiterung - Holen Sie sich DOM-Inhalte

116

Ich versuche, über mein Popup auf den ActiveTab-DOM-Inhalt zuzugreifen. Hier ist mein Manifest:

{
  "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"
  }
}

Ich bin wirklich verwirrt, ob Hintergrundskripte (Ereignisseiten mit Persistenz: false) oder content_scripts der richtige Weg sind. Ich habe die gesamte Dokumentation und andere SO-Beiträge gelesen und es macht für mich immer noch keinen Sinn.

Kann jemand erklären, warum ich eins über das andere verwenden könnte.

Hier ist die background.js, die ich versucht habe:

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

Und ich führe dies nur über die Popup-Konsole aus:

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

Ich erhalte:

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

AKTUALISIEREN:

{
  "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);
  });
});

Wenn ich es ausführe, erhalte ich immer noch den gleichen Fehler:

undefined
Port: Could not establish connection. Receiving end does not exist. lastError:30
undefined
brandonhilkert
quelle

Antworten:

184

Die Begriffe "Hintergrundseite", "Popup", "Inhaltsskript" verwirren Sie immer noch. Ich empfehle dringend, die Dokumentation zu Google Chrome-Erweiterungen genauer zu betrachten .

In Bezug auf Ihre Frage, ob Inhaltsskripte oder Hintergrundseiten der richtige Weg sind:

Inhaltsskripte : Definitiv sind
Inhaltsskripte die einzige Komponente einer Erweiterung, die Zugriff auf das DOM der Webseite hat.

Hintergrundseite / Popup : Möglicherweise (wahrscheinlich max. 1 der beiden)
Möglicherweise muss das Inhaltsskript den DOM-Inhalt zur weiteren Verarbeitung entweder an eine Hintergrundseite oder an das Popup übergeben.


Lassen Sie mich wiederholen, dass ich dringend empfehle, die verfügbaren Unterlagen genauer zu studieren!
Hier ist eine Beispielerweiterung, die den DOM-Inhalt auf StackOverflow-Seiten abruft und an die Hintergrundseite sendet, die ihn wiederum in der Konsole druckt:

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"]
}
gkalpak
quelle
6
@solvingPuzzles: chrome.runtime.sendMessageSendet Nachrichten an die BackgroundPage und an Popups. chrome.tabs.sendMessagesendet Nachrichten an ContentScripts.
Gkalpak
22
Da diese Antwort nicht bewertet wurde, wird nicht erklärt, wie das IST-DOM von der aktuellen Registerkarte abgerufen werden kann.
John Paul Barbagallo
2
@JohnPaulBarbagallo: Die Frage war, wie man den DOM-Inhalt erhält und nicht, wie man auf das eigentliche DOM zugreift / es manipuliert. Ich denke, meine Antwort macht das (und andere scheinen genauso zu denken). Wenn Sie eine bessere Lösung haben, geben Sie diese als Antwort an. Wenn Sie eine andere Anforderung haben, stellen Sie diese als neue Frage. Auf jeden Fall danke für das Feedback :)
gkalpak
2
@zoltar: Es wird in der Konsole der Hintergrundseite gedruckt.
Gkalpak
2
Ich habe diese Antwort kopiert / gepastert, kann aber keine console.log aus dem Inhaltsskript erhalten. Hilfe bitte!
ClementWalter
72

Sie müssen die Nachrichtenübergabe nicht verwenden, um DOM abzurufen oder zu ändern. Ich habe chrome.tabs.executeScriptstattdessen verwendet. In meinem Beispiel verwende ich nur die ActiveTab-Berechtigung, daher wird das Skript nur auf der aktiven Registerkarte ausgeführt.

Teil von 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]);
    });
});
Oskar
quelle
1
Funktioniert perfekt! Danke dir. Ich weiß nicht warum, aber ich konnte die akzeptierte Lösung nicht für mich arbeiten lassen.
Goodfellow
Ihre Aussage, dass Sie nur die activeTabBerechtigung verwenden, ist ungenau. Sie erhalten eindeutig <all_urls>zusätzlich zu activeTab.
Makyen
1
test.js ist ein Skript , das Sie in Ihrer Seite HTML enthalten haben, so dass ich nicht sicher bin , müssen Sie alle Berechtigungen.
Scott Baker
11

Für diejenigen, die gkalpak Antwort versucht und es hat nicht funktioniert,

Beachten Sie, dass Chrome das Inhaltsskript nur dann zu einer benötigten Seite hinzufügt, wenn Ihre Erweiterung beim Starten von Chrome aktiviert wurde. Außerdem sollten Sie den Browser nach diesen Änderungen neu starten

bxN5
quelle
1
Dies rettete meinen Tag
Romain Derie