Chrome Extension: Message Passing (Sending the DOM to popup.js) returns 'null'

末鹿安然 提交于 2019-12-19 11:40:18

问题


I would like to use a Chrome Extension to download the current page's DOM. I'm not sure why, but when my download occurs, the result is just a text file with either 'null' or 'undefined', rather than the DOM. I've tried to assimilate the knowledge from here and here, but I can't seem to get the message from content.js through to popup.js.

Additionally, I'm not sure why this actually works. When I read the docs, it seems like I need to send the message from popup.js to content.js by selecting the active tab:

chrome.tabs.query({currentWindow: true, active: true}, function(tabs) {
    chrome.tabs.sendMessage(tabs[0].id, {message: 'getMessageFromContent'}, function(response) {
        //Code to handle response from content.js
    }
});

My current code:

content.js

var page_html = DOMtoString(document);
chrome.runtime.sendMessage({method: 'downloadPageDOM', pageDOM: thisPage});

function DOMtoString(document_root) { ... }

background.js

chrome.tabs.query({currentWindow: true, active: true}, function(tab) {
    var page_html;
    chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
        if (request.message == 'downloadPageDOM')
            page_html = request.pageDOM;
        else if (request.message == 'getPageDOM')
            sendResponse(page_html);
    });
}); 

popup.js

document.addEventListener('DOMContentLoaded', function() {
    var download_button = document.getElementById('download_button');
    download_button.addEventListener('click', function() {
        chrome.runtime.sendMessage({message:'getPageDOM'}, function(response) {
            download(response, "download.html", "text/html");
        });
    });
});

function download(data, fileName, mimeType) { ... }

I feel like I'm missing a crucial understanding of how message passing works. If anyone could take a second to help me understand why the file that downloads just has 'null', I would sincerely appreciate it.


回答1:


You're over-complicating this, which leads to a lot of logical errors.

You've set up the background page to act like a message proxy, and the content script itself triggers updating your page_html variable. Then the popup pulls that data with another message.

Note that page_html will not contain the current tab's data in any case: you're overwriting this data with the last loaded tab.


What you can do is completely cut out the middleman (i.e. background.js). I guess you got confused by the fact that sending a message TO a popup is a generally a bad idea (no guarantee it's open), but the other way around is usually safe (and you can make it always safe).

Solution 1 (bad, but here for educational purposes)

The logic of your app is: once the user clicks the button, make the snapshot at that moment. So, instead of making your content script do its work immediately, add a message listener:

// content.js
chrome.runtime.onMessage(function(message, sender, sendResponse) {
  else if (request.message == 'getPageDOM')
    sendResponse(DOMtoString(document));
});

function DOMtoString(document_root) { ... }

And in your popup, request it:

// popup.js
// (Inside the click listener)
chrome.tabs.query({currentWindow: true, active: true}, function(tabs) {
  // Note that sending a message to a content script is different
  chrome.tabs.sendMessage(tabs[0].id, {message:'getPageDOM'}, function(response) {
    download(response, "download.html", "text/html");
  });
});

However, this solution is not 100% robust. It will fail if the content script is not injected into the page (and this can happen). But it's possible to fix this.

Solution 2

Let's not assume the content script is injected. In fact, most of the time you don't NEED to inject it automatically, only when the user clicks your button.

So, remove the content script from the manifest, make sure you have host permissions ("<all_urls>" works well, though consider activeTab permission), and the use programmatic injection.

There is a little-used form of programmatic injection that collects the value of the last executed statement. We're going to use that.

// content.js
DOMtoString(document); // This will be the last executed statement

function DOMtoString(document_root) { ... }

In the popup, execute script, collect results:

// popup.js
// (Inside the click listener)
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
  chrome.tabs.executeScript(tabs[0].id, {file: "content.js"}, function(data) {
    // Data is an array of values, in case it was executed in multiple tabs/frames
    download(data[0], "download.html", "text/html");
  });
});

NOTE: All of the above assumes that your function DOMtoString actually works.



来源:https://stackoverflow.com/questions/27477641/chrome-extension-message-passing-sending-the-dom-to-popup-js-returns-null

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!