I'm trying to create a chrome extension that interacts with youtube. It loads the content script, which is then supposed to inject the experiment.js script from web_accessible_resources. None of my code from experiment.js works.
I've followed this as reference: Insert code into the page context using a content script
manifest.json
{
"version": "1.0",
"manifest_version": 2,
"permissions": ["tabs", "https://*/*"],
"content_scripts": [{
"js": ["contentscript.js"],
"matches": [ "https://*.youtube.com/*", "http://*.youtube.com/*"]
}],
"web_accessible_resources": ["experiment.js"],
"browser_action": {
"default_icon": "icon.png"
}
}
contentscript.js
var s = document.createElement('script');
s.src = chrome.extension.getURL('experiment.js');
s.onload = function() {
this.parentNode.removeChild(this);
};
(document.head||document.documentElement).appendChild(s);
experiment.js
alert('loaded');
console.log('loaded');
EDIT: I just used another solution by including the code from experiment.js into contentscript.js in an array and joining each line. The process is referred to as "Method 2" in the reference post I added earlier.
Basically the problem was caused by content scripts limitations. For security reasons (I guess) content scripts can't use variables or functions defined by web pages or by other content scripts. It's called sandboxing.
When you add new script to the page by creating new <script> element you add this script to the page's sandbox. Therefore content script couldn't see the one added to the page, even if it is your extension's code.
Simplest solution is you quick fix. You just need to add the script to the manifest file or using programmatic injection.
Besides their limitations, content scripts can use shared DOM to communicate with the page. In this case you could add script to the page using <script> tag and communicate with content script using window.postMessage.
Related
I am trying to make a Chrome extension, which will monitor GMail and do something when user starts to write a message. After some study of examples and documentation I have figured out that I should do it with declarativeContent, which reacts on page change.
This is what I have done by now.
manifest.json:
{
"manifest_version": 2,
"name": "Gmail helper",
"version": "0.1",
"permissions": [ "declarativeContent" ],
"background": {
"scripts": ["background.js"],
"persistent": false
}
}
background.js:
chrome.runtime.onInstalled.addListener (function (details) {
chrome.declarativeContent.onPageChanged.removeRules (undefined, function () {
chrome.declarativeContent.onPageChanged.addRules ([{
conditions: [
new chrome.declarativeContent.PageStateMatcher({
pageUrl: { hostEquals: 'mail.google.com', schemes: ['https'] },
css: ["div"]
// css: ["div[aria-label='Message Body']"]
})
],
actions: [ new chrome.declarativeContent.RequestContentScript({js: ["content.js"]}) ]
}]);
});
});
content.js:
alert ("Test");
My plan was to declare a content script that would trigger on page changes in GMail. I have added a declarative rule, which has pageURL, css and actions defined. According to my understanding content.js should be executed when pageUrl and css content match. However, content.js is not executed.
What am I doing wrong?
Thanks.
Running a content script on a site requires permissions for the site, which isn't stated explicitly in the documentation of declarativeContent API, but is deduced by the lack of "This action can be used without host permissions" note, which is present in other actions. The purpose of declarativeContent API is to install extensions from the WebStore without any permission confirmation warning so naturally this API can't grant you access to mail.google.com unless you add it explicitly in your manifest:
"permissions": ["declarativeContent", "https://mail.google.com/"]
From description of your task it looks like you don't need declarativeContent.
You need to add a content script to page if GMail page is open and in content script you need to add a listener to message editor DOM element (or whatever another element you need to track).
Assuming that you know how to do the second, to add content script to GMail page you need to add the following into manifest:
"content_scripts": [
{
"matches": [
"https://mail.google.com/*"
],
"js": ["content.js"]
}
You also don't need background script and permissions in this case.
Note:
Although you don't need to specify permissions, your extension will require to ask them from the user.
During installation, Chrome will warn the user that your extension will have access to all user data on the page to make the user able to cancel installation if he or she does not agree.
In the document description, RequestContentScript is experimental, but it is not. In my test, Chrome version >= 60 is available, but allFrames does not seem to work.
Before using "RequestContentScript", you need to declare host permissions in "permissions".
https://developer.chrome.com/extensions/declarativeContent#type-RequestContentScript
I created a Firefox extension that works, for the most part. I am having a hard time importing jQuery. I have downloaded it locally. I am getting no errors. So, sometimes the extension will work and jQuery will load. Sometimes it won't. Other times I have to reload the page 5 or 6 times to get it to work.
I am not a JavaScript developer and this is my first time attempting an extension. I have Googled and tried a bunch of things with no luck.
Below is my manifest.json
"web_accessible_resources" : ["/jquery-3.2.1.min.js","/jquery.csv.min.js","/ui.js"],
"icons": {
"128": "icon_128px.png",
"48": "icon_48px.png"
},
"browser_action": {
"default_icon": "icon_48px.png"
},
"content_scripts": [
{
"matches": ["https://*****.com/*"],
"js": ["content.js"],
"run_at": "document_end"
}
],
"permissions":[
"activeTab"
],
"homepage_url": "https://*****.com"
}
content.js
function injectJs(link) {
var scr = document.createElement("script");
scr.type="text/javascript";
scr.src=link;
(document.head || document.body || document.documentElement).appendChild(scr);
}
injectJs(chrome.extension.getURL("/jquery-3.2.1.min.js"));
injectJs(chrome.extension.getURL("/jquery.csv.min.js"));
injectJs(chrome.extension.getURL("/ui.js"));
Normally, you would load jQuery by including it within the js key in your manifest.json content_scripts entry. For example:
"content_scripts": [
{
"matches": ["https://example.com/*"],
"js": ["jquery-3.2.1.min.js", "jquery.csv.min.js", "ui.js", "content.js"]
}
]
Scripts are loaded in the order listed. So, you need to list the libraries which depend on others after the ones they depend upon (e.g. "jquery-3.2.1.min.js" before "jquery.csv.min.js").
What you were doing
The way that you were doing it inserted the scripts into the page context using <script> tags. Such tags are loaded asynchronously. Thus, there was no guarantee that your scripts which depended on jQuery were actually loaded after jQuery. For what you are doing, you don't want to be loading the scripts into the page context, which is separate from the content script context where your content scripts normally run. If you want more information as to how you can do that successfully, you can see my answer to How to sequentially insert scripts into the page context using tags, which has fully functional code to insert multiple dependent libraries into the page context.
Use .tabs.executeScript() to dynamically load scripts when they are not used 100% of the time
However, if you are loading your content script into a large number of pages (e.g. matches being "<all_urls>", *://*/*, etc.), then you should use a manifest.json content_scripts entry to load only the bare minimum needed to show the initial portion of your user interface (i.e. just the static portion seen prior to the user interacting). Only once the user begins interacting with your user interface should you then send a message, using .runtime.sendMessage(), to your background script, received using .runtime.onMessage(), to instruct your background script to inject the rest of the files needed for your complete user interface. You background script would then use .tabs.executeScript() to load the additional scripts you need and, perhaps, tabs.insertCSS() to inject any additional CSS which you may need.
The point of doing the above is to minimize the impact your extension has on the user/browser during the time which the user is not actively using your extension, which is most of the time under most conditions.
A bit of a novice attempting to create my first Chrome extension here.
I've verified my JQuery and html code to be working by running it via opening it in a browser in a normal .html file that calls the .JS file.
However, the Chrome extension with the same code fails to run, seemingly due to CSP issues. (I am calling the ajax library in my html via https)
Refused to load the script
'http://www.reddit.com/r/abandonedporn/.json?jsonp=jQuery111307901595502626151_1438751974832&_=1438751974833'
because it violates the following Content Security Policy directive:
"script-src'self' https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js". send # library.js:5
I've modified my manifest.json file with every last thing that I thought necessary, including:
{"content_security_policy": "script-src 'self' https://ajax.googleapis.com/*; object-src 'self'"},
"permissions": [
"http://www.reddit.com/r/abandonedporn/.json?jsonp=?",
"http://www.reddit.com/*", "https://www.reddit.com/*",
"http://www.reddit.com/r/abandonedporn/",
"https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js/",
"http://www.reddit.com/r/abandonedporn/.json?jsonp=jQuery111309090442832093686_1438671168851&_=1438671168852",
"http://*/*", "https://*/*",
"http://reddit.com/json*",
"http://www.reddit.com/r/abandonedporn/*"],
and
"content_scripts": [
{
"matches": [
"https://www.google.com/*",
"http://www.reddit.com/abandonedporn/*"
],
"js": [
"src/inject/inject.js",
"src/content.js",
"src/override/get_pics.js",
"src/override/get_pics2.js",
"src/override/library.js"
]
}
]
}
Lastly, my actual Jquery script:
(Note that changing ".getJSON" to ".getScript" did not help.)
document.addEventListener('DOMContentLoaded', function () {
$.getJSON("http://www.reddit.com/r/abandonedporn/.json?jsonp=?", function(data) {
$.each(data.data.children, function(i,item){
$("<img/>").attr("src", item.data.url).appendTo("#images");
});
});
});
You can't load external (CDN) script files in chrome extension as per new CSP from google. You need to package all your resources, download jquery and access from your package. "jquery.min.js" instead CDN-URL/jquery.min.js. Here is in detail
I just started playing with Chrome extensions, trying to load JQuery from manifest file, and test if it's loaded. It is not loading, although I did specify it in content-scripts! Any idea why?
sample.js
if (typeof jQuery != 'undefined') {
alert("jQuery library is loaded!");
}
manifest.json
{
"manifest_version": 2,
"name": "__MSG_extName__",
"version": "0.1",
"default_locale": "en",
"background": { "scripts": ["sample.js"] },
"permissions": ["contextMenus"],
"minimum_chrome_version": "33",
"content_scripts": [{
"matches": ["<all_urls>"],
"css": ["style.css"],
"js": ["lib/jquery-1.11.0.js"]
}]
}
As noted in the comments to the question, you are doing things in two different contexts.
The "background" key defines what is loaded in a single invisible page where the "main" code resides.
"content_scripts" injects code into other pages instead, that can communicate with the background page via Chrome API.
A good overview is available here.
It's not immediately clear why <all_urls> match pattern does not inject the script into your background page, but fact is, it's not.
How to modify your example depends on what you try to achieve.
If you need sample.js to be executed on every page when it loads, you need to move it to the "content_scripts" key. Note that most of the Chrome API will be unavailable.
If you also need jQuery in your background page (doubtful, but possible), you can add it to the background.scripts list.
If you need some processing done that is not available to the content scripts, then you need to have two scripts, one background script that does the work and an injected content script that communicates with it via Messaging API.
To inject jQuery to every page you can do use this approach.
Add the following line to the manifest.json file. This specify that packaged resource (jQuery file inside extension) is expected to be usable in the context of a web page.
"web_accessible_resources": ["lib/jquery-1.11.0.js"]
Add the following code into new file located in your (file manifest.json) "content_scripts: [{ "js": new_file.js }]" to actually inject jQuery when onLoad event triggers:
The new_file.js file:
var s = document.createElement('script');
s.src = chrome.extension.getURL('lib/jquery-1.11.0.js');
(document.head||document.documentElement).appendChild(s);
s.onload = function() {
s.parentNode.removeChild(s);
};
I'm working on a chrome extension and I need to get the event when the tab is closed so I can fire of an post to a server. This is what I have atm.
chrome.tabs.onRemoved.addListener(function (tabId) {
alert(tabId);
});
But I can't get it to work. Anyone got any ideas?
Edit:
When I'm running it, it says
Uncaught TypeError: Cannot read property 'onRemoved' of undefined
Edit2: manifest.json
{
"name": "WebHistory Extension",
"version": "1.0",
"manifest_version": 2,
"description": "storing webhistory",
"content_scripts":[
{
"matches": ["http://*/*"],
"js": ["jquery-1.7.min.js","myscript.js"],
"run_at": "document_end"
}
],
"permissions" : ["tabs"]
}
You can't use chrome.tabs API in content scripts:
However, content scripts have some limitations. They cannot: Use
chrome.* APIs (except for parts of chrome.extension)
source
What you need to do is to establish communication between content script and background page. Background page has access to chrome.tabs API:
These limitations aren't as bad as they sound. Content scripts can
indirectly use the chrome.* APIs, get access to extension data, and
request extension actions by exchanging messages with their parent
extension.
source
Everything is in the first five paragraphs of content script documentation.