Bug 759157 - Intermittent browser_tilt_02_notifications.js, browser_tilt_03_tab_switch.js, browser_tilt_04_initialization.js, browser_tilt_picking_miv.js, browser_tilt_picking_highlight01.js | Exited with code 1, timed out after 330 seconds, could not initialize Tilt; r=rcampbell
authorVictor Porof <vporof@mozilla.com>
Sun, 03 Jun 2012 20:15:41 +0300
changeset 95685 aba0194d088f
parent 95684 482e07a4fb05
child 95686 7dded40bb32b
push id22827
push userrcampbell@mozilla.com
push dateSun, 03 Jun 2012 20:41:58 +0000
treeherdermozilla-central@0e4f8e1a141b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrcampbell
bugs759157
milestone15.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 759157 - Intermittent browser_tilt_02_notifications.js, browser_tilt_03_tab_switch.js, browser_tilt_04_initialization.js, browser_tilt_picking_miv.js, browser_tilt_picking_highlight01.js | Exited with code 1, timed out after 330 seconds, could not initialize Tilt; r=rcampbell
browser/devtools/tilt/Tilt.jsm
browser/devtools/tilt/TiltGL.jsm
browser/devtools/tilt/TiltUtils.jsm
browser/devtools/tilt/test/browser_tilt_02_notifications-seq.js
browser/devtools/tilt/test/browser_tilt_02_notifications.js
browser/devtools/tilt/test/browser_tilt_03_tab_switch.js
browser/devtools/tilt/test/browser_tilt_04_initialization-key.js
browser/devtools/tilt/test/browser_tilt_04_initialization.js
browser/devtools/tilt/test/browser_tilt_05_destruction-esc.js
browser/devtools/tilt/test/browser_tilt_05_destruction-url.js
browser/devtools/tilt/test/browser_tilt_05_destruction.js
browser/devtools/tilt/test/browser_tilt_arcball-reset-typeahead.js
browser/devtools/tilt/test/browser_tilt_arcball-reset.js
browser/devtools/tilt/test/browser_tilt_controller.js
browser/devtools/tilt/test/browser_tilt_picking.js
browser/devtools/tilt/test/browser_tilt_picking_delete.js
browser/devtools/tilt/test/browser_tilt_picking_highlight01-offs.js
browser/devtools/tilt/test/browser_tilt_picking_highlight01.js
browser/devtools/tilt/test/browser_tilt_picking_highlight02.js
browser/devtools/tilt/test/browser_tilt_picking_highlight03.js
browser/devtools/tilt/test/browser_tilt_picking_miv.js
browser/devtools/tilt/test/browser_tilt_zoom.js
browser/devtools/tilt/test/head.js
--- a/browser/devtools/tilt/Tilt.jsm
+++ b/browser/devtools/tilt/Tilt.jsm
@@ -95,16 +95,17 @@ Tilt.prototype = {
       contentWindow: this.chromeWindow.gBrowser.selectedBrowser.contentWindow,
       parentNode: this.chromeWindow.gBrowser.selectedBrowser.parentNode,
       notifications: this.NOTIFICATIONS
     });
 
     // make sure the visualizer object was initialized properly
     if (!this.visualizers[id].isInitialized()) {
       this.destroy(id);
+      this.failureCallback && this.failureCallback();
       return;
     }
 
     Services.obs.notifyObservers(null, TILT_NOTIFICATIONS.INITIALIZING, null);
   },
 
   /**
    * Starts destroying a specific instance of the visualizer.
@@ -255,24 +256,24 @@ Tilt.prototype = {
     this.chromeWindow.gBrowser.tabContainer.addEventListener("TabSelect",
       this._onTabSelect.bind(this), false);
 
 
     // FIXME: this shouldn't be done here, see bug #705131
     let onOpened = function() {
       if (this.inspector && this.highlighter && this.currentInstance) {
         this.inspector.stopInspecting();
-        this.inspector.inspectToolbutton.disabled = true;
+        this.inspectButton.disabled = true;
         this.highlighter.hide();
       }
     }.bind(this);
 
     let onClosed = function() {
       if (this.inspector && this.highlighter) {
-        this.inspector.inspectToolbutton.disabled = false;
+        this.inspectButton.disabled = false;
         this.highlighter.show();
       }
     }.bind(this);
 
     Services.obs.addObserver(onOpened,
       this.chromeWindow.InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
     Services.obs.addObserver(onClosed,
       this.chromeWindow.InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false);
@@ -328,10 +329,17 @@ Tilt.prototype = {
   },
 
   /**
    * Gets the Tilt button in the Inspector toolbar.
    */
   get tiltButton()
   {
     return this.chromeWindow.document.getElementById("inspector-3D-button");
+  },
+
+  /**
+   * Gets the Inspect button in the Inspector toolbar.
+   */
+  get inspectButton() {
+    return this.chromeWindow.document.getElementById("inspector-inspect-toolbutton");
   }
 };
--- a/browser/devtools/tilt/TiltGL.jsm
+++ b/browser/devtools/tilt/TiltGL.jsm
@@ -1549,20 +1549,20 @@ TiltGL.isWebGLSupported = function TGL_i
     let gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
     let angle = gfxInfo.FEATURE_WEBGL_ANGLE;
     let opengl = gfxInfo.FEATURE_WEBGL_OPENGL;
 
     // if either the Angle or OpenGL renderers are available, WebGL should work
     supported = gfxInfo.getFeatureStatus(angle) === gfxInfo.FEATURE_NO_INFO ||
                 gfxInfo.getFeatureStatus(opengl) === gfxInfo.FEATURE_NO_INFO;
   } catch(e) {
-    TiltUtils.Output.error(e.message);
-  } finally {
-    return supported;
+    if (e && e.message) { TiltUtils.Output.error(e.message); }
+    return false;
   }
