I am in the process of writing an extension for Chrome to display the users 3 most visited sites. (Yes, I am aware that the "New Tab" page already does this) However, whenever I try to query the users history then it seems like the entire script shuts down.
My manifest files does contain:
{
"name": "Most Visited Sites Test",
"description": "Show your most visited sites",
"version": "1.0",
"background_page": "background.html",
"app": {
"launch": {
"web_url": "http://localhost/*"
}
},
"permissions": [
"tabs",
"history",
"unlimitedStorage",
"notifications"
],
"icons": {"128": "icon.png" },
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["contentscript.js"]
}
]
}
So I believe this ought to give my background page the ability to use the history. However, my background page contains:
function onRequest(request, sender, sendResponse)
{
alert("Call 1");
var oneWeekAgo = //Code for getting last weeks date;
chrome.history.search({
'text': '',
'startTime': oneWeekAgo
},
function(historyItems)
{
// Do stuff...
});
alert("Call 2");
};
The request is sent from my contentscript.js
chrome.extension.sendRequest("foo");
When run, "Call 1" is shown but then nothing is done with the history and "Call 2" is never shown. What might be causing this? I apologize if this is a simple problem but this is my first attempt at a legitimate Chrome extension.
Opening console to see if there any errors is the first thing I always do (go to the Extensions tab and click on "background.html").
Your history call is correct, so maybe your last week calculation isn't? This is what works for me:
chrome.history.search({text: "", startTime:(new Date()).getTime()-7*24*3600*1000}, function(items) {
console.log(items);
});
Related
Good day.
I have a problem sending messages from background script to content script.
I try to send messages every time a user switches between browser tabs.
I get this error.
Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist.
Below is the code for the extension itself.
Background Script:
chrome.tabs.onActivated.addListener(function changeTab(activeInfo) {
var port = chrome.tabs.connect(activeInfo.tabId);
console.log(port);
port.postMessage({tabId: activeInfo.tabId});
});
Content Script:
chrome.runtime.onConnect.addListener(function(port) {
console.log(11111111);
port.onMessage.addListener(function(msg) {
console.log(22222222);
if(msg == undefined || Object.keys(msg).length == 0) return;
if(msg.checkExtensionRBPopup)
port.postMessage({active: window.localStorage.getItem('extension_rb_popup')});
});
});
Manifest:
{
"manifest_version": 2,
"name": "Rebatemango Extension",
"description": "Rebatemango",
"version": "1.0",
"browser_action": {
"default_popup": "popup.html"
},
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"js": [
"content.js",
]
}
],
"permissions": [
"background",
"tabs",
"activeTab",
"declarativeContent",
"storage",
"clipboardWrite",
"cookies",
"tabCapture",
"displaySource",
"webNavigation"
],
"background": {
"scripts": [
"js/jquery.js",
"js/libs.js",
"background.js"
],
"persistent": false
},
"icons": {
"16": "images/mango.png",
"48": "images/mango.png",
"128": "images/mango.png"
}
}
Please tell me what am I doing wrong?
How to fix or track this error?
Your content script is using port.postMessage but there is no listener on the other side of the port in the background script, which is why the error is shown.
Solution:
Use simple messaging. Don't use ports, it's not needed here and your implementation is causing a memory leak since it creates a new port each time without releasing it. The simple messaging will handle this automatically.
chrome.tabs.sendMessage(activeInfo.tabId, {foo: 1}, response => {
if (chrome.runtime.lastError) return;
console.log(response);
});
The content script listener:
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
console.log(msg);
sendResponse({bar: 2});
});
Other causes.
The error message may also appear if there is no content script running at the exact moment the message was sent. It may happen because by default content scripts run after DOMContentLoaded event in the web page. Or maybe the tab contained an unsupported URL like chrome:// or chrome-extension:// from an extension page or https://chrome.google.com/webstore/ or any URL forbidden by the runtime_blocked_hosts policy. For the first problem you can add "run_at": "document_start" to your content script declaration in manifest.json. For the other cases there's nothing you can do except for suppressing and ignoring the error message.
Also the background script sends {tabId: activeInfo.tabId} but onMessage listener in the content script doesn't read it. Instead it reads msg.checkExtensionRBPopup which doesn't make any sense in case it's not a copypaste artifact of posting the question.
Disclaimer: I am new to JavaScript and I have never developed a Chrome extension before.
I am trying to develop a Chrome extension that runs some JavaScript when the user selects some text on a page, right-clicks, then clicks a context menu button. I have determined (based on running it from the Chrome console) that the JavaScript I've written runs as expected. Now all is left is to make an extension.
I can get the extension to load, and I can get it to appear on the page and to appear to run. However, it doesn't seem to do anything, and the console doesn't return any output. (I read that I can't run inline JavaScript with event pages, hence using addListener.) Have I set up the context menu incorrectly? Is there an error (or several) in my script?
manifest.json
{
"name": "My Extension",
"description": "sample",
"version": "0.0.1",
"permissions": ["contextMenus"],
"background": {
"persistent": false,
"scripts": ["background.js"]
},
"manifest_version": 2
}
background.js
chrome.runtime.onInstalled.addListener(function() {
var context = "selection";
var title = "My Extension";
var id = chrome.contextMenus.create({"title": title, "contexts":[context],
"id": "context" + context});
});
chrome.contextMenus.onClicked.addListener(getSHA);
// Get file path of file to be staged
// Get SHA
function getSHA(){
stagedFile = window.getSelection().toString()
console.log(stagedFile)
baseURL = window.location.href.slice(0, -6);
prNumber = baseURL.slice(-4);
xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.github.com/repos/kubernetes/kubernetes.github.io/pulls/"+prNumber, false);
xhr.send();
json_data = JSON.parse(xhr.responseText);
shaValue = (json_data.head.sha)
console.log("SHA: "+shaValue)
getNetlify;
};
// Get Netlify URL
function getNetlify(){
xhr2 = new XMLHttpRequest();
xhr2.open("GET", "https://api.github.com/repos/kubernetes/kubernetes.github.io/commits/"+shaValue+"/status", false);
xhr2.send();
json_data2 = JSON.parse(xhr2.responseText, function(key, value) { if (key == "target_url" && value.includes("netlify")) { netlifyURL = value; }});
openStaging
};
// Stage file
function openStaging(){
window.open(netlifyURL+"/"+stagedFile)
};
You need to add a "content script" to your manifest.json. That is the kind of code that gets injected into the page to run. The background script didn't have access to the page at all. So, check out the documentation on content scripts. https://developer.chrome.com/extensions/content_scripts
You need to add the following piece to your code to your manifest.json:
"content_scripts": [
{
"matches": ["http://www.google.com/*"],
"css": ["mystyles.css"],
"js": ["myscript.js"]
}
]
With this code, any time the user goes to a site that "matches" the url I provided, then the extension will inject into that page mystyles.css and myscript.js. So... your pattern would be something like http*://*/* . That will inject the script onto any page that the user will go to.
Next, to accomplish what you are trying to accomplish, you don't need a background script. So you can remove that from your manifest.json.
So your manifest.json would look like this:
{
"name": "My Extension",
"description": "sample",
"version": "0.0.1",
"permissions": ["contextMenus"],
"content_scripts": [
{
"matches": ["http://www.google.com/*"],
"css": ["mystyles.css"],
"js": ["myscript.js"]
}
],
"manifest_version": 2
}
Then put your code into the myscript.js file (or whatever you want to call yours), and you should see this start to run on the page.
I'm writing a chrome extension and I want to manage all of the data/variables with chrome storage. From what I understand, I should be able to use chrome.storage across my extension.
I want to set something in browser_action script and then access it in the window created by the background script. The HTML and JS files all have corresponding names.
This is what I have tried with no luck:
//manifest.json
{
"manifest_version": 2,
"name": "extension",
"description": "my extension",
"version": "0.1",
"permissions": [
"tabs",
"storage"
],
"background": {
"scripts": ["background.js"],
"persistent": false
},
"browser_action": {
"default_icon": {
"38": "images/icon38.png"
},
"default_popup": "settings.html"
}
}
//background.js
chrome.windows.create({'url': chrome.extension.getURL("newPage.html"),'type': "detached_panel",'focused': true}, function(){
//created newPage.html which has newPage.js
});
//setting.js
document.addEventListener('DOMContentLoaded', function(){
chrome.storage.local.set({'enabled': 'TRUE'});
});
//newPage.js
chrome.storage.local.get('enabled', function(result){
document.getElementById("myId").innerHTML += "<br>script loaded... and status: " + result.enabled;
});
When I do this, newPage.html displays "script loaded... and status: undefined".
Even with it being asyncronous, the storage value should be populated on running the script a second time, right?
I may just be using this incorrectly. If that's the case, what's the correct way to set something with chrome storage to access in my new window?
Any help would be appreciated!
I think that your newPage.html is asking for the value of 'enabled' before it's been set by the event handler in settings.html, which only executes after you've actually opened the browser popup. So this is why it's undefined. Open the popup and you should see that it's defined.
Every once in a while my chrome extension's background.js page freezes, i have no idea what is causing it.
When the background.js file has frozen, it no longer responds to messages from the content script, and when I try to open the background page via the extensions manager to inspect it, the window pops up but it stays blank, and no interface appears.
The only things im doing in the background page are message passing and retrieving localstorage variables.
I cant figure out what is causing this, the bug only seems to have happened since i transitioned to the new chrome.runtime api, from the chrome.extension api
Can anyone tell me what is going wrong here? or help me figure it out? Thanks!
Heres the background.js file's code in its entirety
if (!chrome.runtime) {
// Chrome 20-21
chrome.runtime = chrome.extension;
} else if(!chrome.runtime.onMessage) {
// Chrome 22-25
chrome.runtime.onMessage = chrome.extension.onMessage;
chrome.runtime.sendMessage = chrome.extension.sendMessage;
}
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.method == "getLocalStorage")
sendResponse({data: localStorage[request.key]}); // decodeURIComponent
else if (request.method == "setLocalStorage")
sendResponse({data: localStorage[request.key]=request.value});
else
sendResponse({}); // send empty response
});
Is it possible a deadlock situation is occurring that is freezing the page? It doesnt cause the CPU to go mad, so im guessing its not an endless loop.
Update
here is the manifest.json as requested
{
"manifest_version": 2,
"content_scripts": [ {
"exclude_globs": [ "http://*.facebook.com/ajax/*", "https://*.facebook.com/ajax/*" , "http://www.facebook.com/ai.php?*", "https://www.facebook.com/ai.php?*", "http://www.facebook.com/ajax/*", "https://www.facebook.com/ajax/*"],
"include_globs": [ "http://*.facebook.com/*", "https://*.facebook.com/*" ],
"js": [ "script.js" ],
"matches": [ "http://*.facebook.com/*", "https://*.facebook.com/*" ],
"run_at": "document_start"
} ],
"converted_from_user_script": true,
"background": {"scripts": ["background.js"],
"persistent": false},
"icons": {
"128": "ET-128x128.png",
"48": "ET-48x48.png"
},
"key": "xxxxxxxxxxxxxxxxxxxx",
"name": "Extension Test",
"short_name": "ET",
"description": "ET Does this and that, but doesnt phone home",
"version": "999",
"homepage_url": "http://www.etphonehome.com"
}
Only disabling and re-enabling the extension get it to start working again, once the background page has frozen
Below is a screenshot of the frozen background page inspection window:
The localStorage API is problematic because in chrome it is a synchronous API to an inherently asynchronous operation (called from a renderer process, which must then communicate with the browser process that reads from / writes to a backing store in the filesystem and possibly replies back to the renderer process). While it should not in theory be possible to cause deadlocks from webpage or extension code, it's possible there are bugs in chrome's implementation.
One thing you might try is switching from localStorage to chrome.storage.local. It is a little more work to use since it has an asynchronous API, but does not suffer from the same implementation complexity as localStorage.
E.g.
sendResponse({data: localStorage[request.key]});
becomes
chrome.storage.local.get(request.key, function(storageResult) {
sendResponse(storageResult[request.key]);
});
So i'd like to run a script when the tab reloads in a specified URL. It almost works, but actually id doesn't :)
This is my manifest file:
{
"manifest_version": 2,
"name": "Sample Extension",
"description": "Sample Chrome Extension",
"version": "1.0",
"content_scripts":
[
{
"matches": ["http://translate.google.hu/*"],
"js": ["run.js"]
}
],
"permissions":
[
"activeTab",
"tabs"
],
"browser_action":
{
"default_title": "Sample",
"default_icon": "icon.png"
}
}
and this is run.js:
chrome.tabs.onUpdated.addListener(
function ( tabId, changeInfo, tab )
{
if ( changeInfo.status === "complete" )
{
chrome.tabs.executeScript( null, {file: "program.js"} );
}
}
);
The programs.js just alerts some text (yet). When I put an alert to the first line of the run.js, it alerts, but when I put it in the if, it doesn't. I can't find the problem. Did I type something wrong?
Assuming that http://translate.google.hu/* pages are the ones you wish to inject code into on reload, you would have to go about it in a slightly different way. Currently you are always injecting code into those pages (without the permission to do so, no less) and then trying to use the chrome.tabs api inside that content script, which you can't do. Instead, we will put the listener in a background page and inject the code only on a page refresh, like you want. First the manifest:
{
"manifest_version": 2,
"name": "Sample Extension",
"description": "Sample Chrome Extension",
"version": "1.0",
"background": {
"scripts": ["background.js"]
},
"permissions":[
"http://translate.google.hu/*", "tabs"
]
}
background.js
chrome.tabs.onUpdated.addListener(function(tabId,changeInfo,tab){
if (tab.url.indexOf("http://translate.google.hu/") > -1 &&
changeInfo.url === undefined){
chrome.tabs.executeScript(tabId, {file: "program.js"} );
}
});
This will listen for the onUpdated event, checks if it is one of the url's that we want to inject into, and then it checks if the page was reloaded. That last step is accomplished by checking if changeInfo.url exists. If it does, then that means that the url was changed and thus not a refresh. Conversely, if it doesn't exist, then the page must have only been refreshed.
2021
If you want to detect reload from background.js in manifest 3 (maybe also 2), chrome.tabs.onUpdated approach didn't work for me :/ It was invoked too many times.
That what worked for me in the end!
// --- On Reloading or Entering example.com ---
chrome.webNavigation.onCommitted.addListener((details) => {
if (["reload", "link", "typed", "generated"].includes(details.transitionType) &&
details.url === "http://example.com/") {
codeAfterReload();
// If you want to run only when the reload finished (at least the DOM was loaded)
chrome.webNavigation.onCompleted.addListener(function onComplete() {
codeAfterReloadAndFinishSomeLoading();
chrome.webNavigation.onCompleted.removeListener(onComplete);
});
}
});
For more transition types: https://developer.chrome.com/docs/extensions/reference/history/#transition_types
good luck :)
content_scripts are run at every page (re)load, so it's best to just use those to detect it.
This way you also don't risk running any code in the background before your content_script is ready to receive any message.