Youtube iframe API fails to post message - javascript

For awhile now, a piece of javascript I wrote which listens to youtube actions on a certain page worked wonderfully. I am using Youtube's iframe js api: https://developers.google.com/youtube/iframe_api_reference .
But one recent content addition, a specific youtube video, the tracking wouldn't work. The events won't fire at all.
In the console, I noticed this post message error:
Unable to post message to http://youtube.com. Recipient has origin http://www.youtube.com.
So nothing with my own code helped. Some questions here on stackoverflow suggested this is an issue with initiating new YT.player too soon, so I tried a whole bunch of things like loading the yt js api file on window load and only apply the api after, but that didn't seem to do any good either.

I know this post is 3 years old, but for those who are still searching for an answer:
Add this script and everything works fine:
<script src="https://www.youtube.com/iframe_api"></script>
I've had the same problem with jwplayer and fixed it with that script.

It took me over an hour, but the answer was right in front of me. It's actually pretty self explained: You cannot use youtube's js api to track an iframe video without www. I don't know why, it certainly does not say so in their documentation.
I tested this a few times and confirmed, as of now, tracking an iframe with the source www.youtube.com/embed/0GN2kpBoFs4 would work wonderfully while tracking youtube.com/embed/0GN2kpBoFs4 will throw:
Unable to post message to http://youtube.com. Recipient has origin http://www.youtube.com.
The confusing part of course is that both the video load and play fine. It's only the API which is not working properly.
fiddle - http://jsfiddle.net/8tkgW/ (Tested on chrome / mountain lion)
Btw, while writing this answer I came across YouTube iframe API: how do I control a iframe player that's already in the HTML? - notice this guy's fiddle. He wrote his own youtube iframe implementation (wow!). If you change the iframe source address in the fiddle to one without www, it will work. This only means youtube writes bad js. Bad bad bad!

Don't forget to add it to the whitelist:
<!-- Add the whitelist plugin -->
<plugin name="cordova-plugin-whitelist" source="npm" spec="*"/>
<!-- White list https access to Youtube -->
<allow-navigation href="https://*youtube.com/*"/>

