The chrome extension guide has a tutorial for the old analytics install: https://developer.chrome.com/extensions/tut_analytics.html
The instructions just say to link to the https version and update the manifest to allow loading scripts from that URL. So those should still apply to the new version. And in fact I can see the script loading from the server.
Once the script loads analytics does not properly initialize it self and never processes it's internal queue (ga.f) to send those events to the server. There is no error in the console. It's just quietly does nothing.
My guess is that the new Universal Analytics is just not set up to run in the the extension environment but the universal docs make no mention of that: https://developers.google.com/analytics/devguides/collection/analyticsjs/
does anyone know if it's even possible to add Universal Analytics to an extension yet and when that might be added?
There's an issue for that on Google code: The solution is to pass analytics your own protocol check function or simply null for no checking, in an official way.
This has to come after ga('create', ...) :
ga('set', 'checkProtocolTask', null); // Disable file protocol checking.
So you don't need to modify the original analytics.js script. Just include the standard tracking code snippet (dont' forget to add the "https:" prefix) and add "https://www.google-analytics.com" to your Content Security Policy.
A note to ayal gelles' solution:
It is not necessary to add chrome-extension://... to the Content Security Policy since it's already included in the 'self' statement. Also, instead of loading the script via hardcoded URL you should use chrome.runtime.getURL("path/to/analytics.js"). This way you don't need to know your extension's ID, Chrome will fill it in for you.
I wrote up a blog post on this - How to add Google’s Universal Analytics tracking to a Chrome extension
Here's the guts of it:
// Standard Google Universal Analytics code
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); // Note: https protocol here
ga('create', 'UA-XXXXX-YY', 'auto');
ga('set', 'checkProtocolTask', function(){});
ga('send', 'pageview', '/options.html');
There are 3 points I’d particularly like to highlight:
Specify “https” at the start of the script address to match with the listing in the manifest.json file
Override checkProtocolTask with an empty function
Send a virtual pageview by specifying the path – /options.html – otherwise Google Analytics will reject a URL in the format chrome-extension://gdocgfhmbfbbbmhnhmmejncjdcbjkhfc/options.html
I just encountered this and seem to have hacked my way through. This might break at some point or not be fully functional, but here goes:
Download the GA uglified+minified source code from here: https://www.google-analytics.com/analytics.js, put in your chrome extension folder, where it could be later loaded by the background page.
In it, find a function that looks something like this:
function Oa(){var a=M[B][E];if("http:"!=a&&"https:"!=a)throw"abort";}.
This is the "point of failure" since our "protocol" is "chrome-extension:" and not either of the two.
So.. change this function to be something like:
function Oa(){var a=M[B][E];if("chrome-extension:"!=a&&"http:"!=a&&"https:"!=a)throw"abort";}
add a "Content Security Policy" of this sort to your manifest file, make sure it points to YOUR LOCAL version of analytics.js you have just modified:
"content_security_policy": "script-src 'self' chrome-extension://EXTENSIONID/path/to/analytics.js; object-src 'self'",
Change the GA snippet to ALSO point to that same file, something like this:
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','chrome-extension://EXTENSIONID/path/to/analytics.js','ga');
hope this helps.
I managed to get Google Analytics up and running using Chrome Platform Analytics (CPA). The only confusing part is how to set up a property in the administration console of GA. I had to create a Mobile Application property, which is not too intuitive.
Also, I created an options page that lets users disable analytics if desired, to comply with the opt-out requirements.
I hope that helps!
Regarding new analytics.js (as opposite to old ga.js) this example works for me:
function setupGoogleAnalytics() {
if (!window.ga) {
(function(){
window.ga = function() {
(window.ga.q = window.ga.q || []).push(arguments);
}, window.ga.l = 1 * new Date();
var tag = 'script';
var a = document.createElement(tag);
var m = document.getElementsByTagName(tag)[0];
a.async = 1;
a.src = 'https://www.google-analytics.com/analytics.js';
m.parentNode.insertBefore(a, m);
})();
ga('create', 'UA-XXXXXXX-Y', 'auto');
ga('set', 'checkProtocolTask', null);
}
}
Please note that you need to add following content_security_policy snippet to the manifest.json:
{
...
"content_security_policy": "script-src 'self' https://www.google-analytics.com; object-src 'self'"
...
}
There's a way to use the Measurement Protocol to communicate with Google Analytics.
I have developed a script for that :
https://github.com/melalj/universal-ga-extension
Related
I found chrome.declarativeNetRequest only supports static rules, What I want is to call some custom methods before actions like redirect/request. I haven't found a solution so far. I'm not sure if I can still do this under the Manifest V3.
There are two usecases for my extension.
Before the redirect, I need to execute custom method.
chrome.webRequest.onBeforeRequest.addListener(
function(requestDetails) {
//
// I can get id from requestDetails.url,
// then do some custom business logic.
//
custom_function(requestDetails.url);
return {redirectUrl:"javascript:"};
},
{urls: [ "url_pattern?id=*" ]},
["blocking"]
);
Before some request, I want add/modify requestHeaders according to the user's browser.
chrome.webRequest.onBeforeSendHeaders.addListener(
function (details) {
details.requestHeaders.push({
"name": "User-Agent",
"value": navigator.userAgent + "version_1.0.0"
});
return {requestHeaders: details.requestHeaders};
},
{
urls: ["*://url_pattern"],
types: ["xmlhttprequest"]
},
["blocking", "requestHeaders"]
);
#wOxxOm Thank you very much for your patient answer !
I prefer to spinner.html. But I have another problem.
I can't set the regexSubstitution to the extension address,
I can use the extensionPath, but the corresponding capture groups doesn't work here.
"regexFilter": "google.com*"
The following are all incorrect:
can't use the corresponding capture groups.
"extensionPath": "/spinner.html?url=\\0"
can't use the extension's address.
"regexSubstitution": "spinner.html?url=\\0"
Is my configuration incorrect?
Adding/deleting headers can only accept static values and it's shown in the official example.
Conditionally adding/deleting/modifying headers based on response headers is tracked in https://crbug.com/1141166.
Nontrivial transformations that exceed the functionality of the actions listed in the documentation naturally cannot be re-implemented.
When https://crbug.com/1262147 is fixed we will be able to define a declarativeNetRequest rule to redirect to a page inside your extension via regexSubstitution or extensionPath and append the original URL as a parameter. This page will act as an interstitial, it will display some kind of UI or a simple progress spinner, process the URL parameters, and redirect the current tab to another URL.
In many cases this approach would introduce flicker and unnecessary visual fuss while the interstitial is displayed shortly, thus frustrating users who will likely abandon using such extensions altogether. Chromium team members who work on extensions seem to think this obscene workaround is acceptable so it's likely they'll roll with it, see also https://crbug.com/1013582.
Use the observational webRequest (without 'blocking' parameter) and chrome.tabs.update to redirect the tab. The downside is that the original request will be sent to the remote server. And this approach obviously won't work for iframes, to redirect those you'll have to inject/declare a content script, to which your webRequest listener would send a message with a frameId parameter.
Keep a visible tab with an html page from your extension, and use the blocking chrome.webRequest inside its scripts. It's a terrible UX, of course, even though endorsed by the Chromium's extensions team, with many extensions using this kludge the user's browsers will have to keep a lot of such tabs open.
P.S. The blocking webRequest will be still available for force-installed extensions via policies, but it's not something most users would be willing to use.
I'm working on a chrome extension which must get in touch with some apis.
The first version had this permission in the manifest :
"permissions": [ "API_1" ],
And I could contact this API:
var xhr = new XMLHttpRequest();
xhr.open('GET', "API_1" + someArguments, true);
xhr.onreadystatechange = function()
{
// ...
}
This version is already published, but now I need my extension to contact another API, so I'm using the same code with the new API:
var xhr = new XMLHttpRequest();
xhr.open('GET', "API_2" + someArguments, true);
xhr.onreadystatechange = function()
{
// ...
}
In this new version I don't have any warning or error whatever there is no permission for "API_2". If I add permission for API_2, installed extensions will be disabled on update. So my question is : Are permissions for API_1 and API_2 really mandatory?
If that's all you're using the API host permission for, it depends on exactly one thing: CORS policy of the remote server.
When making XHR requests, if the request is cross-domain (which, from an extension, always except for content scripts on that same domain) - Chrome will examine CORS headers in server's reply.
By default, if the server does not indicate anything, cross-domain requests are not allowed by the web security model. This is typical if you're requesting something that was never intended to be a public API. Listing the API match pattern in permissions overrides this.
However, for public API it is typical to include a permissive CORS header (after all, other web applications that may use this API cannot override the security model, only extensions can). In that case, the permission is not necessary.
Its hard to know without listing the API's, but google's documentation provides a simple way to check how new permissions will affect warnings:
If you'd like to see exactly which warnings your users will get, package your extension into a .crx file, and install it.
To see the warnings users will get when your extension is autoupdated, you can go to a little more trouble and set up an autoupdate server. To do this, first create an update manifest and point to it from your extension, using the "update_url" key (see Autoupdating). Next, package the extension into a new .crx file, and install the app from this .crx file. Now, change the extension's manifest to contain the new permissions, and repackage the extension. Finally, update the extension (and all other extensions that have outstanding updates) by clicking the chrome://extensions page's Update extensions now button.
Basically, create two test extensions, one being your original and another being your updated. Follow this process to go through a simulated update, and you will see what warnings you get, if any.
If you leave out API_2 permissions in the update and everything is fine, then its permissions are not mandatory to include in the manifest.
Source
Content Script can be injected programatically or permanently by declaring in Extension manifest file. Programatic injection require host permission, which is generally grant by browser or page action.
In my use case, I want to inject gmail, outlook.com and yahoo mail web site without user action. I can do by declaring all of them manifest, but by doing so require all data access to those account. Some use may want to grant only outlook.com, but not gmail. Programatic injection does not work because I need to know when to inject. Using tabs permission is also require another permission.
Is there any good way to optionally inject web site?
You cannot run code on a site without the appropriate permissions. Fortunately, you can add the host permissions to optional_permissions in the manifest file to declare them optional and still allow the extension to use them.
In response to a user gesture, you can use chrome.permission.request to request additional permissions. This API can only be used in extension pages (background page, popup page, options page, ...). As of Chrome 36.0.1957.0, the required user gesture also carries over from content scripts, so if you want to, you could add a click event listener from a content script and use chrome.runtime.sendMessage to send the request to the background page, which in turn calls chrome.permissions.request.
Optional code execution in tabs
After obtaining the host permissions (optional or mandatory), you have to somehow inject the content script (or CSS style) in the matching pages. There are a few options, in order of my preference:
Use the chrome.declarativeContent.RequestContentScript action to insert a content script in the page. Read the documentation if you want to learn how to use this API.
Use the webNavigation API (e.g. chrome.webNavigation.onCommitted) to detect when the user has navigated to the page, then use chrome.tabs.executeScript to insert the content script in the tab (or chrome.tabs.insertCSS to insert styles).
Use the tabs API (chrome.tabs.onUpdated) to detect that a page might have changed, and insert a content script in the page using chrome.tabs.executeScript.
I strongly recommend option 1, because it was specifically designed for this use case. Note: This API was added in Chrome 38, but only worked with optional permissions since Chrome 39. Despite the "WARNING: This action is still experimental and is not supported on stable builds of Chrome." in the documentation, the API is actually supported on stable. Initially the idea was to wait for a review before publishing the API on stable, but that review never came and so now this API has been working fine for almost two years.
The second and third options are similar. The difference between the two is that using the webNavigation API adds an additional permission warning ("Read your browsing history"). For this warning, you get an API that can efficiently filter the navigations, so the number of chrome.tabs.executeScript calls can be minimized.
If you don't want to put this extra permission warning in your permission dialog, then you could blindly try to inject on every tab. If your extension has the permission, then the injection will succeed. Otherwise, it fails. This doesn't sound very efficient, and it is not... ...on the bright side, this method does not require any additional permissions.
By using either of the latter two methods, your content script must be designed in such a way that it can handle multiple insertions (e.g. with a guard). Inserting in frames is also supported (allFrames:true), but only if your extension is allowed to access the tab's URL (or the frame's URL if frameId is set).
I advise against using declarativeContent APIs because they're deprecated and buggy with CSS, as described by the last comment on https://bugs.chromium.org/p/chromium/issues/detail?id=708115.
Use the new content script registration APIs instead. Here's what you need, in two parts:
Programmatic script injection
There's a new contentScripts.register() API which can programmatically register content scripts and they'll be loaded exactly like content_scripts defined in the manifest:
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. If you're using Manifest v3, there's the native chrome.scripting.registerContentScript which does the same thing but slightly differently.
Acquiring new permissions
By using chrome.permissions.request you can add new domains on which you can inject content scripts. An example would be:
// In a content script or options page
document.querySelector('button').addEventListener('click', () => {
chrome.permissions.request({
origins: ['https://your-dynamic-domain.example.com/*']
}, granted => {
if (granted) {
/* Use contentScripts.register */
}
});
});
And you'll have to add optional_permissions in your manifest.json to allow new origins to be requested:
{
"optional_permissions": [
"*://*/*"
]
}
In Manifest v3 this property was renamed to optional_host_permissions.
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 automatically register your scripts in the next browser launches and allow the user the remove the new permissions and scripts.
Since the existing answer is now a few years old, optional injection is now much easier and is described here. It says that to inject a new file conditionally, you can use the following code:
// The lines I have commented are in the documentation, but the uncommented
// lines are the important part
//chrome.runtime.onMessage.addListener((message, callback) => {
// if (message == “runContentScript”){
chrome.tabs.executeScript({
file: 'contentScript.js'
});
// }
//});
You will need the Active Tab Permission to do this.
Ok, so just the other day I learned that you can inspect the devtools if it is in its own window(explained here). I also learned that you can style the devtools with your own css by editing the Custom.css file in your profile on your computer(more on that here).
What I want to do is not only add css, but also javascript, via a chrome extension. I am very aware of devtools pages, but those do not do what I want. Pretty much I want to get a content script to run on the devtools inspector itself. I found one extension that does exactly this, but for the life of me I have not been able to replicate it(even when copy-pasting the code!!). The extension is the "Discover DevTools Companion extension" from Code School(on the webstore). They even explain how it works, but I still have had no luck. That was the only extension I have found that does what I want. So I guess what I'm really asking is if its just me that cannot get it to work or if others that try are having trouble also.
Usually, you cannot create a Chrome extension which injects code in a devtools page.
The "Discover DevTools Companion" extension from now on, referred to as DDC is allowed to do this, because this extension is whitelisted in the source code of Chromium: (this is no longer the case)
// Whitelist "Discover DevTools Companion" extension from Google that
// needs the ability to script DevTools pages. Companion will assist
// online courses and will be needed while the online educational programs
// are in place.
scripting_whitelist_.push_back("angkfkebojeancgemegoedelbnjgcgme");
If you want to publish an extension in the Chrome Web Store with these capabilities, give up.
If you want to create such an extension for personal / internal use, read further.
Method 1: Impersonate the DDC a whitelisted extension
The easiest way to create an extension with such permissions is to create an extension with the extension ID of a whitelisted extension (e.g. ChromeVox). This is achieved by copying the "key" key of its manifest file to your extension's manifest (see also: How to get the key?). This is a minimal example:
manifest.json
{
// WARNING: Do NOT load this extension if you use ChromeVox!
// WARNING: Do NOT load this extension if you use ChromeVox!
// WARNING: This is a REALLY BIG HAMMER.
"content_scripts": [{
"js": [ "run_as_devtools.js" ],
"matches": [ "<all_urls>" ]
}],
// This is the key for kgejglhpjiefppelpmljglcjbhoiplfn (ChromeVox)
"key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDEGBi/oD7Yl/Y16w3+gee/95/EUpRZ2U6c+8orV5ei+3CRsBsoXI/DPGBauZ3rWQ47aQnfoG00sXigFdJA2NhNK9OgmRA2evnsRRbjYm2BG1twpaLsgQPPus3PyczbDCvhFu8k24wzFyEtxLrfxAGBseBPb9QrCz7B4k2QgxD/CwIDAQAB",
"manifest_version": 2,
"name": "Elevated Devtools extension",
"version": "1.0"
}
run_as_devtools.js
if (location.protocol === 'chrome-devtools:') (function() {
'use strict';
// Whatever you want to do with the devtools.
})();
Note: This method is truly a hack. Since the extension shares the same ID as ChromeVox, both extensions cannot co-exist. And if Chrome decides to remove the whitelisted extension, then your permissions will evaporate.
Instead of filtering via the content script, you can also use the include_globs key to restrict the content script to devtools only.
Method 2: Modify resources.pak
I suggest to go with method 1 if possible. When method 1 fails (e.g. because the extension is no longer whitelisted), use the next method.
Get paktools.py, unpack.py and pack.py from DennisKehrig/patch_devtools (on Github).
Locate your Chrome directory containing resources.pak.
Run python2 unpack.py resources.pak, which creates a directory resources containing all files (all file names are numbers).
Locate the file containing a script which runs in the context of the developer tools. Add your desired code there.
Remove resources.pak
Run python2 pack.py resources to create the new resources.pak file.
Note: resources.pak may be replaced when Chrome is updated, so I suggest to create a script which automates my described algorithm. That shouldn't be too difficult.
If you're interested, you can look up the .pak file format in ui/base/resource/data_pack_literal.cc (description in human language).
For those googlers who end up at this page (as I did) looking for something else, this page answers the question of manipulating DevTools ITSELF - not adding jQuery to DevTools, or injecting javaScript onto a web page. For those, see here instead:
Inject jQuery into DevTools (Chrome or Firefox):
Firefox DevTools: Automatically injecting jQuery
Inject your own javascript/css onto any web page:
How to edit a website's background colors
I suspect this is a total newbie question, but I seem to be missing the basics here. I am NOT new to coding and have a lifetime of experience (27 years) with various languages, but the plugin process is eluding me.
I have developed custom bookmarking system in php & js, it works great and I've been using it for months as I develop it.
I simply want to get the url of the page in the active tab and pass it to my php handler. I want my web site script return the html form into the popup. I can think of a thousand ways that "should" work.
ALL the code examples I am finding seem to over-complicate what should be a simple task.
In short I just want:
<script type="text/javascript">
<!--
var loadurl = "http://my.site.com?theUrl=" + window.location;
location.href = loadurl;
//-->
</script>
And have that page show in the popup. So far I'm at a loss. Even tried ajax calls etc.
Can somebody clue me in on how to achieve this simple task? Maybe I can get started writing extensions with the info.
For the record, most of the examples I have found are deprecated under manifest 2.0
Manifest 2.0 introduces a new feature contentSecurityPolicy. All external resources are blocked by default. For the best practice, you should include all needed asset files in the extension. The communication between your extension and your service (php side) is only data using XHR2.
So, In order to make bookmark extension work, I guess you need to something like this:
Add your service's domain to permissions array
{
...
permissions: ['*://my.site.com/*', 'tabs']
}
Move all javascript from popup.html to popup.js. In popup.js, You create a ajax request to your bookmarking service. More document here
function addBookmark(url) {
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://my.site.com/new_bookmark.php?url=" + encodeURIComponent(url), true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
var resp = xhr.responseText;
// handle service result here
}
}
xhr.send();
}
chrome.tabs.getSelected(null,function(tab) {
addBookmark(tab.url);
});