Bug 933646 - Blackboxing a program in the shader editor should hide the rendered geometry, r=rcampbell
authorVictor Porof <vporof@mozilla.com>
Mon, 04 Nov 2013 22:48:10 +0200
changeset 153591 64e809f87e60868a26a362bd93f2c21a3fb8c957
parent 153590 0325da4d4bbce5fa225ed9d9f908d2b2e09a9600
child 153592 cdafebd0eb7575e2f4b811fee4929c0db18889bc
push id25595
push userryanvm@gmail.com
push dateTue, 05 Nov 2013 20:19:27 +0000
treeherdermozilla-central@2ada3a06d5e7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrcampbell
bugs933646
milestone28.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 933646 - Blackboxing a program in the shader editor should hide the rendered geometry, r=rcampbell
browser/devtools/shadereditor/shadereditor.js
browser/devtools/shadereditor/test/browser.ini
browser/devtools/shadereditor/test/browser_se_programs-blackbox.js
browser/devtools/shadereditor/test/browser_se_programs-highlight.js
browser/devtools/shadereditor/test/browser_webgl-actor-test-06.js
browser/devtools/shadereditor/test/browser_webgl-actor-test-17.js
browser/devtools/shadereditor/test/doc_overlapping-geometry.html
browser/devtools/shadereditor/test/head.js
toolkit/devtools/server/actors/webgl.js
--- a/browser/devtools/shadereditor/shadereditor.js
+++ b/browser/devtools/shadereditor/shadereditor.js
@@ -27,17 +27,16 @@ const EVENTS = {
   SOURCES_SHOWN: "ShaderEditor:SourcesShown",
 
   // When a shader's source was edited and compiled via the editor.
   SHADER_COMPILED: "ShaderEditor:ShaderCompiled"
 };
 
 const STRINGS_URI = "chrome://browser/locale/devtools/shadereditor.properties"
 const HIGHLIGHT_COLOR = [1, 0, 0, 1];
-const BLACKBOX_COLOR = [0, 0, 0, 0];
 const TYPING_MAX_DELAY = 500;
 const SHADERS_AUTOGROW_ITEMS = 4;
 const DEFAULT_EDITOR_CONFIG = {
   mode: Editor.modes.text,
   lineNumbers: true,
   showAnnotationRuler: true
 };
 