The youtube api documentation recommend to load the api like this
var tag = document.createElement('script');
tag.src = "http://youtube.com/iframe_api";
tag.id = "youtubeScript";
var firstScriptTag = document.getElementsByTagName('script')[1];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
But getting this error:
Unable to post message to http://youtube.com. Recipient has origin http://www.youtube.com
Here is the best solution I found from [a now-dead site]:
So add this first at the top before calling the Api
if (!window['YT']) {var YT = {loading: 0,loaded: 0};}
if (!window['YTConfig']) {var YTConfig = {'host': 'http://www.youtube.com'};}
if (!YT.loading) {YT.loading = 1;(function(){var l = [];YT.ready = function(f) {if (YT.loaded) {f();}
else
{l.push(f);}};
window.onYTReady = function() {YT.loaded = 1;for (var i = 0; i < l.length; i++) {try {l[i]();} catch (e) {}}};
YT.setConfig = function(c) {for (var k in c) {if (c.hasOwnProperty(k)) {YTConfig[k] = c[k];}}};
var a = document.createElement('script');
a.type = 'text/javascript';
a.id = 'www-widgetapi-script';
a.src = 'https:' + '//s.ytimg.com/yts/jsbin/www-widgetapi-vflumC9r0/www-widgetapi.js';
a.async = true;
var b = document.getElementsByTagName('script')[0];
b.parentNode.insertBefore(a, b);})();}
//===========THEN=============================
function onYouTubeIframeAPIReady () {// do stuff here}

Related

Accessing the console log commands via chrome extension

I'm looking to override the existing console commands via my Chrome extension - the reason for this is I wish to record the console logs for a specific site.
Unfortunately I cannot seem to update the DOM, this is what i've tried so far:
// Run functions on page change
chrome.tabs.onUpdated.addListener( function (tabId, changeInfo, tab) {
var s = document.createElement('script');
// TODO: add "script.js" to web_accessible_resources in manifest.json
s.src = chrome.runtime.getURL('core/js/app/console.js');
s.onload = function() {
this.remove();
};
(document.head || document.documentElement).appendChild(s);
});
console.js
// Replace functionality of console log
console.defaultLog = console.log.bind(console);
console.logs = [];
console.log = function(){
console.defaultLog.apply(console, arguments);
console.logs.push(Array.from(arguments));
};
// Replace functionality of console error
console.defaultError = console.error.bind(console);
console.errors = [];
console.error = function(){
console.defaultError.apply(console, arguments);
console.errors.push(Array.from(arguments));
};
// Replace functionality of console warn
console.defaultWarn = console.warn.bind(console);
console.warns = [];
console.warn = function(){
console.defaultWarn.apply(console, arguments);
console.warns.push(Array.from(arguments));
};
// Replace functionality of console debug
console.defaultDebug = console.debug.bind(console);
console.debugs = [];
console.debug = function(){
console.defaultDebug.apply(console, arguments);
console.debugs.push(Array.from(arguments));
};
The script runs successfully with an alert().
The goal for me is to access console.logs - but its undefined which means I haven't gotten access to the DOM, despite injecting a script.
If not possible, even a third party integration would be helpful i.e. Java or C?
Any thoughts would be greatly appreciated :)
I found this post and I think Tampermonkey injects a script with the immediate function that you add in the Tampermonkey Chrome extension page, I found something similar in extensions like Wappalyzer, and looks good and safe, you could use WebRequest to inject to your website the new "polyfill" before the page is fully loaded as the post says.
Here the example of Wappalyzer that I mentioned before, this is the JS load in StackOverflow with Wappalyzer using the code injection, I didn't test it with Tampermonkey yet
EDIT
Checking Wappalyzer, how to inject the code is the easy part, you can use (Wappalyzer github example):
const script = document.createElement('script')
script.setAttribute('src', chrome.extension.getURL('js/inject.js'))
This probably will not fix your problem, this code is executed after all the content was loaded in the DOM. But, you can find how to fix that problem in this post
I'll suggest to use onCommitted event (doc1/doc2)
Using the mozilla.org example you will have something like
const filter = {
url: //website to track logs
[
{hostContains: "example.com"},
{hostPrefix: "developer"}
]
}
function logOnCommitted(details) {
//Inject Script on webpage
}
browser.webNavigation.onCommitted.addListener(logOnCommitted, filter);
It might be worth trying to redefine the entire console object:
const saved = window.console
window.console = {...saved, log: function(...args){ saved.log("Hello", ...args) }}
But it's probably impossible, because content scripts live in an isolated world:
Isolated worlds do not allow for content scripts, the extension, and the web page to access any variables or functions created by the others. This also gives content scripts the ability to enable functionality that should not be accessible to the web page.
Although in Tampermonkey this script works.
I believe Tampermonkey handles this by knowing the subtleties and tracking changes in the extensions host's protection mechanism.
BTW, for small tasks, there is a decent alternative to chrome extensions in the form of code snippets.

Failed to execute 'postMessage' on 'DOMWindow': https://www.youtube.com !== http://localhost:9000

