Bug 1313119 - wrap all rep render() methods with try/catch;r=nchevobbe
authorJulian Descottes <jdescottes@mozilla.com>
Fri, 30 Dec 2016 18:12:38 +0100
changeset 328499 42ea47c0d3d45734d816280f9c3efb041afa606f
parent 328498 d4446c207c1359ded2bde9288e5eab27e8066f0b
child 328530 a4014426d32456f82c7d60c2827ee5bddf198917
push id35794
push userjdescottes@mozilla.com
push dateMon, 09 Jan 2017 09:16:02 +0000
treeherderautoland@42ea47c0d3d4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnchevobbe
bugs1313119
milestone53.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1313119 - wrap all rep render() methods with try/catch;r=nchevobbe MozReview-Commit-ID: 5nXcv3in4WT
devtools/client/shared/components/reps/array.js
devtools/client/shared/components/reps/attribute.js
devtools/client/shared/components/reps/caption.js
devtools/client/shared/components/reps/comment-node.js
devtools/client/shared/components/reps/date-time.js
devtools/client/shared/components/reps/document.js
devtools/client/shared/components/reps/element-node.js
devtools/client/shared/components/reps/error.js
devtools/client/shared/components/reps/event.js
devtools/client/shared/components/reps/function.js
devtools/client/shared/components/reps/grip-array.js
devtools/client/shared/components/reps/grip-map.js
devtools/client/shared/components/reps/grip.js
devtools/client/shared/components/reps/infinity.js
devtools/client/shared/components/reps/long-string.js
devtools/client/shared/components/reps/nan.js
devtools/client/shared/components/reps/null.js
devtools/client/shared/components/reps/number.js
devtools/client/shared/components/reps/object-with-text.js
devtools/client/shared/components/reps/object-with-url.js
devtools/client/shared/components/reps/object.js
devtools/client/shared/components/reps/promise.js
devtools/client/shared/components/reps/prop-rep.js
devtools/client/shared/components/reps/regexp.js
devtools/client/shared/components/reps/rep-utils.js
devtools/client/shared/components/reps/reps.css
devtools/client/shared/components/reps/string.js
devtools/client/shared/components/reps/stylesheet.js
devtools/client/shared/components/reps/symbol.js
devtools/client/shared/components/reps/text-node.js
devtools/client/shared/components/reps/undefined.js
devtools/client/shared/components/reps/window.js
devtools/client/shared/components/test/mochitest/chrome.ini
devtools/client/shared/components/test/mochitest/test_reps_failure.html
--- a/devtools/client/shared/components/reps/array.js
+++ b/devtools/client/shared/components/reps/array.js
@@ -5,17 +5,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   // Dependencies
   const React = require("devtools/client/shared/vendor/react");
-  const { createFactories } = require("./rep-utils");
+  const {
+    createFactories,
+    wrapRender,
+  } = require("./rep-utils");
   const { Caption } = createFactories(require("./caption"));
   const { MODE } = require("./constants");
 
   // Shortcuts
   const DOM = React.DOM;
 
   /**
    * Renders an array. The array is enclosed by left and right bracket
@@ -111,17 +114,17 @@ define(function (require, exports, modul
     // Event Handlers
 
     onToggleProperties: function (event) {
     },
 
     onClickBracket: function (event) {
     },
 
-    render: function () {
+    render: wrapRender(function () {
       let {
         object,
         mode = MODE.SHORT,
       } = this.props;
 
       let items;
       let brackets;
       let needSpace = function (space) {
@@ -153,38 +156,38 @@ define(function (require, exports, modul
             object: object
           }, brackets.right),
           DOM.span({
             className: "arrayProperties",
             role: "group"}
           )
         )
       );
-    },
+    }),
   });
 
   /**
    * Renders array item. Individual values are separated by a comma.
    */
   let ItemRep = React.createFactory(React.createClass({
     displayName: "ItemRep",
 
-    render: function () {
+    render: wrapRender(function () {
       const { Rep } = createFactories(require("./rep"));
 
       let object = this.props.object;
       let delim = this.props.delim;
       let mode = this.props.mode;
       return (
         DOM.span({},
           Rep({object: object, mode: mode}),
           delim
         )
       );
-    }
+    })
   }));
 
   function supportsObject(object, type) {
     return Array.isArray(object) ||
       Object.prototype.toString.call(object) === "[object Arguments]";
   }
 
   // Exports from this module
--- a/devtools/client/shared/components/reps/attribute.js
+++ b/devtools/client/shared/components/reps/attribute.js
@@ -6,17 +6,21 @@
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   // ReactJS
   const React = require("devtools/client/shared/vendor/react");
 
   // Reps
-  const { createFactories, isGrip } = require("./rep-utils");
+  const {
+    createFactories,
+    isGrip,
+    wrapRender,
+  } = require("./rep-utils");
   const { StringRep } = require("./string");
 
   // Shortcuts
   const { span } = React.DOM;
   const { rep: StringRepFactory } = createFactories(StringRep);
 
   /**
    * Renders DOM attribute
@@ -27,17 +31,17 @@ define(function (require, exports, modul
     propTypes: {
       object: React.PropTypes.object.isRequired
     },
 
     getTitle: function (grip) {
       return grip.preview.nodeName;
     },
 
-    render: function () {
+    render: wrapRender(function () {
       let object = this.props.object;
       let value = object.preview.value;
       let objectLink = this.props.objectLink || span;
 
       return (
         objectLink({className: "objectLink-Attr", object},
           span({},
             span({className: "attrTitle"},
@@ -45,17 +49,17 @@ define(function (require, exports, modul
             ),
             span({className: "attrEqual"},
               "="
             ),
             StringRepFactory({object: value})
           )
         )
       );
-    },
+    }),
   });
 
   // Registration
 
   function supportsObject(grip, type) {
     if (!isGrip(grip)) {
       return false;
     }
--- a/devtools/client/shared/components/reps/caption.js
+++ b/devtools/client/shared/components/reps/caption.js
@@ -7,25 +7,27 @@
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   // Dependencies
   const React = require("devtools/client/shared/vendor/react");
   const DOM = React.DOM;
 
+  const { wrapRender } = require("./rep-utils");
+
   /**
    * Renders a caption. This template is used by other components
    * that needs to distinguish between a simple text/value and a label.
    */
   const Caption = React.createClass({
     displayName: "Caption",
 
-    render: function () {
+    render: wrapRender(function () {
       return (
         DOM.span({"className": "caption"}, this.props.object)
       );
-    },
+    }),
   });
 
   // Exports from this module
   exports.Caption = Caption;
 });
