GoogleChrome / chrome-extensions-samples

Chrome Extensions Samples
https://developer.chrome.com/docs/extensions
Apache License 2.0
14.97k stars 8.07k forks source link

Dictionary side panel example doesn't work with Uncaught (in promise) Error: Could not establish connection. Receiving end does not exist. #1020

Closed AlexMikhalev closed 5 months ago

AlexMikhalev commented 10 months ago

Describe the bug Dictionary side panel example doesn't work with the error

Uncaught (in promise) Error: Could not establish connection. Receiving end does not exist. 

in the service worker. To Reproduce

  1. Clone chrome-extension-samples, commit b25a56bf8ea0bf339c0f0b5e882d5695e32726f7
  2. cd functional-samples/sample.sidepanel-dictionary
  3. Follow the steps in Readme to install the extension
  4. Define item appear in context menu
  5. Clicking on define sidepanel will not appear
  6. Service worker will have error: "Uncaught (in promise) Error: Could not establish connection. Receiving end does not exist."

Chrome: Version 118.0.5993.70 (Official Build) (64-bit) No other extensions or blockers are installed or active.

Expected behavior Sidepanel appear

AmySteam commented 9 months ago

Hey @AlexMikhalev, thanks for flagging this. 😃

Out of curiosity, did you select the word "extensions", then right-click and choose the context menu Define?

This sample has a long list of steps, it can be easily missed 😟

AlexMikhalev commented 9 months ago

Yes. The point of a bug report is that we need working examples of how to pass data - even the string into the side panel with any reliability that our users will have it working in their environment. The case of nothing selected shall be handled too.

AlexMikhalev commented 9 months ago

The fix is to inject client-side script with chrome.runtime.sendMessage

ChuckStrusz commented 9 months ago

The fix is to inject client-side script with chrome.runtime.sendMessage

As someone who is floundering with the absolute paucity of good (ie, functioning) examples of V3 extensions, I'm hoping you can offer a bit more detail on this fix for this broken sample (I have the same issues) - is there any chance you could post the full updated source for the file in question, or indicate which file you mean by "client-side script" since I think there are two - both service-worker.js and sidepanel.js are "client-side", right?

ChuckStrusz commented 9 months ago

I am having this same issue, but I'll note that the instructions in the README, as written, do not work. To wit, Step 3 is "Open the side panel UI" - but you can't open the side panel UI, because the extension button is disabled (greyed out). I have tried this in a normal and an incognito window with the same results. extension

patrickkettner commented 9 months ago

Hi chuck You wouldn’t open the side panel with the extension action button. You open the side panel by clicking on the side panel button that’s built into Chrome.

https://support.google.com/chrome/answer/13156494?hl=en#zippy=%2Cuse-side-panel

On Thu, Nov 16, 2023 at 6:16 AM Chuck Strusz @.***> wrote:

I am having this same issue, but I'll note that the instructions in the README, as written, do not work. To wit, Step 3 is "Open the side panel UI" - but you can't open the side panel UI, because the extension button is disabled (greyed out). I have tried this in a normal and an incognito window with the same results. [image: extension] https://user-images.githubusercontent.com/22245534/283445282-b9cbced5-f2a2-44c5-8e52-b42c868b9ea6.png

— Reply to this email directly, view it on GitHub https://github.com/GoogleChrome/chrome-extensions-samples/issues/1020#issuecomment-1814329743, or unsubscribe https://github.com/notifications/unsubscribe-auth/AADRUBXLYFNY4AXZDVTCMXLYEX7ZXAVCNFSM6AAAAAA6KD3XIKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTQMJUGMZDSNZUGM . You are receiving this because you are subscribed to this thread.Message ID: @.*** com>

AlexMikhalev commented 9 months ago

@ChuckStrusz I was hoping Google Chrome's team would take ownership to ensure examples are working. As it stands now the example isn't going to work (by design): service worker (background.js) in my case shall have:

