Using Marketo RTP in an IFRAME

Because why not? When user AA wondered why his IFRAMEd pages (inside other pages in the same domain) seemed to be ignoring RTP reactions, I resolved to get to the bottom of it.

On the technical level, as it turns out, there's no mystery: the RTP JS library disables itself when it determines it's running in an <iframe>.

On the business level, this makes little sense:

(a) it assumes an adversarial relationship between the outer domain and the IFRAMEd domain, which obviously isn't the case when you operate both domains

and

(b) even if the inner and outer domains don't trust each other, there's tech that already exists to solve that: the outer can use the <iframe sandbox> attribute to neuter the inner, and the inner can have an optional (as opposed to mandatory) restriction on running in a frame

Like some other Marketo features (think pURLs, which I'm writing about in another browser tab right now) it's as if an edge case became the default case somewhere along the way. Maybe it was supposed to be a optional switch, defaulted to off — but now there's no official switch, and it defaults to on!

Luckily, you can fix this by inserting one line of JS after the last line of the RTP embed code:

(function(c,h,a,f,i,e){c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
c[a].a=i;c[a].e=e;var g=h.createElement("script");g.async=true;g.type="text/javascript";
g.src=f+'?aid='+i;var b=h.getElementsByTagName("script")[0];b.parentNode.insertBefore(g,b);
})(window,document,"rtp","//sjrtp3-cdn.marketo.com/rtp-api/v1/rtp.js","watchguard");
rtp('send','view');
rtp('get', 'campaign',true);
Object.defineProperty(rtp, 'd', {value:false}); // ← here's the magic

For the learners

When it detects that it's been IFRAMEd, the RTP library attempts to set the property d (for disabled) on the global rtp object to true.

While we can't stop the attempt, we can hard-code d to false on startup using the beloved-but-little-exercised Object.defineProperty function, so it can't be changed afterward. The third arg passed to defineProperty is a property descriptor with the defaults except for the value. One of the unseen defaults is writable, which defaults to false, meaning the value can't be changed after it's been set.

Bit of a strange thing to be forced to do, but that's what happens with inflexible APIs.