+  return supported;
 };
 
 /**
  * Helper function to create a 3D context.
  *
  * @param {HTMLCanvasElement} aCanvas
  *                            the canvas to get the WebGL context from
  * @param {Object} aFlags
@@ -1575,20 +1575,20 @@ TiltGL.create3DContext = function TGL_cr
   TiltGL.clearCache();
 
   // try to get a valid context from an existing canvas
   let context = null;
 
   try {
     context = aCanvas.getContext(WEBGL_CONTEXT_NAME, aFlags);
   } catch(e) {
-    TiltUtils.Output.error(e.message);
-  } finally {
-    return context;
+    if (e && e.message) { TiltUtils.Output.error(e.message); }
+    return null;
   }
+  return context;
 };
 
 /**
  * Clears the cache and sets all the variables to default.
  */
 TiltGL.clearCache = function TGL_clearCache()
 {
   TiltGL.ProgramUtils._activeProgram = -1;
--- a/browser/devtools/tilt/TiltUtils.jsm
+++ b/browser/devtools/tilt/TiltUtils.jsm
@@ -28,16 +28,19 @@ TiltUtils.Output = {
   /**
    * Logs a message to the console.
    *
    * @param {String} aMessage
    *                 the message to be logged
    */
   log: function TUO_log(aMessage)
   {
+    if (this.suppressLogs) {
+      return;
+    }
     // get the console service
     let consoleService = Cc["@mozilla.org/consoleservice;1"]
       .getService(Ci.nsIConsoleService);
 
     // log the message
     consoleService.logStringMessage(aMessage);
   },
 
@@ -46,16 +49,19 @@ TiltUtils.Output = {
    *
    * @param {String} aMessage
    *                 the message to be logged
    * @param {Object} aProperties
    *                 and object containing script error initialization details
    */
   error: function TUO_error(aMessage, aProperties)
   {
+    if (this.suppressErrors) {
+      return;
+    }
     // make sure the properties parameter is a valid object
     aProperties = aProperties || {};
 
     // get the console service
     let consoleService = Cc["@mozilla.org/consoleservice;1"]
       .getService(Ci.nsIConsoleService);
 
     // get the script error service
@@ -80,16 +86,19 @@ TiltUtils.Output = {
    *
    * @param {String} aTitle
    *                 the title of the popup
    * @param {String} aMessage
    *                 the message to be logged
    */
   alert: function TUO_alert(aTitle, aMessage)
   {
+    if (this.suppressAlerts) {
+      return;
+    }
     if (!aMessage) {
       aMessage = aTitle;
       aTitle = "";
     }
 
     // get the prompt service
     let prompt = Cc["@mozilla.org/embedcomp/prompt-service;1"]
       .getService(Ci.nsIPromptService);
--- a/browser/devtools/tilt/test/browser_tilt_02_notifications-seq.js
+++ b/browser/devtools/tilt/test/browser_tilt_02_notifications-seq.js
@@ -13,26 +13,29 @@ function test() {
     info("Skipping notifications test because WebGL isn't supported.");
     return;
   }
 
   requestLongerTimeout(10);
   waitForExplicitFinish();
 
   createTab(function() {
-    Services.obs.addObserver(cleanup, DESTROYED, false);
-
+    Services.obs.addObserver(finalize, DESTROYED, false);
     Services.obs.addObserver(obs_INITIALIZING, INITIALIZING, false);
     Services.obs.addObserver(obs_INITIALIZED, INITIALIZED, false);
     Services.obs.addObserver(obs_DESTROYING, DESTROYING, false);
     Services.obs.addObserver(obs_BEFORE_DESTROYED, BEFORE_DESTROYED, false);
     Services.obs.addObserver(obs_DESTROYED, DESTROYED, false);
 
     info("Starting up the Tilt notifications test.");
-    createTilt({});
+    createTilt({}, false, function suddenDeath()
+    {
+      info("Tilt could not be initialized properly.");
+      cleanup();
+    });
   });
 }
 
 function obs_INITIALIZING() {
   info("Handling the INITIALIZING notification.");
   tabEvents += "INITIALIZING;";
 }
 
@@ -53,25 +56,32 @@ function obs_BEFORE_DESTROYED() {
   tabEvents += "BEFORE_DESTROYED;";
 }
 
 function obs_DESTROYED() {
   info("Handling the DESTROYED notification.");
   tabEvents += "DESTROYED;";
 }
 
-function cleanup() {
-  info("Cleaning up the notifications test.");
+function finalize() {
+  if (!tabEvents) {
+    return;
+  }
 
   is(tabEvents, "INITIALIZING;INITIALIZED;DESTROYING;BEFORE_DESTROYED;DESTROYED;",
     "The notifications weren't fired in the correct order.");
 
-  Services.obs.removeObserver(cleanup, DESTROYED);
+  cleanup();
+}
+
+function cleanup() {
+  info("Cleaning up the notifications test.");
 
-  Services.obs.removeObserver(obs_INITIALIZING, INITIALIZING, false);
-  Services.obs.removeObserver(obs_INITIALIZED, INITIALIZED, false);
-  Services.obs.removeObserver(obs_DESTROYING, DESTROYING, false);
-  Services.obs.removeObserver(obs_BEFORE_DESTROYED, BEFORE_DESTROYED, false);
-  Services.obs.removeObserver(obs_DESTROYED, DESTROYED, false);
+  Services.obs.removeObserver(finalize, DESTROYED);
+  Services.obs.removeObserver(obs_INITIALIZING, INITIALIZING);
+  Services.obs.removeObserver(obs_INITIALIZED, INITIALIZED);
+  Services.obs.removeObserver(obs_DESTROYING, DESTROYING);
+  Services.obs.removeObserver(obs_BEFORE_DESTROYED, BEFORE_DESTROYED);
+  Services.obs.removeObserver(obs_DESTROYED, DESTROYED);
 
   gBrowser.removeCurrentTab();
   finish();
 }
--- a/browser/devtools/tilt/test/browser_tilt_02_notifications.js
+++ b/browser/devtools/tilt/test/browser_tilt_02_notifications.js
@@ -22,30 +22,33 @@ function test() {
   gBrowser.tabContainer.addEventListener("TabSelect", tabSelect, false);
   createNewTab();
 }
 
 function createNewTab() {
   tab0 = gBrowser.selectedTab;
 
   tab1 = createTab(function() {
-    Services.obs.addObserver(cleanup, DESTROYED, false);
-
+    Services.obs.addObserver(finalize, DESTROYED, false);
     Services.obs.addObserver(tab_INITIALIZING, INITIALIZING, false);
     Services.obs.addObserver(tab_DESTROYING, DESTROYING, false);
     Services.obs.addObserver(tab_SHOWN, SHOWN, false);
     Services.obs.addObserver(tab_HIDDEN, HIDDEN, false);
 
     info("Starting up the Tilt notifications test.");
     createTilt({
       onTiltOpen: function()
       {
         testStep = 0;
         tabSelect();
       }
+    }, false, function suddenDeath()
+    {
+      info("Tilt could not be initialized properly.");
+      cleanup();
     });
   });
 }
 
 function tab_INITIALIZING() {
   info("Handling the INITIALIZING notification.");
   tabEvents += "INITIALIZING;";
 }
@@ -75,33 +78,40 @@ let testSteps = [
     gBrowser.selectedTab = tab1;
   },
   function step2() {
     info("Killing it.");
     Tilt.destroy(Tilt.currentWindowId, true);
   }
 ];
 
-function cleanup() {
-  info("Cleaning up the notifications test.");
+function finalize() {
+  if (!tabEvents) {
+    return;
+  }
 
   is(tabEvents, "INITIALIZING;HIDDEN;SHOWN;DESTROYING;",
     "The notifications weren't fired in the correct order.");
 
+  cleanup();
+}
+
+function cleanup() {
+  info("Cleaning up the notifications test.");
+
   tab0 = null;
   tab1 = null;
 
-  Services.obs.removeObserver(cleanup, DESTROYED);
+  Services.obs.removeObserver(finalize, DESTROYED);
+  Services.obs.removeObserver(tab_INITIALIZING, INITIALIZING);
+  Services.obs.removeObserver(tab_DESTROYING, DESTROYING);
+  Services.obs.removeObserver(tab_SHOWN, SHOWN);
+  Services.obs.removeObserver(tab_HIDDEN, HIDDEN);
 
-  Services.obs.removeObserver(tab_INITIALIZING, INITIALIZING, false);
-  Services.obs.removeObserver(tab_DESTROYING, DESTROYING, false);
-  Services.obs.removeObserver(tab_SHOWN, SHOWN, false);
-  Services.obs.removeObserver(tab_HIDDEN, HIDDEN, false);
-
-  gBrowser.tabContainer.removeEventListener("TabSelect", tabSelect, false);
+  gBrowser.tabContainer.removeEventListener("TabSelect", tabSelect);
   gBrowser.removeCurrentTab();
   finish();
 }
 
 function tabSelect() {
   if (testStep !== -1) {
     executeSoon(testSteps[testStep]);
     testStep++;
--- a/browser/devtools/tilt/test/browser_tilt_03_tab_switch.js
+++ b/browser/devtools/tilt/test/browser_tilt_03_tab_switch.js
@@ -25,92 +25,159 @@ function createTab1() {
   tab0 = gBrowser.selectedTab;
 
   tab1 = createTab(function() {
     createTilt({
       onInspectorOpen: function()
       {
         ok(Tilt.tiltButton.checked === false,
           "The toolbar tilt button shouldn't be pressed before Tilt is open.");
+        ok(InspectorUI.inspecting,
+          "The Inspector should be inspecting for tab1.");
+        ok(InspectorUI.highlighter.hidden === false,
+          "The Highlighter should be visible for tab1.");
       },
       onTiltOpen: function()
       {
+        ok(InspectorUI.inspecting === false,
+          "The Inspector should not be inspecting for tab1 after Tilt is open.");
+        ok(InspectorUI.highlighter.hidden,
+          "The Highlighter should not be visible for tab1 after Tilt is open.");
+
         createTab2();
       }
+    }, false, function suddenDeath()
+    {
+      info("Tilt could not be initialized properly.");
+      cleanup();
     });
   });
 }
 
 function createTab2() {
   tab2 = createTab(function() {
 
     createTilt({
       onInspectorOpen: function()
       {
         ok(Tilt.tiltButton.checked === false,
           "The toolbar tilt button shouldn't be pressed before Tilt is open.");
+        ok(InspectorUI.inspecting,
+          "The Inspector should be inspecting for tab2.");
+        ok(InspectorUI.highlighter.hidden === false,
+          "The Highlighter should be visible for tab2.");
       },
       onTiltOpen: function()
       {
+        ok(InspectorUI.inspecting === false,
+          "The Inspector should not be inspecting for tab2 after Tilt is open.");
+        ok(InspectorUI.highlighter.hidden,
+          "The Highlighter should be visible for tab2 after Tilt is open.");
+
         testStep = 0;
         tabSelect();
       }
+    }, false, function suddenDeath()
+    {
+      info("Tilt could not be initialized properly.");
+      cleanup();
     });
   });
 }
 
 let testSteps = [
   function step0() {
     ok(Tilt.tiltButton.checked === true,
       "The toolbar tilt button should have been pressed at step0 (tab 2).");
+    ok(InspectorUI.inspecting === false,
+      "The Inspector should not be inspecting at step0.");
+    ok(InspectorUI.highlighter.hidden,
+      "The Highlighter should be hidden at step0.");
 
     gBrowser.selectedTab = tab1;
   },
   function step1() {
     ok(Tilt.tiltButton.checked === true,
       "The toolbar tilt button should have been pressed at step1 (tab 1).");
+    ok(InspectorUI.inspecting === false,
+      "The Inspector should not be inspecting at step1.");
+    ok(InspectorUI.highlighter.hidden,
+      "The Highlighter should be hidden at step1.");
 
     gBrowser.selectedTab = tab0;
   },
   function step2() {
     ok(Tilt.tiltButton.checked === false,
       "The toolbar tilt button shouldn't have been pressed at step2 (tab 0).");
+    ok(InspectorUI.inspecting === false,
+      "The Inspector should not be inspecting at step2.");
+    is(InspectorUI.highlighter, null,
+      "The Highlighter should be dead while in step2.");
 
     gBrowser.selectedTab = tab1;
   },
   function step3() {
     ok(Tilt.tiltButton.checked === true,
       "The toolbar tilt button should have been pressed at step3 (tab 1).");
+    ok(InspectorUI.inspecting === false,
+      "The Inspector should not be inspecting at step3.");
+    ok(InspectorUI.highlighter.hidden,
+      "The Highlighter should be hidden at step3.");
 
     gBrowser.selectedTab = tab2;
   },
   function step4() {
     ok(Tilt.tiltButton.checked === true,
       "The toolbar tilt button should have been pressed at step4 (tab 2).");
+    ok(InspectorUI.inspecting === false,
+      "The Inspector should not be inspecting at step4.");
+    ok(InspectorUI.highlighter.hidden,
+      "The Highlighter should be hidden at step4.");
 
     Tilt.destroy(Tilt.currentWindowId);
     gBrowser.removeCurrentTab();
+    tab2 = null;
   },
   function step5() {
     ok(Tilt.tiltButton.checked === true,
       "The toolbar tilt button should have been pressed at step5 (tab 1).");
+    ok(InspectorUI.inspecting === false,
+      "The Inspector should not be inspecting at step5.");
+    ok(InspectorUI.highlighter.hidden,
+      "The Highlighter should be hidden at step5.");
 
     Tilt.destroy(Tilt.currentWindowId);
     gBrowser.removeCurrentTab();
+    tab1 = null;
   },
   function step6_cleanup() {
     ok(Tilt.tiltButton.checked === false,
       "The toolbar tilt button shouldn't have been pressed at step6 (tab 0).");
+    ok(InspectorUI.inspecting === false,
+      "The Inspector should not be inspecting at step6.");
+    is(InspectorUI.highlighter, null,
+      "The Highlighter should be dead while in step6.");
 
-    tab1 = null;
-    tab2 = null;
-
-    gBrowser.tabContainer.removeEventListener("TabSelect", tabSelect, false);
-    finish();
+    cleanup();
   }
 ];
 
+function cleanup() {
+  gBrowser.tabContainer.removeEventListener("TabSelect", tabSelect, false);
+
+  if (tab1) {
+    gBrowser.removeTab(tab1);
+    tab1 = null;
+  }
+  if (tab2) {
+    gBrowser.removeTab(tab2);
+    tab2 = null;
+  }
+
+  finish();
+}
+
 function tabSelect() {
   if (testStep !== -1) {
     executeSoon(testSteps[testStep]);
     testStep++;
   }
 }
--- a/browser/devtools/tilt/test/browser_tilt_04_initialization-key.js
+++ b/browser/devtools/tilt/test/browser_tilt_04_initialization-key.js
@@ -26,25 +26,32 @@ function test() {
   } else {
     eventType = { type: "keypress", altKey: true };
   }
 
   Services.obs.addObserver(onInspectorOpen, INSPECTOR_OPENED, false);
   InspectorUI.toggleInspectorUI();
 }
 
+function suddenDeath() {
+  Services.obs.removeObserver(onTiltOpen, INITIALIZING);
+  cleanup();
+}
+
 function onInspectorOpen() {
   Services.obs.removeObserver(onInspectorOpen, INSPECTOR_OPENED);
 
   executeSoon(function() {
     is(Tilt.visualizers[id], null,
       "A instance of the visualizer shouldn't be initialized yet.");
 
     info("Pressing the accesskey should open Tilt.");
 
+    Tilt.failureCallback = suddenDeath;
+
     Services.obs.addObserver(onTiltOpen, INITIALIZING, false);
     EventUtils.synthesizeKey(tiltKey, eventType);
   });
 }
 
 function onTiltOpen() {
   Services.obs.removeObserver(onTiltOpen, INITIALIZING);
 
@@ -60,11 +67,16 @@ function onTiltOpen() {
     EventUtils.synthesizeKey(tiltKey, eventType);
   });
 }
 
 function onTiltClose() {
   is(Tilt.visualizers[id], null,
     "The current instance of the visualizer wasn't destroyed properly.");
 
+  cleanup();
+}
+
+function cleanup() {
+  Tilt.failureCallback = null;
   InspectorUI.closeInspectorUI();
   finish();
 }
--- a/browser/devtools/tilt/test/browser_tilt_04_initialization.js
+++ b/browser/devtools/tilt/test/browser_tilt_04_initialization.js
@@ -40,14 +40,22 @@ function test() {
         is(document.activeElement, gBrowser.selectedBrowser,
           "The focus wasn't correctly given back to the selectedBrowser.");
 
         is(Tilt.visualizers[id], null,
           "The current instance of the visualizer wasn't destroyed properly.");
       },
       onEnd: function()
       {
-        gBrowser.removeCurrentTab();
-        finish();
+        cleanup();
       }
-    }, true);
+    }, true, function suddenDeath()
+    {
+      info("Tilt could not be initialized properly.");
+      cleanup();
+    });
   });
 }