chrome.contextMenus.onClicked.addListener(async (info, tab) => {
    const { id, url } = tab;
    const { menuItemId } = info
    var query = info.selectionText;
    switch (menuItemId) {
      case "define-word":
        await chrome.sidePanel.open({ tabId: id });
        await chrome.sidePanel.setOptions({
            tabId: id,
            path: 'sidepanel.html',
            enabled: true
        });
        await chrome.scripting.executeScript(
            {
                target: { tabId: id, allFrames: true },
                files: ['clientside_concept.js']
            }
        )
    }
});

than clientside_concepts.js:

// // content script
console.log("Running clientside_concepts.js");
(async () => {
    let selection = document.getSelection().toString();
    console.log("Selection", selection);
    const response = await chrome.runtime.sendMessage({ type: 'concept', data:selection });
    console.log("Response from background ", response);
    console.log('concept complete')
}
)();

than again in background js I have a part:

        } else if (message.type === "concept") {
            let data = message.data;
            console.log("Concept", data);

            senderResponse({ data: data });
            return true;

        }

than in sidepanel.js

chrome.runtime.onMessage.addListener(async ({ type, data }) => {
  console.log('Message received', type, data);
  if (type === 'concept') {
    // Hide instructions.
    document.body.querySelector('#select-a-word').style.display = 'none';

    // Show word and definition.
    document.body.querySelector('#definition-word').innerText = data;
    run('@cf/meta/m2m100-1.2b', {
      text: data,
      source_lang: "english", // defaults to english
      target_lang: "spanish"
      }).then((response) => {
          console.log(JSON.stringify(response));
          if (!response.success) {
              console.log("Error", response);
          }else{
              console.log("Response", response);
              // let translated_text=response.result.translated_text
              document.body.querySelector('#definition-text').innerText = response.result.translated_text;
          }
      });

    // document.body.querySelector('#definition-text').innerText =
    //   words[data.toLowerCase()];
  }
});

my extension runs cloudflare AI worker to translate English into Spanish. Service worker will not be able to communicate directly with sidepanel.js without clientside message. @patrickkettner please read https://github.com/GoogleChrome/chrome-extensions-samples/tree/main/functional-samples/sample.sidepanel-dictionary

ChuckStrusz commented 9 months ago

Hi chuck You wouldn’t open the side panel with the extension action button. You open the side panel by clicking on the side panel button that’s built into Chrome. https://support.google.com/chrome/answer/13156494?hl=en#zippy=%2Cuse-side-panel

Oh my gosh! Thanks so much. I use the side panel so infrequently that I often forget it's there.

paulclindo commented 6 months ago

I had the same issue after moving our extension to sidepanel, my contextual menus such as Summarize this page or Send this selected text were broken when the sidepanel is closed. It only open sidepanel and then get the same error when sending a message.

This is happening because the context menu needs to open the sidepanel and then send message to sidepanel page BUT this doesn't work as expected. The chrome.sidePanel.open promise doesn't wait until the sidepanel content is fully loaded, therefore you get the connection error Error: Could not establish connection. Receiving end does not exist.

I found a workaround and seems to work for me but ideally I believe chrome.sidePanel.open promise should get fulfilled when the sidepanel content is loaded so we can call it asynchronously

 chrome.contextMenus.onClicked.addListener((info, tab) => {
  chrome.sidePanel.open({ windowId: tab.windowId });

  // wait for side panel to open - 500ms 
  setTimeout(() => {
     if (!info.selectionText || !tab?.id) return;
     const message = {
       type: 'summarize-page',
       data: { textContent: info.selectionText },
      };
     chrome.runtime.sendMessage(message);
  }, 500);
});
oliverdunk commented 6 months ago

Thanks all for the feedback. When we first launched chrome.sidePanel, the sidePanel.open() API wasn't available, so you had to open the side panel explicitly. This sample was designed with that in mind and therefore it makes the assumption it is always open before you use the context menu.

That has changed of course, and I appreciate sending data can be a little awkward (we're talking about ways we might be able to simplify that). In the meantime, I've worked on an update which we can hopefully merge soon which demonstrates using the open API and also passing data using chrome.storage to do something that works regardless of the current state: https://github.com/GoogleChrome/chrome-extensions-samples/pull/1087