c_idle
c_idle

Reputation: 1534

chrome.scripting.executeScript not working in my manifest v3 Chrome Extension

I have an extension where I call

function doScript(window) {
  chrome.scripting.executeScript(
    {
      target: {tabId: window.tabs[0].id},
      files: ['myscript.js'],
    });
}

myscript.js simply has

alert("Made it")

but I get no alert in my tab.

If I change the tabId to something random like 123

  chrome.scripting.executeScript(
    {
      target: {tabId: 123},
      files: ['myscript.js'],
    });

then I get an error "Unchecked runtime.lastError: No tab with id: 123"

So it looks like my tabId is right, but for some reason myscript.js is not triggering an alert.

If I mess up the script name like this

  chrome.scripting.executeScript(
    {
      target: {tabId: window.tabs[0].id}},
      files: ['ttttttttttt.js'],
    });

I get "runtime.lastError: Could not load file: 'ttttttttttt.js'."

I tried looking through the console logs and the only thing I see is this error upon clicking but it looks like a red herring.

"Unchecked runtime.lastError: Cannot access contents of url "". Extension manifest must request permission to access this host." https://github.com/GoogleChrome/web-vitals-extension/issues/54

Here is my manifest

{
    "manifest_version": 3,
    "name": "pokemon bokemon",
    "description": "",
    "version": "0.3.0",
    "action": {
        "default_popup": "popup.html"
    },
    "icons": {
        "16": "icon16.png",
        "32": "icon32.png"
    },
    "permissions": [
        "tabs",
        "storage",
        "scripting",
        "activeTab",
        "cookies"
    ],
    "host_permissions": [
        "*://*/*",
        "<all_urls>"
    ],
    "options_page": "options.html"
}

EDIT: Apparently it's a bug in chrome/manifest v3. Opened a bug here: https://bugs.chromium.org/p/chromium/issues/detail?id=1191971

Upvotes: 25

Views: 48996

Answers (4)

GorvGoyl
GorvGoyl

Reputation: 49150

[For Manifest v3]

You can also execute a function directly instead of passing a file:

Add this line in manifest.json:

"permissions": ["scripting"]


 let [tab] = await chrome.tabs.query({ active: true, currentWindow: true })

 // Execute script in the current tab
  await chrome.scripting.executeScript({
    target: { tabId: tab.id },
    func: (items) => {
      Object.keys(items).forEach((key) => {
        localStorage.setItem(key, items[key])
      })
    },
    args: [items] // pass any parameters to function
  })

Upvotes: 8

Carson
Carson

Reputation: 7958

I also encountered the same problem, and I share my experience as follows.

If you use chrome.windows.create to create a new tab(panel, popup,...) and then call chrome.scripting.executeScript in the new one. Unfortunately, it will not work.

for example

// popup.js
const targetURL = 'templates/main.html'
chrome.windows.create(
  {
    focused: true,
    url: targetURL,
    type: 'popup',
  },
  (subWindow) => {
  }
)
// But you can put the ``chrome.scripting.executeScript``here, which will be fine.
chrome.scripting.executeScript({})

does not work in templates/main.html

<script>
// templates/main.html

chrome.tabs.query({active: true, currentWindow: true}).then(([tab]) => {
  chrome.scripting.executeScript(
    {
      target: {tabId: tab.id},
      files: ['myscript.js'],
      // function: () => {}, // files or function, both do not work.
    })
})
</script>

If you want to communicate with each other, you can try.

chrome.tabs.sendMessage(myTabID, {event: 'myTest'}, ({msg, other})=>{
  console.log(msg) // hello
  console.log(other) // test
})

chrome.runtime.onMessage.addListener((media, sender, sendResponse) => {
  if (media.event === 'myTest') {
    sendResponse({msg:"hello", other:"test"})
  }
})

Supplement for sendMessage & onMessage

If you need a working example, you can refer to one of my repositories.

https://github.com/CarsonSlovoka/chrome/tree/7f1e731/tutorials/extensions/tabs-info

enter image description here

Click the button, and the following log will show some information from the tabs.

Suggested order for your reading:

two parts

  1. sendMessage

    1. manifest.json -> popup.html
    2. popup.html -> popup.js
    3. popup.js -> main.html
    4. main.html -> main.js
    5. main.js: chrome.tabs.sendMessage 👈
  2. onMessage


Supplement

  • handle the error from chrome.tabs.sendMessage: When you add callback parameters, it will return undefined. if you want to handle the error by yourself, you should omit this parameter, which will return the Promise. example

Upvotes: 6

Yannick Pezeu
Yannick Pezeu

Reputation: 800

The problem comes from the fact that the chrome.scripting.executeScript part is executed too fast.

For instance it starts working if you add a timer of 2 seconds:

const timer = ms => {
    function randn_bm(middle_number) {
        let u = 0, v = 0;
        while(u === 0) u = Math.random(); //Converting [0,1) to (0,1)
        while(v === 0) v = Math.random();
        let num = Math.sqrt( -2.0 * Math.log( u ) ) * Math.cos( 2.0 * Math.PI * v );
        num = num / 10.0 + 0.5; // Translate to 0 -> 1
        if (num > 1 || num < 0) return randn_bm() // resample between 0 and 1
        return num * 2 * middle_number
      }
      ms=randn_bm(ms);
      console.log(ms)
    return new Promise(res => setTimeout(res, ms));
  }

You can use this timer like so: await timer(2000)

For sure an async behaviour would be preferable but so you understand why this happens: the script is executed before the new tab completely opened. Therefore the function is never executed.

Upvotes: 2

Hakan
Hakan

Reputation: 599

Manifest version 3 Use this:

chrome.tabs.query({currentWindow: true, active: true}, function(tabs){
    console.log(tabs[0].url);
    console.log(tabs[0].id);
    my_tabid=tabs[0].id;
}); 
alert(my_tabid);

chrome.scripting.executeScript(
{
    target: {tabId: my_tabid},
    files: ['myscript.js'],
});

Upvotes: 2

Related Questions