+
+function cleanup() {
+  gBrowser.removeCurrentTab();
+  finish();
+}
--- a/browser/devtools/tilt/test/browser_tilt_05_destruction-esc.js
+++ b/browser/devtools/tilt/test/browser_tilt_05_destruction-esc.js
@@ -1,40 +1,52 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
+let tiltOpened = false;
+
 function test() {
   if (!isTiltEnabled()) {
     info("Skipping destruction test because Tilt isn't enabled.");
     return;
   }
   if (!isWebGLSupported()) {
     info("Skipping destruction test because WebGL isn't supported.");
     return;
   }
 
   waitForExplicitFinish();
 
   createTab(function() {
     createTilt({
       onTiltOpen: function()
       {
-        Services.obs.addObserver(cleanup, DESTROYED, false);
+        tiltOpened = true;
+
+        Services.obs.addObserver(finalize, DESTROYED, false);
         EventUtils.sendKey("ESCAPE");
       }
+    }, false, function suddenDeath()
+    {
+      info("Tilt could not be initialized properly.");
+      cleanup();
     });
   });
 }
 
-function cleanup() {
+function finalize() {
   let id = TiltUtils.getWindowId(gBrowser.selectedBrowser.contentWindow);
 
   is(Tilt.visualizers[id], null,
     "The current instance of the visualizer wasn't destroyed properly.");
 
   ok(InspectorUI.highlighter && InspectorUI.breadcrumbs,
     "The Inspector should not close while Tilt is opened.");
 
-  Services.obs.removeObserver(cleanup, DESTROYED);
+  cleanup();
+}
+
+function cleanup() {
+  if (tiltOpened) { Services.obs.removeObserver(finalize, DESTROYED); }
   gBrowser.removeCurrentTab();
   finish();
 }
--- a/browser/devtools/tilt/test/browser_tilt_05_destruction-url.js
+++ b/browser/devtools/tilt/test/browser_tilt_05_destruction-url.js
@@ -1,37 +1,49 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
+let tiltOpened = false;
+
 function test() {
   if (!isTiltEnabled()) {
     info("Skipping destruction test because Tilt isn't enabled.");
     return;
   }
   if (!isWebGLSupported()) {
     info("Skipping destruction test because WebGL isn't supported.");
     return;
   }
 
   waitForExplicitFinish();
 
   createTab(function() {
     createTilt({
       onTiltOpen: function()
       {
-        Services.obs.addObserver(cleanup, DESTROYED, false);
+        tiltOpened = true;
+
+        Services.obs.addObserver(finalize, DESTROYED, false);
         window.content.location = "about:mozilla";
       }
+    }, false, function suddenDeath()
+    {
+      info("Tilt could not be initialized properly.");
+      cleanup();
     });
   });
 }
 
-function cleanup() {
+function finalize() {
   let id = TiltUtils.getWindowId(gBrowser.selectedBrowser.contentWindow);
 
   is(Tilt.visualizers[id], null,
     "The current instance of the visualizer wasn't destroyed properly.");
 
-  Services.obs.removeObserver(cleanup, DESTROYED);
+  cleanup();
+}
+
+function cleanup() {
+  if (tiltOpened) { Services.obs.removeObserver(finalize, DESTROYED); }
   gBrowser.removeCurrentTab();
   finish();
 }
--- a/browser/devtools/tilt/test/browser_tilt_05_destruction.js
+++ b/browser/devtools/tilt/test/browser_tilt_05_destruction.js
@@ -1,37 +1,49 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
+let tiltOpened = false;
+
 function test() {
   if (!isTiltEnabled()) {
     info("Skipping destruction test because Tilt isn't enabled.");
     return;
   }
   if (!isWebGLSupported()) {
     info("Skipping destruction test because WebGL isn't supported.");
     return;
   }
 
   waitForExplicitFinish();
 
   createTab(function() {
     createTilt({
       onTiltOpen: function()
       {
-        Services.obs.addObserver(cleanup, DESTROYED, false);
+        tiltOpened = true;
+
+        Services.obs.addObserver(finalize, DESTROYED, false);
         InspectorUI.closeInspectorUI();
       }
+    }, false, function suddenDeath()
+    {
+      info("Tilt could not be initialized properly.");
+      cleanup();
     });
   });
 }
 
-function cleanup() {
+function finalize() {
   let id = TiltUtils.getWindowId(gBrowser.selectedBrowser.contentWindow);
 
   is(Tilt.visualizers[id], null,
     "The current instance of the visualizer wasn't destroyed properly.");
 
-  Services.obs.removeObserver(cleanup, DESTROYED);
+  cleanup();
+}
+
+function cleanup() {
+  if (tiltOpened) { Services.obs.removeObserver(finalize, DESTROYED); }
   gBrowser.removeCurrentTab();
   finish();
 }
--- a/browser/devtools/tilt/test/browser_tilt_arcball-reset-typeahead.js
+++ b/browser/devtools/tilt/test/browser_tilt_arcball-reset-typeahead.js
@@ -1,12 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
+let tiltOpened = false;
+
 function test() {
   if (!isTiltEnabled()) {
     info("Skipping part of the arcball test because Tilt isn't enabled.");
     return;
   }
   if (!isWebGLSupported()) {
     info("Skipping part of the arcball test because WebGL isn't supported.");
     return;
@@ -15,26 +17,32 @@ function test() {
   requestLongerTimeout(10);
   waitForExplicitFinish();
   Services.prefs.setBoolPref("accessibility.typeaheadfind", true);
 
   createTab(function() {
     createTilt({
       onTiltOpen: function(instance)
       {
+        tiltOpened = true;
+
         performTest(instance.presenter.canvas,
                     instance.controller.arcball, function() {
 
           info("Killing arcball reset test.");
 
           Services.prefs.setBoolPref("accessibility.typeaheadfind", false);
           Services.obs.addObserver(cleanup, DESTROYED, false);
           InspectorUI.closeInspectorUI();
         });
       }
+    }, false, function suddenDeath()
+    {
+      info("Tilt could not be initialized properly.");
+      cleanup();
     });
   });
 }
 
 function performTest(canvas, arcball, callback) {
   is(document.activeElement, canvas,
     "The visualizer canvas should be focused when performing this test.");
 
@@ -112,12 +120,12 @@ function performTest(canvas, arcball, ca
       }, Math.random() * 1000); // leave enough time for transforms to happen
     }, Math.random() * 1000);
   }, Math.random() * 1000);
 }
 
 function cleanup() {
   info("Cleaning up arcball reset test.");
 
-  Services.obs.removeObserver(cleanup, DESTROYED);
+  if (tiltOpened) { Services.obs.removeObserver(cleanup, DESTROYED); }
   gBrowser.removeCurrentTab();
   finish();
 }
