Extension Chrome - Obtenez du contenu DOM

116

J'essaye d'accéder au contenu DOM activeTab à partir de ma fenêtre contextuelle. Voici mon manifeste:

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

Je ne sais vraiment pas si les scripts d'arrière-plan (pages d'événements avec persistance: false) ou content_scripts sont la voie à suivre. J'ai lu toute la documentation et d'autres articles SO et cela n'a toujours aucun sens pour moi.

Quelqu'un peut-il expliquer pourquoi je pourrais utiliser l'un sur l'autre.

Voici le background.js que j'ai essayé:

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

Et j'exécute simplement cela à partir de la console contextuelle:

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

Je suis en train:

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

METTRE À JOUR:

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

Lorsque je l'exécute, j'obtiens toujours la même erreur:

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

Réponses:

184

Les termes "page de fond", "popup", "script de contenu" vous déroutent toujours; Je suggère fortement un examen plus approfondi de la documentation des extensions Google Chrome .

Concernant votre question si les scripts de contenu ou les pages d'arrière-plan sont la voie à suivre:

Scripts de contenu : les scripts de
contenu sont certainement le seul composant d'une extension qui a accès au DOM de la page Web.

Page d'arrière-plan / Popup : Peut-être (probablement au maximum 1 des deux)
Vous devrez peut-être demander au script de contenu de transmettre le contenu DOM soit à une page d'arrière-plan, soit à la popup pour un traitement ultérieur.


Permettez-moi de répéter que je recommande fortement une étude plus approfondie de la documentation disponible!
Cela dit, voici un exemple d'extension qui récupère le contenu DOM sur les pages StackOverflow et l'envoie à la page d'arrière-plan, qui à son tour l'imprime dans la console:

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
la source
6
@solvingPuzzles: chrome.runtime.sendMessageenvoie des messages à BackgroundPage et aux Popups. chrome.tabs.sendMessageenvoie des messages à ContentScripts.
gkalpak
23
Évalué car cette réponse n'explique pas comment obtenir le DOM ACTUEL à partir de l'onglet actuel.
John Paul Barbagallo
2
@JohnPaulBarbagallo: La question portait sur l'obtention du contenu DOM, pas sur l'accès / la manipulation du DOM réel. Je pense que ma réponse fait cela (et d'autres semblent penser de la même manière). Si vous avez une meilleure solution, postez-la comme réponse. Si vous avez une exigence différente, postez-la comme nouvelle question. Dans tous les cas, merci pour les commentaires :)
gkalpak
2
@zoltar: Il est imprimé dans la console de la page d'arrière-plan.
gkalpak
2
J'ai copier / coller cette réponse mais je ne peux pas obtenir de console.log du script de contenu. Aidez-moi, s'il vous plaît!
ClementWalter
72

Vous n'avez pas besoin d'utiliser le message passant pour obtenir ou modifier le DOM. J'ai utilisé à la chrome.tabs.executeScriptplace. Dans mon exemple, j'utilise uniquement l'autorisation activeTab, par conséquent le script est exécuté uniquement sur l'onglet actif.

partie de 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
la source
1
Fonctionne parfaitement! Je vous remercie. Je ne sais pas pourquoi mais je n'ai pas pu faire fonctionner la solution acceptée pour moi.
goodfellow
Votre déclaration selon laquelle vous n'utilisez que l' activeTabautorisation est inexacte. Vous obtenez clairement <all_urls>en plus de activeTab.
Makyen
1
test.js est un script que vous avez inclus dans le code HTML de votre page, je ne suis donc pas sûr que vous ayez besoin d' autorisations.
Scott Baker
11

Pour ceux qui ont essayé la réponse gkalpak et cela n'a pas fonctionné,

sachez que chrome ajoutera le script de contenu à une page nécessaire uniquement lorsque votre extension sera activée lors du lancement de chrome et qu'il est également judicieux de redémarrer le navigateur après avoir effectué ces modifications

bxN5
la source
1
Cela m'a sauvé la journée
Romain Derie