Coffee Space


Listen:

Do Not Track Part 2

Preview Image

In 2017 (5 years ago at the time of writing!) I wrote a custom “Do Not Track” script. The whole idea was that my site would attempt to respect the DNT request from a browser, even if the third-party I put in an iframe does not.

All was well in this world - until it wasn’t. There was never meant to be a part 2. Why is there a part 2?!

Original Version

The idea was simple: Loop over <iframe> elements and replace them with links to the original content.

0001 if(
0002   navigator.doNotTrack === "1" ||
0003   navigator.doNotTrack === "yes" ||
0004   navigator.msDoNotTrack === "1" ||
0005   window.doNotTrack === "1" ||
0006   window.external.InPrivateFilteringEnabled() ||
0007   window.external.msTrackingProtectionEnabled()
0008 ){
0009   var e = document.getElementsByTagName("iframe");
0010   for(var x = 0; x < e.length; x++){
0011     var s = e[x].getAttribute("src");
0012     if(s.indexOf("http") == 0 && s.indexOf("://coffeespace.org.uk") < 0){
0013       var a = document.createElement("a");
0014       a.setAttribute("class", "dnt");
0015       a.setAttribute("href", e[x].getAttribute("src"));
0016       a.innerHTML = "[DoNotTrack] " + e[x].getAttribute("src");
0017       e[x].replaceWith(a);
0018     }
0019   }
0020 }

I remember testing this code on multiple browsers. The initial if-statement does a pretty good job of picking up the request from all the crappy browser implementations. I was happy with it and called it a day, testing no further. The problem is solved! Or so I thought.

Imagine my surprise when I load a recent article I wrote on the possibility of a Russian war, only to find the first HTML element is correctly replaced, but not the second! What the hell!

It turns out that after the initial replace e[x].replaceWith(a);, the array e itself is changed and it stops looping early. There has been a basic bug sitting there unnoticed for 5 years. I remember having this exact issue with replacement elements in the DOM maybe some 10 years ago when I initially ran tests with HTML and javascript…

Fixed Version

Of course, I cannot be easily defeated:

0021 if(
0022   navigator.doNotTrack === "1" ||
0023   navigator.doNotTrack === "yes" ||
0024   navigator.msDoNotTrack === "1" ||
0025   window.doNotTrack === "1" ||
0026   window.external.InPrivateFilteringEnabled() ||
0027   window.external.msTrackingProtectionEnabled()
0028 ){
0029   var p = true;
0030   while(p){
0031     p = false;
0032     var e = document.getElementsByTagName("iframe");
0033     for(var x = 0; x < e.length; x++){
0034       var s = e[x].getAttribute("src");
0035       if(s.indexOf("http") == 0 && s.indexOf("://coffeespace.org.uk") < 0){
0036         var a = document.createElement("a");
0037         a.setAttribute("class", "dnt");
0038         a.setAttribute("href", e[x].getAttribute("src"));
0039         a.innerHTML = "[DoNotTrack] " + e[x].getAttribute("src");
0040         e[x].replaceWith(a);
0041         p = true;
0042         break;
0043       }
0044     }
0045   }
0046 }

Now we keep re-running the loop, as long as we are making successful replacements. As soon as we stop making successful replacements, we stop looping. There is some awkwardness to it and it could be improved to prevent double-treading data we already visited, but I also believe the simplicity of the solution is a benefit.

This time… This time I solved it for sure!

For those of you with javascript and DNT enabled, don’t enjoy this awesome video of leek spin for 10 hours straight:

Expect an update on this simple script in 5 years time.