--- a/browser/devtools/tilt/test/browser_tilt_arcball-reset.js
+++ b/browser/devtools/tilt/test/browser_tilt_arcball-reset.js
@@ -1,12 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
+let tiltOpened = false;
+
 function test() {
   if (!isTiltEnabled()) {
     info("Skipping part of the arcball test because Tilt isn't enabled.");
     return;
   }
   if (!isWebGLSupported()) {
     info("Skipping part of the arcball test because WebGL isn't supported.");
     return;
@@ -14,25 +16,31 @@ function test() {
 
   requestLongerTimeout(10);
   waitForExplicitFinish();
 
   createTab(function() {
     createTilt({
       onTiltOpen: function(instance)
       {
+        tiltOpened = true;
+
         performTest(instance.presenter.canvas,
                     instance.controller.arcball, function() {
 
           info("Killing arcball reset test.");
 
           Services.obs.addObserver(cleanup, DESTROYED, false);
           InspectorUI.closeInspectorUI();
         });
       }
+    }, false, function suddenDeath()
+    {
+      info("Tilt could not be initialized properly.");
+      cleanup();
     });
   });
 }
 
 function performTest(canvas, arcball, callback) {
   is(document.activeElement, canvas,
     "The visualizer canvas should be focused when performing this test.");
 
@@ -110,12 +118,12 @@ function performTest(canvas, arcball, ca
       }, Math.random() * 1000); // leave enough time for transforms to happen
     }, Math.random() * 1000);
   }, Math.random() * 1000);
 }
 
 function cleanup() {
   info("Cleaning up arcball reset test.");
 
-  Services.obs.removeObserver(cleanup, DESTROYED);
+  if (tiltOpened) { Services.obs.removeObserver(cleanup, DESTROYED); }
   gBrowser.removeCurrentTab();
   finish();
 }
