skip to Main Content

I’m new to extension creation and have a problem, which I’ve already been able to find various ways to solve, but which are all different from mine and/or fixed with manifest V2 instead of V3 which I need.

Also, some fixes found work on their end, but not on mine, so I really don’t understand the problem.

Here is my problem:

  • I want to make a chrome extension to take screenshots of my browser and apps

  • I found an online tutorial that seemed correct to me (by the way, the only tutorial that uses AND the screenshots AND the V3 manifest, so perfect!)

  • Following the tutorial, I got the following error: Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist.

  • I looked for various ways, but nothing worked, I ended up downloading the git code of the tutorial, but it does not change anything, the error is still present

  • From what I understand, the error is in the following line:

chrome.action.onClicked.addListener(function (tab) {
  chrome.desktopCapture.chooseDesktopMedia(
    ["screen", "window", "tab"],
    tab,
    (streamId) => {
      if (streamId && streamId.length) {
        setTimeout(() => {
          chrome.tabs.sendMessage(
            tab.id,
            { name: "stream", streamId },
            (response) => console.log("received user data", response) // error is here, response is undefined
          );
        }, 200);
      }
    }
  );
});

I get undefined instead of the response, and I think it’s from there that it’s a problem, because it never goes on and therefore never activates the onMessage function, nor the content_script

Here is the full background.js code :

chrome.action.onClicked.addListener(function (tab) {
  chrome.desktopCapture.chooseDesktopMedia(
    ["screen", "window", "tab"],
    tab,
    (streamId) => {
      if (streamId && streamId.length) {
        setTimeout(() => {
          chrome.tabs.sendMessage(
            tab.id,
            { name: "stream", streamId },
            (response) => console.log("received user data", response)
          );
        }, 200);
      }
    }
  );
});

chrome.runtime.onMessage.addListener((message, sender, senderResponse) => {
  if (message.name === "download" && message.url) {
    chrome.downloads.download(
      {
        filename: "screenshot.png",
        url: message.url,
      },
      (downloadId) => {
        senderResponse({ success: true });
      }
    );

    return true;
  }
});

Content_script

chrome.runtime.onMessage.addListener((message, sender, senderResponse) => {
    if (message.name === 'stream' && message.streamId) {
        let track, canvas
        navigator.mediaDevices.getUserMedia({
            video: {
                mandatory: {
                    chromeMediaSource: 'desktop',
                    chromeMediaSourceId: message.streamId
                },
            }
        }).then((stream) => {
            track = stream.getVideoTracks()[0]
            const imageCapture = new ImageCapture(track)
            return imageCapture.grabFrame()
        }).then((bitmap) => {
            track.stop()
            canvas = document.createElement('canvas');
            canvas.width = bitmap.width; //if not set, the width will default to 200px
            canvas.height = bitmap.height;//if not set, the height will default to 200px
            let context = canvas.getContext('2d');
            context.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height)
            return canvas.toDataURL();
        }).then((url) => {
            chrome.runtime.sendMessage({name: 'download', url}, (response) => {
                if (response.success) {
                    alert("Screenshot saved");
                } else {
                    alert("Could not save screenshot")
                }
                canvas.remove()
                senderResponse({success: true})
            })
        }).catch((err) => {
            alert("Could not take screenshot")
            senderResponse({success: false, message: err})
        })
        return true;
    }
})

manifest v3

{
  "name": "Screenshots",
  "version": "0.0.1",
  "description": "Take screenshots",
  "manifest_version": 3,
  "background": {
    "service_worker": "background.js"
  },
  "permissions": ["desktopCapture", "downloads", "tabs", "nativeMessaging"],
  "action": {
    "default_title": "Take a Screenshot"
  },
  "icons": {
    "16": "/assets/icon-16.png",
    "32": "/assets/icon-32.png",
    "48": "/assets/icon-48.png",
    "128": "/assets/icon-128.png"
  },
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["content_script.js"]
    }
  ]
}

I tried several things after various research like

  • Disable my extensions (which makes no sense, but you never know)

  • Add a timeout for the response, I tried up to 20 seconds delay, but without success

  • Added breakpoints everywhere to see if it crosses the line or not

3

Answers


  1. Chosen as BEST ANSWER

    So, @Norio Yamamoto 's solution suits me perfectly, because I then need to make a popup to give a name and do other processing on my screen, so thanks to your help, I'm already moving on by starting to understand it HTML popups on extensions! Thanks !

    For the problem itself, I was able to "fix" it in the end by reinstalling chrome, and it works as @Thomas Muller tells me... not sure why, maybe I had to break something with many tests, so the app was already working

    But I noticed a problem on the version of the tutorial compared to the one with popup, the tutorial version does not work on: non-reload pages (thanks @wOxxOm for the tip by the way), nor on chrome home pages, nor on the extension page, so I really prefer the popup version, but I need to dig more to improve that

    Thanks again !


  2. Works for me, using Chromium 107.0.5304.121 (Official. Build) Arch Linux (64-Bit).

    1. Go to https://stackoverflow.com/
    2. Click on the extension icon.
    3. A new window opens, with the text "Select what you want to share. Screenshots wants to share the contents of your screen with stackoverflow.com"
    4. Click on one of the tabs: Entire Screen, Window, Chromium Tab
    5. Click on a screenshot preview or tab title
    6. Click "Share"
    7. The browser displays an alert with the text "Screenshot saved", and a file named "Screenshot.png" is created in the default downloads directory.
    Login or Signup to reply.
  3. Here is an implementation without service worker and content scripts.

    manifest.json

    {
      "name": "desktopCapture",
      "version": "1.0",
      "manifest_version": 3,
      "permissions": [
        "desktopCapture",
        "tabs",
        "downloads"
      ],
      "action": {
        "default_popup": "popup.html"
      }
    }
    

    popup.html

    <html>
    <body>
      <script src="popup.js"></script>
    </body>
    </html>
    

    popup.js

    const createDate = {
      url: "desktopCaptuer.html",
      type: "popup",
      width: 800,
      height: 600
    };
    chrome.windows.create(createDate);
    

    desktopCaptuer.html

    <html>
    <body>
      <input type="button" id="captuer" value="Captuer">
      <script src="desktopCaptuer.js"></script>
    </body>
    </html>
    

    desktopCaptuer.js

    chrome.windows.getCurrent({}, w => {
      chrome.windows.update(w.id, { focused: true }, () => {
        document.getElementById("captuer").onclick = () => {
          const sources = ["screen", "window", "tab"];
          chrome.tabs.getCurrent((tab) => {
            chrome.desktopCapture.chooseDesktopMedia(sources, tab, (streamId) => {
              let track, canvas;
              navigator.mediaDevices.getUserMedia({
                video: {
                  mandatory: {
                    chromeMediaSource: "desktop",
                    chromeMediaSourceId: streamId
                  },
                }
              }).then((stream) => {
                track = stream.getVideoTracks()[0];
                const imageCapture = new ImageCapture(track);
                return imageCapture.grabFrame();
              }).then((bitmap) => {
                track.stop();
                canvas = document.createElement("canvas");
                canvas.width = bitmap.width;
                canvas.height = bitmap.height;
                let context = canvas.getContext("2d");
                context.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height);
                return canvas.toDataURL();
              }).then((url) => {
                chrome.downloads.download({
                  filename: "screenshot.png",
                  url: url,
                }, () => {
                  canvas.remove();
                });
              }).catch((err) => {
                console.log(err);
                alert("Could not take screenshot");
              })
            });
          });
        }
      });
    });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search