'Problems with asynchronous calls in chrome extension

I had this idea for a new chrome extension: It should create a linking QR code for the currently open website, displayed in the popup.html which can be easily scanned with the smartphone. If you watch a video on YouTube, the current time of the video should be also embedded in the QR code, which makes it possible to continue watching the video directly in the YouTube app on the smartphone.

So far so good. But now I have the following problem: This extension works fine on all websites. Only on Youtube there seems to be a problem with the asynchrony of the onmessage listener and the sending of the message to the contentScript (requesting the current time of the viewed YouTube video). In the debugging console I get the following errors:

Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist.

... getting this after trying to execute line 7 of background.js

Error handling response: TypeError: Cannot read property 'videoTime' of undefined

at chrome-extension://...../background.js:8:70

... getting this after trying to execute line 8 of background.js

popup.js

$(function() {
    chrome.runtime.sendMessage({text: 'sendURL'}, function(response) {
        $('#qr-code').attr('src', getQRCodeImgURL(response.url));
    });
});

function getQRCodeImgURL(url) {
    var qrCodeURL = new URL('http://api.qrserver.com/v1/create-qr-code/');
    qrCodeURL.searchParams.set('data', encodeURI(url));
    qrCodeURL.searchParams.set('size', '200x200');
    return qrCodeURL;
}

background.js

chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {

    if (message.text == 'sendURL') {
        chrome.tabs.query({active: true, /* lastFocusedWindow: true */}, function (tabs) {
            var currentURL = new URL(tabs[0].url);
            if (currentURL.href.indexOf('youtube.com/watch?v=') >= 0) { // if current website is youtube
                chrome.tabs.sendMessage(tabs[0].id, { text: 'sendVideoTime' }, function (response) {
                    const ytVideoTime = timeStringToSeconds(response.videoTime);

                    var ytURL = new URL('https://youtu.be/');
                    ytURL.pathname = '/' + currentURL.searchParams.get('v');
                    ytURL.searchParams.set('t', ytVideoTime);
                    currentURL = ytURL;
                    sendResponse({ url: currentURL.href });
                });
            } else {
                sendResponse({ url: currentURL.href });
            }
        });
    }
    return true;
});

function timeStringToSeconds(timeString) {
    var seconds = 0;
    var hms = timeString.split(':');
    if (hms.length == 3) {
        seconds = parseInt(hms[0])/* hours */ * 60 /* minutes per hour */ * 60 /* seconds per minute */;
        hms.shift(); /* remove first element, for accessing first element in next step (also if hms doesnt is in this hh:mm:ss format)  */
    }
    return seconds + (parseInt(hms[0]) * 60) /* seconds per minute */ + parseInt(hms[1]) /* seconds */;
}

contentScript.js


chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
    if (message.text == 'sendVideoTime') {
        const time = document.evaluate('//*[@id="movie_player"]/div[27]/div[2]/div[1]/div[1]/span[1]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.textContent;
        sendResponse({videoTime: time});
    }
    return true;
});

manifest.json

{
    "manifest_version": 2,
    "name": "URL-QR-Code-Creator",
    "version": "1.0",
    "description": "This extension creates a linking QR code for the currently open website, which can be easily scanned with the smartphone. If you watch a video on the YouTube website, the current time of the video is also embedded in the QR code, which makes it possible to continue watching the video directly in the YouTube app on the smartphone.",
    "icons": {
        "16": "images/qr-code-16px.png",
        "32": "images/qr-code-32px.png",
        "48": "images/qr-code-48px.png",
        "64": "images/qr-code-64px.png",
        "128": "images/qr-code-128px.png"
    },
    "browser_action": {
        "default_icon": {
            "16": "images/qr-code-16px.png",
            "32": "images/qr-code-32px.png",
            "48": "images/qr-code-48px.png",
            "64": "images/qr-code-64px.png",
            "128": "images/qr-code-128px.png"
        },
        "default_title": "show QR-Code",
        "default_popup": "popup.html"
    },
    "background": {
        "scripts": [
            "background.js"
        ],
        "persistant": false
    },
    "content_scripts": [
        {
            "matches": ["*://www.youtube.com/watch?v=*"],
            "js": ["contentScript.js"]
        }
    ],
    "permissions": [
        "activeTab",
        "tabs"
    ]
}


Solution 1:[1]

I did it!

At first I said goodbye to the idea of regulating access to the youtube DOM via an extra content script. So I deleted contentScript.js and deleted it from manifest.json. Now I simply accessed the DOM using the chrome.tabs.executeScript method in the background.js file and got the result using the callback function. Quite simply - without having to send messages around all the time.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1