This is the error message that I get:
Failed to execute 'postMessage' on 'DOMWindow': The target origin provided
('https://www.youtube.com') does not match the recipient window's origin
('http://localhost:9000').
I've seen other similar problems where the target origin is http://www.youtube.com and the recipient origin is https://www.youtube.com, but none like mine where the target is https://www.youtube.com and the origin is http://localhost:9000.
I don't get the problem. What is the problem?
How can I fix it?
I believe this is an issue with the target origin being https. I suspect it is because your iFrame url is using http instead of https. Try changing the url of the file you are trying to embed to be https.
For instance:
'//www.youtube.com/embed/' + id + '?showinfo=0&enablejsapi=1&origin=http://localhost:9000';
to be:
'https://www.youtube.com/embed/' + id + '?showinfo=0&enablejsapi=1&origin=http://localhost:9000';
Just add the parameter "origin" with the URL of your site in the paramVars attribute of the player, like this:
this.player = new window['YT'].Player('player', {
videoId: this.mediaid,
width: '100%',
playerVars: {
'autoplay': 1,
'controls': 0,
'autohide': 1,
'wmode': 'opaque',
'origin': 'http://localhost:8100'
},
}
Setting this seems to fix it:
this$1.player = new YouTube.Player(this$1.elementId, {
videoId: videoId,
host: 'https://www.youtube.com',
You can save the JavaScript into local files:
https://www.youtube.com/player_api
https://s.ytimg.com/yts/jsbin/www-widgetapi-vfluxKqfs/www-widgetapi.js
Into the first file, player_api put this code:
if(!window.YT)var YT={loading:0,loaded:0};if(!window.YTConfig)var YTConfig={host:"https://www.youtube.com"};YT.loading||(YT.loading=1,function(){var o=[];YT.ready=function(n){YT.loaded?n():o.push(n)},window.onYTReady=function(){YT.loaded=1;for(var n=0;n<o.length;n++)try{o[n]()}catch(i){}},YT.setConfig=function(o){for(var n in o)o.hasOwnProperty(n)&&(YTConfig[n]=o[n])}}());
Into the second file, find the code: this.a.contentWindow.postMessage(a,b[c]);
and replace it with:
if(this._skiped){
this.a.contentWindow.postMessage(a,b[c]);
}
this._skiped = true;
Of course, you can concatenate into one file - will be more efficient.
This is not a perfect solution, but it's works!
My Source : yt_api-concat
Make sure you are loading from a URL such as:
https://www.youtube.com/embed/HIbAz29L-FA?modestbranding=1&playsinline=0&showinfo=0&enablejsapi=1&origin=https%3A%2F%2Fintercoin.org&widgetid=1
Note the "origin" component, as well as "enablejsapi=1". The origin must match what your domain is, and then it will be whitelisted and work.
In my case this had to do with lazy loading the iframe. Removing the iframe HTML attribute loading="lazy" solved the problem for me.
I got the same error. My mistake was that the enablejsapi=1 parameter was not present in the iframe src.
You also get this message when you do not specify a targetOrigin in calls to window.postMessage().
In this example we post a message to the first iFrame and use * as target, which should allow communication to any targetOrigin.
window.frames[0].postMessage({
message : "Hi there",
command :"hi-there-command",
data : "Some Data"
}, '*')
Try using window.location.href for the url to match the window's origin.
Remove DNS Prefetch will solve this issue.
If you're using WordPress, add this line in your theme's functions.php
remove_action( 'wp_head', 'wp_resource_hints', 2 );
There could be any of the following, but all of them lead into DOM not loaded before its accessed by the javascript.
So here is what you have to ensure before actually calling JS code:
* Make sure the container has loaded before any javascript is called
* Make sure the target URL is loaded in whatever container it has to
I came across the similar issue but on my local when I am trying to have my Javascript run well before onLoad of the main page which causes the error message. I have fixed it by simply waiting for whole page to load and then call the required function.
You could simply do this by adding a timeout function when page has loaded and call your onload event like:
window.onload = new function() {
setTimeout(function() {
// some onload event
}, 10);
}
that will ensure what you are trying will execute well after onLoad is trigger.
In my instance at least this seems to be a harmless "not ready" condition that the API retries until it succeeds.
I get anywhere from two to nine of these (on my worst-case-tester, a 2009 FossilBook with 20 tabs open via cellular hotspot).... but then the video functions properly. Once it's running my postMessage-based calls to seekTo definitely work, haven't tested others.
It looks it's only a Chrome security system to block repeated requests, using CORB.
https://www.chromestatus.com/feature/5629709824032768
In my case, YouTube was blocking Access after the first load of the same webpage which has many video API data request, high payload.
For pages with low payload, the issue does not occur.
In Safari and other non Chronuim based browsers, the issue does not occur.
If I load the webpage in a new browser, the issue does not occur, when I reload the same page, the issue appears.
In some cases (as one commenter mentioned) this might be caused if you are moving the player within DOM, like append or etc..
This helped me (with Vue.js)
Found here vue-youtube
mounted() {
window.YTConfig = {
host: 'https://www.youtube.com/iframe_api'
}
const host = this.nocookie ? 'https://www.youtube-nocookie.com' : 'https://www.youtube.com'
this.player = player(this.$el, {
host,
width: this.width,
height: this.height,
videoId: this.videoId,
playerVars: this.playerVars
})
...
}
UPDATE:
Working like a charm like this:
...
youtube(
video-id="your_video_code_here"
nocookie
)
...
data() {
return {
playerVars: {
origin: window.location.href,
},
};
},
I think the description of the error is misleading and has originally to do with wrong usage of the player object.
I had the same issue when switching to new Videos in a Slider.
When simply using the player.destroy() function described here the problem is gone.
I had this same problem and it turns out it was because I had the Chrome extension "HTTPS Everywhere" running. Disabling the extension solved my problem.
This exact error was related to a content block by Youtube when "playbacked on certain sites or applications". More specifically by WMG (Warner Music Group).
The error message did however suggest that a https iframe import to a http site was the issue, which it wasn't in this case.
You could change your iframe to be like this and add origin to be your current website. It resolves error on my browser.
<iframe class="test-testimonials-youtube-group" type="text/html" width="100%" height="100%"
src="http://www.youtube.com/embed/HiIsKeXN7qg?enablejsapi=1&origin=http://localhost:8000"
frameborder="0">
</div>
ref: https://developers.google.com/youtube/iframe_api_reference#Loading_a_Video_Player
Just wishing to avoid the console error, I solved this using a similar approach to Artur's earlier answer, following these steps:
Downloaded the YouTube Iframe API (from https://www.youtube.com/iframe_api) to a local yt-api.js file.
Removed the code which inserted the www-widgetapi.js script.
Downloaded the www-widgetapi.js script (from https://s.ytimg.com/yts/jsbin/www-widgetapi-vfl7VfO1r/www-widgetapi.js) to a local www-widgetapi.js file.
Replaced the targetOrigin argument in the postMessage call which was causing the error in the console, with a "*" (indicating no preference - see https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage).
Appended the modified www-widgetapi.js script to the end of the yt-api.js script.
This is not the greatest solution (patched local script to maintain, losing control of where messages are sent) but it solved my issue.
Please see the security warning about removing the targetOrigin URI stated here before using this solution - https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
Patched yt-api.js example
Adding origin=${window.location.host} or "*" is not enough.
Add https:// before it and it will work.
Also, make sure that you are using an URL that can be embedded: take the video ID out and concatenate a string that has the YouTube video prefix and the video ID + embed definition.
I think we could customize the sendMessage of the YT.Player
playerOptions.playerVars.origin = window.location.origin or your domain.
this.youtubePlayer = new YT.Player(element,playerOptions);
this.youtubePlayer.sendMessage = function (a) {
a.id = this.id, a.channel = "widget", a = JSON.stringify(a);
var url = new URL(this.h.src), origin = url.searchParams.get("origin");
if (origin && this.h.contentWindow) {
this.h.contentWindow.postMessage(a, origin)
}
}
I used this function to resolve in my project.
Extending #Hokascha's answer above it was also lazy loading for me being automatically added by WordPress. This code will remove all lazy loading on the site's iframes (add to functions.php):
function disable_post_content_iframe_lazy_loading( $default, $tag_name, $context ) {
if ( 'iframe' === $tag_name ) {
return false;
}
return $default;
}
add_filter('wp_lazy_loading_enabled', 'disable_post_content_iframe_lazy_loading', 10, 3);
I got a similar error message in my attempt to embed a Stripe pricing table when:
Adding the embed code via PHP through a custom WordPress short code
Or by appending the code to the page dynamically with JavaScript (Even a using a setTimeout() delay to ensure the DOM was loaded didn't work).
I was able to solve this on my WordPress site by adding the code to the WordPress page itself using plain html code in the block editor.
mine was:
<youtube-player
[videoId]="'paxSz8UblDs'"
[playerVars]="playerVars"
[width]="291"
[height]="194">
</youtube-player>
I just removed the line with playerVars, and it worked without errors on console.
You can try :
document.getElementById('your_id_iframe').contentWindow.postMessage('your_message', 'your_domain_iframe')
I was also facing the same issue then I visit official Youtube Iframe Api where i found this:
The user's browser must support the HTML5 postMessage feature. Most modern browsers support postMessage
and wander to see that official page was also facing this issue. Just Visit official Youtube Iframe Api and see console logs. My Chrome version is 79.0.3945.88.

YouTube embed API issue on IOS and Android

An issues popped up in the last few days with the YouTube embed API. The issue is that when you embed a video with the official API, it simply doesn't allow you to access to the API. When you try to access to the API, you got error message on the log (IOS) and if you try to play the video through the API the video blacks out. If you load it via the API, but you do not use the API, the user is able to play the video with tap.
The issue persist on the following browsers:
IOS 7 Safari on iPad and iPhone
IOS 7 Chrome on iPad and iPhone
Android 4 Chrome
(My play button uses the API to play the video and that produce the error)
JSfiddle: http://jsfiddle.net/frdd8nvr/6/
Error message:
Unable to post message to https://www.youtube.com. Recipient has origin http://fiddle.jshell.net.
postMessage[native code]:0
Jwww-widgetapi.js:26:357
Nwww-widgetapi.js:25
(anonymous function)[native code]:0
html5player.js:1201:97
Blocked a frame with origin "https://www.youtube.com" from accessing a frame with origin "http://jsfiddle.net". The frame requesting access has a protocol of "https", the frame being accessed has a protocol of "http". Protocols must match.
Some debug info:
As I see the API create the iframe on the site. The src is sometime http and sometime https.
http://www.youtube.com/embed/ZPy9VCovVME?enablejsapi=1&origin=http%3A%2F%2Ffiddle.jshell.net&autoplay=0&modestbranding=1&wmode=opaque&forceSSL=false
My test showed that most of the times YouTube servers simply LOCATION: https://... the request to the https url, but around 10% they served the http request with proper content.
I think somehow the issue related with the forced https, but I was not able to figure out the solution.
Have you experienced the same? Do you have some kind of solution for this problem? Is it a YouTube bug?
My test code:
<div id="myvideo"></div>
<button id="play-button">Play</button>
JS:
var tag = document.createElement("script");
tag.src = "//www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName("script")[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
onYouTubeIframeAPIReady = function () {
var vars = {
enablejsapi: 1,
origin: window.location.protocol + "//" + window.location.host,
autoplay: 0,
modestbranding: 1,
wmode: "opaque",
forceSSL: false
};
if (+(navigator.platform.toUpperCase().indexOf('MAC') >= 0 && navigator.userAgent.search("Firefox") > -1)){
vars.html5 = 1;
}
var playerobj = new YT.Player('myvideo', {
videoId: 'ZPy9VCovVME',
wmode: 'opaque',
playerVars: vars,
events: {
onReady: function(){
$('#play-button').on('click', function(){
playerobj.playVideo();
});
//playerobj.playVideo();
},
onStateChange: function(state){
switch(state.data){
case YT.PlayerState.PLAYING:
break;
//case YT.PlayerState.PAUSED:
case YT.PlayerState.ENDED:
break;
}
}
}
});
}
First, I think this is a duplicate of the following question: YouTube IFrame API play method doesn't work before touch on some Android tablets
I could reproduce the same problem on the YouTube Player Demo page.
The error
Unable to post message to https://www.youtube.com. Recipient has origin http://fiddle.jshell.net.
is only occuring on your jsfiddle. On the demo page this error is not occuring, therefore your observed bug seems not to be related to the error logged in the console. I checked this with chrome on Android 4.4.4.
Workaround
I have a dirty workaround which works on chrome on Android 4.4.4 (Nexus5) and 4.1.2 (S2). I have no iOS Device to test this. The workaround works on my phones, because the video always starts the second time I click the play-button.
http://jsfiddle.net/kjwpwpx8/6/
I can always start the video on click-Events after the playVideo function was called once before. In my workaround I put two iframes on the page. One is hidden while the other one is visible. If the page is viewed by a mobile browser I call the playVideo function of the second video after the OnReady-Event. The video won't play as the browser blocks this.
Now when our play-button is pressed the first video becomes hidden and stopped and the second video becomes visible and the playVideo function is called again.
Here is the important code:
var playerOptions = {
videoId: 'ZPy9VCovVME',
wmode: 'opaque',
playerVars: vars,
events: {}
};
var playerobj1 = new YT.Player('myvideo1', playerOptions);
var playerobj2 = null;
playerOptions.events.onReady = function(){
$('#play-button').on('click', function(){
$("#videowrapper .video1wrapper").hide();
playerobj1.stopVideo();
$("#videowrapper .video2wrapper").show();
playerobj2.playVideo();
});
if(isMobileBrowser)
playerobj2.playVideo();
};
playerobj2 = new YT.Player('myvideo2', playerOptions);

Javascript error & GET request failure from Google custom search

I've encountered an issue with a custom Google search engine I've integrated into my site. I have some other Google elements on the same page, such as the +1 button and G+ badge, but I've isolated and verified that the search element is, in fact, the culprit.
The problem is the following: the GET request for http://www.google.com/uds/api/ads/3.0/9f53ed6be164615d919d9e4bd4f7fe8d/search.I.js seems to fail (although searching the site still works). The dev console in Chrome says that the request is initiated by their jsapi on line 21 and has the following highlighted in red:
google.(anonymous function).d
(anonymous function)
I've tried multiple test scenarios, including inserting the search box on a completely barebones HTML page, but I get the same error. What is causing the request to fail? Is it simply something on their end (and thus unfixable)?
Edit: Relevant code
This is all taken verbatim from Google's code generators.
This goes right before </head>
<script>
(function() {
var cx = '004344714102800561193:mo5u_njahwy';
var gcse = document.createElement('script'); gcse.type = 'text/javascript'; gcse.async = true;
gcse.src = (document.location.protocol == 'https:' ? 'https:' : 'http:') +
'//www.google.com/cse/cse.js?cx=' + cx;
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(gcse, s);
})();
</script>
The search box code:
<gcse:searchbox-only></gcse:searchbox-only>
Search results page code:
<gcse:searchresults-only></gcse:searchresults-only>
Do you have an ad blocker installed like Adblock Plus? I'm getting this error when I run your code in JSFiddle:
Failed to load resource: net::ERR_BLOCKED_BY_CLIENT
It looks like that error is often caused by an ad blocker preventing the file from being loaded: Meta - What are these errors about in the Chrome console when visiting StackOverflow?

is _gaq.push not working properly?

I am trying to track my pages via google analytics, here is my code
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'XXXXXXXXXX']);
_gaq.push(['_setDomainName', 'somesite.com']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
(function ($) {
// Log all jQuery AJAX requests to Google Analytics
$(document).bind('ajaxComplete', function(event, xhr, settings){
console.log('ajax Request');
console.log(settings.url);
_gaq.push(['_trackPageview', settings.url]);
});
})(jQuery);
On each ajax request I can see the console has values
ajax Request
url of the page
it means _gap.push is working (as there is no js error on page). But when I am checking my req/res via Live HttpHeaders there is no req/res to google analytics, How to track it?
here is the screenshot in firebug
The point in _gaq.push is that, until Google Analytics is actually loaded, "_gaq" is just a normal array. That is: the lack of errors is indeed expected, regardless of whether or not it is "working" in the sense of triggering a request to Google.
The way Google Analytics works is not via ajax (or at least, there is no specific implementation detail regarding how the request will be sent). The method usually used is to create an Image element with the tracking data included in the query string of that image's URL. After all, the page doesn't care what response Google Analytics has, it just wants to send its data and go!
Rather than using LiveHttpHeaders, I would check the 'Network' panel of your developer tools -- if you have the Javascript console, you probably have access to that as well. You should be able to see all of the details of the requests on that panel.
You can also use the debug version of ga.js to diagnose errors. It prints things like "Invalid tracking code" and so on to the Javascript console.
Search for "Debugging with ga_debug.js" on this page:
https://developers.google.com/analytics/resources/articles/gaTrackingTroubleshooting
Look at the http headers sent - I use HttpFox - and filter for 'utm'. Look at the query string (httpfox breaks this out into a table for you) and you can see all of the utm parameters of the hit - account number (utmac), page (utmp), etc. If any of the utm params are unfamiliar, check this reference. This sort of simulation and analysis of the image requests sent to google's servers is very useful for debugging Google Analytics problems.

Categories

Resources