@@ -169,35 +168,35 @@ let ShadersListView = Heritage.extend(Wi
    * Initialization function, called when the tool is started.
    */
   initialize: function() {
     this.widget = new SideMenuWidget(this._pane = $("#shaders-pane"), {
       showArrows: true,
       showItemCheckboxes: true
     });
 
-    this._onShaderSelect = this._onShaderSelect.bind(this);
-    this._onShaderCheck = this._onShaderCheck.bind(this);
-    this._onShaderMouseEnter = this._onShaderMouseEnter.bind(this);
-    this._onShaderMouseLeave = this._onShaderMouseLeave.bind(this);
+    this._onProgramSelect = this._onProgramSelect.bind(this);
+    this._onProgramCheck = this._onProgramCheck.bind(this);
+    this._onProgramMouseEnter = this._onProgramMouseEnter.bind(this);
+    this._onProgramMouseLeave = this._onProgramMouseLeave.bind(this);
 
-    this.widget.addEventListener("select", this._onShaderSelect, false);
-    this.widget.addEventListener("check", this._onShaderCheck, false);
-    this.widget.addEventListener("mouseenter", this._onShaderMouseEnter, true);
-    this.widget.addEventListener("mouseleave", this._onShaderMouseLeave, true);
+    this.widget.addEventListener("select", this._onProgramSelect, false);
+    this.widget.addEventListener("check", this._onProgramCheck, false);
+    this.widget.addEventListener("mouseenter", this._onProgramMouseEnter, true);
+    this.widget.addEventListener("mouseleave", this._onProgramMouseLeave, true);
   },
 
   /**
    * Destruction function, called when the tool is closed.
    */
   destroy: function() {
-    this.widget.removeEventListener("select", this._onShaderSelect, false);
-    this.widget.removeEventListener("check", this._onShaderCheck, false);
-    this.widget.removeEventListener("mouseenter", this._onShaderMouseEnter, true);
-    this.widget.removeEventListener("mouseleave", this._onShaderMouseLeave, true);
+    this.widget.removeEventListener("select", this._onProgramSelect, false);
+    this.widget.removeEventListener("check", this._onProgramCheck, false);
+    this.widget.removeEventListener("mouseenter", this._onProgramMouseEnter, true);
+    this.widget.removeEventListener("mouseleave", this._onProgramMouseLeave, true);
   },
 
   /**
    * Adds a program to this programs container.
    *
    * @param object programActor
    *        The program actor coming from the active thread.
    */
@@ -243,19 +242,19 @@ let ShadersListView = Heritage.extend(Wi
    * @param boolean
    *        True if the program was added, false otherwise.
    */
   hasProgram: function(programActor) {
     return !!this.attachments.filter(e => e.programActor == programActor).length;
   },
 
   /**
-   * The select listener for the sources container.
+   * The select listener for the programs container.
    */
-  _onShaderSelect: function({ detail: sourceItem }) {
+  _onProgramSelect: function({ detail: sourceItem }) {
     if (!sourceItem) {
       return;
     }
     // The container is not empty and an actual item was selected.
     let attachment = sourceItem.attachment;
 
     function getShaders() {
       return promise.all([
@@ -275,44 +274,44 @@ let ShadersListView = Heritage.extend(Wi
         fs: fragmentShaderText
       });
     }
 
     getShaders().then(getSources).then(showSources).then(null, Cu.reportError);
   },
 
   /**
-   * The check listener for the sources container.
+   * The check listener for the programs container.
    */
-  _onShaderCheck: function({ detail: { checked }, target }) {
+  _onProgramCheck: function({ detail: { checked }, target }) {
     let sourceItem = this.getItemForElement(target);
     let attachment = sourceItem.attachment;
     attachment.isBlackBoxed = !checked;
-    attachment.programActor[checked ? "unhighlight" : "highlight"](BLACKBOX_COLOR);
+    attachment.programActor[checked ? "unblackbox" : "blackbox"]();
   },
 
   /**
-   * The mouseenter listener for the sources container.
+   * The mouseenter listener for the programs container.
    */
-  _onShaderMouseEnter: function(e) {
+  _onProgramMouseEnter: function(e) {
     let sourceItem = this.getItemForElement(e.target, { noSiblings: true });
     if (sourceItem && !sourceItem.attachment.isBlackBoxed) {
       sourceItem.attachment.programActor.highlight(HIGHLIGHT_COLOR);
 
       if (e instanceof Event) {
         e.preventDefault();
         e.stopPropagation();
       }
     }
   },
 
   /**
-   * The mouseleave listener for the sources container.
+   * The mouseleave listener for the programs container.
    */
-  _onShaderMouseLeave: function(e) {
+  _onProgramMouseLeave: function(e) {
     let sourceItem = this.getItemForElement(e.target, { noSiblings: true });
     if (sourceItem && !sourceItem.attachment.isBlackBoxed) {
       sourceItem.attachment.programActor.unhighlight();
 
       if (e instanceof Event) {
         e.preventDefault();
         e.stopPropagation();
       }
--- a/browser/devtools/shadereditor/test/browser.ini
+++ b/browser/devtools/shadereditor/test/browser.ini
@@ -1,11 +1,12 @@
 [DEFAULT]
 support-files =
   doc_multiple-contexts.html
+  doc_overlapping-geometry.html
   doc_shader-order.html
   doc_simple-canvas.html
   head.js
 
 [browser_se_aaa_run_first_leaktest.js]
 [browser_se_bfcache.js]
 [browser_se_editors-contents.js]
 [browser_se_editors-lazy-init.js]
@@ -29,8 +30,9 @@ support-files =
 [browser_webgl-actor-test-09.js]
 [browser_webgl-actor-test-10.js]
 [browser_webgl-actor-test-11.js]
 [browser_webgl-actor-test-12.js]
 [browser_webgl-actor-test-13.js]
 [browser_webgl-actor-test-14.js]
 [browser_webgl-actor-test-15.js]
 [browser_webgl-actor-test-16.js]
+[browser_webgl-actor-test-17.js]
--- a/browser/devtools/shadereditor/test/browser_se_programs-blackbox.js
+++ b/browser/devtools/shadereditor/test/browser_se_programs-blackbox.js
@@ -50,62 +50,62 @@ function ifWebGLSupported() {
     "The first program should now be blackboxed.");
   is(getBlackBoxCheckbox(panel, 0).checked, false,
     "The first blackbox checkbox should now be unchecked.");
   ok(!ShadersListView.attachments[1].isBlackBoxed,
     "The second program should still not be blackboxed.");
   is(getBlackBoxCheckbox(panel, 1).checked, true,
     "The second blackbox checkbox should still be checked.");
 
-  yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas1");
+  yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
   yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
-  yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas1");
+  yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
   yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
   ok(true, "The first program was correctly blackboxed.");
 
   getBlackBoxCheckbox(panel, 1).click();
 
   ok(ShadersListView.selectedAttachment.isBlackBoxed,
     "The first program should still be blackboxed.");
   is(getBlackBoxCheckbox(panel, 0).checked, false,
     "The first blackbox checkbox should still be unchecked.");
   ok(ShadersListView.attachments[1].isBlackBoxed,
     "The second program should now be blackboxed.");
   is(getBlackBoxCheckbox(panel, 1).checked, false,
     "The second blackbox checkbox should now be unchecked.");
 
-  yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas1");
-  yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas2");
-  yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas1");
-  yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas2");
+  yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
+  yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
+  yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
+  yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
   ok(true, "The second program was correctly blackboxed.");
 
-  ShadersListView._onShaderMouseEnter({ target: getItemLabel(panel, 0) });
+  ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 0) });
 
-  yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas1");
-  yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas2");
-  yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas1");
-  yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas2");
+  yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
+  yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
+  yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
+  yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
   ok(true, "Highlighting didn't work while blackboxed (1).");
 
-  ShadersListView._onShaderMouseLeave({ target: getItemLabel(panel, 0) });
-  ShadersListView._onShaderMouseEnter({ target: getItemLabel(panel, 1) });
+  ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 0) });
+  ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 1) });
 
