Unexpected behavior in Chrome extension and "localStorage"-property - javascript

I am building a fairly simple Chrome extension: When "active", it should copy some source code whenever the content.js is sending the correct data:
background.js
...
// when active, listen for content.js and copy content into clipboard
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.from === "content" && active === true) {
copyTextToClipboard(request.message)
}
});
Setting the extension "active" is done by clicking the extension icon in the Chrome tab:
background.js
...
// turn the logo color and set activity status upon clicking the extension logo in the tab
chrome.browserAction.onClicked.addListener(function() {
active = !active;
localStorage.setItem("active_stored", active);
if (active) {
chrome.browserAction.setIcon({path: "icon_yellow.png"})
} else {
chrome.browserAction.setIcon({path: "icon_grey.png"})
}
});
...
It is almost running as expected (i.e. clicking the icon sets active=true or false, indicated by the extension logo changing accordingly).
However, I want to save the activity status using localStorage, so that it remains intact even after shutting down Chrome.
So I added the following code:
background.js:
...
// turn the logo color and set activity status upon starting up Chrome browser
let active = localStorage.getItem("active_stored");
chrome.runtime.onStartup.addListener(function () {
if (active) {
chrome.browserAction.setIcon({path: "icon_yellow.png"})
} else {
chrome.browserAction.setIcon({path: "icon_grey.png"})
}
});
...
However, when restarting Chrome, regardless of the state I left active in, the "active" logo is being displayed and the variable is set to true (which I find weird, but I could live with), but the source code is not being copied! Clicking the icon twice, i.e. deactivating and activating, will result in the expected behavior!
Can someone tell me where I am going wrong with this?
P.S.: I originally tried this with chrome.storage.set and chrome.storage.get, but the using the callback function really confused me. After having read multiple articles and other questions, I feel like this might be necessary in order to solve this problem. Please let me if a) it indeed is the only way to get this done and b) how to properly integrate this functionality.
Thanks!

The problem is that the values in localStorage are saved as string which means that both the "true" and "false" strings are truthy values.
To convert the values from localStorage to boolean, use something like this:
let active = JSON.parse(localStorage.getItem("active_stored"));

Related

Chrome extension: Activate and execute background.js on click from popup.js

As I understand from Manage Events with Background Scripts and Migrate to Event Driven Background Scripts background script should be activated when events triggered.
background.js
chrome.runtime.onMessage.addListener((message, sender, reply) => {
const json = message.data;
// some code
reply({ result: true })
return true;
});
popup.js
chrome.runtime.sendMessage({ data: [<ArrayWithData>] },
function (response) {
logger.log(response);
}
);
Everything works well, but only in case of active background.
Why background not become active? Can someone explain what is my mistake?
Or how can I activate and execute background.js on click from popup.js?
I know that if I change persistence: true in manifest.json or just remove it, everything will works fine. But I want to keep persistence false and trigger background.js when needed.
You missed this part in the documentation that explains how a background script should be activated from popup.js. After retrieving the background page object, you just need to call any function or even access a field.
I always put this at the top of my popup.js:
// Initialize background page
chrome.runtime.getBackgroundPage(function(backgroundPage) {
console = backgroundPage.console;
})
That way I can also view console logs from the popup together with logs from the background view

Take a screenshot of a specific element from a Google Chrome extension

I need my Chrome extension to take a screenshot of the whole page/tab, excluding an element that I have injected in it after the closing </body> that is overlaying everything else.
Currently, I take the screenshot from a background script with the following code:
chrome.extension.onMessage.addListener((request, sender, sendResponse) => {
if (request.type === 'cheese') {
setTimeout(() => {
chrome.tabs.captureVisibleTab(null, { format: 'png' }, (src) => {
sendResponse({ src });
});
}, 10);
return true;
}
});
The only solution I've found so far to exclude that element is hiding it momentarily before taking the screenshot, but sometimes it still appears, thus the 10ms delay in the code above. That reduces the frequency of the issue, but doesn't fix it completely and, anyway, is just a workaround that I'd like to get rid of.
Before, I was using requestAnimationFrame to send the screenshot request to the background script only after the element has been hidden, but surprisingly, that didn't fix the issue, that's why I tried with that setTimeout instead.
Please, note the element I want to get rid of on the screenshot is on top of a relevant area of the page, so cropping the captured area is not an option, as I won't be able to see whatever is behind that element.
I took a look at the remote debugging protocol, where the closest I found was Page.captureScreenshot, which accepts a clip parameter that would crop the screenshot, but still capture the overlay.

Creating a variable in a Chrome extension popup from a variable in the DOM

