0

I am working on a Chrome extension that highlights text in the same was as CTRL+F and I found this code that highlights text.

However, upon trying to implement it, I've been running into some trouble where everything run perfectly except the document.execCommand("HiliteColor") and document.execCommand("BackColor") functions.

I've read from this post that execCommand does not work in content_script, so it must be sent to the background page.

However, background pages have been replaced by service_workers, which when trying to use it, does not recognize the window variable.

Is there a way to implement the following functions without downgrading to manifest_version 2 and refactoring most of the code.

Code

manifest.json:

{
    "manifest_version": 3,
    "name": "ext",
    "version": "1.0",
    "description": "Extension",
    "icons": {
        "48": "icon.png"
    },
    "permissions": [
        "tabs",
        "storage",
        "activeTab"
    ],
    "host_permissions": [
        "https://api.com/*"
    ],
    "action": {
        "default_popup": "popup.html",
        "default_icon": "icon.png"
    },
    "content_scripts": [
        {
          "matches": [
            "<all_urls>"
          ],
          "js": [
            "scripts/Highlight.js"
          ]
        }
    ]
}

scripts/Highlight.js

function highlightSelection(color) {
  var sel = window.getSelection();
  var range = sel.getRangeAt(0);
  document.designMode = "on";
  if (range) {
      sel.removeAllRanges();
      sel.addRange(range);
  }
  console.log("Design mode on");
  if (!document.execCommand("HiliteColor", false, color)) {
    document.execCommand("BackColor", false, color);
  }
  console.log("Design mode off");
  document.designMode = "off";
}

chrome.runtime.onMessage.addListener((request) => {
  console.log(request);
  for (let index = 0; index < request.textArray.length; index++) {
    const element = request.textArray[index];
    console.log(element)
    window.find(element.text);
    highlightSelection(element.color);
  }
});

The Listener receives a message with the following code:

function sendHighlight(color, errors) {
  chrome.tabs.query({ active: true, currentWindow: true}, function(activeTabs) {
    chrome.tabs.sendMessage(activeTabs[0].id, { errors, color }, (response) => {
      console.log(response);
    });
  });
}

document.getElementById("button").addEventListener("click", () => sendHighlight("blue", ["Hello", "apple", "arraycontent"]));

What I tried

So far, I tried to comment the window.find() and just hightlight the selected text on the window, however the code seems to completely ignore the execCommand or at least not work as intended.

I also tried to modify the manifest as such:

    "background": {
        "service_worker": "scripts/Highlight.js"
    }

But it does not seem to recognize global var such as window and document.

  • Why did you believe that document.execCommand doesn't work in a content script? It works. Anyway, a service worker of an extension definitely won't help as it doesn't have DOM and it runs in an entirely different process and context. – wOxxOm Jan 08 '23 at 07:31
  • @wOxxOm, as explained, when I tried to run the highlightSelection log were displayed, but the page display did not change and the selected word was not highlighted. It might also be related, my IDE displays execCommand crossed out – Guerlain BLANCHARD guerlblanch Jan 08 '23 at 07:44
  • Please see this. [Document.execCommand()](https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand) – Norio Yamamoto Jan 08 '23 at 07:52
  • I have seen that execCommand is being deprecated, it might also be the reason why it is crossed out on my IDE. But I didn't find any alternatives to it for text highlighting on an extension. – Guerlain BLANCHARD guerlblanch Jan 08 '23 at 07:57
  • The documentation says HiliteColor requires prior use of useCSS command. – wOxxOm Jan 08 '23 at 08:31
  • @wOxxOm, I used document.execCommand("useCSS", false, true); after following your advice but to no avail, I also set styleWithCSS as true just in case since the documentation says useCss is being deprecated – Guerlain BLANCHARD guerlblanch Jan 08 '23 at 09:20
  • The documentation says you need to use false to enable CSS not true. – wOxxOm Jan 08 '23 at 10:07
  • @wOxxOm, I am sorry, but the only documentation I can find says the opposite https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand#hiliteColor, I also tried to set it as false, but it yielded the same results – Guerlain BLANCHARD guerlblanch Jan 08 '23 at 13:05
  • See the [documentation for useCSS](https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand#hilitecolor:~:text=use%20false%20to%20use%20CSS). Try styleWithCSS using both true and false. – wOxxOm Jan 08 '23 at 13:07
  • @wOxxOm, just tried your recommendation, and it still does not seem to work no matter useCSS and styleWithCSS value, I also tried to remove the for loop it case calling designMode multiple times refreshed the modifications but nothing happened – Guerlain BLANCHARD guerlblanch Jan 08 '23 at 13:47
  • Maybe these commands aren't implemented. Try removing document.designMode lines, they aren't necessary in Chrome. Try using `window.find`. – wOxxOm Jan 08 '23 at 14:19

1 Answers1

0

I would first like to thank @wOxxOm for helping me find the answer.

As explained by them, we first need to add the following call before using any HiliteColor functiondocument.execCommand("styleWithCSS", false, true);

Next, The biggest mistake I've made was in the argument handling, where the color argument was undefined in the HighlightSelection function.

Here is the final version of the Highlight.js function where, upon call, highlights all the given arguments.

function highlightSelection(color) {
  document.designMode = "on";
  document.execCommand("styleWithCSS", false, true);
  if (!document.execCommand("HiliteColor", false, color)) {
    document.execCommand("BackColor", false, color);
  }
  document.designMode = "off";
}

chrome.runtime.onMessage.addListener((request) => {
  for (let index = 0; index < request.search.length; index++) {
    const element = request.search[index];
    while(window.find(element)) {
      highlightSelection(request.color);
    }
  }
});

I am quite ashamed of making such a rookie mistake and hope that my code will be able to help some of you.