-  yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas1");
-  yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas2");
-  yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas1");
-  yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas2");
+  yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
+  yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
+  yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
+  yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
   ok(true, "Highlighting didn't work while blackboxed (2).");
 
-  ShadersListView._onShaderMouseLeave({ target: getItemLabel(panel, 1) });
+  ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 1) });
 
-  yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas1");
-  yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas2");
-  yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas1");
-  yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas2");
+  yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
+  yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
+  yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
+  yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
   ok(true, "Highlighting didn't work while blackboxed (3).");
 
   getBlackBoxCheckbox(panel, 0).click();
   getBlackBoxCheckbox(panel, 1).click();
 
   ok(!ShadersListView.selectedAttachment.isBlackBoxed,
     "The first program should now be unblackboxed.");
   is(getBlackBoxCheckbox(panel, 0).checked, true,
@@ -116,34 +116,34 @@ function ifWebGLSupported() {
     "The second blackbox checkbox should now be rechecked.");
 
   yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
   yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
   yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
   yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
   ok(true, "The two programs were correctly unblackboxed.");
 
-  ShadersListView._onShaderMouseEnter({ target: getItemLabel(panel, 0) });
+  ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 0) });
 
   yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
   yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
   yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
   yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
   ok(true, "The first program was correctly highlighted.");
 
-  ShadersListView._onShaderMouseLeave({ target: getItemLabel(panel, 0) });
-  ShadersListView._onShaderMouseEnter({ target: getItemLabel(panel, 1) });
+  ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 0) });
+  ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 1) });
 
   yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
   yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas2");
   yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
   yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas2");
   ok(true, "The second program was correctly highlighted.");
 
-  ShadersListView._onShaderMouseLeave({ target: getItemLabel(panel, 1) });
+  ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 1) });
 
   yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
   yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
   yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
   yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
   ok(true, "The two programs were correctly unhighlighted.");
 
   yield teardown(panel);