--- a/devtools/client/shared/components/reps/comment-node.js
+++ b/devtools/client/shared/components/reps/comment-node.js
@@ -4,17 +4,22 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   // Dependencies
   const React = require("devtools/client/shared/vendor/react");
-  const { isGrip, cropString, cropMultipleLines } = require("./rep-utils");
+  const {
+    isGrip,
+    cropString,
+    cropMultipleLines,
+    wrapRender,
+  } = require("./rep-utils");
   const { MODE } = require("./constants");
   const nodeConstants = require("devtools/shared/dom-node-constants");
 
   // Shortcuts
   const { span } = React.DOM;
 
   /**
    * Renders DOM comment node.
@@ -23,31 +28,31 @@ define(function (require, exports, modul
     displayName: "CommentNode",
 
     propTypes: {
       object: React.PropTypes.object.isRequired,
       // @TODO Change this to Object.values once it's supported in Node's version of V8
       mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
     },
 
-    render: function () {
+    render: wrapRender(function () {
       let {
         object,
         mode = MODE.SHORT
       } = this.props;
 
       let {textContent} = object.preview;
       if (mode === MODE.TINY) {
         textContent = cropMultipleLines(textContent, 30);
       } else if (mode === MODE.SHORT) {
         textContent = cropString(textContent, 50);
       }
 
       return span({className: "objectBox theme-comment"}, `<!-- ${textContent} -->`);
-    },
+    }),
   });
 
   // Registration
   function supportsObject(object, type) {
     if (!isGrip(object)) {
       return false;
     }
     return object.preview && object.preview.nodeType === nodeConstants.COMMENT_NODE;
--- a/devtools/client/shared/components/reps/date-time.js
+++ b/devtools/client/shared/components/reps/date-time.js
@@ -6,17 +6,20 @@
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   // ReactJS
   const React = require("devtools/client/shared/vendor/react");
 
   // Reps
-  const { isGrip } = require("./rep-utils");
+  const {
+    isGrip,
+    wrapRender,
+  } = require("./rep-utils");
 
   // Shortcuts
   const { span } = React.DOM;
 
   /**
    * Used to render JS built-in Date() object.
    */
   let DateTime = React.createClass({
@@ -30,31 +33,32 @@ define(function (require, exports, modul
       if (this.props.objectLink) {
         return this.props.objectLink({
           object: grip
         }, grip.class + " ");
       }
       return "";
     },
 
-    render: function () {
+    render: wrapRender(function () {
       let grip = this.props.object;
       let date;
       try {
         date = span({className: "objectBox"},
           this.getTitle(grip),
           span({className: "Date"},
             new Date(grip.preview.timestamp).toISOString()
           )
         );
       } catch (e) {
         date = span({className: "objectBox"}, "Invalid Date");
       }
+
       return date;
-    },
+    }),
   });
 
   // Registration
 
   function supportsObject(grip, type) {
     if (!isGrip(grip)) {
       return false;
     }
--- a/devtools/client/shared/components/reps/document.js
+++ b/devtools/client/shared/components/reps/document.js
@@ -6,17 +6,21 @@
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   // ReactJS
   const React = require("devtools/client/shared/vendor/react");
 
   // Reps
-  const { isGrip, getURLDisplayString } = require("./rep-utils");
+  const {
+    isGrip,
+    getURLDisplayString,
+    wrapRender,
+  } = require("./rep-utils");
 
   // Shortcuts
   const { span } = React.DOM;
 
   /**
    * Renders DOM document object.
    */
   let Document = React.createClass({
@@ -41,28 +45,28 @@ define(function (require, exports, modul
       }
       return "";
     },
 
     getTooltip: function (doc) {
       return doc.location.href;
     },
 
-    render: function () {
+    render: wrapRender(function () {
       let grip = this.props.object;
 
       return (
         span({className: "objectBox objectBox-object"},
           this.getTitle(grip),
           span({className: "objectPropValue"},
             this.getLocation(grip)
           )
         )
       );
-    },
+    }),
   });
 
   // Registration
 
   function supportsObject(object, type) {
     if (!isGrip(object)) {
       return false;
     }
--- a/devtools/client/shared/components/reps/element-node.js
+++ b/devtools/client/shared/components/reps/element-node.js
@@ -6,17 +6,20 @@
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   // ReactJS
   const React = require("devtools/client/shared/vendor/react");
 
   // Utils
-  const { isGrip } = require("./rep-utils");
+  const {
+    isGrip,
+    wrapRender,
+  } = require("./rep-utils");
   const { MODE } = require("./constants");
   const nodeConstants = require("devtools/shared/dom-node-constants");
 
   // Shortcuts
   const { span } = React.DOM;
 
   /**
    * Renders DOM element node.
@@ -83,17 +86,17 @@ define(function (require, exports, modul
       return [
         "<",
         nodeNameElement,
         ...attributeElements,
         ">",
       ];
     },
 
-    render: function () {
+    render: wrapRender(function () {
       let {
         object,
         mode,
         onDOMNodeMouseOver,
         onDOMNodeMouseOut
       } = this.props;
       let elements = this.getElements(object, mode);
       let objectLink = this.props.objectLink || span;
@@ -109,17 +112,17 @@ define(function (require, exports, modul
         Object.assign(baseConfig, {
           onMouseOut: onDOMNodeMouseOut
         });
       }
 
       return objectLink({object},
         span(baseConfig, ...elements)
       );
-    },
+    }),
   });
 
   // Registration
   function supportsObject(object, type) {
     if (!isGrip(object)) {
       return false;
     }
     return object.preview && object.preview.nodeType === nodeConstants.ELEMENT_NODE;
--- a/devtools/client/shared/components/reps/error.js
+++ b/devtools/client/shared/components/reps/error.js
@@ -3,34 +3,37 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   // ReactJS
   const React = require("devtools/client/shared/vendor/react");
   // Utils
-  const { isGrip } = require("./rep-utils");
+  const {
+    isGrip,
+    wrapRender,
+  } = require("./rep-utils");
   const { MODE } = require("./constants");
   // Shortcuts
   const { span } = React.DOM;
 
   /**
    * Renders Error objects.
    */
   const ErrorRep = React.createClass({
     displayName: "Error",
 
     propTypes: {
       object: React.PropTypes.object.isRequired,
       // @TODO Change this to Object.values once it's supported in Node's version of V8
       mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
     },
 
-    render: function () {
+    render: wrapRender(function () {
       let object = this.props.object;
       let preview = object.preview;
       let name = preview && preview.name
         ? preview.name
         : "Error";
 
       let content = this.props.mode === MODE.TINY
         ? name
@@ -46,17 +49,17 @@ define(function (require, exports, modul
       }
 
       let objectLink = this.props.objectLink || span;
       return (
         objectLink({object, className: "objectBox-stackTrace"},
           span({}, content)
         )
       );
-    },
+    }),
   });
 
   // Registration
   function supportsObject(object, type) {
     if (!isGrip(object)) {
       return false;
     }
     return (object.preview && type === "Error");
--- a/devtools/client/shared/components/reps/event.js
+++ b/devtools/client/shared/components/reps/event.js
@@ -6,17 +6,21 @@
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   // ReactJS
   const React = require("devtools/client/shared/vendor/react");
 
   // Reps
-  const { createFactories, isGrip } = require("./rep-utils");
+  const {
+    createFactories,
+    isGrip,
+    wrapRender,
+  } = require("./rep-utils");
   const { rep } = createFactories(require("./grip").Grip);
 
   /**
    * Renders DOM event objects.
    */
   let Event = React.createClass({
     displayName: "event",
 
@@ -29,17 +33,17 @@ define(function (require, exports, modul
       let title = preview.type;
 
       if (preview.eventKind == "key" && preview.modifiers && preview.modifiers.length) {
         title = `${title} ${preview.modifiers.join("-")}`;
       }
       return title;
     },
 
-    render: function () {
+    render: wrapRender(function () {
       // Use `Object.assign` to keep `this.props` without changes because:
       // 1. JSON.stringify/JSON.parse is slow.
       // 2. Immutable.js is planned for the future.
       let props = Object.assign({
         title: this.getTitle(this.props)
       }, this.props);
       props.object = Object.assign({}, this.props.object);
       props.object.preview = Object.assign({}, this.props.object.preview);
@@ -75,17 +79,17 @@ define(function (require, exports, modul
         default:
           props.isInterestingProp = (type, value, name) => {
             // We want to show the properties in the order they are declared.
             return Object.keys(props.object.preview.ownProperties).includes(name);
           };
       }
 
       return rep(props);
-    }
+    })
   });
 
   // Registration
 
   function supportsObject(grip, type) {
     if (!isGrip(grip)) {
       return false;
     }
--- a/devtools/client/shared/components/reps/function.js
+++ b/devtools/client/shared/components/reps/function.js
@@ -6,17 +6,21 @@
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   // ReactJS
   const React = require("devtools/client/shared/vendor/react");
 
   // Reps
-  const { isGrip, cropString } = require("./rep-utils");
+  const {
+    isGrip,
+    cropString,
+    wrapRender,
+  } = require("./rep-utils");
 
   // Shortcuts
   const { span } = React.DOM;
 
   /**
    * This component represents a template for Function objects.
    */
   let Func = React.createClass({
@@ -35,28 +39,28 @@ define(function (require, exports, modul
       return "";
     },
 
     summarizeFunction: function (grip) {
       let name = grip.userDisplayName || grip.displayName || grip.name || "function";
       return cropString(name + "()", 100);
     },
 
-    render: function () {
+    render: wrapRender(function () {
       let grip = this.props.object;
 
       return (
         // Set dir="ltr" to prevent function parentheses from
         // appearing in the wrong direction
         span({dir: "ltr", className: "objectBox objectBox-function"},
           this.getTitle(grip),
           this.summarizeFunction(grip)
         )
       );
-    },
+    }),
   });
 
   // Registration
 
   function supportsObject(grip, type) {
     if (!isGrip(grip)) {
       return (type == "function");
     }
--- a/devtools/client/shared/components/reps/grip-array.js
+++ b/devtools/client/shared/components/reps/grip-array.js
@@ -5,17 +5,21 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   // Dependencies
   const React = require("devtools/client/shared/vendor/react");
-  const { createFactories, isGrip } = require("./rep-utils");
+  const {
+    createFactories,
+    isGrip,
+    wrapRender,
+  } = require("./rep-utils");
   const { Caption } = createFactories(require("./caption"));
   const { MODE } = require("./constants");
 
   // Shortcuts
   const { span } = React.DOM;
 
   /**
    * Renders an array. The array is enclosed by left and right bracket
@@ -104,17 +108,17 @@ define(function (require, exports, modul
             object: this.props.object
           }, leftItemNum + " moreā€¦")
         }));
       }
 
       return items;
     },
 
-    render: function () {
+    render: wrapRender(function () {
       let {
         object,
         mode = MODE.SHORT
       } = this.props;
 
       let items;
       let brackets;
       let needSpace = function (space) {
@@ -149,17 +153,17 @@ define(function (require, exports, modul
             object: object
           }, brackets.right),
           span({
             className: "arrayProperties",
             role: "group"}
           )
         )
       );
-    },
+    }),
   });
 
   /**
    * Renders array item. Individual values are separated by
    * a delimiter (a comma by default).
    */
   let GripArrayItem = React.createFactory(React.createClass({
     displayName: "GripArrayItem",
--- a/devtools/client/shared/components/reps/grip-map.js
+++ b/devtools/client/shared/components/reps/grip-map.js
@@ -4,17 +4,21 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   // Dependencies
   const React = require("devtools/client/shared/vendor/react");
-  const { createFactories, isGrip } = require("./rep-utils");
+  const {
+    createFactories,
+    isGrip,
+    wrapRender,
+  } = require("./rep-utils");
   const { Caption } = createFactories(require("./caption"));
   const { PropRep } = createFactories(require("./prop-rep"));
   const { MODE } = require("./constants");
   // Shortcuts
   const { span } = React.DOM;
   /**
    * Renders an map. A map is represented by a list of its
    * entries enclosed in curly brackets.
@@ -139,17 +143,17 @@ define(function (require, exports, modul
               indexes.push(i);
             }
           }
 
           return indexes;
         }, []);
     },
 
-    render: function () {
+    render: wrapRender(function () {
       let object = this.props.object;
       let props = this.safeEntriesIterator(object,
         (this.props.mode === MODE.LONG) ? 10 : 3);
 
       let objectLink = this.props.objectLink || span;
       if (this.props.mode === MODE.TINY) {
         return (
           span({className: "objectBox objectBox-object"},
@@ -171,17 +175,17 @@ define(function (require, exports, modul
           }, " { "),
           props,
           objectLink({
             className: "objectRightBrace",
             object: object
           }, " }")
         )
       );
-    },
+    }),
   });
 
   function supportsObject(grip, type) {
     if (!isGrip(grip)) {
       return false;
     }
     return (grip.preview && grip.preview.kind == "MapLike");
   }
--- a/devtools/client/shared/components/reps/grip.js
+++ b/devtools/client/shared/components/reps/grip.js
@@ -5,17 +5,21 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   // ReactJS
   const React = require("devtools/client/shared/vendor/react");
   // Dependencies
-  const { createFactories, isGrip } = require("./rep-utils");
+  const {
+    createFactories,
+    isGrip,
+    wrapRender,
+  } = require("./rep-utils");
   const { Caption } = createFactories(require("./caption"));
   const { PropRep } = createFactories(require("./prop-rep"));
   const { MODE } = require("./constants");
   // Shortcuts
   const { span } = React.DOM;
 
   /**
    * Renders generic grip. Grip is client representation
@@ -193,17 +197,17 @@ define(function (require, exports, modul
           value = property.value;
         } else if (keys.includes("getterValue")) {
           value = property.getterValue;
         }
       }
       return value;
     },
 
-    render: function () {
+    render: wrapRender(function () {
       let object = this.props.object;
       let props = this.safePropIterator(object,
         (this.props.mode === MODE.LONG) ? 10 : 3);
 
       let objectLink = this.props.objectLink || span;
       if (this.props.mode === MODE.TINY) {
         return (
           span({className: "objectBox objectBox-object"},
@@ -225,17 +229,17 @@ define(function (require, exports, modul
           }, " { "),
           ...props,
           objectLink({
             className: "objectRightBrace",
             object: object
           }, " }")
         )
       );
-    },
+    }),
   });
 
   // Registration
   function supportsObject(object, type) {
     if (!isGrip(object)) {
       return false;
     }
     return (object.preview && object.preview.ownProperties);
--- a/devtools/client/shared/components/reps/infinity.js
+++ b/devtools/client/shared/components/reps/infinity.js
@@ -6,32 +6,34 @@
 
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   // Dependencies
   const React = require("devtools/client/shared/vendor/react");
 
+  const { wrapRender } = require("./rep-utils");
+
   // Shortcuts
   const { span } = React.DOM;
 
   /**
    * Renders a Infinity object
    */
   const InfinityRep = React.createClass({
     displayName: "Infinity",
 
-    render: function () {
+    render: wrapRender(function () {
       return (
         span({className: "objectBox objectBox-number"},
           this.props.object.type
         )
       );
-    }
+    })
   });
 
   function supportsObject(object, type) {
     return type == "Infinity" || type == "-Infinity";
   }
 
   // Exports from this module
   exports.InfinityRep = {
--- a/devtools/client/shared/components/reps/long-string.js
+++ b/devtools/client/shared/components/reps/long-string.js
@@ -3,17 +3,21 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   // Dependencies
   const React = require("devtools/client/shared/vendor/react");
-  const { sanitizeString, isGrip } = require("./rep-utils");
+  const {
+    sanitizeString,
+    isGrip,
+    wrapRender,
+  } = require("./rep-utils");
   // Shortcuts
   const { span } = React.DOM;
 
   /**
    * Renders a long string grip.
    */
   const LongStringRep = React.createClass({
     displayName: "LongStringRep",
@@ -24,17 +28,17 @@ define(function (require, exports, modul
     },
 
     getDefaultProps: function () {
       return {
         useQuotes: true,
       };
     },
 
-    render: function () {
+    render: wrapRender(function () {
       let {
         cropLimit,
         member,
         object,
         style,
         useQuotes
       } = this.props;
       let {fullText, initial, length} = object;
@@ -48,17 +52,17 @@ define(function (require, exports, modul
         ? fullText || initial
         : initial.substring(0, cropLimit);
 
       if (string.length < length) {
         string += "\u2026";
       }
       let formattedString = useQuotes ? `"${string}"` : string;
       return span(config, sanitizeString(formattedString));
-    },
+    }),
   });
 
   function supportsObject(object, type) {
     if (!isGrip(object)) {
       return false;
     }
     return object.type === "longString";
   }
--- a/devtools/client/shared/components/reps/nan.js
+++ b/devtools/client/shared/components/reps/nan.js
@@ -6,32 +6,34 @@
 
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   // Dependencies
   const React = require("devtools/client/shared/vendor/react");
 
+  const { wrapRender } = require("./rep-utils");
+
   // Shortcuts
   const { span } = React.DOM;
 
   /**
    * Renders a NaN object
    */
   const NaNRep = React.createClass({
     displayName: "NaN",
 
-    render: function () {
+    render: wrapRender(function () {
       return (
         span({className: "objectBox objectBox-nan"},
           "NaN"
         )
       );
-    }
+    })
   });
 
   function supportsObject(object, type) {
     return type == "NaN";
   }
 
   // Exports from this module
   exports.NaNRep = {
--- a/devtools/client/shared/components/reps/null.js
+++ b/devtools/client/shared/components/reps/null.js
@@ -6,32 +6,34 @@
 
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   // Dependencies
   const React = require("devtools/client/shared/vendor/react");
 
+  const { wrapRender } = require("./rep-utils");
+
   // Shortcuts
   const { span } = React.DOM;
 
   /**
    * Renders null value
    */
   const Null = React.createClass({
     displayName: "NullRep",
 
-    render: function () {
+    render: wrapRender(function () {
       return (
         span({className: "objectBox objectBox-null"},
           "null"
         )
       );
-    },
+    }),
   });
 
   function supportsObject(object, type) {
     if (object && object.type && object.type == "null") {
       return true;
     }
 
     return (object == null);
--- a/devtools/client/shared/components/reps/number.js
+++ b/devtools/client/shared/components/reps/number.js
@@ -6,41 +6,43 @@
 
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   // Dependencies
   const React = require("devtools/client/shared/vendor/react");
 
+  const { wrapRender } = require("./rep-utils");
+
   // Shortcuts
   const { span } = React.DOM;
 
   /**
    * Renders a number
    */
   const Number = React.createClass({
     displayName: "Number",
 
     stringify: function (object) {
       let isNegativeZero = Object.is(object, -0) ||
         (object.type && object.type == "-0");
 
       return (isNegativeZero ? "-0" : String(object));
     },
 
-    render: function () {
+    render: wrapRender(function () {
       let value = this.props.object;
 
       return (
         span({className: "objectBox objectBox-number"},
           this.stringify(value)
         )
       );
-    }
+    })
   });
 
   function supportsObject(object, type) {
     return ["boolean", "number", "-0"].includes(type);
   }
 
   // Exports from this module
 
--- a/devtools/client/shared/components/reps/object-with-text.js
+++ b/devtools/client/shared/components/reps/object-with-text.js
@@ -6,17 +6,20 @@
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   // ReactJS
   const React = require("devtools/client/shared/vendor/react");
 
   // Reps
-  const { isGrip } = require("./rep-utils");
+  const {
+    isGrip,
+    wrapRender,
+  } = require("./rep-utils");
 
   // Shortcuts
   const { span } = React.DOM;
 
   /**
    * Renders a grip object with textual data.
    */
   let ObjectWithText = React.createClass({
@@ -40,27 +43,27 @@ define(function (require, exports, modul
     getType: function (grip) {
       return grip.class;
     },
 
     getDescription: function (grip) {
       return "\"" + grip.preview.text + "\"";
     },
 
-    render: function () {
+    render: wrapRender(function () {
       let grip = this.props.object;
       return (
         span({className: "objectBox objectBox-" + this.getType(grip)},
           this.getTitle(grip),
           span({className: "objectPropValue"},
             this.getDescription(grip)
           )
         )
       );
-    },
+    }),
   });
 
   // Registration
 
   function supportsObject(grip, type) {
     if (!isGrip(grip)) {
       return false;
     }
--- a/devtools/client/shared/components/reps/object-with-url.js
+++ b/devtools/client/shared/components/reps/object-with-url.js
@@ -6,17 +6,21 @@
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   // ReactJS
   const React = require("devtools/client/shared/vendor/react");
 
   // Reps
-  const { isGrip, getURLDisplayString } = require("./rep-utils");
+  const {
+    isGrip,
+    getURLDisplayString,
+    wrapRender,
+  } = require("./rep-utils");
 
   // Shortcuts
   const { span } = React.DOM;
 
   /**
    * Renders a grip object with URL data.
    */
   let ObjectWithURL = React.createClass({
@@ -40,27 +44,27 @@ define(function (require, exports, modul
     getType: function (grip) {
       return grip.class;
     },
 
     getDescription: function (grip) {
       return getURLDisplayString(grip.preview.url);
     },
 
-    render: function () {
+    render: wrapRender(function () {
       let grip = this.props.object;
       return (
         span({className: "objectBox objectBox-" + this.getType(grip)},
           this.getTitle(grip),
           span({className: "objectPropValue"},
             this.getDescription(grip)
           )
         )
       );
-    },
+    }),
   });
 
   // Registration
 
   function supportsObject(grip, type) {
     if (!isGrip(grip)) {
       return false;
     }
--- a/devtools/client/shared/components/reps/object.js
+++ b/devtools/client/shared/components/reps/object.js
@@ -4,17 +4,20 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   // Dependencies
   const React = require("devtools/client/shared/vendor/react");
-  const { createFactories } = require("./rep-utils");
+  const {
+    createFactories,
+    wrapRender,
+  } = require("./rep-utils");
   const { Caption } = createFactories(require("./caption"));
   const { PropRep } = createFactories(require("./prop-rep"));
   const { MODE } = require("./constants");
   // Shortcuts
   const { span } = React.DOM;
   /**
    * Renders an object. An object is represented by a list of its
    * properties enclosed in curly brackets.
@@ -127,17 +130,17 @@ define(function (require, exports, modul
         }
       } catch (err) {
         console.error(err);
       }
 
       return props;
     },
 
-    render: function () {
+    render: wrapRender(function () {
       let object = this.props.object;
       let props = this.safePropIterator(object);
       let objectLink = this.props.objectLink || span;
 
       if (this.props.mode === MODE.TINY || !props.length) {
         return (
           span({className: "objectBox objectBox-object"},
             objectLink({className: "objectTitle"}, this.getTitle(object))
@@ -154,17 +157,17 @@ define(function (require, exports, modul
           }, " { "),
           ...props,
           objectLink({
             className: "objectRightBrace",
             object: object
           }, " }")
         )
       );
-    },
+    }),
   });
   function supportsObject(object, type) {
     return true;
   }
 
   // Exports from this module
   exports.Obj = {
     rep: Obj,
--- a/devtools/client/shared/components/reps/promise.js
+++ b/devtools/client/shared/components/reps/promise.js
@@ -5,17 +5,22 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   // ReactJS
   const React = require("devtools/client/shared/vendor/react");
   // Dependencies
-  const { createFactories, isGrip } = require("./rep-utils");
+  const {
+    createFactories,
+    isGrip,
+    wrapRender,
+  } = require("./rep-utils");
+
   const { PropRep } = createFactories(require("./prop-rep"));
   const { MODE } = require("./constants");
   // Shortcuts
   const { span } = React.DOM;
 
   /**
    * Renders a DOM Promise object.
    */
@@ -50,17 +55,17 @@ define(function (require, exports, modul
           name: `<${key}>`,
           object: promiseState[key],
           equal: ": ",
           delim: i < keys.length - 1 ? ", " : ""
         }));
       });
     },
 
-    render: function () {
+    render: wrapRender(function () {
       const object = this.props.object;
       const {promiseState} = object;
       let objectLink = this.props.objectLink || span;
 
       if (this.props.mode === MODE.TINY) {
         let { Rep } = createFactories(require("./rep"));
 
         return (
@@ -89,17 +94,17 @@ define(function (require, exports, modul
           }, " { "),
           ...props,
           objectLink({
             className: "objectRightBrace",
             object: object
           }, " }")
         )
       );
-    },
+    }),
   });
 
   // Registration
   function supportsObject(object, type) {
     if (!isGrip(object)) {
       return false;
     }
     return type === "Promise";
--- a/devtools/client/shared/components/reps/prop-rep.js
+++ b/devtools/client/shared/components/reps/prop-rep.js
@@ -4,44 +4,47 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   // Dependencies
   const React = require("devtools/client/shared/vendor/react");
-  const { createFactories } = require("./rep-utils");
+  const {
+    createFactories,
+    wrapRender,
+  } = require("./rep-utils");
   const { MODE } = require("./constants");
   // Shortcuts
   const { span } = React.DOM;
 
   /**
    * Property for Obj (local JS objects), Grip (remote JS objects)
    * and GripMap (remote JS maps and weakmaps) reps.
    * It's used to render object properties.
    */
-  let PropRep = React.createFactory(React.createClass({
+  let PropRep = React.createClass({
     displayName: "PropRep",
 
     propTypes: {
       // Property name.
       name: React.PropTypes.oneOfType([
         React.PropTypes.string,
         React.PropTypes.object,
       ]).isRequired,
       // Equal character rendered between property name and value.
       equal: React.PropTypes.string,
       // Delimiter character used to separate individual properties.
       delim: React.PropTypes.string,
       // @TODO Change this to Object.values once it's supported in Node's version of V8
       mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
     },
 
-    render: function () {
+    render: wrapRender(function () {
       const { Grip } = require("./grip");
       let { Rep } = createFactories(require("./rep"));
 
       let key;
       // The key can be a simple string, for plain objects,
       // or another object for maps and weakmaps.
       if (typeof this.props.name === "string") {
         key = span({"className": "nodeName"}, this.props.name);
@@ -61,14 +64,14 @@ define(function (require, exports, modul
             "className": "objectEqual"
           }, this.props.equal),
           Rep(this.props),
           span({
             "className": "objectComma"
           }, this.props.delim)
         )
       );
-    }
-  }));
+    })
+  });
 
   // Exports from this module
   exports.PropRep = PropRep;
 });
--- a/devtools/client/shared/components/reps/regexp.js
+++ b/devtools/client/shared/components/reps/regexp.js
@@ -6,17 +6,20 @@
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   // ReactJS
   const React = require("devtools/client/shared/vendor/react");
 
   // Reps
-  const { isGrip } = require("./rep-utils");
+  const {
+    isGrip,
+    wrapRender,
+  } = require("./rep-utils");
 
   // Shortcuts
   const { span } = React.DOM;
 
   /**
    * Renders a grip object with regular expression.
    */
   let RegExp = React.createClass({
@@ -25,29 +28,29 @@ define(function (require, exports, modul
     propTypes: {
       object: React.PropTypes.object.isRequired,
     },
 
     getSource: function (grip) {
       return grip.displayString;
     },
 
-    render: function () {
+    render: wrapRender(function () {
       let grip = this.props.object;
       let objectLink = this.props.objectLink || span;
 
       return (
         span({className: "objectBox objectBox-regexp"},
           objectLink({
             object: grip,
             className: "regexpSource"
           }, this.getSource(grip))
         )
       );
-    },
+    }),
   });
 
   // Registration
 
   function supportsObject(object, type) {
     if (!isGrip(object)) {
       return false;
     }
--- a/devtools/client/shared/components/reps/rep-utils.js
+++ b/devtools/client/shared/components/reps/rep-utils.js
@@ -142,19 +142,41 @@ define(function (require, exports, modul
     return {
       protocol: m[1],
       domain: m[2],
       path: m[2] + m[3],
       name: m[4] + m[5]
     };
   }
 
+  /**
+   * Wrap the provided render() method of a rep in a try/catch block that will render a
+   * fallback rep if the render fails.
+   */
+  function wrapRender(renderMethod) {
+    return function () {
+      try {
+        return renderMethod.call(this);
+      } catch (e) {
+        return React.DOM.span(
+          {
+            className: "objectBox objectBox-failure",
+            title: "This object could not be rendered, " +
+                   "please file a bug on bugzilla.mozilla.org"
+          },
+          /* Labels have to be hardcoded for reps, see Bug 1317038. */
+          "Invalid object");
+      }
+    };
+  }
+
   // Exports from this module
   exports.createFactories = createFactories;
   exports.isGrip = isGrip;
   exports.cropString = cropString;
   exports.cropMultipleLines = cropMultipleLines;
   exports.parseURLParams = parseURLParams;
   exports.parseURLEncodedText = parseURLEncodedText;
   exports.getFileName = getFileName;
   exports.getURLDisplayString = getURLDisplayString;
+  exports.wrapRender = wrapRender;
   exports.sanitizeString = sanitizeString;
 });
--- a/devtools/client/shared/components/reps/reps.css
+++ b/devtools/client/shared/components/reps/reps.css
@@ -91,16 +91,25 @@
   position: absolute;
   right: 4px;
   top: 2px;
   padding-left: 8px;
   font-weight: bold;
   color: var(--source-link-color);
 }
 
+.objectBox-failure {
+  color: var(--string-color);
+  border-width: 1px;
+  border-style: solid;
+  border-radius: 2px;
+  font-size: 0.8em;
+  padding: 0 2px;
+}
+
 /******************************************************************************/
 
 .objectLink-event,
 .objectLink-eventLog,
 .objectLink-regexp,
 .objectLink-object,
 .objectLink-Date {
   font-weight: bold;
--- a/devtools/client/shared/components/reps/string.js
+++ b/devtools/client/shared/components/reps/string.js
@@ -5,17 +5,21 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   // Dependencies
   const React = require("devtools/client/shared/vendor/react");
-  const { cropString } = require("./rep-utils");
+
+  const {
+    cropString,
+    wrapRender,
+  } = require("./rep-utils");
 
   // Shortcuts
   const { span } = React.DOM;
 
   /**
    * Renders a string. String value is enclosed within quotes.
    */
   const StringRep = React.createClass({
@@ -27,17 +31,17 @@ define(function (require, exports, modul
     },
 
     getDefaultProps: function () {
       return {
         useQuotes: true,
       };
     },
 
-    render: function () {
+    render: wrapRender(function () {
       let text = this.props.object;
       let member = this.props.member;
       let style = this.props.style;
 
       let config = {className: "objectBox objectBox-string"};
       if (style) {
         config.style = style;
       }
@@ -48,17 +52,17 @@ define(function (require, exports, modul
 
       let croppedString = this.props.cropLimit ?
         cropString(text, this.props.cropLimit) : cropString(text);
 
       let formattedString = this.props.useQuotes ?
         "\"" + croppedString + "\"" : croppedString;
 
       return span(config, formattedString);
-    },
+    }),
   });
 
   function supportsObject(object, type) {
     return (type == "string");
   }
 
   // Exports from this module
 
--- a/devtools/client/shared/components/reps/stylesheet.js
+++ b/devtools/client/shared/components/reps/stylesheet.js
@@ -6,17 +6,21 @@
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   // ReactJS
   const React = require("devtools/client/shared/vendor/react");
 
   // Reps
-  const { isGrip, getURLDisplayString } = require("./rep-utils");
+  const {
+    isGrip,
+    getURLDisplayString,
+    wrapRender
+  } = require("./rep-utils");
 
   // Shortcuts
   const DOM = React.DOM;
 
   /**
    * Renders a grip representing CSSStyleSheet
    */
   let StyleSheet = React.createClass({
@@ -39,28 +43,28 @@ define(function (require, exports, modul
     },
 
     getLocation: function (grip) {
       // Embedded stylesheets don't have URL and so, no preview.
       let url = grip.preview ? grip.preview.url : "";
       return url ? getURLDisplayString(url) : "";
     },
 
-    render: function () {
+    render: wrapRender(function () {
       let grip = this.props.object;
 
       return (
         DOM.span({className: "objectBox objectBox-object"},
           this.getTitle(grip),
           DOM.span({className: "objectPropValue"},
             this.getLocation(grip)
           )
         )
       );
-    },
+    }),
   });
 
   // Registration
 
   function supportsObject(object, type) {
     if (!isGrip(object)) {
       return false;
     }
--- a/devtools/client/shared/components/reps/symbol.js
+++ b/devtools/client/shared/components/reps/symbol.js
@@ -6,39 +6,41 @@
 
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   // Dependencies
   const React = require("devtools/client/shared/vendor/react");
 
+  const { wrapRender } = require("./rep-utils");
+
   // Shortcuts
   const { span } = React.DOM;
 
   /**
    * Renders a symbol.
    */
   const SymbolRep = React.createClass({
     displayName: "SymbolRep",
 
     propTypes: {
       object: React.PropTypes.object.isRequired
     },
 
-    render: function () {
+    render: wrapRender(function () {
       let {object} = this.props;
       let {name} = object;
 
       return (
         span({className: "objectBox objectBox-symbol"},
           `Symbol(${name || ""})`
         )
       );
-    },
+    }),
   });
 
   function supportsObject(object, type) {
     return (type == "symbol");
   }
 
   // Exports from this module
   exports.SymbolRep = {
--- a/devtools/client/shared/components/reps/text-node.js
+++ b/devtools/client/shared/components/reps/text-node.js
@@ -6,17 +6,21 @@
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   // ReactJS
   const React = require("devtools/client/shared/vendor/react");
 
   // Reps
-  const { isGrip, cropString } = require("./rep-utils");
+  const {
+    isGrip,
+    cropString,
+    wrapRender,
+  } = require("./rep-utils");
   const { MODE } = require("./constants");
 
   // Shortcuts
   const DOM = React.DOM;
 
   /**
    * Renders DOM #text node.
    */
@@ -38,17 +42,17 @@ define(function (require, exports, modul
       if (this.props.objectLink) {
         return this.props.objectLink({
           object: grip
         }, title);
       }
       return title;
     },
 
-    render: function () {
+    render: wrapRender(function () {
       let {
         object: grip,
         mode = MODE.SHORT,
       } = this.props;
 
       let baseConfig = {className: "objectBox objectBox-textNode"};
       if (this.props.onDOMNodeMouseOver) {
         Object.assign(baseConfig, {
@@ -70,17 +74,17 @@ define(function (require, exports, modul
         DOM.span(baseConfig,
           this.getTitle(grip),
           DOM.span({className: "nodeValue"},
             " ",
             `"${this.getTextContent(grip)}"`
           )
         )
       );
-    },
+    }),
   });
 
   // Registration
 
   function supportsObject(grip, type) {
     if (!isGrip(grip)) {
       return false;
     }
--- a/devtools/client/shared/components/reps/undefined.js
+++ b/devtools/client/shared/components/reps/undefined.js
@@ -6,32 +6,34 @@
 
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   // Dependencies
   const React = require("devtools/client/shared/vendor/react");
 
+  const { wrapRender } = require("./rep-utils");
+
   // Shortcuts
   const { span } = React.DOM;
 
   /**
    * Renders undefined value
    */
   const Undefined = React.createClass({
     displayName: "UndefinedRep",
 
-    render: function () {
+    render: wrapRender(function () {
       return (
         span({className: "objectBox objectBox-undefined"},
           "undefined"
         )
       );
-    },
+    }),
   });
 
   function supportsObject(object, type) {
     if (object && object.type && object.type == "undefined") {
       return true;
     }
 
     return (type == "undefined");
--- a/devtools/client/shared/components/reps/window.js
+++ b/devtools/client/shared/components/reps/window.js
@@ -6,17 +6,21 @@
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   // ReactJS
   const React = require("devtools/client/shared/vendor/react");
 
   // Reps
-  const { isGrip, getURLDisplayString } = require("./rep-utils");
+  const {
+    isGrip,
+    getURLDisplayString,
+    wrapRender
+  } = require("./rep-utils");
 
   // Shortcuts
   const DOM = React.DOM;
 
   /**
    * Renders a grip representing a window.
    */
   let Window = React.createClass({
@@ -36,28 +40,28 @@ define(function (require, exports, modul
       }
       return "";
     },
 
     getLocation: function (grip) {
       return getURLDisplayString(grip.preview.url);
     },
 
-    render: function () {
+    render: wrapRender(function () {
       let grip = this.props.object;
 
       return (
         DOM.span({className: "objectBox objectBox-Window"},
           this.getTitle(grip),
           DOM.span({className: "objectPropValue"},
             this.getLocation(grip)
           )
         )
       );
-    },
+    }),
   });
 
   // Registration
 
   function supportsObject(object, type) {
     if (!isGrip(object)) {
       return false;
     }
--- a/devtools/client/shared/components/test/mochitest/chrome.ini
+++ b/devtools/client/shared/components/test/mochitest/chrome.ini
@@ -10,16 +10,17 @@ support-files =
 [test_reps_array.html]
 [test_reps_attribute.html]
 [test_reps_comment-node.html]
 [test_reps_date-time.html]
 [test_reps_document.html]
 [test_reps_element-node.html]
 [test_reps_error.html]
 [test_reps_event.html]
+[test_reps_failure.html]
 [test_reps_function.html]
 [test_reps_grip.html]
 [test_reps_grip-array.html]
 [test_reps_grip-map.html]
 [test_reps_infinity.html]
 [test_reps_long-string.html]
 [test_reps_nan.html]
 [test_reps_null.html]
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/components/test/mochitest/test_reps_failure.html
@@ -0,0 +1,60 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test fallback for rep rendering when a rep fails to render.
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Rep test - Failure</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript;version=1.8"></script>
+<script type="application/javascript;version=1.8">
+window.onload = Task.async(function* () {
+  try {
+    let { Rep } = browserRequire("devtools/client/shared/components/reps/rep");
+    let { ArrayRep } = browserRequire("devtools/client/shared/components/reps/array");
+    let { RegExp } = browserRequire("devtools/client/shared/components/reps/regexp");
+
+    // Force the RegExp rep to crash by creating RegExp grip that throws when accessing
+    // the displayString property
+    let gripStub = {
+      "type": "object",
+      "class": "RegExp",
+      "actor": "server1.conn22.obj39",
+      "extensible": true,
+      "frozen": false,
+      "sealed": false,
+      "ownPropertyLength": 1,
+      get displayString() {
+        throw new Error("failure");
+      }
+    };
+
+    // Test that correct rep is chosen.
+    const renderedRep = shallowRenderComponent(Rep, { object: gripStub });
+    is(renderedRep.type, RegExp.rep, `Rep correctly selects ${RegExp.rep.displayName}`);
+
+    // Test fallback message is displayed when rendering bad rep directly.
+    let renderedComponent = renderComponent(RegExp.rep, { object: gripStub });
+    is(renderedComponent.textContent, "Invalid object", "Fallback rendering has expected text content");
+
+    // Test fallback message is displayed when bad rep is nested in another rep.
+    renderedComponent = renderComponent(ArrayRep.rep, { object: [1, gripStub, 2] });
+    is(renderedComponent.textContent, "[ 1, Invalid object, 2 ]", "Fallback rendering has expected text content");
+  } catch(e) {
+    ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+  } finally {
+    SimpleTest.finish();
+  }
+});
+</script>
+</pre>
+</body>
+</html>
\ No newline at end of file