Recaptcha V3 assets cause Pagespeed issues - how to defer - css

We're currently using Google Recaptcha V3 across the public-facing portions of our site - while doing Pagespeed Insights performance testing (Mobile), Google themselves is reporting unused/undeferred CSS as a problem on their own Recaptcha css file:
Full resource address is:
https://www.gstatic.com/recaptcha/releases/[...]/styles__ltr.css (so it is clearly coming from a subsequent Google Recaptcha script request)
We are including the original Google recaptcha script with the 'defer' attribute set - not sure what else we can do to cause this css to be deferred such that Pagespeed does not complain about it. Can't find any documentation on the Google Recaptcha site itself to help with this issue.
Does anyone know how to defer this CSS to improve page load time? Not sure if this is somehow a Mobile specific issue, as Pagespeed doesn't report it at all on Desktop.

Firstly, bear in mind that 'remove unused CSS' is more of a guidance point (provided it isn't render blocking), it is indicating that it is wasted bytes (which it actually isn't if recaptcha triggers, as it then needs that CSS to render the image 'are you human check' etc.)
Although I can't give you an ideal answer as it is code you have no control over, I can give you two ways to test it's impact / whether it is actually a problem and a 'hack' to get around the load order.
Test using applied throttling
Simulated throttling can cause unexpect behaviour sometimes, which is what the Page Speed Insights website uses.
If you use the browser audit (which uses the same engine - Lighthouse) to run the tests you have an option to change the throttling from simulated to applied.
Although your score will change (applied throttling is less forgiving than simulated throttling), you get a much more realistic order of events as the latency and slowdown is 'real' vs making best guesses based on loading the page at full speed and applying formula's to guess load times.
Open Dev Tools in Chrome (F12) -> Audits -> Throttling -> set to Applied Slow 4G, 4x CPU Slowdown. -> Run Audits.
See if the problem persists when using this way of assessing page speed.
If it does, a workaround / test for real world performance is as follows:-
Force the script to load after an amount of time (the hacky way!)
This is not an ideal solution but it is good for testing and as a last resort if it does actually slow down your website key load times.
Insert the script dynamically after 5 seconds.
(please note the below code is untested and is likely to not work, it is for illustration only to point you in the right direction. It is highly probable that you don't need the script.onload section and can include that normally)
setTimeout(function(){
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.onload = function() {
grecaptcha.ready(function() {
grecaptcha.execute('_reCAPTCHA_site_key_', {action: 'homepage'}).then(function(token) {
...
});
});
}
script.src = "https://www.google.com/recaptcha/api.js?render=_reCAPTCHA_site_key";
head.appendChild(script);
}, 5000);

We can use IntersectionObserver to defer Recaptcha script.
var io = new IntersectionObserver(
entries => {
console.log(entries[0]);
if (entries[0].isIntersecting) {
var recaptchaScript = document.createElement('script');
recaptchaScript.src = 'https://www.google.com/recaptcha/api.js?hl=en';
recaptchaScript.defer = true;
document.body.appendChild(recaptchaScript);
}
},
{
root: document.querySelector('.page-wrapper'),
rootMargin: "0px",
threshold: 1.0,
}
);
io.observe(initForm);

We can load the reCaptcha v3 script once after an initial scroll on load:
var fired = false;
window.addEventListener('scroll', function () {
let scroll = window.scrollY;
if (scroll > 0 && fired === false) {
var recaptchaScript = document.createElement('script');
recaptchaScript.src = 'https://www.google.com/recaptcha/api.js?render=Your_SITE_KEY';
recaptchaScript.defer = true;
document.body.appendChild(recaptchaScript);
fired = true;
// console.log('On scroll fired');
}
}, true);
Code Example:
https://codepen.io/iamrobert/pen/NWazNpd
Page Speed Insight Score

Related

Meteor: send message to user at hot code push

How can I let the user know when they are getting a hot code push?
At the moment the screen will go blank during the push, and the user will feel it's rather weird. I want to reassure them the app is updating.
Is there a hook or something which I can use?
Here's the shortest solution I've found so far that doesn't require external packages:
var ALERT_DELAY = 3000;
var needToShowAlert = true;
Reload._onMigrate(function (retry) {
if (needToShowAlert) {
console.log('going to reload in 3 seconds...');
needToShowAlert = false;
_.delay(retry, ALERT_DELAY);
return [false];
} else {
return [true];
}
});
You can just copy that into the client code of your app and change two things:
Replace the console.log with an alert modal or something informing the user that the screen is about to reload.
Replace ALERT_DELAY with some number of milliseconds that you think are appropriate for the user to read the modal from (1).
Other notes
I'd recommend watching this video on Evented Mind, which explains what's going on in a little more detail.
You can also read the comments in the reload source for further enlightenment.
I can image more complex reload logic, especially around deciding when to allow a reload. Also see this pacakge for one possible implementation.
You could send something on Meteor.startup() in your client-side code. I personally use Bert to toast messages.

Sometimes Initialization of Google Earth Plugin fails in IE10

This is my code for the initialization of google earth plugin.
Sometimes Initialization of Google Earth Plugin fails in IE10(I have it in compatability mode) IE7 Standards. This error happens only in IE and no other browser.
90% of the time createInstance() method creates the google earth plugin instance and control goes to mygeeEarthPluginInitCb() method but few times mostly after restarting the machine or after few hours of inactivity if I load the page createInstance fails and control goes to geeEarthPluginFailureCb() method.
This is causing an error page, a very intermittent one.
function geeInit() {
alert("google.earth.createInstance : Start");
google.earth.createInstance(geeDivIds.map, mygeeEarthPluginInitCb,
geeEarthPluginFailureCb, earthArgs);
alert("google.earth.createInstance : End");
}
function mygeeEarthPluginInitCb(object) {
alert("Success mygeeEarthPluginInitCb: Inside");
geeEarthPluginInitCb(object);
gex = new GEarthExtensions(ge);
createSearchResultsMarkers(null, 'results');
var lookAt = ge.createLookAt('');
lookAt.setLongitude(Number('-73.784190'));
lookAt.setLatitude(Number('42.643446'));
lookAt.setRange(25000.00);
ge.getView().setAbstractView(lookAt);
initRadSearchValsOnLoad();
}
function geeEarthPluginFailureCb(message) {
alert("Failure geeEarthPluginFailureCb: Inside" + message);
if (google.earth.isInstalled()) {
} else {
var result = confirm('Google Earth Plugin is not'
+ ' installed.Please download and install it.');
if (result == true) {
window.location.href = 'install.html';
}
}
}
Remove all the alert lines, e.g.
alert("google.earth.createInstance : Start");
and
alert("google.earth.createInstance : End");
alert is a special method that blocks execution and user interaction - it could well be that it is blocking the initialisation of the plugin. This is something I have seen before.
Perhaps try using the console, or else outputting data to the document in some way that avoids blocking. e.g.
console && console.log("google.earth.createInstance, "End");
Google acknowledged the issue and mentioned they are working on a fix.
For right now there is a temporary fix below is the shorter version of Google's response.
******** Start Google's Response *************
"We have been able to reproduce this issue, intermittently. It is now pending additional investigation for the Google Earth client team, to find the root cause here. Unfortunately, it is not possible to provide an estimate for a deadline when this will be fixed. This issue definitely has a high priority since it impacts all Google Earth users with custom globes (GEE, and GME), and we have let the team know that this is now critical for your applications.
The only workaround that we can see, right now, is to refresh the page when the plugin fails to load (or you could do that programmatically: implement a timeout, and if after 5 seconds, the Earth API has not yet loaded, reload the plugin, or refresh the page). You could also consider using the Google Earth client, but I'm not sure if this is something that would be applicable to your use case."
**********End Google's Response ***************

Run multiple simultaneous tests with Google Content Experiments API

I'm trying to use Google Content Experiments API to run several A/B tests simultaneously in the same page (similar to a multivariate test).
I've tried just putting all the recommended code for each test one after another, like this:
<script src="//www.google-analytics.com/cx/api.js?experiment=8RsNt4b7T0aE8RC2s9R3IA">/script>
<script>
var chosenVariation = cxApi.chooseVariation();
var pageVariations = [
function() {},
function() {
$(".block1").hide();
},
];
$(document).ready(
pageVariations[chosenVariation]
);
</script>
<script src="//www.google-analytics.com/cx/api.js?experiment=T3m-MvunQ6wY6StbPDqzTg"></script>
<script>
var chosenVariation = cxApi.chooseVariation();
var pageVariations = [
function() {},
function() {
$(".block2").hide();
}
];
$(document).ready(
pageVariations[chosenVariation]
);
</script>
This is working but not correctly: All the tests get visitors according to the Google Analytics dashboard, but the last one to appear in the code gets a lot of them (probably all the real visitors to the page) and the others only a fraction of the total visitors. I guess this could be related to loading the "cx/api.js" script several times.
After searching a lot, I've found two related issues with possible solutions, but I don't fully understand how they work:
https://productforums.google.com/forum/#!topic/analytics/R3u8yagLr48
How can you choose variations for several content experiments?
(I've tried to comment on this answer instead of creating a new question, but I don't have enough reputation in StackOverflow to comment).
I guess the solution could be some variation of the code provided in the answer of the last link, but I don't know much Javascript and therefore don't know what I should change exactly to achieve what I want (multiple experiments concurrently in the same page).
Thanks a lot for your help! ;)

Google Maps from Firefox addon (without SDK)

I need to add a map in my adddon and I know how to do what I need in a "common webpage", like I did here: http://jsfiddle.net/hCymP/6/
The problem is I really don't know how to to the same in a Firefox Addon. I tryed importing the scripts with LoadSubScript and also tryed adding a chrome html with the next line:
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false"></script>
But nothing works. The best solution I found was to add part of the code in this file (the code of the script src) in my function, to import this file with loadSubScript, and all my function is executed but an empty div is returned.
Components.utils.import("resource://gre/modules/Services.jsm");
window.google = {};
window.google.maps = {};
window.google.maps.modules = {};
var modules = window.google.maps.modules;
var loadScriptTime = (new window.Date).getTime();
window.google.maps.__gjsload__ = function(name, text) { modules[name] = text;};
window.google.maps.Load = function(apiLoad) {
delete window.google.maps.Load;
apiLoad([0.009999999776482582,[[["https://mts0.googleapis.com/vt?lyrs=m#227000000\u0026src=api\u0026hl=en-US\u0026","https://mts1.googleapis.com/vt?lyrs=m#227000000\u0026src=api\u0026hl=en-US\u0026"],null,null,null,null,"m#227000000"],[["https://khms0.googleapis.com/kh?v=134\u0026hl=en-US\u0026","https://khms1.googleapis.com/kh?v=134\u0026hl=en-US\u0026"],null,null,null,1,"134"],[["https://mts0.googleapis.com/vt?lyrs=h#227000000\u0026src=api\u0026hl=en-US\u0026","https://mts1.googleapis.com/vt?lyrs=h#227000000\u0026src=api\u0026hl=en-US\u0026"],null,null,null,null,"h#227000000"],[["https://mts0.googleapis.com/vt?lyrs=t#131,r#227000000\u0026src=api\u0026hl=en-US\u0026","https://mts1.googleapis.com/vt?lyrs=t#131,r#227000000\u0026src=api\u0026hl=en-US\u0026"],null,null,null,null,"t#131,r#227000000"],null,null,[["https://cbks0.googleapis.com/cbk?","https://cbks1.googleapis.com/cbk?"]],[["https://khms0.googleapis.com/kh?v=80\u0026hl=en-US\u0026","https://khms1.googleapis.com/kh?v=80\u0026hl=en-US\u0026"],null,null,null,null,"80"],[["https://mts0.googleapis.com/mapslt?hl=en-US\u0026","https://mts1.googleapis.com/mapslt?hl=en-US\u0026"]],[["https://mts0.googleapis.com/mapslt/ft?hl=en-US\u0026","https://mts1.googleapis.com/mapslt/ft?hl=en-US\u0026"]],[["https://mts0.googleapis.com/vt?hl=en-US\u0026","https://mts1.googleapis.com/vt?hl=en-US\u0026"]],[["https://mts0.googleapis.com/mapslt/loom?hl=en-US\u0026","https://mts1.googleapis.com/mapslt/loom?hl=en-US\u0026"]],[["https://mts0.googleapis.com/mapslt?hl=en-US\u0026","https://mts1.googleapis.com/mapslt?hl=en-US\u0026"]],[["https://mts0.googleapis.com/mapslt/ft?hl=en-US\u0026","https://mts1.googleapis.com/mapslt/ft?hl=en-US\u0026"]]],["en-US","US",null,0,null,null,"https://maps.gstatic.com/mapfiles/","https://csi.gstatic.com","https://maps.googleapis.com","https://maps.googleapis.com"],["https://maps.gstatic.com/intl/en_us/mapfiles/api-3/13/11","3.13.11"],[3047554353],1.0,null,null,null,null,1,"",null,null,1,"https://khms.googleapis.com/mz?v=134\u0026",null,"https://earthbuilder.googleapis.com","https://earthbuilder.googleapis.com",null,"https://mts.googleapis.com/vt/icon"], loadScriptTime);
};
//I can't use document.write but use loadSubScript insthead
Services.scriptloader.loadSubScript("chrome://googleMaps/content/Google-Maps-V3.js", window, "utf8"); //chrome://MoWA/content/Google-Maps-V3.js", window, "utf8");
var mapContainer = window.content.document.createElement('canvas');
mapContainer.setAttribute('id', "map");
mapContainer.setAttribute('style',"width: 500px; height: 300px");
mapContainer.style.backgroundColor = "red";
var mapOptions = {
center: new window.google.maps.LatLng(latitude, longitude),
zoom: 5,
mapTypeId: window.google.maps.MapTypeId.ROADMAP
}
var map = new window.google.maps.Map(mapContainer,mapOptions);
return mapContainer;
Can you help me? I'm developing a "Firefox for Android" addon and that's why I need to do things like *window.content.*document.createElement because document is not declared, only window and I think thats may be the problem... But I can't declare everything if I don't know what Google Maps uses.
Added: I also read that Google Maps API Team has specific code that disallows you from copying the main script locally. In particular, that code "expires" every so many hours. I'm combined part of this script: https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false because I can't execute this directly (Error: write called on an object that does not implement interface HTMLDocument). So I don't have any alternative!
Use an iframe (type=content if in XUL) to display web content. There you can include whatever scripts you like. The content in the iframe will not have any special privileges, or at least should not. If you need to communicate with the privileged add-on part of your code, you can use e.g. regular HTML events (createEvent, addEventListener and friends) or the postMessage web API to pass messages.
Do not try to load remote code directly into other pages, or worse, into the browser, as this is a compatibility and security nightmare.
Because loading remote code and/or code not properly reviewed for running in a privileged context, the platform will refuse to load such scripts from remote sources (http, etc.) via loadSubScript, etc.
Should be noted, that if you'd later like to host your add-on on addons.mozilla.org and still do include remote scripts in privileged code, your add-on will be rejected until you fix it.
Also, mozilla might blocklist your add-on even if you host elsewhere if it is discovered that there are known security vulnerabilities in your add-on, per the Add-on Guidelines.

Run Javascript on the body of a Gmail message

I want to display LaTeX math in the gmail messages that I receive, so that for example $\mathbb P^2$ would show as a nice formula. Now, there are several Javascripts available (for example, this one, or MathJax which would do the job, I just need to call them at the right time to manipulate the gmail message.
I know that this is possible to do in "basic HTML" and "print" views. Is it possible to do in the standard Gmail view? I tried to insert a call to the javascript right before the "canvas_frame" iframe, but that did not work.
My suspicion is that manipulating a Gmail message by any Javascript would be a major security flaw (think of all the malicious links one could insert) and that Google does everything to prevent this. And so the answer to my question is probably 'no'. Am I right in this?
Of course, it would be very easy for Google to implement viewing of LaTeX and MathML math simply by using MathJax on their servers. I made the corresponding Gmail Lab request, but no answer, and no interest from Google apparently.
So, again: is this possible to do without Google's cooperation, on the client side?
I think one of the better ways to do this might be to embed images using the Google Charts API.
<img src="http://chart.apis.google.com/chart?cht=tx&chl=x=\frac{-b%20\pm%20\sqrt{b^2-4ac}}{2a}">
To Learn more: https://developers.google.com/chart/image/ [note, the API has been officially deprecated, but will work until April 2015]
If you really must use LaTeX and some js library, I think one way you could accomplish this is by injecting a script tag into the iframe.
I hope this is a good starting point.
Example:
// ==UserScript==
// #name Test Gmail Alterations
// #version 1
// #author Justen
// #description Test Alter Email
// #include https://mail.google.com/mail/*
// #include http://mail.google.com/mail/*
// #license GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html
// ==/UserScript==
(function GmailIframeInject() {
GM_log('Starting GMail iFrame Injection');
var GmailCode = function() {
// Your code here;
// The ':pd' (div id) changes, so you might have to do some extra work
var mail = document.getElementById(':pd');
mail.innerHTML = '<h1>Hello, World!</h1>';
};
var iframe = document.getElementById('canvas_frame');
var doc = null;
if( iframe ) {
GM_log('Got iFrame');
doc = iframe.contentDocument;
} else {
GM_log('ERROR: Could not get iframe with id canvas_frame');
return
}
if( doc ) {
GM_log('Injecting GmailCode');
var code = "(" + GmailCode + ")();"
doc.body.appendChild(doc.createElement('script')).innerHTML=code;
} else {
GM_log('ERROR: Could not get iframe content document');
return;
}
})();
Well, there are already greasemonkey scripts that do things to GMail as far as i know (like this one). Is this a possible security hole? Of course, anything you'd do with executable code has that risk. Google seems to move a glacial speeds on things they're not interested in. They really do seem to function based on internal championing of ideas, so best way forward is to go find sympathetic googlers, if you want them to include something into GMail. Otherwise stick to Greasemonkey, at least you'll have an easy install path for other people who'd like to see the same functionality.

Resources