--- a/browser/devtools/shadereditor/test/browser_se_programs-highlight.js
+++ b/browser/devtools/shadereditor/test/browser_se_programs-highlight.js
@@ -30,50 +30,50 @@ function ifWebGLSupported() {
     ok(false, "No sources should be changed form this point onward.");
   });
 
   yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
   yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
   yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
   yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
 
-  ShadersListView._onShaderMouseEnter({ target: getItemLabel(panel, 0) });
+  ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 0) });
 
   yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
   yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
   yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
   yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
   ok(true, "The first program was correctly highlighted.");
 
-  ShadersListView._onShaderMouseLeave({ target: getItemLabel(panel, 0) });
-  ShadersListView._onShaderMouseEnter({ target: getItemLabel(panel, 1) });
+  ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 0) });
+  ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 1) });
 
   yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
   yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas2");
   yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
   yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas2");
   ok(true, "The second program was correctly highlighted.");
 
-  ShadersListView._onShaderMouseLeave({ target: getItemLabel(panel, 1) });
+  ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 1) });
 
   yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
   yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
   yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
   yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
   ok(true, "The two programs were correctly unhighlighted.");
 
-  ShadersListView._onShaderMouseEnter({ target: getBlackBoxCheckbox(panel, 0) });
+  ShadersListView._onProgramMouseEnter({ target: getBlackBoxCheckbox(panel, 0) });
 
   yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
   yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
   yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
   yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
   ok(true, "The two programs were left unchanged after hovering a blackbox checkbox.");
 
-  ShadersListView._onShaderMouseLeave({ target: getBlackBoxCheckbox(panel, 0) });
+  ShadersListView._onProgramMouseLeave({ target: getBlackBoxCheckbox(panel, 0) });
 
   yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
   yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
   yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
   yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
   ok(true, "The two programs were left unchanged after unhovering a blackbox checkbox.");
 
   yield teardown(panel);
--- a/browser/devtools/shadereditor/test/browser_webgl-actor-test-06.js
+++ b/browser/devtools/shadereditor/test/browser_webgl-actor-test-06.js
@@ -1,40 +1,52 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
- * Tests that the highlight/unhighlight operations on program actors
- * work as expected.
+ * Tests that the highlight/unhighlight and blackbox/unblackbox operations on
+ * program actors work as expected.
  */
 
 function ifWebGLSupported() {
   let [target, debuggee, front] = yield initBackend(SIMPLE_CANVAS_URL);
   front.setup({ reload: true });
 
   let programActor = yield once(front, "program-linked");
   let vertexShader = yield programActor.getVertexShader();
   let fragmentShader = yield programActor.getFragmentShader();
 
   yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
   yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
   yield checkShaderSource("The shader sources are correct before highlighting.");
-  ok(true, "The top left pixel color was correct before highlighting.");
+  ok(true, "The corner pixel colors are correct before highlighting.");
 
   yield programActor.highlight([0, 0, 1, 1]);
   yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 255, a: 255 }, true);
   yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 0, b: 255, a: 255 }, true);
   yield checkShaderSource("The shader sources are preserved after highlighting.");
-  ok(true, "The top left pixel color is correct after highlighting.");
+  ok(true, "The corner pixel colors are correct after highlighting.");
 
   yield programActor.unhighlight();
   yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
   yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
   yield checkShaderSource("The shader sources are correct after unhighlighting.");
