How to show users information about extension updates, redirect to a url after update or other methods - google-chrome-extension

In the past, I've seen other extensions be able to redirect you to an info page after the update is completed. I will soon be releasing an update and would like to inform users as soon as the update is completed. Not after they click the icon.
I came across update_info_url which can be placed in the manifest.json however it is unclear if it has been depreciated or not
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Updates#Update_objects
All other google results date back to 2009.
I'm just trying to figure out how to go about this, i believe I definitely have seen other extensions achieve this.

You can use something like this in the background script:
chrome.runtime.onInstalled.addListener(details => {
if (details.reason == 'update'){
chrome.tabs.create({url:'https://example.com/extensionupdated.html'});
}
});

Related

Can I use Chrome declarativeNetRequest to completely replace Chrome webRequest?

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.

How to fill login prompt with Webdriver IO?

I'm working on a CLI with OCLIF. In one of the commands, I need to simulate a couple of clicks on a web page (using the WebdriverIO framework for that). Before you're able to reach the desired page, there is a redirect to a page with a login prompt. When I use WebdriverIO methods related to alerts such as browser.getAlertText(), browser.sendAlertText() or browser.acceptAlert, I always get the error no such alert.
As an alternative, I tried to get the URL when I am on the page that shows the login prompt. With the URL, I wanted to do something like browser.url(https://<username>:<password>#<url>) to circumvent the prompt. However, browser.url() returns chrome-error://chromewebdata/ as URL when I'm on that page. I guess because the focus is on the prompt and that doesn't have an URL. I also don't know the URL before I land on that page. When being redirected, a query string parameter containing a token is added to the URL that I need.
A screenshot of the prompt:
Is it possible to handle this scenario with WebdriverIO? And if so, how?
You are on the right track, probably there are some fine-tunings that you need to address to get it working.
First off, regarding the chrome-error://chromewebdata errors, quoting Chrome DOCs:
If you see errors with a location like chrome-error://chromewebdata/
in the error stack, these errors are not from the extension or from
your app - they are usually a sign that Chrome was not able to load
your app.
When you see these errors, first check whether Chrome was able to load
your app. Does Chrome say "This site can't be reached" or something
similar? You must start your own server to run your app. Double-check
that your server is running, and that the url and port are configured
correctly.
A lot of words that sum up to: Chrome couldn't load the URL you used inside the browser.url() command.
I tried myself on The Internet - Basic Auth page. It worked like a charm.
URL without basic auth credentials:
URL WITH basic auth credentials:
Code used:
it('Bypass HTTP basic auth', () => {
browser.url('https://admin:admin#the-internet.herokuapp.com/basic_auth');
browser.waitForReadyState('complete');
const banner = $('div.example p').getText().trim();
expect(banner).to.equal('Congratulations! You must have the proper credentials.');
});
What I'd do is manually go through each step, trying to emulate the same flow in the script you're using. From history I can tell you, I dealt with some HTTP web-apps that required a refresh after issuing the basic auth browser.url() call.
Another way to tackle this is to make use of some custom browser profiles (Firefox | Chrome) . I know I wrote a tutorial on it somewhere on SO, but I'm too lazy to find it. I reference a similar post here.
Short story, manually complete the basic auth flow (logging in with credentials) in an incognito window (as to isolate the configurations). Open chrome://version/ in another tab of that session and store the contents of the Profile Path. That folder in going to keep all your sessions & preserve cookies and other browser data.
Lastly, in your currentCapabilities, update the browser-specific options to start the sessions with a custom profile, via the '--user-data-dir=/path/to/your/custom/profile. It should look something like this:
'goog:chromeOptions': {
args: [
'--user-data-dir=/Users/iamdanchiv/Desktop/scoped_dir18256_17319',
],
}
Good luck!

Google analytics stores username and password as a part of url

Issue Context:
I am using meteor js for a mobile app.
I have hooked it up with google analytics calls and basically I am using two type of calls:
Screen views
Events
Screen views are just fine, but I'm facing an issue with the events.
When I go to Behavior -> Events -> Screens, in the google analytics dashboard, I can see the URL of every page that has triggered an event under the Screen Name column. My problem is that the page URLs for my login page look something like this:
meteor.local/login?username=*******&password=+++++++&rememberMe=on
Where ******* is an actual username and +++++++ is the corresponding password!
Reason:
Since I have to share this analytics account with multiple people, I do not want this information to be available over here.
Clues:
CLUE 1:
I used to do GET http calls, but I have changed them all to POST and it still has not fixed the issue as I expected it not to pass plain parameters through URL anymore.
CLUE 2:
I've noticed that the default google analytics js framework is working with http and not https. I was wondering if it is calling the analytics server with a GET as well. If so, is there anyway to change that?
CLUE 3:
Here is how I am initiating the GA instance:
(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', '//www.google-analytics.com/analytics.js', 'ga');
ga('create', googleKey, 'auto');
CLUE 4:
I have also noticed that these URLs are getting captured very occasionally. E.g. in the pas 12,500 unique events (about 30,000 total events) it has captured just 9 URLs with the username and password. The remaining 12,491 events have
meteor.local/login
OR
meteor.local/--
OR
localhost/--
as the Screen Name.
CLUE 5:
I have also put 4 "search and replace" global filters on the analytics account to search for this string
meteor.local/.*
and replace it with this one
meteor.local/concealedURI
This does not seem to be working either.
I have added this filter on 4 different fields (Since I still really don't know where the URLs are coming from):
Host Name
Page Title
Referral
Request URI
CLUE 6:
This is how I am calling the GA instance to send the event:
ga('send', 'event', 'button', 'click', eventName);
Okay. So, I had to run a lot of experiments and try out different things to solve this issue.
After trying all the things that I have described in the question, I finally found a way to address this problem.
The main cause of this problem was that I was using a google analytics account set to track an App, to capture the data from an app that was built with meteor js (which basically utilizes cordova).
Using meteor means that my app's screens are actually web pages rendered as a mobile app. It seems like meteor uses URLs to navigate through these screens.
On the other hand, google analytics looks at (and captures) the screen name of an app's page, when an event is triggered from that page. In native apps this screen name will be something similar to "About us", "Contact Us", "Home", etc.
Now since a meteor app is not the same, the screen name returned by meteor is actually the URL of the page that has triggered the event.
This does not have anything to do with the http calls (Whether or not they are GET or POST), because it is the local URL used by meteor for navigating that is being passed down to google analytics and not any http calls.
Solutions
1.
If I had the google analytics account set as a web page tracker, I could have access to "Exclude URL Query Parameters" field and I could potentially exclude username and password as was suggested by #Mike and #PhilipPryde in the comments.
However, I needed to use google analytics set as an app tracker. So, this did not work for me.
Failed
2.
I did put a filter on the whole view in the google analytics and searched for meteor.local/.* and replaced that with hiddenURL. The filters on
Host Name
Page Title
Referral
Request URI
did not work.
But when I put the same filter on
Screen Label
field, it worked.
However, this only looked at the screen names returned by screen view hits and not the event. Thus, this did not actually solve my issue either.
Failed
Finally, I had to do this:
There is a method call on GA instance that lets you set different options up. I ended up using this:
ga('set', 'screenName', 'hiddenURL');
This changed the screen name to "hiddenURL". So, I used this before every event and it worked for me.
My code for sending events to google analytics looked like this:
ga('set', 'screenName', 'hiddenURL');
ga('send', 'event', 'button', 'click', eventName);
PS:
This changes the screen name that was showing up in real-time reports of google analytics to "hiddenURL", whenever someone triggered an event. But, it changes back to a screen name as soon as they go to another page. So, it would not also mess with any of your screen view data either, since it is not being captured as a screen view.
Of course that is because, I pass the screen name to my GA instance every time I send a screen view. So it looks like this:
sendScreenViewToGA = function (screenName) {
ga('send', 'screenview', {
'appName': 'Something',
'screenName': screenName,
'appVersion': x.x
});
}
If I had used the screen name, that is being set on the environment tight now, I would have ended up with all my screen names in analytics set to "hiddenURL".
I really hope this post will help others with same issues and save them some time.

How to know if a page loaded via iframe is within sandbox? [duplicate]

This question already has answers here:
Detect if JavaScript is Executing In a Sandboxed Iframe?
(2 answers)
Closed 7 years ago.
I'm trying to detect if a page is loaded via a sandboxed iframe. Is this possible?
For example,we provide custom embeddable widgets and some people think they are being smart by sandboxing them in their iframe, but this breaks certain things.. such as window.top.location
Obviously, they could enable the features we need, but ideally, I should be able to just do something like:
"sandbox" in window.top
I have also tried doing
try {
// do something that would not work if within sandbox
} catch(e) {
}
But this doesn't work because it's a browser security error, and not related to javascript.
JSFiddle actually sandbox their iframes to prevent window.top.location navigation, so this would be a good example to show you.
If you look at this example here:
http://jsfiddle.net/mwsb8geL/show/
You can see the error when you press the Instant Book Online button.
A project sandblaster can help you detect if you running being sandboxed.
Inside the iframe where you are testing if it is sandbox, open up your script tag and paste the contents of https://raw.githubusercontent.com/JamesMGreene/sandblaster/master/dist/sandblaster.js. This is due to the security issue.
After this, its as simple as the following.
var result = sandblaster.detect();
if(result.sandboxed === true) {
//sandboxed
}
Here is a demo I made for another answer but shows that the solution works.

Using getUSerMedia AND webkitSpeechRecognition allow access x 2

I am creating a site that uses two types of audio input... getUserMedia and webkitSpeechRecognition. Both functions are working fine but Chrome is popping up it's access security pop-up twice - which makes sense. Does anyone know how to have one access permission handle both functions?
Cheers SO!
navigator.getUserMedia({audio:true}, gotStream, function(e) {
alert('Error getting audio');
console.log(e);
});
and...
var recognition = new webkitSpeechRecognition();
The only way to avoid asking for permissions multiple times is to make sure that your web page is using the https url. Then Chrome will remember your permissions and not show the pop-up again.
On Chrome 36 (and possibly earlier ones) I'm getting just one popup bar for both.

Resources