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');
});
Related
I am creating a chrome extension which embeds an iframe on a website and it works well except when a user clicks on the iframe (or anything inside the iframe) and then tries to use the back button to navigate back to a previous page on the parent/actual site. When you attempt to go back the page just reloads the iframe (even when you press the back button many times, each time it just reloads the iframe). If the user doesn't click on the iframe (just mouseover) everything works as expected. Here is what my chrome extension is doing:
background.js:
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
if (changeInfo.status == 'complete') {
chrome.scripting.executeScript({
target: { tabId: tab.id },
function: createIframe
});
}
});
function createIframe() {
var location = document.getElementById(id of location);
var embed = document.createElement("iframe");
embed.setAttribute('src', link);
location.appendChild(embed.cloneNode(true));
}
Responses to similar problems said the issue was setting the src link using setAttribute and that this method added the iframe loading to the browser history. To fix this they suggest using iframe.contentWindow.location.replace(link);. I tried doing this, but the iframe didn't even load the link content (not sure if replace requires a location to be set already):
embed.setAttribute('id', 'uniqueid');
var frame = document.getElementById('uniqueid');
frame.contentWindow.location.replace(link);
I am new to web development (and chrome extension development) and any help would be greatly appreciated! Thanks in advance.
I'm trying to create a Chrome Extension which can dynamically create ContextMenu based on storage info. Each created menu has its owned key and will do some actions then send message to Content.js to show info box on page if it is clicked.
The menu will be updated if user select some options in Options page. The Options.js will send out a message to background.js, background.js update the storage then refresh menu.
The menu behavior works fine after extension was loaded. But no longer work after few minutes. If I open a console of service worker then it can stay longer, but it eventually no response when i click menu button. Further more, if i trigger the menu updating from Option page, the button back to live. But still failed after few minutes.
I made some research and found out seems background.js will be terminated very soon after it was loaded. So i doubt that cause this issue. But how com the added menu listener not working after that? The listener should already be added, isn't it?
Background.js
function eventListener(message, sender, sendResponse) {
if (message.event === "updateMenu") {
updateMenu();
sendResponse({ result: "update menu completed" });
}
}
function updateMenu() {
chrome.storage.local.get("currencyMappings", function (result) {
prepareContextMenuBySetting(result.currencyMappings);
});
}
function prepareContextMenuBySetting(mappings) {
chrome.contextMenus.removeAll();
chrome.contextMenus.create(
{ id: "rootMenu", title: "%s", contexts: ["selection"] },
function () {
if (chrome.extension.lastError) {
console.log("Got expected error: " + chrome.extension.lastError.message);
}
});
mappings.forEach(map => {
currencies = map.split("|");
chrome.contextMenus.create(
{ id: `${currencies[0]}to${currencies[1]}`, title: `${currencies[0]} => ${currencies[1]}`, type: "normal", parentId: "rootMenu", contexts: ["selection"] });
})
chrome.contextMenus.onClicked.addListener(
(info, tab) => {
let rate = null;
rateKey = info.menuItemId.split("to");
makeExchange(rateKey, info)
}
);
}
Update:
Log will display when the function is working. But there was no any log in console when the button action is not working. Event both Server Worker and page console.
I figure out the possible reason. According to https://developer.chrome.com/docs/extensions/mv2/background_pages/#listeners
adding listener in listener function may not working.
The root cause is that the backgound.js will be terminated in short time. Only keep the root listener. After backgound.js is terminated, the listener in my menu button not working anymore.
Further more, the background.js actually back to live if the root listener get called.
Ex.
chrome.runtime.onMessage.addListener(eventListener);
i added a console.log() at the top of background.js and monitor the console. After the Service Worker display unavailable, which means the backgroud.js is shut down, then I fire a message to background.js. The console.log() will be triggerd.
The solution of my issue is moving adding listener of menu to root level.
chrome.contextMenus.onClicked.addListener(
(info, tab) => {
console.log("item clicked");
let rateKey = info.menuItemId.split("to");
makeExchange(rateKey, info)
}
);
Then once background.js is wake up, the button listener will be set.
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
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);
});
My extension is for Facebook. It logs tab state changes correctly on the first load, but if I navigate to another page through a link on the current page, then it reports changeinfo.status == 'complete' immediately on the click.
I can not find anything to suggest I've made a mistake
chrome.tabs.onUpdated.addListener(check);
function check(tab_id, changeinfo, tab){
console.log("tab change: " + changeinfo.status);
// make sure the page is done loading
if ( !(tab.url !== undefined && changeinfo.status == "complete")) {
return;
}
if(tab.url.match(/facebook.com\/[^\/]*$/)){ //if the url of the tab matches a certain website
chrome.pageAction.show(tab_id); //show the icon (by default it is not shown).
console.log("accepted state: " + changeinfo.status);
}
}
you can try with webNavigation API if it fit your needs
manifest
"permissions": ["webNavigation"]
background script
chrome.webNavigation.onCompleted.addListener(function(details) {
if (details.url.startsWith('https://www.facebook.com/')) {
chrome.pageAction.show(details.tabId);
}
});
You haven't made a mistake, facebook loads it's content dynamically. They change the URL without reloading using history.pushState from the History API.
I don't think you can get the information you need using tabs.onUpdated since the status 'complete' is actually correct from it's perspective. You might have to deal with this in your content script.