--- a/browser/devtools/tilt/test/browser_tilt_controller.js
+++ b/browser/devtools/tilt/test/browser_tilt_controller.js
@@ -113,14 +113,22 @@ function test() {
         info("Testing if loosing focus halts any stacked arcball animations.");
 
         testEventCancel(function() {
           gBrowser.selectedBrowser.contentWindow.focus();
         });
       },
       onEnd: function()
       {
-        gBrowser.removeCurrentTab();
-        finish();
+        cleanup();
       }
-    }, true);
+    }, true, function suddenDeath()
+    {
+      info("Tilt could not be initialized properly.");
+      cleanup();
+    });
   });
 }
+
+function cleanup() {
+  gBrowser.removeCurrentTab();
+  finish();
+}
--- a/browser/devtools/tilt/test/browser_tilt_picking.js
+++ b/browser/devtools/tilt/test/browser_tilt_picking.js
@@ -1,12 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
+let pickDone = false;
+
 function test() {
   if (!isTiltEnabled()) {
     info("Skipping picking test because Tilt isn't enabled.");
     return;
   }
   if (!isWebGLSupported()) {
     info("Skipping picking test because WebGL isn't supported.");
     return;
@@ -25,23 +27,28 @@ function test() {
           let p = getPickablePoint(presenter);
 
           presenter.pickNode(p[0], p[1], {
             onpick: function(data)
             {
               ok(data.index > 0,
                 "Simply picking a node didn't work properly.");
 
+              pickDone = true;
               Services.obs.addObserver(cleanup, DESTROYED, false);
               InspectorUI.closeInspectorUI();
             }
           });
         };
       }
+    }, false, function suddenDeath()
+    {
+      info("Tilt could not be initialized properly.");
+      cleanup();
     });
   });
 }
 
 function cleanup() {
-  Services.obs.removeObserver(cleanup, DESTROYED);
+  if (pickDone) { Services.obs.removeObserver(cleanup, DESTROYED); }
   gBrowser.removeCurrentTab();
   finish();
 }
