Skip to content

Commit

Permalink
SPA aborted load beacons
Browse files Browse the repository at this point in the history
  • Loading branch information
querymetrics authored and nicjansma committed Apr 4, 2018
1 parent 6e36d17 commit 6173215
Show file tree
Hide file tree
Showing 8 changed files with 407 additions and 15 deletions.
23 changes: 18 additions & 5 deletions boomerang.js
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ BOOMR_check_doc_domain();
if (!ev) { ev = w.event; }
if (ev.target) { target = ev.target; }
else if (ev.srcElement) { target = ev.srcElement; }
if (target.nodeType === 3) {// defeat Safari bug
if (target.nodeType === 3) { // defeat Safari bug
target = target.parentNode;
}

Expand Down Expand Up @@ -589,7 +589,7 @@ BOOMR_check_doc_domain();
for (i = 0, l = cookies_a.length; i < l; i++) {
kv = cookies_a[i].split("=");
if (kv[0]) {
kv.push(""); // just in case there's no value
kv.push(""); // just in case there's no value
cookies[decodeURIComponent(kv[0])] = decodeURIComponent(kv[1]);
gotcookies = true;
}
Expand Down Expand Up @@ -1302,6 +1302,17 @@ BOOMR_check_doc_domain();
return impl.fireEvent(e_name, data);
},

/**
* Subscribe to an event
*
* @param {string} e_name Event name
* @param {function} fn callback function
* @param {object} cb_data Any passthrough data for the callback function
* @param {object} cb_scope The scope of the callback function if it is a method of an object
* @param {Boolean} once If true subscribe to only one event call
*
* @returns {BOOMR} Boomerang object
*/
subscribe: function(e_name, fn, cb_data, cb_scope, once) {
var i, handler, ev;

Expand Down Expand Up @@ -1337,7 +1348,7 @@ BOOMR_check_doc_domain();
// Attach unload handlers directly to the window.onunload and
// window.onbeforeunload events. The first of the two to fire will clear
// fn so that the second doesn't fire. We do this because technically
// onbeforeunload is the right event to fire, but all browsers don't
// onbeforeunload is the right event to fire, but not all browsers
// support it. This allows us to fall back to onunload when onbeforeunload
// isn't implemented
if (e_name === "page_unload" || e_name === "before_unload") {
Expand Down Expand Up @@ -1691,8 +1702,10 @@ BOOMR_check_doc_domain();
var isSPA = BOOMR.utils.inArray(impl.vars["http.initiator"], BOOMR.constants.BEACON_TYPE_SPAS);
var isPageLoad = typeof impl.vars["http.initiator"] === "undefined" || isSPA;

var pgu = isSPA ? d.URL : d.URL.replace(/#.*/, "");
impl.vars.pgu = BOOMR.utils.cleanupURL(pgu);
if (!impl.vars.pgu) {
impl.vars.pgu = isSPA ? d.URL : d.URL.replace(/#.*/, "");
}
impl.vars.pgu = BOOMR.utils.cleanupURL(impl.vars.pgu);

// Use the current document.URL if it hasn't already been set, or for SPA apps,
// on each new beacon (since each SPA soft navigation might change the URL)
Expand Down
49 changes: 43 additions & 6 deletions plugins/auto-xhr.js
Original file line number Diff line number Diff line change
Expand Up @@ -270,11 +270,13 @@
},
i,
last_ev,
last_ev_index,
index = this.pending_events.length;

for (i = index - 1; i >= 0; i--) {
if (this.pending_events[i] && !this.pending_events[i].complete) {
last_ev = this.pending_events[i];
last_ev_index = i;
break;
}
}
Expand All @@ -287,7 +289,7 @@
// continue with new event
}
// last_ev will no longer receive watches as ev will receive them
// last_ev will wait fall interesting nodes and then send event
// last_ev will wait for all interesting nodes and then send event
}
else if (last_ev.type === "xhr") {
// 3.2
Expand All @@ -305,6 +307,19 @@
if (ev.type === "xhr") {
return null;
}

// If we have a pending SPA event, send an aborted load beacon before
// adding the new SPA event
if (BOOMR.utils.inArray(ev.type, BOOMR.constants.BEACON_TYPE_SPAS)) {
BOOMR.debug("Aborting previous SPA navigation");

// mark the end of this navigation as now
last_ev.resource.timing.loadEventEnd = BOOMR.now();
last_ev.aborted = true;

// send the previous SPA
this.sendEvent(last_ev_index);
}
}
}

Expand Down Expand Up @@ -422,7 +437,7 @@
* @param {number} eventIndex - index of the event in the pending_events array
*/
MutationHandler.prototype.sendResource = function(resource, eventIndex) {
var self = this;
var self = this, ev = self.pending_events[eventIndex];

// Use 'requestStart' as the startTime of the resource, if given
var startTime = resource.timing ? resource.timing.requestStart : undefined;
Expand Down Expand Up @@ -461,20 +476,31 @@
// For SPAs, calculate Back-End and Front-End timings
if (BOOMR.utils.inArray(resource.initiator, BOOMR.constants.BEACON_TYPE_SPAS)) {
self.calculateSpaTimings(resource);

// If the SPA load was aborted, set the rt.quit and rt.abld flags
if (typeof eventIndex === "number" && self.pending_events[eventIndex].aborted) {
// Save the URL otherwise it might change before we have a chance to put it on the beacon
BOOMR.addVar("pgu", d.URL);
BOOMR.addVar("rt.quit", "");
BOOMR.addVar("rt.abld", "");

impl.addedVars.push("pgu", "rt.quit", "rt.abld");
}
}

BOOMR.responseEnd(resource, startTime, resource);

if (eventIndex) {
if (typeof eventIndex === "number") {
self.pending_events[eventIndex] = undefined;
}
};

// send the beacon if we were not told to hold it
if (!resource.wait) {
// if this is a SPA event, make sure it doesn't fire until onload
if (BOOMR.utils.inArray(resource.initiator, BOOMR.constants.BEACON_TYPE_SPAS)) {
if (d && d.readyState && d.readyState !== "complete") {
// if this is a SPA Hard navigation, make sure it doesn't fire until onload
if (resource.initiator === "spa_hard") {
// don't wait for onload if this was an aborted SPA navigation
if ((!ev || !ev.aborted) && d && d.readyState && d.readyState !== "complete") {
BOOMR.window.addEventListener("load", function() {
sendResponseEnd(true);
});
Expand Down Expand Up @@ -1361,6 +1387,8 @@
ie1011fix: true,
excludeFilters: [],
initialized: false,
addedVars: [],

/**
* Filter function iterating over all available {@link FilterObject}s if returns true will not instrument an XHR
* @param {HTMLAnchorElement} anchor - HTMLAnchorElement node created with the XHRs URL as `href` to evaluate by {@link FilterObject}s and passed to {@link FilterObject#cb} callbacks.
Expand Down Expand Up @@ -1396,6 +1424,13 @@
}
}
return false;
},

clear: function() {
if (impl.addedVars && impl.addedVars.length > 0) {
BOOMR.removeVar(impl.addedVars);
impl.addedVars = [];
}
}
};

Expand Down Expand Up @@ -1564,6 +1599,8 @@
}

BOOMR.registerEvent("onxhrerror");

BOOMR.subscribe("onbeacon", impl.clear, null, impl);
},
getMutationHandler: function() {
return handler;
Expand Down
6 changes: 6 additions & 0 deletions plugins/history.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@
impl.routeChangeInProgress = false;
}
else {
// don't track the SPA route change until the onload (page_ready)
// has fired
if (impl.disableHardNav && !BOOMR.onloadFired()) {
return;
}

if (!impl.routeChangeInProgress) {
log("routeChange triggered, sending route_change() event");
impl.routeChangeInProgress = true;
Expand Down
5 changes: 3 additions & 2 deletions plugins/rt.js
Original file line number Diff line number Diff line change
Expand Up @@ -915,7 +915,7 @@

if (ename === "load" ||
ename === "visible" ||
(ename === "xhr" && edata && edata.initiator === "spa_hard")) {
(ename === "xhr" && edata && edata.initiator === "spa_hard")) {
// Only add Boomerang timings to page load and SPA beacons
impl.getBoomerangTimings();
}
Expand All @@ -938,7 +938,7 @@
// make sure old variables don't stick around
BOOMR.removeVar(
"t_done", "t_page", "t_resp", "t_postrender", "t_prerender", "t_load", "t_other",
"rt.tstart", "rt.nstart", "rt.cstart", "rt.bstart", "rt.end", "rt.subres", "rt.abld",
"rt.tstart", "rt.nstart", "rt.cstart", "rt.bstart", "rt.end", "rt.subres",
"http.errno", "http.method", "xhr.sync"
);

Expand Down Expand Up @@ -998,6 +998,7 @@

if (!impl.onloadfired) {
BOOMR.addVar("rt.abld", "");
impl.addedVars.push("rt.abld");
}

if (!impl.visiblefired) {
Expand Down
8 changes: 6 additions & 2 deletions plugins/spa.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,8 @@
* @param {object[]} routeFilterArgs Route Filter arguments
*/
route_change: function(onComplete, routeFilterArgs) {
var firedEvent = false;

// if we have a routeFilter, see if they want to track this route
if (routeFilter) {
try {
Expand Down Expand Up @@ -214,11 +216,13 @@
firstSpaNav = false;

if (!initialRouteChangeCompleted || typeof onComplete === "function") {
initialRouteChangeCompleted = true;

// if we haven't completed our initial SPA navigation yet (this is a hard nav), wait
// for all of the resources to be downloaded
resource.onComplete = function(onCompleteResource) {
if (!initialRouteChangeCompleted) {
initialRouteChangeCompleted = true;
if (!firedEvent) {
firedEvent = true;

// fire a SPA navigation completed event so that other plugins can act on it
BOOMR.fireEvent("spa_navigation");
Expand Down
48 changes: 48 additions & 0 deletions tests/page-templates/05-angular/113-route-change-abld.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<%= header %>
<%= boomerangScript %>
<base href="/pages/05-angular/04-route-change.html" />
<script src="../../vendor/angular/angular.js"></script>
<script src="../../vendor/angular-resource/angular-resource.js"></script>
<script src="../../vendor/angular-route/angular-route.js"></script>
<script src="../../vendor/resourcetiming-compression/src/resourcetiming-decompression.js" type="text/javascript"></script>
<script>
window.angular_imgs = [5000];

window.angular_html5_mode = true;
</script>
<script src="support/app.js"></script>
<div ng-app="app">
<div ng-view>
</div>
</div>
<script src="113-route-change-abld.js" type="text/javascript"></script>
<script>

// view a widget then come back so debugging (F5) is easier
app.run(["$location", "$timeout", function($location, $timeout) {
// issue a route change while the spa hard is still loading
$timeout(function() {
window.nav1time = BOOMR.now();
$location.url("/widgets/10");

// issue a route change while the spa soft is still loading
$timeout(function() {
window.nav2time = BOOMR.now();
$location.url("113-route-change-abld.html");
}, 2000);
}, 2000);
}]);

BOOMR_test.init({
testAfterOnBeacon: 3,
Angular: {
enabled: true
},
ResourceTiming: {
enabled: true
},
instrument_xhr: true,
autorun: false
});
</script>
<%= footer %>
Loading

0 comments on commit 6173215

Please sign in to comment.