-  ok(true, "The top left pixel color is correct after unhighlighting.");
+  ok(true, "The corner pixel colors are correct after unhighlighting.");
+
+  yield programActor.blackbox();
+  yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
+  yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 0, b: 0, a: 255 }, true);
+  yield checkShaderSource("The shader sources are preserved after blackboxing.");
+  ok(true, "The corner pixel colors are correct after blackboxing.");
+
+  yield programActor.unblackbox();
+  yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
+  yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
+  yield checkShaderSource("The shader sources are correct after unblackboxing.");
+  ok(true, "The corner pixel colors are correct after unblackboxing.");
 
   function checkShaderSource(aMessage) {
     return Task.spawn(function() {
       let newVertexShader = yield programActor.getVertexShader();
       let newFragmentShader = yield programActor.getFragmentShader();
       is(vertexShader, newVertexShader,
         "The same vertex shader actor was retrieved.");
       is(fragmentShader, newFragmentShader,
new file mode 100644
--- /dev/null
+++ b/browser/devtools/shadereditor/test/browser_webgl-actor-test-17.js
@@ -0,0 +1,47 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests that the blackbox/unblackbox operations work as expected with
+ * overlapping geometry.
+ */
+
+function ifWebGLSupported() {
+  let [target, debuggee, front] = yield initBackend(OVERLAPPING_GEOMETRY_CANVAS_URL);
+  front.setup({ reload: true });
+
+  let firstProgramActor = yield once(front, "program-linked");
+  let secondProgramActor = yield once(front, "program-linked");
+
+  yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true);
+  yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 255, b: 255, a: 255 }, true);
+  yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true);
+  ok(true, "The corner vs. center pixel colors are correct before blackboxing.");
+
+  yield firstProgramActor.blackbox();
+  yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
+  yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 255, b: 255, a: 255 }, true);
+  yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true);
+  ok(true, "The corner vs. center pixel colors are correct after blackboxing (1).");
+
+  yield firstProgramActor.unblackbox();
+  yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true);
+  yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 255, b: 255, a: 255 }, true);
+  yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true);
+  ok(true, "The corner vs. center pixel colors are correct after unblackboxing (1).");
+
+  yield secondProgramActor.blackbox();
+  yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true);
+  yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 255, g: 255, b: 0, a: 255 }, true);
+  yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true);
+  ok(true, "The corner vs. center pixel colors are correct after blackboxing (2).");
+
+  yield secondProgramActor.unblackbox();
+  yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true);
+  yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 255, b: 255, a: 255 }, true);
+  yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true);
+  ok(true, "The corner vs. center pixel colors are correct after unblackboxing (2).");
+
+  yield removeTab(target.tab);
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/shadereditor/test/doc_overlapping-geometry.html
@@ -0,0 +1,120 @@
+<!-- Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!doctype html>
+
+<html>
+  <head>
+    <meta charset="utf-8"/>
+    <title>WebGL editor test page</title>
+
+    <script id="shader-vs" type="x-shader/x-vertex">
+      precision lowp float;
+      attribute vec3 aVertexPosition;
+      uniform float uDepth;
+
+      void main(void) {
+        gl_Position = vec4(aVertexPosition, uDepth);
+      }
+    </script>
+
+    <script id="shader-fs-0" type="x-shader/x-fragment">
+      precision lowp float;
+
+      void main(void) {
+        gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
+      }
+    </script>
+
+    <script id="shader-fs-1" type="x-shader/x-fragment">
+      precision lowp float;
+
+      void main(void) {
+        gl_FragColor = vec4(0.0, 1.0, 1.0, 1.0);
+      }
+    </script>
+  </head>
+
+  <body>
+    <canvas id="canvas" width="128" height="128"></canvas>
+
+    <script type="text/javascript;version=1.8">
+      "use strict";
+
+      let canvas, gl;
+      let program = [];
+      let squareVerticesPositionBuffer;
+      let vertexPositionAttribute = [];
+      let depthUniform = [];
+
+      window.onload = function() {
+        canvas = document.querySelector("canvas");
+        gl = canvas.getContext("webgl");
+        gl.clearColor(0.0, 0.0, 0.0, 1.0);
+
+        initProgram(0);
+        initProgram(1);
+        initBuffers();
+        drawScene();
+      }
+
+      function initProgram(i) {
+        let vertexShader = getShader("shader-vs");
+        let fragmentShader = getShader("shader-fs-" + i);
+
+        program[i] = gl.createProgram();
+        gl.attachShader(program[i], vertexShader);
+        gl.attachShader(program[i], fragmentShader);
+        gl.linkProgram(program[i]);
+
+        vertexPositionAttribute[i] = gl.getAttribLocation(program[i], "aVertexPosition");
+        gl.enableVertexAttribArray(vertexPositionAttribute[i]);
+
+        depthUniform[i] = gl.getUniformLocation(program[i], "uDepth");
+      }
+
+      function getShader(id) {
+        let script = document.getElementById(id);
+        let source = script.textContent;
+        let shader;
+
+        if (script.type == "x-shader/x-fragment") {
+          shader = gl.createShader(gl.FRAGMENT_SHADER);
+        } else if (script.type == "x-shader/x-vertex") {
+          shader = gl.createShader(gl.VERTEX_SHADER);
+        }
+
+        gl.shaderSource(shader, source);
+        gl.compileShader(shader);
+
+        return shader;
+      }
+
+      function initBuffers() {
+        squareVerticesPositionBuffer = gl.createBuffer();
+        gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesPositionBuffer);
+        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
+           1.0,  1.0, 0.0,
+          -1.0,  1.0, 0.0,
+           1.0, -1.0, 0.0,
+          -1.0, -1.0, 0.0
+        ]), gl.STATIC_DRAW);
+      }
+
+      function drawScene() {
+        gl.clear(gl.COLOR_BUFFER_BIT);
+
+        for (let i = 0; i < 2; i++) {
+          gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesPositionBuffer);
+          gl.vertexAttribPointer(vertexPositionAttribute[i], 3, gl.FLOAT, false, 0, 0);
+
+          gl.useProgram(program[i]);
+          gl.uniform1f(depthUniform[i], i + 1);
+          gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
+        }
+
+        window.requestAnimationFrame(drawScene);
+      }
+    </script>
+  </body>
+
+</html>
--- a/browser/devtools/shadereditor/test/head.js
+++ b/browser/devtools/shadereditor/test/head.js
@@ -22,16 +22,17 @@ let { WebGLFront } = devtools.require("d
 let TiltGL = devtools.require("devtools/tilt/tilt-gl");
 let TargetFactory = devtools.TargetFactory;
 let Toolbox = devtools.Toolbox;
 
 const EXAMPLE_URL = "http://example.com/browser/browser/devtools/shadereditor/test/";
 const SIMPLE_CANVAS_URL = EXAMPLE_URL + "doc_simple-canvas.html";
 const SHADER_ORDER_URL = EXAMPLE_URL + "doc_shader-order.html";
 const MULTIPLE_CONTEXTS_URL = EXAMPLE_URL + "doc_multiple-contexts.html";
+const OVERLAPPING_GEOMETRY_CANVAS_URL = EXAMPLE_URL + "doc_overlapping-geometry.html";
 
 // All tests are asynchronous.
 waitForExplicitFinish();
 
 let gToolEnabled = Services.prefs.getBoolPref("devtools.shadereditor.enabled");
 
 registerCleanupFunction(() => {
   info("finish() was called, cleaning up...");
--- a/toolkit/devtools/server/actors/webgl.js
+++ b/toolkit/devtools/server/actors/webgl.js
@@ -136,16 +136,34 @@ let ProgramActor = protocol.ActorClass({
   unhighlight: method(function() {
     let shaderActor = this._getShaderActor("fragment");
     shaderActor.compile(shaderActor.text);
   }, {
     oneway: true
   }),
 
   /**
+   * Prevents any geometry from being rendered using this program.
+   */
+  blackbox: method(function() {
+    this.observer.cache.blackboxedPrograms.add(this.program);
+  }, {
+    oneway: true
+  }),
+
+  /**
+   * Allows geometry to be rendered using this program.
+   */
+  unblackbox: method(function() {
+    this.observer.cache.blackboxedPrograms.delete(this.program);
+  }, {
+    oneway: true
+  }),
+
+  /**
    * Returns a cached ShaderActor instance based on the required shader type.
    *
    * @param string type
    *        Either "vertex" or "fragment".
    * @return ShaderActor
    *         The respective shader actor instance.
    */
   _getShaderActor: function(type) {
@@ -487,16 +505,25 @@ let WebGLInstrumenter = {
     callback: "uniform_",
     functions: [
       "uniform1i", "uniform2i", "uniform3i", "uniform4i",
       "uniform1f", "uniform2f", "uniform3f", "uniform4f",
       "uniform1iv", "uniform2iv", "uniform3iv", "uniform4iv",
       "uniform1fv", "uniform2fv", "uniform3fv", "uniform4fv",
       "uniformMatrix2fv", "uniformMatrix3fv", "uniformMatrix4fv"
     ]
+  }, {
+    timing: "after",
+    functions: ["useProgram"]
+  }, {
+    timing: "before",
+    callback: "draw_",
+    functions: [
+      "drawArrays", "drawElements"
+    ]
   }]
   // TODO: It'd be a good idea to handle other functions as well:
   //   - getActiveUniform
   //   - getUniform
   //   - getActiveAttrib
   //   - getVertexAttrib
 };
 
@@ -572,43 +599,73 @@ WebGLObserver.prototype = {
    *
    * @param WebGLRenderingContext gl
    *        The WebGL context initiating this call.
    * @param array glArgs
    *        Overridable arguments with which the function is called.
    */
   toggleVertexAttribArray: function(gl, glArgs) {
     glArgs[0] = this.cache.call("getCurrentAttributeLocation", glArgs[0]);
-    return glArgs[0] < 0;
+    return glArgs[0] < 0; // Return true to break original function call.
   },
 
   /**
    * Called immediately *before* 'attribute_' is requested in the context.
    *
    * @param WebGLRenderingContext gl
    *        The WebGL context initiating this call.
    * @param array glArgs
    *        Overridable arguments with which the function is called.
    */
   attribute_: function(gl, glArgs) {
     glArgs[0] = this.cache.call("getCurrentAttributeLocation", glArgs[0]);
-    return glArgs[0] < 0;
+    return glArgs[0] < 0; // Return true to break original function call.
   },
 
   /**
    * Called immediately *before* 'uniform_' is requested in the context.
    *
    * @param WebGLRenderingContext gl
    *        The WebGL context initiating this call.
    * @param array glArgs
    *        Overridable arguments with which the function is called.
    */
   uniform_: function(gl, glArgs) {
     glArgs[0] = this.cache.call("getCurrentUniformLocation", glArgs[0]);
-    return !glArgs[0];
+    return !glArgs[0]; // Return true to break original function call.
+  },
+
+  /**
+   * Called immediately *after* 'useProgram' is requested in the context.
+   *
+   * @param WebGLRenderingContext gl
+   *        The WebGL context initiating this call.
+   * @param array glArgs
+   *        Overridable arguments with which the function is called.
+   * @param void glResult
+   *        The returned value of the original function call.
+   */
+  useProgram: function(gl, glArgs, glResult) {
+    // Manually keeping a cache and not using gl.getParameter(CURRENT_PROGRAM)
+    // because gl.get* functions are slow as potatoes.
+    this.cache.currentProgram = glArgs[0];
+  },
+
+  /**
+   * Called immediately *before* 'drawArrays' or 'drawElements' is requested
+   * in the context.
+   *
+   * @param WebGLRenderingContext gl
+   *        The WebGL context initiating this call.
+   * @param array glArgs
+   *        Overridable arguments with which the function is called.
+   */
+  draw_: function(gl, glArgs) {
+    // Return true to break original function call.
+    return this.cache.blackboxedPrograms.has(this.cache.currentProgram);
   },
 
   /**
    * Executes a function in this object.
    * This method makes sure that any handlers in the context observer are
    * suppressed, hence stopping observing any context function calls.
    *
    * @param string funcName
@@ -629,25 +686,38 @@ WebGLObserver.prototype = {
  * A cache storing WebGL state, like shaders, attributes or uniforms.
  *
  * @param WebGLObserver observer
  *        The observer for the target context.
  */
 function WebGLCache(observer) {
   this._observer = observer;
 
+  this.currentProgram = null;
+  this.blackboxedPrograms = new Set();
+
   this._shaders = new Map();
   this._attributes = [];
   this._uniforms = [];
   this._attributesBridge = new Map();
   this._uniformsBridge = new Map();
 }
 
 WebGLCache.prototype = {
   /**
+   * The current program in the observed WebGL context.
+   */
+  currentProgram: null,
+
+  /**
+   * A set of blackboxed programs in the observed WebGL context.
+   */
+  blackboxedPrograms: null,
+
+  /**
    * Adds shader information to the cache.
    *
    * @param WebGLShader shader
    *        The shader for which the source is to be cached. If the shader
    *        was already cached, nothing happens.
    * @param string text
    *        The current shader text.
    */