TheChosenOne
TheChosenOne

Reputation: 725

Access DOM elements through chrome extension

I'm trying to access some DOM elements from a webpage:

<html>
  <button id="mybutton">click me</button>
</html>

I want to access the innerHTML ("click me") through a chrome extension:

chrome.browserAction.onClicked.addListener(function(tab) {
    var button = document.getElementById("mybutton");
    if(button == null){
        alert("null!");
    }
    else{
        alert("found!");
    }
});

When I click the extension, the popup says: "null". My manifest.json:

{
    "name": "HackExtension",
    "description": "Hack all the things",
    "version": "2.0",
    "permissions": [
    "tabs", "http://*/*"
    ],
    "background": {
    "scripts": ["contentscript.js"],
    "persistent": false
    },
    "browser_action": {
    "scripts": ["contentscript.js"],
    "persistent": false
    },
    "manifest_version": 2
}

Upvotes: 18

Views: 40874

Answers (2)

TeemuK
TeemuK

Reputation: 2529

Starting with Manifest V3, your content scripts won't be able to access anything generated by other loaded scripts and using a trick like inlining a your code inside <script> tag won't work due to stricter CSP rules. This caused me a lot of head ache since I couldn't figure out how to access library-generated DOM properties similar to React or Redux DevTools.

Instead, you have to now inject your script inside the service_worker with eg:

chrome.scripting.registerContentScripts([
  {
    id: 'inject',
    matches: ['<all_urls>'],
    js: ['inject.js'],
    runAt: 'document_end',
    world: 'MAIN'
  }
])

Notice the 'MAIN' property, not the default 'ISOLATED'. Then inside my inject.js I do whatever, eg:

window.addEventListener('load', () => {
  findReact()
})

Also you have to add the script to the manifest.json:

  "web_accessible_resources": [
    {
      "resources": ["inject.js"],
      "matches": ["<all_urls>"],
      "extension_ids": []
    }
  ],
  "externally_connectable": {
    "ids": ["*"]
  },

Not sure is "externally_connectable" needed. And you need to add at least "scripting" permissions. I used the React DevTools migration as my source https://github.com/facebook/react/pull/25145

Upvotes: 0

TheChosenOne
TheChosenOne

Reputation: 725

The solution: You need a manifest file, a background script and a content script. This is not really clear in the documentation that you have to use it and also, how to use it. For alerting the full dom, see here. Because I have a hard time finding a complete solution that actually works and not just snippets that are useless for newbies, like me, I included a specific solution:

manifest.json

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

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

    "permissions": ["activeTab"]
}

content.js

/* Listen for messages */
chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
    /* If the received message has the expected format... */
    if (msg.text && (msg.text == "report_back")) {
        /* Call the specified callback, passing 
           the web-pages DOM content as argument */
    sendResponse(document.getElementById("mybutton").innerHTML);
    }
});

background.js

/* Regex-pattern to check URLs against. 
   It matches URLs like: http[s]://[...]stackoverflow.com[...] */
var urlRegex = /^file:\/\/\/:?/;

/* A function creator for callbacks */
function doStuffWithDOM(element) {
    alert("I received the following DOM content:\n" + element);
}

/* 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);
    }
});

index.html

<html>
  <button id="mybutton">click me</button>
</html>

Just save the index.html somewhere and load in the folder as an extension, containing the three other files. Open the index.html and push the extension button. It should show "click me".

Upvotes: 36

Related Questions