Skip to content

Commit

Permalink
Merge pull request #505 from MindscapeHQ/ht/fix-recursive-loop-on-umd…
Browse files Browse the repository at this point in the history
…-init

Fix infinite recursive loop on UMD module init
  • Loading branch information
Hamish-taylor authored Sep 18, 2023
2 parents e9ea6ce + 143d78e commit 5edd4d6
Show file tree
Hide file tree
Showing 9 changed files with 124 additions and 35 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Unused normalize.css file
-->
## [2.27.3]

### Changed

- Fixes a rare condition with UMD where we assume rg4js is initialised but it is not, causing an infinite loop.



## [2.27.2]
Expand Down
2 changes: 1 addition & 1 deletion bower.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "raygun4js",
"version": "2.27.2",
"version": "2.27.3",
"homepage": "http://raygun.com",
"authors": [
"Mindscape <[email protected]>"
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
],
"title": "Raygun4js",
"description": "Raygun.com plugin for JavaScript",
"version": "2.27.2",
"version": "2.27.3",
"homepage": "https://github.com/MindscapeHQ/raygun4js",
"author": {
"name": "MindscapeHQ",
Expand Down
2 changes: 1 addition & 1 deletion raygun4js.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>raygun4js</id>
<version>2.27.2</version>
<version>2.27.3</version>
<title>Raygun4js</title>
<authors>Raygun Limited</authors>
<owners>Raygun Limited</owners>
Expand Down
2 changes: 1 addition & 1 deletion src/raygun.loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@
window[window['RaygunObject']] = function() {
return executor(arguments);
};

window['RaygunInitialized'] = true;
globalExecutorInstalled = true;
};

Expand Down
59 changes: 30 additions & 29 deletions src/umd.intro.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// https://github.com/umdjs/umd/blob/master/templates/returnExportsGlobal.js

(function (root, factory) {
(function(root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define('raygun4js', function () {
define('raygun4js', function() {
return (root.Raygun = factory());
});
} else if (typeof module === 'object' && module.exports) {
Expand All @@ -15,34 +15,35 @@
// Browser globals
root.Raygun = factory();
}
}(this, function () {
}(this, function() {

var windw = this || window || global;
var originalOnError = windw.onerror;
windw.onerror = function (msg, url, line, col, err) {
if (originalOnError) {
originalOnError(msg, url, line, col, err);
}
var windw = this || window || global;
var originalOnError = windw.onerror;
windw.onerror = function(msg, url, line, col, err) {
if (originalOnError) {
originalOnError(msg, url, line, col, err);
}

if (!err) {
err = new Error(msg);
}
if (!err) {
err = new Error(msg);
}

windw['rg4js'].q = windw['rg4js'].q || [];
windw['rg4js'].q.push({e: err});
};
windw['rg4js'].q = windw['rg4js'].q || [];
windw['rg4js'].q.push({ e: err });
};
// Similar approach as the snippet, creates the rg4js proxy function, which is exported in umd.outro.js once the
// script is executed, and later overwritten by the loader once it's finished
(function(wind) {
wind['RaygunObject'] = 'rg4js';
wind[wind['RaygunObject']] = wind[wind['RaygunObject']] || function() {
if (wind && typeof wind['Raygun'] === 'undefined' ||
(typeof document === 'undefined' || document.readyState !== 'complete') || (!wind['RaygunInitialized'])) {
// onload hasn't been called, cache the commands just like the snippet
(wind[wind['RaygunObject']].o = wind[wind['RaygunObject']].o || []).push(arguments)
} else {
// onload has been called and provider has executed, call the executor proxy function
return wind[wind['RaygunObject']](arguments[0], arguments[1]);
}

// Similar approach as the snippet, creates the rg4js proxy function, which is exported in umd.outro.js once the
// script is executed, and later overwritten by the loader once it's finished
(function(wind) { wind['RaygunObject'] = 'rg4js';
wind[wind['RaygunObject']] = wind[wind['RaygunObject']] || function() {
if (wind && typeof wind['Raygun'] === 'undefined' ||
(typeof document === 'undefined' || document.readyState !== 'complete')) {
// onload hasn't been called, cache the commands just like the snippet
(wind[wind['RaygunObject']].o = wind[wind['RaygunObject']].o || []).push(arguments)
} else {
// onload has been called and provider has executed, call the executor proxy function
return wind[wind['RaygunObject']](arguments[0], arguments[1]);
}

}})(windw);
}
})(windw);
33 changes: 33 additions & 0 deletions tests/fixtures/v2/UMDInfiniteLoop.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<!DOCTYPE html>
<html>

<head>
<title>Raygun4JS with V2 API</title>
<script src="/fixtures/common/instrumentXHRs.js"></script>
<script>
document.onreadystatechange = () => {
if (document.readyState === "complete") {
rg4js('apiKey', 'abcdef=='); //this should trigger an infinite loop
rg4js('enableCrashReporting', false);
rg4js('enablePulse', true);
}
};
</script>
<script src="/dist/raygun.umd.min.js"></script>
</head>

<body>
<script type="text/javascript">

setTimeout(function () {
rg4js('trackEvent', {
type: 'customTiming',
name: 'timingName',
duration: 100,
});
}, 250);
</script>

</body>

</html>
49 changes: 49 additions & 0 deletions tests/specs/v2/UMDInfiniteLoopTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
var webdriverio = require('webdriverio');




/**
* What does this test do?
* When using the UMD module, if raygun events are fired (e.g. rg4js('send', ...))
* before raygun is fully loaded they are stored in an object on the window.
* When Raygun loads these are then processed.
* However a bug was found where we were assuming raygun was loaded when
* `document.readyState === complete` but it is not loaded until slightly
* after when the `load` event is fired. This means that if a rayugn event
* is processed in this gap an infinite loop can be caused.
* This test ensures graceful handling of this situation
*/
describe("UMD Infinite loop test", function() {
beforeEach(async function() {
/**
* Clears the session between tests to ensure
* that the sessionstart event is always fired
*/
await browser.reloadSession();
});

describe('test infinite loop is not caused', function() {
beforeEach(async function() {
await browser.url('http://localhost:4567/fixtures/v2/UMDInfiniteLoop.html');
await browser.pause(1000);
});

it('succesfully sends the event', async function() {
var customTimingData = await browser.execute(function() {
console.log(window.__requestPayloads)
return window.__requestPayloads[2];
});
console.log(customTimingData.eventData[0].data);
expect(JSON.parse(customTimingData.eventData[0].data)[0]).toEqual({
timing: {
a: "0.00",
du: "100.00",
t: "t"
},
url: "timingName",
parentResource: { url: 'http://localhost:4567/fixtures/v2/UMDInfiniteLoop.html', type: 'p' }
});
});
});
});

0 comments on commit 5edd4d6

Please sign in to comment.