--- a/browser/devtools/tilt/test/browser_tilt_picking_delete.js
+++ b/browser/devtools/tilt/test/browser_tilt_picking_delete.js
@@ -1,12 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
+let nodeDeleted = false;
 let presenter;
 
 function test() {
   if (!isTiltEnabled()) {
     info("Skipping picking delete test because Tilt isn't enabled.");
     return;
   }
   if (!isWebGLSupported()) {
@@ -29,21 +30,26 @@ function test() {
           presenter.highlightNodeAt(p[0], p[1], {
             onpick: function()
             {
               ok(presenter._currentSelection > 0,
                 "Highlighting a node didn't work properly.");
               ok(!presenter._highlight.disabled,
                 "After highlighting a node, it should be highlighted. D'oh.");
 
+              nodeDeleted = true;
               presenter.deleteNode();
             }
           });
         };
       }
+    }, false, function suddenDeath()
+    {
+      info("Tilt could not be initialized properly.");
+      cleanup();
     });
   });
 }
 
 function whenNodeRemoved() {
   ok(presenter._currentSelection > 0,
     "Deleting a node shouldn't change the current selection.");
   ok(presenter._highlight.disabled,
@@ -59,12 +65,12 @@ function whenNodeRemoved() {
 
   executeSoon(function() {
     Services.obs.addObserver(cleanup, DESTROYED, false);
     InspectorUI.closeInspectorUI();
   });
 }
 
 function cleanup() {
-  Services.obs.removeObserver(cleanup, DESTROYED);
+  if (nodeDeleted) { Services.obs.removeObserver(cleanup, DESTROYED); }
   gBrowser.removeCurrentTab();
   finish();
 }
--- a/browser/devtools/tilt/test/browser_tilt_picking_highlight01-offs.js
+++ b/browser/devtools/tilt/test/browser_tilt_picking_highlight01-offs.js
@@ -1,12 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
+let nodeHighlighted = false;
 let presenter;
 
 function test() {
   if (!isTiltEnabled()) {
     info("Skipping highlight test because Tilt isn't enabled.");
     return;
   }
   if (!isWebGLSupported()) {
@@ -22,19 +23,24 @@ function test() {
       {
         presenter = instance.presenter;
         Services.obs.addObserver(whenHighlighting, HIGHLIGHTING, false);
 
         presenter._onInitializationFinished = function() {
           let contentDocument = presenter.contentWindow.document;
           let div = contentDocument.getElementById("far-far-away");
 
+          nodeHighlighted = true;
           presenter.highlightNode(div, "moveIntoView");
         };
       }
+    }, false, function suddenDeath()
+    {
+      info("Tilt could not be initialized properly.");
+      cleanup();
     });
   });
 }
 
 function whenHighlighting() {
   ok(presenter._currentSelection > 0,
     "Highlighting a node didn't work properly.");
   ok(!presenter._highlight.disabled,
@@ -58,12 +64,12 @@ function whenUnhighlighting() {
   executeSoon(function() {
     Services.obs.removeObserver(whenUnhighlighting, UNHIGHLIGHTING);
     Services.obs.addObserver(cleanup, DESTROYED, false);
     InspectorUI.closeInspectorUI();
   });
 }
 
 function cleanup() {
-  Services.obs.removeObserver(cleanup, DESTROYED);
+  if (nodeHighlighted) { Services.obs.removeObserver(cleanup, DESTROYED); }
   gBrowser.removeCurrentTab();
   finish();
 }
--- a/browser/devtools/tilt/test/browser_tilt_picking_highlight01.js
+++ b/browser/devtools/tilt/test/browser_tilt_picking_highlight01.js
@@ -1,12 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
+let nodeHighlighted = false;
 let presenter;
 
 function test() {
   if (!isTiltEnabled()) {
     info("Skipping highlight test because Tilt isn't enabled.");
     return;
   }
   if (!isWebGLSupported()) {
@@ -22,19 +23,24 @@ function test() {
       {
         presenter = instance.presenter;
         Services.obs.addObserver(whenHighlighting, HIGHLIGHTING, false);
 
         presenter._onInitializationFinished = function() {
           let contentDocument = presenter.contentWindow.document;
           let div = contentDocument.getElementById("first-law");
 
+          nodeHighlighted = true;
           presenter.highlightNode(div, "moveIntoView");
         };
       }
+    }, false, function suddenDeath()
+    {
+      info("Tilt could not be initialized properly.");
+      cleanup();
     });
   });
 }
 
 function whenHighlighting() {
   ok(presenter._currentSelection > 0,
     "Highlighting a node didn't work properly.");
   ok(!presenter._highlight.disabled,
@@ -58,12 +64,12 @@ function whenUnhighlighting() {
   executeSoon(function() {
     Services.obs.removeObserver(whenUnhighlighting, UNHIGHLIGHTING);
     Services.obs.addObserver(cleanup, DESTROYED, false);
     InspectorUI.closeInspectorUI();
   });
 }
 
 function cleanup() {
-  Services.obs.removeObserver(cleanup, DESTROYED);
+  if (nodeHighlighted) { Services.obs.removeObserver(cleanup, DESTROYED); }
   gBrowser.removeCurrentTab();
   finish();
 }
--- a/browser/devtools/tilt/test/browser_tilt_picking_highlight02.js
+++ b/browser/devtools/tilt/test/browser_tilt_picking_highlight02.js
@@ -1,12 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
+let nodeHighlighted = false;
 let presenter;
 
 function test() {
   if (!isTiltEnabled()) {
     info("Skipping highlight test because Tilt isn't enabled.");
     return;
   }
   if (!isWebGLSupported()) {
@@ -19,19 +20,24 @@ function test() {
   createTab(function() {
     createTilt({
       onTiltOpen: function(instance)
       {
         presenter = instance.presenter;
         Services.obs.addObserver(whenHighlighting, HIGHLIGHTING, false);
 
         presenter._onInitializationFinished = function() {
+          nodeHighlighted = true;
           presenter.highlightNodeAt.apply(this, getPickablePoint(presenter));
         };
       }
+    }, false, function suddenDeath()
+    {
+      info("Tilt could not be initialized properly.");
+      cleanup();
     });
   });
 }
 
 function whenHighlighting() {
   ok(presenter._currentSelection > 0,
     "Highlighting a node didn't work properly.");
   ok(!presenter._highlight.disabled,
@@ -53,12 +59,12 @@ function whenUnhighlighting() {
   executeSoon(function() {
     Services.obs.removeObserver(whenUnhighlighting, UNHIGHLIGHTING);
     Services.obs.addObserver(cleanup, DESTROYED, false);
     InspectorUI.closeInspectorUI();
   });
 }
 
 function cleanup() {
-  Services.obs.removeObserver(cleanup, DESTROYED);
+  if (nodeHighlighted) { Services.obs.removeObserver(cleanup, DESTROYED); }
   gBrowser.removeCurrentTab();
   finish();
 }
--- a/browser/devtools/tilt/test/browser_tilt_picking_highlight03.js
+++ b/browser/devtools/tilt/test/browser_tilt_picking_highlight03.js
@@ -1,12 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
+let nodeHighlighted = false;
 let presenter;
 
 function test() {
   if (!isTiltEnabled()) {
     info("Skipping highlight test because Tilt isn't enabled.");
     return;
   }
   if (!isWebGLSupported()) {
@@ -19,19 +20,24 @@ function test() {
   createTab(function() {
     createTilt({
       onTiltOpen: function(instance)
       {
         presenter = instance.presenter;
         Services.obs.addObserver(whenHighlighting, HIGHLIGHTING, false);
 
         presenter._onInitializationFinished = function() {
+          nodeHighlighted = true;
           presenter.highlightNodeFor(3); // 1 = html, 2 = body, 3 = first div
         };
       }
+    }, false, function suddenDeath()
+    {
+      info("Tilt could not be initialized properly.");
+      cleanup();
     });
   });
 }
 
 function whenHighlighting() {
   ok(presenter._currentSelection > 0,
     "Highlighting a node didn't work properly.");
   ok(!presenter._highlight.disabled,
@@ -53,12 +59,12 @@ function whenUnhighlighting() {
   executeSoon(function() {
     Services.obs.removeObserver(whenUnhighlighting, UNHIGHLIGHTING);
     Services.obs.addObserver(cleanup, DESTROYED, false);
     InspectorUI.closeInspectorUI();
   });
 }
 
 function cleanup() {
-  Services.obs.removeObserver(cleanup, DESTROYED);
+  if (nodeHighlighted) { Services.obs.removeObserver(cleanup, DESTROYED); }
   gBrowser.removeCurrentTab();
   finish();
 }
--- a/browser/devtools/tilt/test/browser_tilt_picking_miv.js
+++ b/browser/devtools/tilt/test/browser_tilt_picking_miv.js
@@ -1,12 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
+let nodeHighlighted = false;
 let presenter;
 
 function test() {
   if (!isTiltEnabled()) {
     info("Skipping highlight test because Tilt isn't enabled.");
     return;
   }
   if (!isWebGLSupported()) {
@@ -22,19 +23,24 @@ function test() {
       {
         presenter = instance.presenter;
         Services.obs.addObserver(whenHighlighting, HIGHLIGHTING, false);
 
         presenter._onInitializationFinished = function() {
           let contentDocument = presenter.contentWindow.document;
           let div = contentDocument.getElementById("far-far-away");
 
+          nodeHighlighted = true;
           presenter.highlightNode(div);
         };
       }
+    }, false, function suddenDeath()
+    {
+      info("Tilt could not be initialized properly.");
+      cleanup();
     });
   });
 }
 
 function whenHighlighting() {
   ok(presenter._currentSelection > 0,
     "Highlighting a node didn't work properly.");
   ok(!presenter._highlight.disabled,
@@ -52,19 +58,19 @@ function whenBringingIntoView() {
     "The node should still be selected.");
   ok(!presenter._highlight.disabled,
     "The node should still be highlighted");
   ok(presenter.controller.arcball._resetInProgress,
     "Highlighting a node that's not already visible should trigger a reset " +
     "when this is being explicitly requested!");
 
   executeSoon(function() {
+    Services.obs.removeObserver(whenHighlighting, HIGHLIGHTING);
     Services.obs.addObserver(cleanup, DESTROYED, false);
     InspectorUI.closeInspectorUI();
   });
 }
 
 function cleanup() {
-  Services.obs.removeObserver(whenHighlighting, HIGHLIGHTING);
-  Services.obs.removeObserver(cleanup, DESTROYED);
+  if (nodeHighlighted) { Services.obs.removeObserver(cleanup, DESTROYED); }
   gBrowser.removeCurrentTab();
   finish();
 }
--- a/browser/devtools/tilt/test/browser_tilt_zoom.js
+++ b/browser/devtools/tilt/test/browser_tilt_zoom.js
@@ -1,14 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 const ZOOM = 2;
 const RESIZE = 50;
+let tiltOpened = false;
 
 function test() {
   if (!isTiltEnabled()) {
     info("Skipping controller test because Tilt isn't enabled.");
     return;
   }
   if (!isWebGLSupported()) {
     info("Skipping controller test because WebGL isn't supported.");
@@ -20,16 +21,18 @@ function test() {
   createTab(function() {
     createTilt({
       onInspectorOpen: function()
       {
         TiltUtils.setDocumentZoom(window, ZOOM);
       },
       onTiltOpen: function(instance)
       {
+        tiltOpened = true;
+
         ok(isApprox(InspectorUI.highlighter.zoom, ZOOM),
           "The Highlighter zoom doesn't have the expected results.");
 
         ok(isApprox(instance.presenter.transforms.zoom, ZOOM),
           "The presenter transforms zoom wasn't initially set correctly.");
 
         let contentWindow = gBrowser.selectedBrowser.contentWindow;
         let initialWidth = contentWindow.innerWidth;
@@ -70,17 +73,21 @@ function test() {
 
           window.resizeBy(RESIZE * ZOOM, RESIZE * ZOOM);
 
 
           Services.obs.addObserver(cleanup, DESTROYED, false);
           InspectorUI.closeInspectorUI();
         });
       }
+    }, false, function suddenDeath()
+    {
+      info("Tilt could not be initialized properly.");
+      cleanup();
     });
   });
 }
 
 function cleanup() {
-  Services.obs.removeObserver(cleanup, DESTROYED);
+  if (tiltOpened) { Services.obs.removeObserver(cleanup, DESTROYED); }
   gBrowser.removeCurrentTab();
   finish();
 }
--- a/browser/devtools/tilt/test/head.js
+++ b/browser/devtools/tilt/test/head.js
@@ -60,21 +60,29 @@ const HIGHLIGHTING = Tilt.NOTIFICATIONS.
 const UNHIGHLIGHTING = Tilt.NOTIFICATIONS.UNHIGHLIGHTING;
 const NODE_REMOVED = Tilt.NOTIFICATIONS.NODE_REMOVED;
 
 const TILT_ENABLED = Services.prefs.getBoolPref("devtools.tilt.enabled");
 const INSP_ENABLED = Services.prefs.getBoolPref("devtools.inspector.enabled");
 
 
 function isTiltEnabled() {
-  return TILT_ENABLED && INSP_ENABLED;
+  let enabled = TILT_ENABLED && INSP_ENABLED;
+
+  info("Apparently, Tilt is" + (enabled ? "" : " not") + " enabled.");
+  return enabled;
 }
 
 function isWebGLSupported() {
-  return TiltGL.isWebGLSupported() && TiltGL.create3DContext(createCanvas());
+  let supported = !TiltGL.isWebGLForceEnabled() &&
+                   TiltGL.isWebGLSupported() &&
+                   TiltGL.create3DContext(createCanvas());
+
+  info("Apparently, WebGL is" + (supported ? "" : " not") + " supported.");
+  return supported;
 }
 
 function isApprox(num1, num2, delta) {
   if (Math.abs(num1 - num2) > (delta || EPSILON)) {
     info("isApprox expected " + num1 + ", got " + num2 + " instead.");
     return false;
   }
   return true;
@@ -113,92 +121,124 @@ function isEqualVec(vec1, vec2) {
 }
 
 function createCanvas() {
   return document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
 }
 
 
 function createTab(callback, location) {
+  info("Creating a tab, with callback " + typeof callback +
+                      ", and location " + location + ".");
+
   let tab = gBrowser.selectedTab = gBrowser.addTab();
 
   gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
     gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
     callback(tab);
   }, true);
 
   gBrowser.selectedBrowser.contentWindow.location = location || DEFAULT_HTML;
   return tab;
 }
 
 
-function createTilt(callbacks, close) {
+function createTilt(callbacks, close, suddenDeath) {
+  info("Creating Tilt, with callbacks {" + Object.keys(callbacks) + "}" +
+                   ", autoclose param " + close +
+          ", and sudden death handler " + typeof suddenDeath + ".");
+
   Services.prefs.setBoolPref("webgl.verbose", true);
+  TiltUtils.Output.suppressAlerts = true;
 
+  info("Attempting to start the inspector.");
   Services.obs.addObserver(onInspectorOpen, INSPECTOR_OPENED, false);
   InspectorUI.toggleInspectorUI();
 
   function onInspectorOpen() {
+    info("Inspector was opened.");
     Services.obs.removeObserver(onInspectorOpen, INSPECTOR_OPENED);
 
     executeSoon(function() {
       if ("function" === typeof callbacks.onInspectorOpen) {
+        info("Calling 'onInspectorOpen'.");
         callbacks.onInspectorOpen();
       }
       executeSoon(function() {
+        info("Attempting to start Tilt.");
         Services.obs.addObserver(onTiltOpen, INITIALIZING, false);
+        handleFailure(suddenDeath);
         Tilt.initialize();
       });
     });
   }
 
   function onTiltOpen() {
+    info("Tilt was opened.");
     Services.obs.removeObserver(onTiltOpen, INITIALIZING);
 
     executeSoon(function() {
       if ("function" === typeof callbacks.onTiltOpen) {
+        info("Calling 'onTiltOpen'.");
         callbacks.onTiltOpen(Tilt.visualizers[Tilt.currentWindowId]);
       }
       if (close) {
         executeSoon(function() {
+          info("Attempting to close Tilt.");
           Services.obs.addObserver(onTiltClose, DESTROYED, false);
           Tilt.destroy(Tilt.currentWindowId);
         });
       }
     });
   }
 
   function onTiltClose() {
+    info("Tilt was closed.");
     Services.obs.removeObserver(onTiltClose, DESTROYED);
 
     executeSoon(function() {
       if ("function" === typeof callbacks.onTiltClose) {
+        info("Calling 'onTiltClose'.");
         callbacks.onTiltClose();
       }
       if (close) {
         executeSoon(function() {
+          info("Attempting to close the Inspector.");
           Services.obs.addObserver(onInspectorClose, INSPECTOR_CLOSED, false);
           InspectorUI.closeInspectorUI();
         });
       }
     });
   }
 
   function onInspectorClose() {
+    info("Inspector was closed.");
     Services.obs.removeObserver(onInspectorClose, INSPECTOR_CLOSED);
 
     executeSoon(function() {
       if ("function" === typeof callbacks.onInspectorClose) {
+        info("Calling 'onInspectorClose'.");
         callbacks.onInspectorClose();
       }
       if ("function" === typeof callbacks.onEnd) {
+        info("Calling 'onEnd'.");
         callbacks.onEnd();
       }
     });
   }
+
+  function handleFailure(suddenDeath) {
+    Tilt.failureCallback = function() {
+      info("Tilt FAIL.");
+      Services.obs.removeObserver(onTiltOpen, INITIALIZING);
+
+      info("Now relying on sudden death handler " + typeof suddenDeath + ".");
+      suddenDeath && suddenDeath();
+    }
+  }
 }
 
 function getPickablePoint(presenter) {
   let vertices = presenter._meshStacks[0].vertices.components;
 
   let topLeft = vec3.create([vertices[0], vertices[1], vertices[2]]);
   let bottomRight = vec3.create([vertices[6], vertices[7], vertices[8]]);
   let center = vec3.lerp(topLeft, bottomRight, 0.5, []);