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?!
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…
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.