getSelection() not getting the selected text

I’m trying to take the selected text on a page, save it to a variable, put it into a link and open that link in a new tab. I’ve got it working apart from taking the selected text.

manifest.json:

{
  "manifest_version": 2,
  "name": "Send2search test",
  "version": "1.0",
  "permissions": ["menus"],
  "background": {
    "scripts": ["bg.js"]
  },
}

bg.js:

browser.menus.create(
    {
      id: "gglSearch",
      title: "Search in new tab.",
      contexts: ["selection"],
    },
  );

browser.menus.onClicked.addListener(async function (info, tab) {
    var selectedTxt = window.getSelection().toString();
    if (info.menuItemId == "gglSearch") {
        var searchUrl = `https://www.google.com/search?q=${selectedTxt}`;
        let openNewTab = browser.tabs.create(
            {
                active: false,
                url: searchUrl,
                index: tab.index+1
            }
        );
    };
});

Every time it just opens blank google search page, so nothing gets saved in the selectedTxt variable. What am I doing wrong?

I’ve also tried content.getSelection().toString() and info.selectionText() , those cause no tab to be opened at all.

PS: I know what I’m making is a feature already, I’m just trying to work out the basics on this simple example.

Background script runs in it’s own “invisible webpage”. So you won’t find any selected text in there.

Instead you need to execute script in the webpage using content script.

See the docs for more info about architecture of addons:

Then use this to run the script in the page:

You’ll need “activeTab” and “scripting” permissions.

So I need to execute a content script to get the selected text, everything else can stay in the background?

Would it go like this?
background:

browser.menus.create(
    {
      id: "gglSearch",
      title: "Search in new tab.",
      contexts: ["selection"],
    },
  );
browser.menus.onClicked.addListener(async function (info, tab) {
    if (info.menuItemId == "gglSearch") {
        await browser.scripting.executeScript({
            target: {
              tabId: tab.id,
              allFrames: true,
            },
            files: ["content_script.js"],
          });
    };
});

content_script:

var selectedTxt = window.getSelection().toString();
var searchUrl = `https://www.google.com/search?q=${selectedTxt}`;

Try:
var selectedTxt = info.selectionText;
(without the parentheses after “selectionText”)

You don’t need a content script.
And the “menus” permisson is enough, you don’t need “activeTab” or “scripting”.

2 Likes

Firefox doesn’t like the last comma.
Did you remove lines from manifest.json?

Almost, the last statement in the content script will be the returned value, so skip the last “var”.
But there is a simpler way - you don’t really need a content script file, you can use the “inline” format, something like:

const selection = await browser.scripting.executeScript({
  target: {tabId: tabId},
  func: () => window.getSelection().toString(),
})
  .then(([x]) => x.result);

But as Hans already mentioned (thank you Hans for pointing that out), you can already access selected text from the browser.menus.onClicked handler:

There’s no comma like that in my manifest, must’ve added it by a mistake while making the post, sorry for confusion.

info.selectionText works like charm, thank you!