Skip to content
Permalink
Browse files

Workaround for document.write changing readyState after onload

  • Loading branch information...
querymetrics authored and nicjansma committed Dec 7, 2017
1 parent 9f1f513 commit 2822a79e0424cb4fdd4cab004c928388888f7aa1
@@ -1105,18 +1105,10 @@ BOOMR_check_doc_domain();

// The developer can override onload by setting autorun to false
if (!impl.onloadfired && (config.autorun === undefined || config.autorun !== false)) {
if (d.readyState && d.readyState === "complete") {
if (BOOMR.hasBrowserOnloadFired()) {
BOOMR.loadedLate = true;
this.setImmediate(BOOMR.page_ready_autorun, null, null, BOOMR);
}
else {
if (w.onpagehide || w.onpagehide === null) {
BOOMR.utils.addListener(w, "pageshow", BOOMR.page_ready_autorun);
}
else {
BOOMR.utils.addListener(w, "load", BOOMR.page_ready_autorun);
}
}
BOOMR.attach_page_ready(BOOMR.page_ready_autorun);
}

BOOMR.utils.addListener(w, "DOMContentLoaded", function() { impl.fireEvent("dom_loaded"); });
@@ -1179,16 +1171,19 @@ BOOMR_check_doc_domain();
},

/**
* Attach a callback to the onload event if the onload has not
* been fired yet
* Attach a callback to the `pageshow` or `onload` event if `onload` has not
* been fired otherwise queue it to run immediately
*
* @param {function} cb - Callback to run when onload fires or page is visible (pageshow)
* @param {function} cb - Callback to run when `onload` fires or page is visible (`pageshow`)
*/
attach_page_ready: function(cb) {
if (d.readyState && d.readyState === "complete") {
if (BOOMR.hasBrowserOnloadFired()) {
this.setImmediate(cb, null, null, BOOMR);
}
else {
// Use `pageshow` if available since it will fire even if page came from a back-forward page cache.
// Browsers that support `pageshow` will not fire `onload` if navigation was through a back/forward button
// and the page was retrieved from back-forward cache.
if (w.onpagehide || w.onpagehide === null) {
BOOMR.utils.addListener(w, "pageshow", cb);
}
@@ -1239,6 +1234,24 @@ BOOMR_check_doc_domain();
return this;
},

/**
* Determines whether or not the page's `onload` event has fired
*
* @returns {boolean} True if page's onload was called
*/
hasBrowserOnloadFired: function() {
var p = BOOMR.getPerformance();
// if the document is `complete` then the `onload` event has already occurred, we'll fire the callback immediately.
// When `document.write` is used to replace the contents of the page and inject boomerang, the document `readyState`
// will go from `complete` back to `loading` and then to `complete` again. The second transition to `complete`
// doesn't fire a second `pageshow` event in some browsers (e.g. Safari). We need to check if
// `performance.timing.loadEventStart` or `BOOMR_onload` has occurred to detect this scenario. Will not work for
// older Safari that doesn't have NavTiming
return ((d.readyState && d.readyState === "complete") ||
(p && p.timing && p.timing.loadEventStart > 0) ||
w.BOOMR_onload > 0);
},

/**
* Determines whether or not the page's `onload` event has fired, or
* if `autorun` is false, whether `BOOMR.page_ready()` was called.
@@ -2088,12 +2101,11 @@ BOOMR_check_doc_domain();
* supported or if the entry doesn't exist
*/
getResourceTiming: function(url, sort) {
var entries;
var entries, p = BOOMR.getPerformance();

try {
if (BOOMR.getPerformance() &&
typeof BOOMR.getPerformance().getEntriesByName === "function") {
entries = BOOMR.getPerformance().getEntriesByName(url);
if (p && typeof p.getEntriesByName === "function") {
entries = p.getEntriesByName(url);
if (entries && entries.length) {
if (typeof sort === "function") {
entries.sort(sort);
@@ -0,0 +1,48 @@
<!DOCTYPE html>
<!--[if IE 8]>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 1.0 Transitional//EN" "http://www.w3.org/TR/html1/DTD/html1-transitional.dtd">
<![endif]-->

<!--
When document.write is used to replace the contents of the page and inject boomerang, the document readyState
will go from "complete" back to "loading" and then to "complete" again. The second transition to "complete"
doesn't fire a second pageshow event in some browsers (e.g. Safari). Boomerang needs to check if performance.timing.loadEventStart has occurred
to detect this scenario.
-->
<html>
<head>
</head>
<body onload="setTimeout(onLoad, 100);">

<!-- the textarea contains the html that we'll use with document.write to replace the page -->
<textarea id="newdocument">
<%= header %>
<script src="122358-document-write.js" type="text/javascript"></script>
<%= boomerangSnippet %>
<script>
BOOMR_test.init({
testAfterOnBeacon: 1,
onBoomerangLoaded: function() {
setTimeout(function() {
document.close();
// readyState will return to "complete" without firing a second pageshow event in Safari
}, 100);
}
});
</script>
<div id="content"></div>
<%= footer %>
</textarea>

<script>
function onLoad() {
// replace our page
var newdocument = document.getElementById("newdocument").value;
document.open();
document.write(newdocument);
// wait until boomerang has loaded before closing the document so that it detects a "loading" readyState
}
</script>
</body>
</html>

@@ -0,0 +1,11 @@
/*eslint-env mocha*/
/*global assert*/

describe("e2e/06-bugs/122358-document-write", function() {
var tf = BOOMR.plugins.TestFramework;
var t = BOOMR_test;

it("Should have sent a beacon", function(done) {
t.ensureBeaconCount(done, 1);
});
});

0 comments on commit 2822a79

Please sign in to comment.
You can’t perform that action at this time.