This question already has answers here:
I'm getting an error "Tabs cannot be edited right now (user may be dragging a tab)" on tab update/activate/focus event in chrome 91
(5 answers)
Closed 1 year ago.
When I'm trying get tab information from within chrome.tabs.onActivated event callback when a tab switched via mouse click I get undefined result and error message
Unchecked runtime.lastError: Tabs cannot be edited right now (user may
be dragging a tab).
With 100ms delay or switch tab with CTRL+TAB or if a tab clicked while window was not in focus it works fine:
background.js
chrome.tabs.onActivated.addListener( info =>
{
chrome.tabs.get(info.tabId, tab => console.log("get", tab));
chrome.tabs.query({active: true, windowId: info.windowId}, tab => console.log("query", tab));
chrome.tabs.getSelected(tab => console.log("getSelected", tab));
setTimeout(()=>
{
chrome.tabs.get(info.tabId, tab => console.log("setTimeout get", tab));
chrome.tabs.query({active: true, windowId: info.windowId}, tab => console.log("setTimeout query", tab));
chrome.tabs.getSelected(tab => console.log("setTimeout getSelected", tab));
}, 100);
});
manifest.json
{
"background": {
"persistent": true,
"scripts": [ "background.js" ]
},
"description": "tab info within chrome.tabs.onActivated event",
"manifest_version": 2,
"name": "test",
"permissions": [ "tabs" ],
"version": "0.0.1"
}
Is this a bug or something?
Testing in Chrome 91.0.4472.77 64bit and MS Edge 91.0.864.41 64bit
Yes, it's a bug:⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
https://bugs.chromium.org/p/chromium/issues/detail?id=1213925
The work around is catch runtimeError together with setTimeout
function tabsGet(id, callback)
{
const cb = tab => (chrome.runtime.lastError) ? setTimeout(e => chrome.tabs.get(id, cb)) : callback(tab);
chrome.tabs.get(id, cb);
}
Related
I have the following code in a background.js.
chrome.tabs.onUpdated.addListener( function (tabId, changeInfo, tab) {
if (changeInfo.status == 'complete') {
chrome.tabs.executeScript(tabId, {
file: 'inject.js'
});
} // if (changeInfo.status == 'complete') {
});
But when I change tabs the inject.js only runs in the active tab even if an inactive tab gets updated. In the inject.js I have an automated process running for a particular website and it's clicking through some pages looking for specific text. I want it to continue running on that one tab even if I open a new tab. But as soon as I change tabs the injection happens only on the active tab. I thought tab.tabId or tabId would be for the tab that was updated but it seems like it's always the active tab.
Is there a way to figure out what tab updated so I can pass the correct tabId to executeScript?
Here is what my manifest looks like:
{
"name": "Automated test",
"version": "0.0.1",
"manifest_version": 2,
"background": {
"scripts": [
"background.js"
],
"persistent": true
},
"browser_action": {
"default_title": "Test"
},
"permissions": [
"https://*/*",
"http://*/*",
"tabs",
"webNavigation"
]
}
So, #wOxxOm led me to what I needed. I did need a content-script and the reason why I think I wasn't seeing the results I was expecting is that I was failing to remove and re-add my extension. Sometimes it seems like it respects the changes made but maybe when you make changes to the manifest you need to remove and re-add the extension.
Once I did that it is correctly working on the inactive tab.
In this minimal example, I have the following manifest.json:
{
"name": "Getting Started Example",
"version": "1.0",
"description": "Build an Extension!",
"manifest_version": 2,
"background": {
"scripts": ["background.js"]
},
"permissions": ["webNavigation", "activeTab"],
"browser_action": {
"default_icon": {
"16": "red.png"
}
}
}
On every navigation, I want to change the icon in the toolbar to a green png. It works, however, there's a problem:
I'm on any page, the icon is green.
I click on a link, it triggers navigation.
The icon resets back to red for a few milliseconds
My listeners get triggered, and they set the icon back to green.
My background.js:
const setIcon = (details) => {
if (details.frameId != 0) {
return; // only update the icon for main page, not iframe/frame
}
chrome.browserAction.setIcon({
path: { '16' : 'green.png'},
tabId: details.tabId
});
}
chrome.webNavigation.onHistoryStateUpdated.addListener(setIcon);
chrome.webNavigation.onBeforeNavigate.addListener(setIcon);
chrome.webNavigation.onCommitted.addListener(setIcon);
What causes this behavior? Is there a way I can prevent the icon getting reset on navigation?
As a learning exercise I'm attempting to build an example Chrome extension that ensures sites on a 'greylist' are always opened in an incognito window.
Here's how far I have got - using the webNavigation.onBeforeNavigate event fired when a grey listed page is about to be navigated to I open a new tab in an incognito window, but now need to prevent the original tab from opening the page.
manifest.js:
"permissions": [
"webNavigation",
"tabs"
],
"background": {
"scripts": [
"background.js"
],
"persistent": false
},
background.js:
chrome.webNavigation.onBeforeNavigate.addListener(function(details) {
chrome.tabs.get(details.tabId, function(tab) {
if(!tab.incognito) {
// Open the page in an incognito window
chrome.windows.create({ url: details.url, incognito: true});
// TODO stop non-incognito tab opening page!
}
});
}, {
url: [
{ hostEquals: 'badsite.com' }
],
});
To stop the navigation use window.stop() by injecting a content script in the tab:
chrome.tabs.executeScript(details.tabId, {code: 'window.stop()'});
Add a permission in manifest.json, otherwise you'll see an error in the background page console:
"permissions": [
"webNavigation",
"tabs",
"<all_urls>"
],
Partial answer arrived at from wOxxOm's input and further experiments and reading - to at least document what I found out.
manifest.js:
"permissions": [
"webNavigation",
"tabs",
"<all_urls>" // Note: Permission
],
...
background.js:
// Note: onCommitted
chrome.webNavigation.onCommitted.addListener(function(details) {
chrome.tabs.get(details.tabId, function(tab) {
if(!tab.incognito) {
// Stop non-incognito tab opening page
// Note runAt: "document_start"
chrome.tabs.executeScript(details.tabId, { runAt: "document_start", code: 'window.stop(); '})
// Open the page in an incognito window
chrome.windows.create({ url: details.url, incognito: true});
}
});
}, {
url: [
{ hostEquals: 'badsite.com' }
],
});
Listening for chrome.webNavigation.onCommitted events instead of onBeforeNavigate allows the script injected by chrome.tabs.executeScript to run when a grey listed page is navigated to from a new tab and a url is pasted into the omni box.
This prevents the grey listed page from being displayed, but the page is at least partially loaded. A history entry is not created but cookies or local storage items are created, so it does not meet the ultimate need of my original question.
two ways:
base on #wOxxOm
chrome.webNavigation.onBeforeNavigate.addListener((details) => {
chrome.tabs.executeScript(tabid,{code: 'window.stop()'});
});
not refresh
window.history.pushState(“object or string”, “Title”, “/new-url”);
I tried this : https://developer.chrome.com/extensions/options.html and made an option page.
So a selection has been added under my extension icon with the name of Option.
My question is that is there a way to rename Option and change it something like Setting or some words in other languages ?
The "Options" label at chrome://extensions is automatically adapted to the user's language. Extensions cannot change the value of this label.
The value of the "Options" option at the dropdown menu at the extension's button cannot be customized either, but you can create a new context menu item under the button as of Chrome 38. E.g.
chrome.contextMenus.create({
id: 'show-settings', // or any other name
title: 'Settings',
contexts: ['page_action', 'browser_action']
});
chrome.contextMenus.onClicked.addListener(function(info, tab) {
if (info.menuItemId == 'show-settings') {
chrome.tabs.create({
url: chrome.runtime.getURL('settings.html')
});
}
});
I suggest to just stick to "Options" though, because users do already know what the option does. Consistency in UI/UX is important, imagine how you productive you'd be if every application had a different way of (e.g.) closing/quiting the application.
manifest.json to test the previous script:
{
"name": "Contextmenu on browserAction button",
"version": "1",
"manifest_version": 2,
"background": {
"scripts": ["background.js"]
},
"browser_action": {
"default_title": "Right-click to see a context menu"
},
"permissions": [
"contextMenus"
]
}
Easier way to trigger events.
chrome.contextMenus.create({
title: 'GitHub',
contexts: ['page_action'],
onclick: () => console.log('GitHub'),
});
When the chrome.tabs.create function is triggered by a message received, it creates 2 tabs. In the following demo code, 1 cat tab is created, and 2 dog tabs are created.
Is this by design or is it a bug? If it is a known bug, can you provide the bug id so I can track its progress? How can I avoid 2 duplicate tabs being created?
The debug console contains the following output, so in fact the duplicate tab is also getting the content script injected even though only one call to secondaryTabCreationCallback_ is printed in the debug output!!!!
Creating secondary tab
Created secondary tab: 11968
Kill request from tab: 11966
Kill request from tab: 11968
background.js
var handler = {
url1_: 'https://www.google.com/?gws_rd=ssl#q=cat',
url2_: 'https://www.google.com/?gws_rd=ssl#q=dog',
windowId_: chrome.windows.WINDOW_ID_CURRENT,
createPrimaryTab: function() {
chrome.tabs.create(
{'url': handler.url1_, 'active': false, 'windowId': handler.windowId_},
handler.primaryTabCreationCallback_);
},
primaryTabCreationCallback_: function(tab) {
chrome.tabs.executeScript(tab.id, {file: "content_script.js"});
},
createSecondaryTab_: function() {
console.log("Creating secondary tab");
chrome.tabs.create(
{'url': handler.url2_, 'active': false, 'windowId': handler.windowId_},
handler.secondaryTabCreationCallback_);
},
secondaryTabCreationCallback_: function(tab) {
console.log("Created secondary tab: " + tab.id);
chrome.tabs.executeScript(tab.id, {file: "content_script2.js"});
},
};
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
switch (message.type) {
case "CREATE_TAB":
handler.createSecondaryTab_();
break;
case "KILL_ME":
console.log("Kill request from tab: " + sender.tab.id);
// chrome.tabs.remove(sender.tab.id);
break;
default:
alert("Not Reached");
break;
}
});
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.create({'url': chrome.extension.getURL('background.html')});
});
window.onload = function() {
document.getElementById("start_button").onclick = handler.createPrimaryTab;
}
content_script.js
chrome.runtime.sendMessage({type: "CREATE_TAB"});
content_script2.js
chrome.runtime.sendMessage({type: "KILL_ME"});
background.html
<!doctype html>
<html>
<head>
<script src="background.js"></script>
</head>
<body>
<div>
<input type="button" id="start_button" value="Start">
</div>
</body>
</html>
manifest.json
{
"manifest_version": 2,
"name": "Tab Bug",
"description": "Demonstrates bug in chrome.tabs.create.",
"version": "1.0",
"permissions": [
"activeTab",
"nativeMessaging",
"tabs",
"https://www.google.com/"
],
"icons": { "128": "icon128.png" },
"browser_action": {
"default_icon": "icon19.png"
},
"background": {
"page": "background.html"
}
}
The issue is that there are 2 "background" pages running.
The official background page specified in the manifest file.
The tab created by chrome.tabs.create({'url':
chrome.extension.getURL('background.html')}).
This means there are 2 message listeners, which is why 2 tabs are opening.
The console messages from the official manifest.json background can be found by looking at extension on the chrome extensions page and click on the "Inspect views: background.html". Which shows:
Creating secondary tab
Created secondary tab: 11966
Kill request from tab: 11966
Kill request from tab: 11968
To work around this issue. The manifest.json background file can point to a script "starter.js" instead of an html page, which simply has the following javascript:
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.create({'url': chrome.extension.getURL('background.html')});
});
My background.js is very simple.
chrome.browserAction.onClicked.addListener(() => {
chrome.tabs.create({
url: chrome.runtime.getURL('popup.html'),
})
})
manifest.json
"background": {
"persistent": false,
"scripts": [
"background.js"
]
},
Tried to disable/enable the extension (so that chrome destroys all the background pages from this extension) but it still opens duplicate tabs!!
So the problem might be there is someone that included the background.js. And guess what! It's popup.html.
The root cause is HtmlWebpackPlugin from webpack.config.js. It defaults to include all chunks (including background).
So we just exclude the background chunk from popup.html config then it should work as expected.
new HtmlWebpackPlugin({
template: 'public/popup.html',
filename: 'popup.html',
excludeChunks: ['background'],
}),