I am trying to create a small chrome extension to help find where cookies are using in websites. The extension is SUPPOSE to work by setting the getter method for the cookie on the document.
Like so:
document.__defineGetter__('cookie',function(){
alert("test");
});
When manually put into chrome's javascript console on a website and then trying to access a cookie (simply typing "document.cookie") it results in the expected behavior where it will pop up the test prompt.
However when I put this into a chrome extension and have it load before the rest of the page it will fail to work.
Here is the manifest.json (I was just using soundcloud as a test website):
{
"name": "Cookie Auditor",
"version": "0.1",
"manifest_version": 2,
"description": "An extension to help examine where cookies are being used in websites.",
"content_scripts": [
{
"matches": ["*://*.soundcloud.com/*"],
"js": ["content.js"],
"run_at": "document_start"
}
]
}
and here is the content.js:
console.log(document.location);
document.__defineGetter__('cookie',function(){
alert("test");
});
console.log(document.__lookupGetter__('cookie'));
When attempting to manually trigger it (document.cookie) it simply returns the normal value and fails to execute the javascript. When it failed to work here I put in the checks for the document location to make sure it was executing on the right domain and was even loading at all.
The weird part is when you load the page with this extension it will print out that it is on the right domain and it even shows that the cookie getter method was overwritten correctly (it print the function in the console).
However when you lookup the getter method it has been reset (document.__lookupGetter__('cookie')).
My last thought was that it was being reset sometime between my content.js script running and the rest of the page initializing. However when I change the "run_at" field in the manifest.json file to "document_end" in an attempt to make it run later and potentually after any sort of re initialization of the document then soundcloud's stuff will start printing on the console showing that it has properly loaded the page however my script still fails to have made an effect.
EDIT: Before it is suggested. I can't use chrome's cookie API because it doesn't provide a way of actually listening to when a cookie is retrieved which is the main thing I care about.
After some digging around I found out why it was failing. Chrome extensions are executed in their own javascript space but with the same DOM as the site they run on. See https://developer.chrome.com/extensions/content_scripts#execution-environment. This causes them to have seperate global variables and thus my script's attempts to change them would only affect itself. For anyone looking at how to get around this limitation all you have to do is add a script tag with your code onto the document from your extension.
Related
chrome.tabs returns undefined despite the fact I set tabs in the permissions block.
"permissions": [
"tabs",
"http://*/*",
"https://*/*"
],
"content_scripts": [
{
"matches": [
"http://*/*",
"https://*/*"
],
"js": [
"js/myScript.js"
],
"all_frames": true
}
],
But in myScript.js the following returns undefined.
chrome.tabs
As content script has its own limitations,
chrome.tabs is only available in background scripts and popup scripts.
If you wanna to use chrome.tabs then pass message from content_script to background script and play with chrome.tabs.
Content scripts have only limited access to Chrome APIs. This access does not include the API you are trying to use (e.g. chrome.tabs). If you need to use that API, you will have to do so in a background script1.
As listed in Chrome's content scripts documentation, the APIs available to a content script are [I have placed deprecated methods in strikethrough format]:
extension ( getURL , inIncognitoContext , lastError , onRequest , sendRequest )
i18n
runtime ( connect , getManifest , getURL , id , onConnect , onMessage , sendMessage )
storage
A couple of the listed APIs are deprecated and have been for some time. Those that are deprecated have moved to different locations (also listed above):
extension.onRequest ➞ runtime.onMessage
extension.sendRequest ➞ runtime.sendMessage
While not officially deprecated, extension.lastError is also available as runtime.lastError. At this point, it is usually referred to at that location:
extension.lastError ➞ runtime.lastError
Partition your extension into background scripts and content scripts
You are going to need to separate your code into what needs to be in a background script and what needs to be in content scripts, based on the capabilities available to each type of script. Content scripts have access to the DOM of the web page into which they are injected, but limited access to extension APIs. Background scripts have full access to the extension APIs, but no access to web page content. You should read the Chrome extension overview, and the pages linked from there, to get a feel for what functionality should be located in which type of script.
It is common to need to communicate between your content scripts and background scripts. To do so you can use message passing. This allows you to communicate information between the two scripts to accomplish things which are not possible using only one type of script.
For instance, in your content script, you may need information which is only available from one of the other Chrome APIs, or you need something to happen which can most appropriately (or only) be done through one of the other Chrome extension APIs. In these cases, you will need to send a message to your background script, using chrome.runtime.sendMessage(), to tell it what needs to be done, while providing enough informaiton for it to be able to do so. Your background script can then return the desired information, if any, to your content script. Alternately, you will have times when the processing will primarily be done in the background script. The background script may inject a content script, or just message an already injected script, to obtain information from a page, or make changes to the web page.
Background script means any script that is in the background context. In addition to actual background scripts, this includes popups and options pages, etc. However, the only page that you can be sure to have consistently available to receive messages from a content script are your actual background scripts defined in manifest.json. Other pages may be available at some times as a result of the user's interaction with the browser, but they are not available consistently.
This answer was moved from a duplicate question, and then modified.
https://developer.chrome.com/extensions/tabs#method-getSelected shows
getSelected
chrome.tabs.getSelected(integer windowId, function
callback)
Deprecated since Chrome 33. Please use tabs.query {active: true}.
Gets the tab that is selected in the specified window.
Maybe, you should use chrome.tabs.query in popup.js like this
chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
console.log(tabs[0].url);
});
, reload your extension and check the result in the inspect element of your extension.
result image
code image
https://developer.chrome.com/extensions/tabs#type-Tab shows that
The URL the tab is displaying. This property is only present if the extension's manifest includes the "tabs" permission.(Just for remind someone forgot. I was forgot it when I just test it.)
Check this answer also https://stackoverflow.com/a/6718277/449345
This one worked for me
chrome.tabs.getSelected(null, function(tab){
console.log(tab);
});
I am developing a Chrome extension for work, and one of the things it needs to do is to read (only read, not modify) an object that we send back to the website after it makes an asynchronous request to our servers. Basically I need to read the window.<our object name> object and get what's in there.
Now, I know this is possible, because I did this in a Tampermonkey script that I wrote. I was able to console.log(window.<our object name>) and it came in.
Tampermonkey is a Chrome extension, so there's no intrinsic reason why it can access something and another extension can't.
But when I try to access this object, both from content scripts and from injected code, I get nothing. When I get the window object only, it comes up only partially, as if the extension were blind to certain parts of it. But if I'm in the console on the page, and I call window, I get a full window object back. Infuriating.
So if content scripts don't work, and injected scripts don't work, and there's no reason why popup scripts would be any good here, how does one do this?
Many thanks!
UPDATE: As requested, here is the manifest.json (I took the page_redder example and worked off that to make sure I wasn't making any weird mistakes):
{
"name": "Page Redder",
"description": "Make the current page red",
"version": "2.0",
"permissions": [
"activeTab"
],
"background": {
"scripts": ["background.js"],
"persistent": false
},
"browser_action": {
"default_title": "get my object"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"]
}
],
"manifest_version": 2
}
And here is content.js:
var getWindow = window.setTimeout(function() { console.log("From content script: " + window.<OBJECT NAME>); }, 5000);
And here is background.js:
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Called when the user clicks on the browser action.
chrome.browserAction.onClicked.addListener(function(tab) {
// No tabs or host permissions needed!
chrome.tabs.executeScript({
code: 'console.log("From injected script:" + window.<OBJECT NAME>);'
});
});
When run, I get:
From content script: undefined
From injected script: undefined
But if I do window. from the console, I get it. I even added a timeout to make sure that the content script wasn't trying to get something that hadn't loaded in yet. But I can retrieve the object manually before the script runs, and it still gives me undefined.
Soo, this is kind of hacky, but I was able to do it and it worked.
To gain access to everything available to the host window, I had to create a script element, put all the code I wanted in there, and add document.body.appendChild(script) for it to work.
Not the sexiest way of doing things, but it will get the job done for small tasks.
Interesting question. Here is a quick and probably incomplete answer :
each tab has its own private separate window object
background.js also has its own
content scripts are a bit tricky in that while they nominally live inside the page, they actually keep a respectful distance : see isolated worlds
I am not familiar with chrome.tabs.executeScript but somehow I would'nt trust it with anything beyond basics
One approach could be as follow :
Open the relevant page from the background script with chrome.tabs.create : hence the backgound will have complete control and dominance over said tab and the window, document and your_object therein. It will also be easier to handle the asynchronous side of thing : you'll learn to love callbacks.
Depending on what is required regarding the UX, another option would be to handle the async request, and fetch your_object, entirely in background.js
One last hint : extensions you download from the store are just zipped files in your Chrome profile. Find tapermonkey.crx or whatever, unzip it and read the sources to figure out what it does.
And oh, relying on timeout to handle asynchronicity is bound to random results.
According to documentation in https://developer.chrome.com/extensions/content_scripts
However, content scripts have some limitations. They cannot:
Use variables or functions defined by web pages or by other content scripts.
So you can access the common window variables from the content script, but not the variables created from the webpage's javascript, another content script or, as in your case, an object you have sent to the website.
I'm trying to access a page's localStorage variable from content_script, similar to how a page's cookies can be accessed from content_script.
I have no issues accessing a page's cookies from content_script.
However, localStorage.getItem("showDash") and localStorage.setItem("showDash", "value") don't work in content script.
The browser doesn't seem to recognize it and throws an error:
Uncaught TypeError: undefined is not a function
Why does localStorage not work in content_scripts?
Any ways I could work around this?
part of manifest.json:
"content_scripts": [{
"matches": ["https://cms.mmu.edu.my/*"],
"js": ["angular.min.js", "jquery.min.js", "page_handler.js"]
}]
part of page_handler.js:
if (! localStorage.getItem("DashIsOn"))
localStorage.setItem("DashIsOn", "true");
The localStorage is accessible in content scripts, and you can easily test it. Open a tab on stackoverflow.com (like this one with your question), keep it active, and run the following code in your background page script/console:
chrome.tabs.query({active:true}, function(tabs) {
chrome.tabs.executeScript(tabs[0].id, {code: 'alert(localStorage.getItem("se:fkey"));'});
});
NOTE: since January 2021, use Manifest V3 with chrome.scripting.executeScript() instead of chrome.tabs.executeScript().
You'll see that the alert will come up showing your Stack Exchange key. Therefore your problem must be in another part of the code, or, maybe, yuo're using localStorage.getItem() somewhere else than in a content script. I suggest you to add more code to your question, like the manifest.json file and the full content.js script.
I'm making a content script extension for Google Chrome, it adds functionality to a website's page. I want to add a couple of options, not big deal really, I'd just need two strings (none of which are sensitive user data).
From this answer, I assume I need a background page, which I'd rather not add to my extension - I don't want it to gain unnecessary weight.
Do I really need a background page, or I could have an options page without it (and which storage could I use)?
UPDATE
As of Chrome 20 you can now use the Storage api.....
http://code.google.com/chrome/extensions/storage.html
Old way
What I do is create an iframe that points to a page in my extension that has a script that gets the settings I need from local storage and then sends that to its parent in a message which the content script then gets.....well that was a crap explanation, the code says it better ;).......
Content Script
// create the iframe for our page that sends the settings
var el = document.createElement("iframe");
el.setAttribute('src', chrome.extension.getURL("gimmeSettings.html"));
el.style.visibility="hidden";
document.body.appendChild(el);
// create the listner that listens for a message from our page that sends the settings
window.addEventListener("message", receiveSettings, false);
// function that gets called when we recieve a message from the page that sends the settings
function receiveSettings(event) {
//check to make sure the message came from our page
if (event.origin !== "chrome-extension://"+chrome.i18n.getMessage("##extension_id")) return;
//message came from our extension, do stuff with it
console.debug(event.data);
// clean up
window.removeEventListener("message", receiveSettings, false);
el.parentNode.removeChild(el);
}
gimmeSettings.html's JS
// post the message with our settings
parent.postMessage( localStorage.getItem("testing"), "*" );
Options.html's JS
localStorage.setItem("testing","bleh");
Manifest
{
"name": "Getting at an extensions local storage from a content script",
"description" : "Getting at an extensions local storage from a content script. Be aware that other pages/extensions can use this to get at your settings, but not change them...so dont include sensitvie data.",
"content_scripts": [
{
"matches": ["<all_urls>"],
"js" : ["myscript.js"],
"run_at":"document_idle"
}
],
"permissions": [
"tabs", "<all_urls>"
],
"manifest_version": 2,
"web_accessible_resources": [
"gimmeSettings.html"
],
"options_page": "options.html",
"version":"1.0"
}
Some things to note....
Other pages and extensions can easily use this to also get the settings from your extension, so dont use any sensitive data with this method.
From the best I can tell there is no way for them to alter your settings through that page tho, if anyone knows different please explain.
Im using manifest version 2 and have set the page gimmeSettings to be accessible. If you dont know the differences manifest version 2 add you really should read up on it.... http://code.google.com/chrome/extensions/trunk/manifestVersion.html
And if you want a working example then go here.....
http://forum.valorsolo.com/viewtopic.php?f=36&t=375
I just had an idea, but I don't know if it's sound or makes sense.
I believe I can access HTML5 localStorage from my content_script, and store the two strings there. Through Message Passing I should be able to tell the content_script that one of them changed (from the options page) and then have it update its localStorage.
Would this be the only option? It doesn't sound too bad if it works...
My Google Chome extension makes use of a content script declared in its manifest via:
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["js/jquery-1.6.1.min.js", "js/content.js"]
}
],
It also comes with a localization-related resource bundle in _locales/en.
Looking up a key using chrome.i18n.getMessage(...) works flawlessly from general extension code (i.e., in options.html), but it fails (i.e., does not return anything) when executed from js/content.js while that script is being run in the context of the regular web page.
Is this a general limitation (this Chrome bug may be related) or did someone manage to get this working?
You should be able to use chrome.i18n.getMessage(...) from content scripts. Try restarting the whole browser.
Currently running: Version 24.0.1297.0 dev-m
I was hit by this as well. I'm not sure about the original poster's scenario, but my problems were caused by the bug (http://code.google.com/p/chromium/issues/detail?id=53628), which was referenced in a reply in Thilo's bug report. The bug is that the reloading of messages.json is unreliable when reloading your extension from the Extensions manager page. I originally started with an empty messages.json and then added to it as I tried to make use of chrome.i18n.getMessage. I too received no errors, but didn't get my message as expected. As illogical as it sounds and being new to development in general, I thought this might be one of those APIs that can't run from content scripts. Luckily that wasn't the case.
I would say a bug report needs to be created.
It doesn't say anywhere that it is supposed to work, but it doesn't throw an error that chrome.i18n.getMessage isn't accessible from a content scripts either, as all other API calls do. So that's a bug already.
It would be a nice feature to have. I think they started implementing it but either forget or left it for better times. A bug report would be a good reminder.
Meanwhile, you can get localization strings from a background page through messaging. Perhaps send one request at the beginning of a content script requesting an array of all strings you will need.
The strings only get loaded once, when activating the extension.
When the function returns "" this just means, that the index wasn't found.
One just needs to go to "Chrome Extensions" disable and re-enable the extension.
Then the function returns the localized strings - just as expected # Chromium 31.