I've dug through other answers for a while now but haven't found anything that I think quite does what I'm looking for, so it's time for my first stackoverflow question! (Be gentle, I started learning js about 3 weeks ago.)
The tl;dr here is that I'm looking to take a DOM element (innerHTML mostly) from the active tab on the page and, when my popup opens, use that element to populate links in specific ways.
For instance, using Twitter as an example, I'd like to grab the top tweet's permalink and have part of my extension's dropdown offer a link to that.
The way I'm seeing it working is with messaging (as referenced here and here), but something's escaping me.
My best guess from the above example and digging through some other scripts is the following in my main page's content.js; (I've already used document.getElementById to set the var previousVariable):
chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
if (msg.text === 'report_back') {
sendResponse(previousVariable.href);
}
});
And then the following in my popup.js:
console.log("This is step one!");
function doStuffWithDom(domContent) {
console.log('Did it work? ' + domContent);
}
// When the browser-action button is clicked...
chrome.browserAction.onClicked.addListener(function (tab) {
chrome.tabs.sendMessage(tab.id, {text: 'report_back'}, doStuffWithDom);
});

Reload Chrome DevTools Extension Sidebar on Page Change

This may be a dupe of the question at Google Chrome DevTools Extension - Detect Page Change, but it's been sitting for over a year without a real answer, and maybe my question will provide some new insight into the problem.
I've written a Chrome Extension which inserts a sidebar panel into DevTools. The side panel lists the dataLayer events, a user can click on the desired event and then use the element picker to select another element on the page, and the plugin displays the path between the elements in dot notation.
Here's the link to the Github project: https://github.com/gruebleenagency/gtm-data-layer-sifter
It works as I'd like it to normally, but if you navigate to another page, the sidebar is not initialized again, so it displays the info from the previous page.
You can see that I have this in my background.js file:
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
if (changeInfo.status == 'complete') {
reloadExtension();
}
});
function reloadExtension() {
// ???
}
I'm wondering if there's any way to make the sidebar reload at this point? Or am I going about this in the wrong way? I've spent a lot of time messing around with this trying to get it to work, but nothing seems to do the trick.
Thanks for any and all help.
Here's my solution. It's very possible that this isn't the way it should be done, but I don't have the time to rewrite the whole plugin at the moment, so it'll have to do for now. Any suggestions for how I should have done it would be greatly appreciated. And maybe it will be helpful to someone in a different situation.
Essentially, listen to the tabs.onUpdated event waiting for the 'complete' status, which indicates we've navigated to a new page. Then send message to Devtools.js to refresh the side panel, by setting its page to 'panel.html' again.
In background.js:
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
if (changeInfo.status == 'complete') {
reloadExtension(port);
}
});
function reloadExtension(port) {
var message = {action: "reloadExtension"};
port.postMessage(message);
}
In Devtools.js:
var sb;
createSidebar();
function createSidebar() {
chrome.devtools.panels.elements.createSidebarPane("GTM dataLayer Sifter", function(sidebar) {
sb = sidebar;
sb.setPage("panel.html");
});
}
var port = chrome.extension.connect({
name: "Devtools.js Communication"
});
// Listen to messages from the background page
port.onMessage.addListener(function(message) {
if(message.action == "reloadExtension"){
sb.setPage("panel.html");
}
});
You can do it by listen onNavigated event:
In Devtools.js
chrome.devtools.network.onNavigated.addListener(() => {
console.log('Inspected page reloaded');
});

How can I get different badge value for every tab on chrome?

I'm trying to do something like adblock does. Adblock counts number of "ads" and update badge value. For now, I tried to do something with 'background pages', but they are run only one time and badge value is the same for all tabs. I can't use browser action popup.html, because it triggers only after the click.
So I need something which takes current tab, is able to read current DOM of tab and after all update badge value. But also after I click on different tab I need to compute new badge value.
Thanks in advance
The badge text is stored for each tab independently provided you specify tabId parameter, you don't have to update it manually after the user switches tabs if you already have set the value.
So if your extension processes the pages immediately after loading, call chrome.browserAction.setBadgeText once. You can do it e.g. by sending a message from your content script to your background/event page which will invoke setBadgeText with the sender tab's id (this parameter is what makes the text unique to a tab).
content script:
chrome.runtime.sendMessage({badgeText: "123"});
background/event script:
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.badgeText != null) {
chrome.browserAction.setBadgeText({
tabId: sender.tab.id,
text: message.badgeText,
}, () => chrome.runtime.lastError); // ignore errors due to closed/prerendered tabs
}
});
You can listen to the Chrome tab events in your background/event page. The following code helped me to solve the same problem:
// fires when tab is updated
chrome.tabs.onUpdated.addListener(updateBadge);
// fires when active tab changes
chrome.tabs.onActivated.addListener(updateBadge);
function updateBadge() {
// get active tab on current window
chrome.tabs.query({active: true, currentWindow: true}, function(arrayOfTabs) {
// the return value is an array
var activeTab = arrayOfTabs[0];
if (!activeTab) return;
// compute number for badge for current tab's url
var count = getCount(activeTab.url);
chrome.browserAction.setBadgeText({
text: count.toString()
});
});
}
function getCount(currentUrl) {
// your logic, e.g., return 42
}
You can write an "onActiveChanged" listener and call your updateBadge function and pass the tabId. Hope it helped.
chrome.tabs.onActiveChanged.addListener(function (tabId, changeInfo, tab) {
updateBadge(tabId);
});
function updateBadge(tabId) {
...
}

Categories