Setting the content_script matches at runtime - javascript

I'm developing a chrome extension that requires a content_script to add custom controls to a media player. However, I want to allow users to set the domain of the player via an options control panel (for their personal media servers).
It seems like the domain for content scripts has to be set statically in the manifest.json file in the chrome extension. Is there a way to set that programmatically? To achieve something like this:
"content_scripts": [
{
"matches": ["<variable_from_config>"],
"js": ["player.js"],
"run_at": "document_start"
}
]

You can use the new contentScripts.register() API, which does exactly what you want: programmatically registers content scripts.
browser.contentScripts.register({
matches: ['https://your-dynamic-domain.example.com/*'],
js: [{file: 'content.js'}]
});
This API is only available in Firefox but there's a Chrome polyfill you can use.
For that to work, you'll still need permission to the domain you want to register the script on, so you can use optional_permissions in the manifest and chrome.permissions.request to let the user add new domains on demand.
I also wrote some tools to further simplify this for you and for the end user, such as
webext-domain-permission-toggle and webext-dynamic-content-scripts. They will also register your scripts in the next browser launches and allow the user the remove the new permissions and scripts.

Related

Chrome Extension - Alert on Tab Switch [duplicate]

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);
});

Chrome extension with declarativeContent.RequestContentScript

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

Can I determine when to install webextensions firefox addon to a page?

I wrote a firefox addon use webextensions tech. By default firefox load the addon when the page loaded over. But some pages hava slowly loaded js,like some ads or statistics code, so the addon will not load if I stop the unnecessary page load. Or wait a long time before the addon loaded.
So can I setup the addon loaded time? (example: Before the page loaded)
Check out the run_at option in the content_scripts' manifest options. For example, you may use the following options:
content_scripts": [
{
"js": ["my-script.js"],
"matches": ["https://example.org/"],
"match_about_blank": true,
"run_at": "document_start"
}
]

Should I use location.href or chrome.tabs.query with message sending to get the tab URL?

I am writing a Chrome extension which recognizes certain URL pattern and perform further DOM manipulation. The content script has to get the current URL and matches with the predefined list of URL patterns.
There are two ways I could think of achieving the goal:
The first one would be using location.href
manifest.json
...
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"],
"run_at": "document_end"
}
],
...
content.js
console.log(location.href);
This method works fine. However, across other similar questions on StackOverflow, they usually suggests using chrome.tabs and message sending from background script to content script as follow:
manifest.json
...
"background" : {
"scripts": ["background.js"]
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"],
"run_at": "document_end"
}
],
...
background.js
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
chrome.tabs.get(tab.id, function(tabInfo) {
chrome.tabs.sendMessage(tab.id, {
url: tabInfo.url
}, function(response) {
});
})
});
content.js
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.url) {
console.log(request.url)
sendResponse(true);
}
return true;
});
Both methods can have the URL correctly. And for the background script, it requires extra memory to keep the background script running in background, compared with content script which is only injected when loading the page.
On the other hand, using background script has benefit as the background script is said to be privileged, it can execute privileged Chrome APIs like the chrome.tabs API.
So as far as I am not using any privileged APIs, should I use the location.href or is there any particular reason most developers suggest using the chrome.tabs and message sending?
Advantages of a background page script:
Complex checks using additional information from the Tab object (N.B. add "tabs" permissions to access url, title, favIconUrl), whereas manifest.json-based injection is limited to URL wildcards/globs.
chrome.declarativeContent API with RequestContentScript action is an example of advanced filtering, it injects the content script(s) based on URL checks and DOM content (only simple selectors as noted in the documentation).
chrome.tabs.executeScript inside chrome.tabs.onUpdated or chrome.webNavigation or chrome.browserAction.onClicked listener to inject the content script(s) only when needed.
Changing the toolbar icon or the browser context menu to reflect change of state; content scripts can't do that, the background page script can.
Accessing privileged API like most of chrome.*, extension's internal storage such as IndexedDB, WebSQL, HTML5 FileSystem, localStorage (the latter is not a good choice though as it's not available in a content script directly, moreover it's synchronous and thus blocks execution).
In all the above cases it makes sense to pass data to content script using messages if that data was used while checking the conditions or it's only available in the background script. Otherwise chrome.storage API inside the content script is as good or could be even better readability-wise.
"Backgroundless" content script is better when all these conditions are met:
URLs to process can be set entirely with wildcards/globs or it really really must be <all_urls>
all required parameters are accessible via chrome.storage API or no parameters needed
no privileged chrome.* APIs are used as those aren't available in content scripts, in other words when the background page is not actually needed.
As for memory consumption: either method may be better or worse depending on how it's used.
If the content script is injected on all pages then each instance will consume memory (some people open 100 tabs so beware!). The worst case case is obviously when both persistent background page and content script on all URLs are used. Non-persistent event page might help but in a limited fashion because chrome.tabs.onUpdated is likely to run pretty frequently forcing the event page to reload (which also takes some time).

Options-enabled content-script Chrome extension without background page?

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...

Categories

Resources