Search code examples
javascriptreactjsgoogle-chrome-extensionrxjsrxjs-observables

Get error while using extend-chrome/messages while sending messgae from background script to content script


I am using this library for messaging.

I have my background script like this

import { sendUrlInfo, waitForUrl } from 'utils/messaging';
import URL from 'url-parse';

chrome.webNavigation.onHistoryStateUpdated.addListener((details) => {
  const url = new URL(details.url);
  console.log('sending', url.hostname, url.pathname);
  sendUrlInfo(new URL(details.url));
});

chrome.webNavigation.onCompleted.addListener((details) => {
  if (details.url !== 'about:blank') {
    const url = new URL(details.url);
    console.log('sending', url.hostname, url.pathname);
    sendUrlInfo(new URL(details.url));
  }
});

And according to documentation i have message.js as shown below

import { getMessage } from '@extend-chrome/messages';
import URL from 'url-parse';

const messageTypes = {
  URL_INFO: 'URL_INFO',
};

export const [sendUrlInfo, urlStream, waitForUrl] = getMessage<URL>(
  messageTypes.URL_INFO,
);

and now content script I have written in react So i am trying to subscribe to stream when the component is mounted

import React, { useState, useEffect, useRef } from 'react';

import Editor from 'content/components/Editor';
import Opener from 'content/components/Opener';
import { urlStream, waitForUrl } from 'utils/messaging';

const Main: React.FC = () => {
  const [isOpen, setIsOpen] = useState(false);
  const [showContent, setShowContent] = useState(false);

  const editorRef = useRef<HTMLTextAreaElement | null>(null);

  useEffect(() => {
    console.log('MOunted');
    urlStream.subscribe(([url]) => {
      console.log('Received', url.hostname, url.pathname);
      if (url.hostname === 'www.youtube.com' && url.pathname === '/watch') {
        if (!showContent) setShowContent(true);
      } else {
        if (showContent) setShowContent(false);
      }
    });
  }, []);

  useEffect(() => {
    document.addEventListener('keydown', onKeyPress);
    return () => {
      document.removeEventListener('keydown', onKeyPress);
    };
  });

  const onKeyPress = (e: KeyboardEvent) => {
    if (e.ctrlKey && e.which === 192) {
      e.preventDefault();
      setIsOpen(!isOpen);
    }
  };

  if (!showContent) return <></>;

  return (
    <>
      <div>test</div>
      <Opener isOpen={isOpen} onClick={() => setIsOpen(true)} />
      <Editor
        isOpen={isOpen}
        ref={editorRef}
        onClose={() => setIsOpen(false)}
      />
    </>
  );
};

export default Main;

The error I am getting in background script console is

Could not establish connection. Receiving end does not exist

I think as much as i understand the background script is trying to send msg but the content script observable is not subscribed yet. As content script run after the page is loaded. If this is the issue is there any way to use this library properly.

BTW this works if we use normal chrome api like this

chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
    chrome.tabs.sendMessage(
        tabs[0].id,
        message,
        function () {
                console.log("msg sent")     
        }
    );
});

and use onMessage to receive the msg.


Solution

  • The background page needs to know that we're sending a message to a tab. Unless options.tabId is defined, sendUrlInfo sends a message using chrome.runtime.sendMessage.

    Add the options argument to sendUrlInfo in the background script:

    chrome.webNavigation.onHistoryStateUpdated.addListener((details) => {
      const url = new URL(details.url);
      console.log('sending', url.hostname, url.pathname);
    
      const options = { tabId: details.tabId }; // Add options.tabId here
      sendUrlInfo(new URL(details.url), options);
    });