Bug 910955 - Implement a live WebGL shader editor, r=dcamp

This commit is contained in:
Victor Porof 2013-10-25 10:18:41 +03:00
parent b1b35492d8
commit 39ed225e41
36 changed files with 1669 additions and 6 deletions

View File

@ -1166,6 +1166,9 @@ pref("devtools.scratchpad.recentFilesMax", 10);
pref("devtools.styleeditor.enabled", true);
pref("devtools.styleeditor.transitions", true);
// Enable the Shader Editor.
pref("devtools.shadereditor.enabled", false);
// Enable tools for Chrome development.
pref("devtools.chrome.enabled", false);

View File

@ -1,4 +1,3 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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

View File

@ -51,6 +51,8 @@ browser.jar:
content/browser/devtools/debugger-view.js (debugger/debugger-view.js)
content/browser/devtools/debugger-toolbar.js (debugger/debugger-toolbar.js)
content/browser/devtools/debugger-panes.js (debugger/debugger-panes.js)
content/browser/devtools/shadereditor.xul (shadereditor/shadereditor.xul)
content/browser/devtools/shadereditor.js (shadereditor/shadereditor.js)
content/browser/devtools/profiler.xul (profiler/profiler.xul)
content/browser/devtools/cleopatra.html (profiler/cleopatra/cleopatra.html)
content/browser/devtools/profiler/cleopatra/css/ui.css (profiler/cleopatra/css/ui.css)

View File

@ -27,6 +27,7 @@ loader.lazyGetter(this, "InspectorPanel", () => require("devtools/inspector/insp
loader.lazyGetter(this, "WebConsolePanel", () => require("devtools/webconsole/panel").WebConsolePanel);
loader.lazyGetter(this, "DebuggerPanel", () => require("devtools/debugger/debugger-panel").DebuggerPanel);
loader.lazyImporter(this, "StyleEditorPanel", "resource:///modules/devtools/StyleEditorPanel.jsm");
loader.lazyGetter(this, "ShaderEditorPanel", () => require("devtools/shadereditor/panel").ShaderEditorPanel);
loader.lazyGetter(this, "ProfilerPanel", () => require("devtools/profiler/panel"));
loader.lazyGetter(this, "NetMonitorPanel", () => require("devtools/netmonitor/netmonitor-panel").NetMonitorPanel);
loader.lazyGetter(this, "ScratchpadPanel", () => require("devtools/scratchpad/scratchpad-panel").ScratchpadPanel);
@ -36,6 +37,7 @@ const toolboxProps = "chrome://browser/locale/devtools/toolbox.properties";
const inspectorProps = "chrome://browser/locale/devtools/inspector.properties";
const debuggerProps = "chrome://browser/locale/devtools/debugger.properties";
const styleEditorProps = "chrome://browser/locale/devtools/styleeditor.properties";
const shaderEditorProps = "chrome://browser/locale/devtools/shadereditor.properties";
const webConsoleProps = "chrome://browser/locale/devtools/webconsole.properties";
const profilerProps = "chrome://browser/locale/devtools/profiler.properties";
const netMonitorProps = "chrome://browser/locale/devtools/netmonitor.properties";
@ -44,6 +46,7 @@ loader.lazyGetter(this, "toolboxStrings", () => Services.strings.createBundle(to
loader.lazyGetter(this, "webConsoleStrings", () => Services.strings.createBundle(webConsoleProps));
loader.lazyGetter(this, "debuggerStrings", () => Services.strings.createBundle(debuggerProps));
loader.lazyGetter(this, "styleEditorStrings", () => Services.strings.createBundle(styleEditorProps));
loader.lazyGetter(this, "shaderEditorStrings", () => Services.strings.createBundle(shaderEditorProps));
loader.lazyGetter(this, "inspectorStrings", () => Services.strings.createBundle(inspectorProps));
loader.lazyGetter(this, "profilerStrings",() => Services.strings.createBundle(profilerProps));
loader.lazyGetter(this, "netMonitorStrings", () => Services.strings.createBundle(netMonitorProps));
@ -166,11 +169,30 @@ Tools.styleEditor = {
}
};
Tools.shaderEditor = {
id: "shadereditor",
ordinal: 5,
visibilityswitch: "devtools.shadereditor.enabled",
icon: "chrome://browser/skin/devtools/tool-styleeditor.png",
url: "chrome://browser/content/devtools/shadereditor.xul",
label: l10n("ToolboxShaderEditor.label", shaderEditorStrings),
tooltip: l10n("ToolboxShaderEditor.tooltip", shaderEditorStrings),
isTargetSupported: function(target) {
return true;
},
build: function(iframeWindow, toolbox) {
let panel = new ShaderEditorPanel(iframeWindow, toolbox);
return panel.open();
}
};
Tools.jsprofiler = {
id: "jsprofiler",
accesskey: l10n("profiler.accesskey", profilerStrings),
key: l10n("profiler2.commandkey", profilerStrings),
ordinal: 5,
ordinal: 6,
modifiers: "shift",
visibilityswitch: "devtools.profiler.enabled",
icon: "chrome://browser/skin/devtools/tool-profiler.png",
@ -193,7 +215,7 @@ Tools.netMonitor = {
id: "netmonitor",
accesskey: l10n("netmonitor.accesskey", netMonitorStrings),
key: l10n("netmonitor.commandkey", netMonitorStrings),
ordinal: 6,
ordinal: 7,
modifiers: osString == "Darwin" ? "accel,alt" : "accel,shift",
visibilityswitch: "devtools.netmonitor.enabled",
icon: "chrome://browser/skin/devtools/tool-network.png",
@ -214,7 +236,7 @@ Tools.netMonitor = {
Tools.scratchpad = {
id: "scratchpad",
ordinal: 7,
ordinal: 8,
visibilityswitch: "devtools.scratchpad.enabled",
icon: "chrome://browser/skin/devtools/tool-scratchpad.png",
url: "chrome://browser/content/devtools/scratchpad.xul",
@ -234,10 +256,11 @@ Tools.scratchpad = {
let defaultTools = [
Tools.options,
Tools.styleEditor,
Tools.webConsole,
Tools.jsdebugger,
Tools.inspector,
Tools.jsdebugger,
Tools.styleEditor,
Tools.shaderEditor,
Tools.jsprofiler,
Tools.netMonitor,
Tools.scratchpad

View File

@ -4,3 +4,10 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
TEST_DIRS += ['test']
JS_MODULES_PATH = 'modules/devtools/shadereditor'
EXTRA_JS_MODULES += [
'panel.js'
]

View File

@ -0,0 +1,65 @@
/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* 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/. */
"use strict";
const { Cc, Ci, Cu, Cr } = require("chrome");
const promise = require("sdk/core/promise");
const EventEmitter = require("devtools/shared/event-emitter");
const { WebGLFront } = require("devtools/server/actors/webgl");
function ShaderEditorPanel(iframeWindow, toolbox) {
this.panelWin = iframeWindow;
this._toolbox = toolbox;
this._destroyer = null;
EventEmitter.decorate(this);
};
exports.ShaderEditorPanel = ShaderEditorPanel;
ShaderEditorPanel.prototype = {
open: function() {
let targetPromise;
// Local debugging needs to make the target remote.
if (!this.target.isRemote) {
targetPromise = this.target.makeRemote();
} else {
targetPromise = promise.resolve(this.target);
}
return targetPromise
.then(() => {
this.panelWin.gTarget = this.target;
this.panelWin.gFront = new WebGLFront(this.target.client, this.target.form);
return this.panelWin.startupShaderEditor();
})
.then(() => {
this.isReady = true;
this.emit("ready");
return this;
})
.then(null, function onError(aReason) {
Cu.reportError("ShaderEditorPanel open failed. " +
aReason.error + ": " + aReason.message);
});
},
// DevToolPanel API
get target() this._toolbox.target,
destroy: function() {
// Make sure this panel is not already destroyed.
if (this._destroyer) {
return this._destroyer;
}
return this._destroyer = this.panelWin.shutdownShaderEditor().then(() => {
this.emit("destroyed");
});
}
};

View File

@ -0,0 +1,396 @@
/* 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/. */
"use strict";
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/devtools/Loader.jsm");
Cu.import("resource:///modules/devtools/SideMenuWidget.jsm");
Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
const promise = require("sdk/core/promise");
const EventEmitter = require("devtools/shared/event-emitter");
const Editor = require("devtools/sourceeditor/editor");
// The panel's window global is an EventEmitter firing the following events:
const EVENTS = {
// When the vertex and fragment sources were shown in the editor.
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
};
/**
* The current target and the WebGL Editor front, set by this tool's host.
*/
let gTarget, gFront;
/**
* Initializes the shader editor controller and views.
*/
function startupShaderEditor() {
return promise.all([
EventsHandler.initialize(),
ShadersListView.initialize(),
ShadersEditorsView.initialize()
]);
}
/**
* Destroys the shader editor controller and views.
*/
function shutdownShaderEditor() {
return promise.all([
EventsHandler.destroy(),
ShadersListView.destroy(),
ShadersEditorsView.destroy()
]);
}
/**
* Functions handling target-related lifetime events.
*/
let EventsHandler = {
/**
* Listen for events emitted by the current tab target.
*/
initialize: function() {
this._onWillNavigate = this._onWillNavigate.bind(this);
this._onProgramLinked = this._onProgramLinked.bind(this);
gTarget.on("will-navigate", this._onWillNavigate);
gFront.on("program-linked", this._onProgramLinked);
},
/**
* Remove events emitted by the current tab target.
*/
destroy: function() {
gTarget.off("will-navigate", this._onWillNavigate);
gFront.off("program-linked", this._onProgramLinked);
},
/**
* Called for each location change in the debugged tab.
*/
_onWillNavigate: function() {
gFront.setup();
ShadersListView.empty();
ShadersEditorsView.setText({ vs: "", fs: "" });
$("#reload-notice").hidden = true;
$("#waiting-notice").hidden = false;
$("#content").hidden = true;
},
/**
* Called every time a program was linked in the debugged tab.
*/
_onProgramLinked: function(programActor) {
$("#waiting-notice").hidden = true;
$("#reload-notice").hidden = true;
$("#content").hidden = false;
ShadersListView.addProgram(programActor);
}
};
/**
* Functions handling the sources UI.
*/
let ShadersListView = Heritage.extend(WidgetMethods, {
/**
* 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.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);
},
/**
* 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);
},
/**
* Adds a program to this programs container.
*
* @param object programActor
* The program actor coming from the active thread.
*/
addProgram: function(programActor) {
// Currently, there's no good way of differentiating between programs
// in a way that helps humans. It will be a good idea to implement a
// standard of allowing debuggees to add some identifiable metadata to their
// program sources or instances.
let label = L10N.getFormatStr("shadersList.programLabel", this.itemCount);
// Append a program item to this container.
this.push([label, ""], {
index: -1, /* specifies on which position should the item be appended */
relaxed: true, /* this container should allow dupes & degenerates */
attachment: {
programActor: programActor,
checkboxState: true,
checkboxTooltip: L10N.getStr("shadersList.blackboxLabel")
}
});
// Make sure there's always a selected item available.
if (!this.selectedItem) {
this.selectedIndex = 0;
}
},
/**
* The select listener for the sources container.
*/
_onShaderSelect: 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([
attachment.vs || (attachment.vs = attachment.programActor.getVertexShader()),
attachment.fs || (attachment.fs = attachment.programActor.getFragmentShader())
]);
}
function getSources([vertexShaderActor, fragmentShaderActor]) {
return promise.all([
vertexShaderActor.getText(),
fragmentShaderActor.getText()
]);
}
function showSources([vertexShaderText, fragmentShaderText]) {
ShadersEditorsView.setText({
vs: vertexShaderText,
fs: fragmentShaderText
});
}
getShaders().then(getSources).then(showSources).then(null, Cu.reportError);
},
/**
* The check listener for the sources container.
*/
_onShaderCheck: function({ detail: { checked }, target }) {
let sourceItem = this.getItemForElement(target);
let attachment = sourceItem.attachment;
attachment.isBlackBoxed = !checked;
attachment.programActor[checked ? "unhighlight" : "highlight"](BLACKBOX_COLOR);
},
/**
* The mouseenter listener for the sources container.
*/
_onShaderMouseEnter: 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.
*/
_onShaderMouseLeave: 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();
}
}
}
});
/**
* Functions handling the editors displaying the vertex and fragment shaders.
*/
let ShadersEditorsView = {
/**
* Initialization function, called when the tool is started.
*/
initialize: function() {
XPCOMUtils.defineLazyGetter(this, "_editorPromises", () => new Map());
this._vsFocused = this._onFocused.bind(this, "vs", "fs");
this._fsFocused = this._onFocused.bind(this, "fs", "vs");
this._vsChanged = this._onChanged.bind(this, "vs");
this._fsChanged = this._onChanged.bind(this, "fs");
},
/**
* Destruction function, called when the tool is closed.
*/
destroy: function() {
this._toggleListeners("off");
},
/**
* Sets the text displayed in the vertex and fragment shader editors.
*
* @param object sources
* An object containing the following properties
* - vs: the vertex shader source code
* - fs: the fragment shader source code
*/
setText: function(sources) {
function setTextAndClearHistory(editor, text) {
editor.setText(text);
editor.clearHistory();
}
this._toggleListeners("off");
this._getEditor("vs").then(e => setTextAndClearHistory(e, sources.vs));
this._getEditor("fs").then(e => setTextAndClearHistory(e, sources.fs));
this._toggleListeners("on");
window.emit(EVENTS.SOURCES_SHOWN, sources);
},
/**
* Lazily initializes and returns a promise for an Editor instance.
*
* @param string type
* Specifies for which shader type should an editor be retrieved,
* either are "vs" for a vertex, or "fs" for a fragment shader.
*/
_getEditor: function(type) {
if ($("#content").hidden) {
return promise.reject(null);
}
if (this._editorPromises.has(type)) {
return this._editorPromises.get(type);
}
let deferred = promise.defer();
this._editorPromises.set(type, deferred.promise);
// Initialize the source editor and store the newly created instance
// in the ether of a resolved promise's value.
let parent = $("#" + type +"-editor");
let editor = new Editor(DEFAULT_EDITOR_CONFIG);
editor.appendTo(parent).then(() => deferred.resolve(editor));
return deferred.promise;
},
/**
* Toggles all the event listeners for the editors either on or off.
*
* @param string flag
* Either "on" to enable the event listeners, "off" to disable them.
*/
_toggleListeners: function(flag) {
["vs", "fs"].forEach(type => {
this._getEditor(type).then(editor => {
editor[flag]("focus", this["_" + type + "Focused"]);
editor[flag]("change", this["_" + type + "Changed"]);
});
});
},
/**
* The focus listener for a source editor.
*
* @param string focused
* The corresponding shader type for the focused editor (e.g. "vs").
* @param string focused
* The corresponding shader type for the other editor (e.g. "fs").
*/
_onFocused: function(focused, unfocused) {
$("#" + focused + "-editor-label").setAttribute("selected", "");
$("#" + unfocused + "-editor-label").removeAttribute("selected");
},
/**
* The change listener for a source editor.
*
* @param string type
* The corresponding shader type for the focused editor (e.g. "vs").
*/
_onChanged: function(type) {
setNamedTimeout("gl-typed", TYPING_MAX_DELAY, () => this._doCompile(type));
},
/**
* Recompiles the source code for the shader being edited.
* This function is fired at a certain delay after the user stops typing.
*
* @param string type
* The corresponding shader type for the focused editor (e.g. "vs").
*/
_doCompile: function(type) {
Task.spawn(function() {
let editor = yield this._getEditor(type);
let shaderActor = yield ShadersListView.selectedAttachment[type];
try {
yield shaderActor.compile(editor.getText());
window.emit(EVENTS.SHADER_COMPILED, null);
// TODO: remove error gutter markers, after bug 919709 lands.
} catch (error) {
window.emit(EVENTS.SHADER_COMPILED, error);
// TODO: add error gutter markers, after bug 919709 lands.
}
}.bind(this));
}
};
/**
* Localization convenience methods.
*/
let L10N = new ViewHelpers.L10N(STRINGS_URI);
/**
* Convenient way of emitting events from the panel window.
*/
EventEmitter.decorate(this);
/**
* DOM query helper.
*/
function $(selector, target = document) target.querySelector(selector);

View File

@ -0,0 +1,64 @@
<?xml version="1.0"?>
<!-- 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/. -->
<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/devtools/common.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/devtools/widgets.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/devtools/shadereditor.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/devtools/widgets.css" type="text/css"?>
<!DOCTYPE window [
<!ENTITY % debuggerDTD SYSTEM "chrome://browser/locale/devtools/shadereditor.dtd">
%debuggerDTD;
]>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript" src="shadereditor.js"/>
<vbox id="body" flex="1">
<hbox id="reload-notice"
class="notice-container"
align="center"
pack="center"
flex="1">
<button id="requests-menu-reload-notice-button"
class="devtools-toolbarbutton"
label="&shaderEditorUI.reloadNotice1;"
oncommand="gFront.setup();"/>
<label id="requests-menu-reload-notice-label"
class="plain"
value="&shaderEditorUI.reloadNotice2;"/>
</hbox>
<hbox id="waiting-notice"
class="notice-container"
align="center"
pack="center"
flex="1"
hidden="true">
<label id="requests-menu-waiting-notice-label"
class="plain"
value="&shaderEditorUI.emptyNotice;"/>
</hbox>
<hbox id="content" flex="1" hidden="true">
<vbox id="shaders-pane"/>
<splitter class="devtools-side-splitter"/>
<hbox id="shaders-editors" flex="1">
<vbox flex="1">
<vbox id="vs-editor" flex="1"/>
<label id="vs-editor-label"
class="plain editor-label"
value="&shaderEditorUI.vertexShader;"/>
</vbox>
<splitter id="editors-splitter" class="devtools-side-splitter"/>
<vbox flex="1">
<vbox id="fs-editor" flex="1"/>
<label id="fs-editor-label"
class="plain editor-label"
value="&shaderEditorUI.fragmentShader;"/>
</vbox>
</hbox>
</hbox>
</vbox>
</window>

View File

@ -5,6 +5,18 @@ support-files =
doc_simple-canvas.html
head.js
[browser_se_aaa_run_first_leaktest.js]
[browser_se_editors-contents.js]
[browser_se_editors-lazy-init.js]
[browser_se_first-run.js]
[browser_se_navigation.js]
[browser_se_programs-blackbox.js]
[browser_se_programs-cache.js]
[browser_se_programs-highlight.js]
[browser_se_programs-list.js]
[browser_se_shaders-edit-01.js]
[browser_se_shaders-edit-02.js]
[browser_se_shaders-edit-03.js]
[browser_webgl-actor-test-01.js]
[browser_webgl-actor-test-02.js]
[browser_webgl-actor-test-03.js]

View File

@ -0,0 +1,18 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests if the shader editor leaks on initialization and sudden destruction.
* You can also use this initialization format as a template for other tests.
*/
function ifWebGLSupported() {
let [target, debuggee, panel] = yield initShaderEditor(SIMPLE_CANVAS_URL);
ok(target, "Should have a target available.");
ok(debuggee, "Should have a debuggee available.");
ok(panel, "Should have a panel available.");
yield teardown(panel);
finish();
}

View File

@ -0,0 +1,26 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests if the editors contain the correct text when a program
* becomes available.
*/
function ifWebGLSupported() {
let [target, debuggee, panel] = yield initShaderEditor(SIMPLE_CANVAS_URL);
let { gFront, ShadersEditorsView } = panel.panelWin;
reload(target);
yield once(gFront, "program-linked");
let vsEditor = yield ShadersEditorsView._getEditor("vs");
let fsEditor = yield ShadersEditorsView._getEditor("fs");
is(vsEditor.getText().indexOf("gl_Position"), 170,
"The vertex shader editor contains the correct text.");
is(fsEditor.getText().indexOf("gl_FragColor"), 97,
"The fragment shader editor contains the correct text.");
yield teardown(panel);
finish();
}

View File

@ -0,0 +1,48 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests if source editors are lazily initialized.
*/
function ifWebGLSupported() {
let [target, debuggee, panel] = yield initShaderEditor(SIMPLE_CANVAS_URL);
let { gFront, ShadersEditorsView } = panel.panelWin;
try {
yield ShadersEditorsView._getEditor("vs");
ok(false, "The promise for a vertex shader editor should be rejected.");
} catch (e) {
ok(true, "The vertex shader editors wasn't initialized.");
}
try {
yield ShadersEditorsView._getEditor("fs");
ok(false, "The promise for a fragment shader editor should be rejected.");
} catch (e) {
ok(true, "The fragment shader editors wasn't initialized.");
}
reload(target);
yield once(gFront, "program-linked");
let vsEditor = yield ShadersEditorsView._getEditor("vs");
let fsEditor = yield ShadersEditorsView._getEditor("fs");
ok(vsEditor, "A vertex shader editor was initialized.");
ok(fsEditor, "A fragment shader editor was initialized.");
isnot(vsEditor, fsEditor,
"The vertex shader editor is distinct from the fragment shader editor.");
let vsEditor2 = yield ShadersEditorsView._getEditor("vs");
let fsEditor2 = yield ShadersEditorsView._getEditor("fs");
is(vsEditor, vsEditor2,
"The vertex shader editor instances are cached.");
is(fsEditor, fsEditor2,
"The fragment shader editor instances are cached.");
yield teardown(panel);
finish();
}

View File

@ -0,0 +1,43 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests if the shader editor shows the appropriate UI when opened.
*/
function ifWebGLSupported() {
let [target, debuggee, panel] = yield initShaderEditor(SIMPLE_CANVAS_URL);
let { gFront, $ } = panel.panelWin;
is($("#reload-notice").hidden, false,
"The 'reload this page' notice should initially be visible.");
is($("#waiting-notice").hidden, true,
"The 'waiting for a WebGL context' notice should initially be hidden.");
is($("#content").hidden, true,
"The tool's content should initially be hidden.");
let navigating = once(target, "will-navigate");
let linked = once(gFront, "program-linked");
reload(target);
yield navigating;
is($("#reload-notice").hidden, true,
"The 'reload this page' notice should be hidden when navigating.");
is($("#waiting-notice").hidden, false,
"The 'waiting for a WebGL context' notice should be visible when navigating.");
is($("#content").hidden, true,
"The tool's content should still be hidden.");
yield linked;
is($("#reload-notice").hidden, true,
"The 'reload this page' notice should be hidden after linking.");
is($("#waiting-notice").hidden, true,
"The 'waiting for a WebGL context' notice should be hidden after linking.");
is($("#content").hidden, false,
"The tool's content should not be hidden anymore.");
yield teardown(panel);
finish();
}

View File

@ -0,0 +1,82 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests target navigations are handled correctly in the UI.
*/
function ifWebGLSupported() {
let [target, debuggee, panel] = yield initShaderEditor(SIMPLE_CANVAS_URL);
let { gFront, $, ShadersListView, ShadersEditorsView } = panel.panelWin;
reload(target);
yield once(gFront, "program-linked");
is($("#reload-notice").hidden, true,
"The 'reload this page' notice should be hidden after linking.");
is($("#waiting-notice").hidden, true,
"The 'waiting for a WebGL context' notice should be visible after linking.");
is($("#content").hidden, false,
"The tool's content should not be hidden anymore.");
is(ShadersListView.itemCount, 1,
"The shaders list contains one entry.");
is(ShadersListView.selectedItem, ShadersListView.items[0],
"The shaders list has a correct item selected.");
is(ShadersListView.selectedIndex, 0,
"The shaders list has a correct index selected.");
let vsEditor = yield ShadersEditorsView._getEditor("vs");
let fsEditor = yield ShadersEditorsView._getEditor("fs");
is(vsEditor.getText().indexOf("gl_Position"), 170,
"The vertex shader editor contains the correct text.");
is(fsEditor.getText().indexOf("gl_FragColor"), 97,
"The fragment shader editor contains the correct text.");
let navigating = once(target, "will-navigate");
let navigated = once(target, "will-navigate");
navigate(target, "about:blank");
yield navigating;
is($("#reload-notice").hidden, true,
"The 'reload this page' notice should be hidden while navigating.");
is($("#waiting-notice").hidden, false,
"The 'waiting for a WebGL context' notice should be visible while navigating.");
is($("#content").hidden, true,
"The tool's content should be hidden now that there's no WebGL content.");
is(ShadersListView.itemCount, 0,
"The shaders list should be empty.");
is(ShadersListView.selectedItem, null,
"The shaders list has no correct item.");
is(ShadersListView.selectedIndex, -1,
"The shaders list has a negative index.");
try {
yield ShadersEditorsView._getEditor("vs");
ok(false, "The promise for a vertex shader editor should be rejected.");
} catch (e) {
ok(true, "The vertex shader editors wasn't initialized.");
}
try {
yield ShadersEditorsView._getEditor("fs");
ok(false, "The promise for a fragment shader editor should be rejected.");
} catch (e) {
ok(true, "The fragment shader editors wasn't initialized.");
}
yield navigated;
is($("#reload-notice").hidden, true,
"The 'reload this page' notice should still be hidden after navigating.");
is($("#waiting-notice").hidden, false,
"The 'waiting for a WebGL context' notice should still be visible after navigating.");
is($("#content").hidden, true,
"The tool's content should be still hidden since there's no WebGL content.");
yield teardown(panel);
finish();
}

View File

@ -0,0 +1,167 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests if blackboxing a program works properly.
*/
function ifWebGLSupported() {
let [target, debuggee, panel] = yield initShaderEditor(MULTIPLE_CONTEXTS_URL);
let { gFront, EVENTS, ShadersListView, ShadersEditorsView } = panel.panelWin;
once(panel.panelWin, EVENTS.SHADER_COMPILED).then(() => {
ok(false, "No shaders should be publicly compiled during this test.");
});
reload(target);
let firstProgramActor = yield once(gFront, "program-linked");
let secondProgramActor = yield once(gFront, "program-linked");
let vsEditor = yield ShadersEditorsView._getEditor("vs");
let fsEditor = yield ShadersEditorsView._getEditor("fs");
vsEditor.once("change", () => {
ok(false, "The vertex shader source was unexpectedly changed.");
});
fsEditor.once("change", () => {
ok(false, "The fragment shader source was unexpectedly changed.");
});
once(panel.panelWin, EVENTS.SOURCES_SHOWN).then(() => {
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");
ok(!ShadersListView.selectedAttachment.isBlackBoxed,
"The first program should not be blackboxed yet.");
is(getBlackBoxCheckbox(panel, 0).checked, true,
"The first blackbox checkbox should be initially checked.");
ok(!ShadersListView.attachments[1].isBlackBoxed,
"The second program should not be blackboxed yet.");
is(getBlackBoxCheckbox(panel, 1).checked, true,
"The second blackbox checkbox should be initially checked.");
getBlackBoxCheckbox(panel, 0).click();
ok(ShadersListView.selectedAttachment.isBlackBoxed,
"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: 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: 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");
ok(true, "The second program was correctly blackboxed.");
ShadersListView._onShaderMouseEnter({ 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");
ok(true, "Highlighting didn't work while blackboxed (1).");
ShadersListView._onShaderMouseLeave({ target: getItemLabel(panel, 0) });
ShadersListView._onShaderMouseEnter({ 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");
ok(true, "Highlighting didn't work while blackboxed (2).");
ShadersListView._onShaderMouseLeave({ 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");
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,
"The first blackbox checkbox should now be rechecked.");
ok(!ShadersListView.attachments[1].isBlackBoxed,
"The second program should now be unblackboxed.");
is(getBlackBoxCheckbox(panel, 1).checked, true,
"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) });
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) });
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) });
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);
finish();
}
function getItemLabel(aPanel, aIndex) {
return aPanel.panelWin.document.querySelectorAll(
".side-menu-widget-item-label")[aIndex];
}
function getBlackBoxCheckbox(aPanel, aIndex) {
return aPanel.panelWin.document.querySelectorAll(
".side-menu-widget-item-checkbox")[aIndex];
}
function once(aTarget, aEvent) {
let deferred = promise.defer();
aTarget.once(aEvent, deferred.resolve);
return deferred.promise;
}

View File

@ -0,0 +1,37 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that program and shader actors are cached in the frontend.
*/
function ifWebGLSupported() {
let [target, debuggee, panel] = yield initShaderEditor(MULTIPLE_CONTEXTS_URL);
let { gFront, ShadersListView, ShadersEditorsView } = panel.panelWin;
reload(target);
let programActor = yield once(gFront, "program-linked");
let programItem = ShadersListView.selectedItem;
is(programItem.attachment.programActor, programActor,
"The correct program actor is cached for the selected item.");
is((yield programActor.getVertexShader()),
(yield programItem.attachment.vs),
"The cached vertex shader promise returns the correct actor.");
is((yield programActor.getFragmentShader()),
(yield programItem.attachment.fs),
"The cached fragment shader promise returns the correct actor.");
is((yield (yield programActor.getVertexShader()).getText()),
(yield (yield ShadersEditorsView._getEditor("vs")).getText()),
"The cached vertex shader promise returns the correct text.");
is((yield (yield programActor.getFragmentShader()).getText()),
(yield (yield ShadersEditorsView._getEditor("fs")).getText()),
"The cached fragment shader promise returns the correct text.");
yield teardown(panel);
finish();
}

View File

@ -0,0 +1,97 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests if highlighting a program works properly.
*/
function ifWebGLSupported() {
let [target, debuggee, panel] = yield initShaderEditor(MULTIPLE_CONTEXTS_URL);
let { gFront, EVENTS, ShadersListView, ShadersEditorsView } = panel.panelWin;
once(panel.panelWin, EVENTS.SHADER_COMPILED).then(() => {
ok(false, "No shaders should be publicly compiled during this test.");
});
reload(target);
let firstProgramActor = yield once(gFront, "program-linked");
let secondProgramActor = yield once(gFront, "program-linked");
let vsEditor = yield ShadersEditorsView._getEditor("vs");
let fsEditor = yield ShadersEditorsView._getEditor("fs");
vsEditor.once("change", () => {
ok(false, "The vertex shader source was unexpectedly changed.");
});
fsEditor.once("change", () => {
ok(false, "The fragment shader source was unexpectedly changed.");
});
once(panel.panelWin, EVENTS.SOURCES_SHOWN).then(() => {
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) });
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) });
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) });
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) });
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) });
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);
finish();
}
function getItemLabel(aPanel, aIndex) {
return aPanel.panelWin.document.querySelectorAll(
".side-menu-widget-item-label")[aIndex];
}
function getBlackBoxCheckbox(aPanel, aIndex) {
return aPanel.panelWin.document.querySelectorAll(
".side-menu-widget-item-checkbox")[aIndex];
}
function once(aTarget, aEvent) {
let deferred = promise.defer();
aTarget.once(aEvent, deferred.resolve);
return deferred.promise;
}

View File

@ -0,0 +1,82 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests if the programs list contains an entry after vertex and fragment
* shaders are linked.
*/
function ifWebGLSupported() {
let [target, debuggee, panel] = yield initShaderEditor(MULTIPLE_CONTEXTS_URL);
let { gFront, EVENTS, L10N, ShadersListView, ShadersEditorsView } = panel.panelWin;
is(ShadersListView.itemCount, 0,
"The shaders list should initially be empty.");
is(ShadersListView.selectedItem, null,
"The shaders list has no selected item.");
is(ShadersListView.selectedIndex, -1,
"The shaders list has a negative index.");
reload(target);
let firstProgramActor = yield once(gFront, "program-linked");
is(ShadersListView.itemCount, 1,
"The shaders list contains one entry.");
is(ShadersListView.selectedItem, ShadersListView.items[0],
"The shaders list has a correct item selected.");
is(ShadersListView.selectedIndex, 0,
"The shaders list has a correct index selected.");
let secondProgramActor = yield once(gFront, "program-linked");
is(ShadersListView.itemCount, 2,
"The shaders list contains two entries.");
is(ShadersListView.selectedItem, ShadersListView.items[0],
"The shaders list has a correct item selected.");
is(ShadersListView.selectedIndex, 0,
"The shaders list has a correct index selected.");
is(ShadersListView.labels[0], L10N.getFormatStr("shadersList.programLabel", 0),
"The correct first label is shown in the shaders list.");
is(ShadersListView.labels[1], L10N.getFormatStr("shadersList.programLabel", 1),
"The correct second label is shown in the shaders list.");
let vertexShader = yield firstProgramActor.getVertexShader();
let fragmentShader = yield firstProgramActor.getFragmentShader();
let vertSource = yield vertexShader.getText();
let fragSource = yield fragmentShader.getText();
let vsEditor = yield ShadersEditorsView._getEditor("vs");
let fsEditor = yield ShadersEditorsView._getEditor("fs");
is(vertSource, vsEditor.getText(),
"The vertex shader editor contains the correct text.");
is(fragSource, fsEditor.getText(),
"The vertex shader editor contains the correct text.");
let compiled = once(panel.panelWin, EVENTS.SHADER_COMPILED).then(() => {
ok(false, "Selecting a different program shouldn't recompile its shaders.");
});
let shown = once(panel.panelWin, EVENTS.SOURCES_SHOWN).then(() => {
ok(true, "The vertex and fragment sources have changed in the editors.");
});
EventUtils.sendMouseEvent({ type: "mousedown" }, ShadersListView.items[1].target);
yield shown;
is(ShadersListView.selectedItem, ShadersListView.items[1],
"The shaders list has a correct item selected.");
is(ShadersListView.selectedIndex, 1,
"The shaders list has a correct index selected.");
yield teardown(panel);
finish();
}
function once(aTarget, aEvent) {
let deferred = promise.defer();
aTarget.once(aEvent, deferred.resolve);
return deferred.promise;
}

View File

@ -0,0 +1,76 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests if editing a vertex and a fragment shader works properly.
*/
function ifWebGLSupported() {
let [target, debuggee, panel] = yield initShaderEditor(SIMPLE_CANVAS_URL);
let { gFront, $, EVENTS, ShadersEditorsView } = panel.panelWin;
reload(target);
yield once(gFront, "program-linked");
let vsEditor = yield ShadersEditorsView._getEditor("vs");
let fsEditor = yield ShadersEditorsView._getEditor("fs");
is(vsEditor.getText().indexOf("gl_Position"), 170,
"The vertex shader editor contains the correct text.");
is(fsEditor.getText().indexOf("gl_FragColor"), 97,
"The fragment shader editor contains the correct text.");
is($("#vs-editor-label").hasAttribute("selected"), false,
"The vertex shader editor shouldn't be initially selected.");
is($("#fs-editor-label").hasAttribute("selected"), false,
"The vertex shader editor shouldn't be initially selected.");
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
yield ensurePixelIs(debuggee, { x: 128, y: 128 }, { r: 191, g: 64, b: 0, a: 255 }, true);
yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
vsEditor.focus();
is($("#vs-editor-label").hasAttribute("selected"), true,
"The vertex shader editor should now be selected.");
is($("#fs-editor-label").hasAttribute("selected"), false,
"The vertex shader editor shouldn't still not be selected.");
vsEditor.replaceText("2.0", { line: 7, ch: 44 }, { line: 7, ch: 47 });
yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
ok(true, "Vertex shader was changed.");
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
yield ensurePixelIs(debuggee, { x: 128, y: 128 }, { r: 255, g: 0, b: 0, a: 255 }, true);
yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 0, b: 0, a: 255 }, true);
ok(true, "The vertex shader was recompiled successfully.");
fsEditor.focus();
is($("#vs-editor-label").hasAttribute("selected"), false,
"The vertex shader editor should now be deselected.");
is($("#fs-editor-label").hasAttribute("selected"), true,
"The vertex shader editor should now be selected.");
fsEditor.replaceText("0.5", { line: 5, ch: 44 }, { line: 5, ch: 47 });
yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
ok(true, "Fragment shader was changed.");
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
yield ensurePixelIs(debuggee, { x: 128, y: 128 }, { r: 255, g: 0, b: 0, a: 127 }, true);
yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 0, b: 0, a: 255 }, true);
ok(true, "The fragment shader was recompiled successfully.");
yield teardown(panel);
finish();
}
function once(aTarget, aEvent) {
let deferred = promise.defer();
aTarget.once(aEvent, deferred.resolve);
return deferred.promise;
}

View File

@ -0,0 +1,71 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests if compile or linkage errors are emitted when a shader source
* gets malformed after being edited.
*/
function ifWebGLSupported() {
let [target, debuggee, panel] = yield initShaderEditor(SIMPLE_CANVAS_URL);
let { gFront, EVENTS, ShadersEditorsView } = panel.panelWin;
reload(target);
yield once(gFront, "program-linked");
let vsEditor = yield ShadersEditorsView._getEditor("vs");
let fsEditor = yield ShadersEditorsView._getEditor("fs");
vsEditor.replaceText("vec3", { line: 7, ch: 22 }, { line: 7, ch: 26 });
let error = yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
ok(error,
"The new vertex shader source was compiled with errors.");
is(error.compile, "",
"The compilation status should be empty.");
isnot(error.link, "",
"The linkage status should not be empty.");
is(error.link.split("ERROR").length - 1, 2,
"The linkage status contains two errors.");
ok(error.link.contains("ERROR: 0:8: 'constructor'"),
"A constructor error is contained in the linkage status.");
ok(error.link.contains("ERROR: 0:8: 'assign'"),
"An assignment error is contained in the linkage status.");
fsEditor.replaceText("vec4", { line: 2, ch: 14 }, { line: 2, ch: 18 });
let error = yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
ok(error,
"The new fragment shader source was compiled with errors.");
is(error.compile, "",
"The compilation status should be empty.");
isnot(error.link, "",
"The linkage status should not be empty.");
is(error.link.split("ERROR").length - 1, 1,
"The linkage status contains one error.");
ok(error.link.contains("ERROR: 0:6: 'constructor'"),
"A constructor error is contained in the linkage status.");
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);
vsEditor.replaceText("vec4", { line: 7, ch: 22 }, { line: 7, ch: 26 });
let error = yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
ok(!error, "The new vertex shader source was compiled successfully.");
fsEditor.replaceText("vec3", { line: 2, ch: 14 }, { line: 2, ch: 18 });
let error = yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
ok(!error, "The new fragment shader source was compiled successfully.");
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 teardown(panel);
finish();
}
function once(aTarget, aEvent) {
let deferred = promise.defer();
aTarget.once(aEvent, (aName, aData) => deferred.resolve(aData));
return deferred.promise;
}

View File

@ -0,0 +1,85 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests if editing a vertex and a fragment shader works properly.
*/
function ifWebGLSupported() {
let [target, debuggee, panel] = yield initShaderEditor(MULTIPLE_CONTEXTS_URL);
let { gFront, EVENTS, ShadersListView, ShadersEditorsView } = panel.panelWin;
reload(target);
let firstProgramActor = yield once(gFront, "program-linked");
let secondProgramActor = yield once(gFront, "program-linked");
let vsEditor = yield ShadersEditorsView._getEditor("vs");
let fsEditor = yield ShadersEditorsView._getEditor("fs");
is(ShadersListView.selectedIndex, 0,
"The first program is currently selected.");
is(vsEditor.getText().indexOf("1);"), 136,
"The vertex shader editor contains the correct initial text (1).");
is(fsEditor.getText().indexOf("1);"), 117,
"The fragment shader editor contains the correct initial text (1).");
is(vsEditor.getText().indexOf("2.);"), -1,
"The vertex shader editor contains the correct initial text (2).");
is(fsEditor.getText().indexOf(".0);"), -1,
"The fragment shader editor contains the correct initial text (2).");
vsEditor.replaceText("2.", { line: 5, ch: 44 }, { line: 5, ch: 45 });
yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
fsEditor.replaceText(".0", { line: 5, ch: 35 }, { line: 5, ch: 37 });
yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
ok(true, "Vertex and fragment shaders were changed.");
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
yield ensurePixelIs(debuggee, { x: 32, y: 32 }, { r: 255, g: 255, b: 0, a: 0 }, true, "#canvas1");
yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 255, g: 255, 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: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
yield ensurePixelIs(debuggee, { x: 32, y: 32 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
ok(true, "The vertex and fragment shaders were recompiled successfully.");
EventUtils.sendMouseEvent({ type: "mousedown" }, ShadersListView.items[1].target);
yield once(panel.panelWin, EVENTS.SOURCES_SHOWN);
is(ShadersListView.selectedIndex, 1,
"The second program is currently selected.");
is(vsEditor.getText().indexOf("1);"), 136,
"The vertex shader editor contains the correct text (1).");
is(fsEditor.getText().indexOf("1);"), 117,
"The fragment shader editor contains the correct text (1).");
is(vsEditor.getText().indexOf("2.);"), -1,
"The vertex shader editor contains the correct text (2).");
is(fsEditor.getText().indexOf(".0);"), -1,
"The fragment shader editor contains the correct text (2).");
EventUtils.sendMouseEvent({ type: "mousedown" }, ShadersListView.items[0].target);
yield once(panel.panelWin, EVENTS.SOURCES_SHOWN);
is(ShadersListView.selectedIndex, 0,
"The first program is currently selected again.");
is(vsEditor.getText().indexOf("1);"), -1,
"The vertex shader editor contains the correct text (3).");
is(fsEditor.getText().indexOf("1);"), -1,
"The fragment shader editor contains the correct text (3).");
is(vsEditor.getText().indexOf("2.);"), 136,
"The vertex shader editor contains the correct text (4).");
is(fsEditor.getText().indexOf(".0);"), 116,
"The fragment shader editor contains the correct text (4).");
yield teardown(panel);
finish();
}
function once(aTarget, aEvent) {
let deferred = promise.defer();
aTarget.once(aEvent, deferred.resolve);
return deferred.promise;
}

View File

@ -31,9 +31,12 @@ const MULTIPLE_CONTEXTS_URL = EXAMPLE_URL + "doc_multiple-contexts.html";
// All tests are asynchronous.
waitForExplicitFinish();
let gToolEnabled = Services.prefs.getBoolPref("devtools.shadereditor.enabled");
registerCleanupFunction(() => {
info("finish() was called, cleaning up...");
Services.prefs.setBoolPref("devtools.debugger.log", gEnableLogging);
Services.prefs.setBoolPref("devtools.shadereditor.enabled", gToolEnabled);
});
function addTab(aUrl, aWindow) {
@ -190,6 +193,12 @@ function ensurePixelIs(aDebuggee, aPosition, aColor, aWaitFlag = false, aSelecto
return promise.reject(null);
}
function navigate(aTarget, aUrl) {
let navigated = once(aTarget, "navigate");
aTarget.client.activeTab.navigateTo(aUrl);
return navigated;
}
function reload(aTarget) {
let navigated = once(aTarget, "navigate");
aTarget.client.activeTab.reload();
@ -215,3 +224,29 @@ function initBackend(aUrl) {
return [target, debuggee, front];
});
}
function initShaderEditor(aUrl) {
info("Initializing a shader editor pane.");
return Task.spawn(function*() {
let tab = yield addTab(aUrl);
let target = TargetFactory.forTab(tab);
let debuggee = target.window.wrappedJSObject;
yield target.makeRemote();
Services.prefs.setBoolPref("devtools.shadereditor.enabled", true);
let toolbox = yield gDevTools.showToolbox(target, "shadereditor");
let panel = toolbox.getCurrentPanel();
return [target, debuggee, panel];
});
}
function teardown(aPanel) {
info("Destroying the specified shader editor.");
return promise.all([
once(aPanel, "destroyed"),
removeTab(aPanel.target.tab)
]);
}

View File

@ -118,6 +118,11 @@ Telemetry.prototype = {
userHistogram: "DEVTOOLS_STYLEEDITOR_OPENED_PER_USER_FLAG",
timerHistogram: "DEVTOOLS_STYLEEDITOR_TIME_ACTIVE_SECONDS"
},
shadereditor: {
histogram: "DEVTOOLS_SHADEREDITOR_OPENED_BOOLEAN",
userHistogram: "DEVTOOLS_SHADEREDITOR_OPENED_PER_USER_FLAG",
timerHistogram: "DEVTOOLS_SHADEREDITOR_TIME_ACTIVE_SECONDS"
},
jsprofiler: {
histogram: "DEVTOOLS_JSPROFILER_OPENED_BOOLEAN",
userHistogram: "DEVTOOLS_JSPROFILER_OPENED_PER_USER_FLAG",

View File

@ -973,6 +973,18 @@ this.WidgetMethods = {
return "";
},
/**
* Retrieves the attachment of the selected element.
* @return string
*/
get selectedAttachment() {
let selectedElement = this._widget.selectedItem;
if (selectedElement) {
return this._itemsByElement.get(selectedElement).attachment;
}
return null;
},
/**
* Selects the element with the entangled item in this container.
* @param Item | function aItem

View File

@ -226,6 +226,7 @@ Editor.prototype = {
this.showContextMenu(doc, ev.screenX, ev.screenY);
}, false);
cm.on("focus", () => this.emit("focus"));
cm.on("change", () => this.emit("change"));
cm.on("gutterClick", (cm, line) => this.emit("gutterClick", line));
cm.on("cursorActivity", (cm) => this.emit("cursorActivity"));

View File

@ -0,0 +1,32 @@
<!-- 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/. -->
<!-- LOCALIZATION NOTE : FILE This file contains the Debugger strings -->
<!-- LOCALIZATION NOTE : FILE Do not translate commandkey -->
<!-- LOCALIZATION NOTE : FILE The correct localization of this file might be to
- keep it in English, or another language commonly spoken among web developers.
- You want to make that choice consistent across the developer tools.
- A good criteria is the language in which you'd find the best
- documentation on web development on the web. -->
<!-- LOCALIZATION NOTE (shaderEditorUI.vertexShader): This is the label for
- the pane that displays a vertex shader's source. -->
<!ENTITY shaderEditorUI.vertexShader "Vertex Shader">
<!-- LOCALIZATION NOTE (shaderEditorUI.fragmentShader): This is the label for
- the pane that displays a fragment shader's source. -->
<!ENTITY shaderEditorUI.fragmentShader "Fragment Shader">
<!-- LOCALIZATION NOTE (shaderEditorUI.reloadNotice1): This is the label shown
- on the button that triggers a page refresh. -->
<!ENTITY shaderEditorUI.reloadNotice1 "Reload">
<!-- LOCALIZATION NOTE (shaderEditorUI.reloadNotice2): This is the label shown
- along with the button that triggers a page refresh. -->
<!ENTITY shaderEditorUI.reloadNotice2 "the page to be able to edit GLSL code.">
<!-- LOCALIZATION NOTE (shaderEditorUI.emptyNotice): This is the label shown
- while the page is refreshing and the tool waits for a WebGL context. -->
<!ENTITY shaderEditorUI.emptyNotice "Waiting for a WebGL context to be created…">

View File

@ -0,0 +1,32 @@
# 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/.
# LOCALIZATION NOTE These strings are used inside the Debugger
# which is available from the Web Developer sub-menu -> 'Debugger'.
# The correct localization of this file might be to keep it in
# English, or another language commonly spoken among web developers.
# You want to make that choice consistent across the developer tools.
# A good criteria is the language in which you'd find the best
# documentation on web development on the web.
# LOCALIZATION NOTE (ToolboxShaderEditor.label):
# This string is displayed in the title of the tab when the Shader Editor is
# displayed inside the developer tools window and in the Developer Tools Menu.
ToolboxShaderEditor.label=Shader Editor
# LOCALIZATION NOTE (ToolboxShaderEditor.tooltip):
# This string is displayed in the tooltip of the tab when the Shader Editor is
# displayed inside the developer tools window.
ToolboxShaderEditor.tooltip=Live GLSL shader language editor for WebGL
# LOCALIZATION NOTE (shadersList.programLabel):
# This string is displayed in the programs list of the Shader Editor,
# identifying a set of linked GLSL shaders.
shadersList.programLabel=Program %S
# LOCALIZATION NOTE (shadersList.blackboxLabel):
# This string is displayed in the programs list of the Shader Editor, while
# the user hovers over the checkbox used to toggle blackboxing of a program's
# associated fragment shader.
shadersList.blackboxLabel=Toggle geometry visibility

View File

@ -27,6 +27,8 @@
locale/browser/devtools/debugger.properties (%chrome/browser/devtools/debugger.properties)
locale/browser/devtools/netmonitor.dtd (%chrome/browser/devtools/netmonitor.dtd)
locale/browser/devtools/netmonitor.properties (%chrome/browser/devtools/netmonitor.properties)
locale/browser/devtools/shadereditor.dtd (%chrome/browser/devtools/shadereditor.dtd)
locale/browser/devtools/shadereditor.properties (%chrome/browser/devtools/shadereditor.properties)
locale/browser/devtools/gcli.properties (%chrome/browser/devtools/gcli.properties)
locale/browser/devtools/gclicommands.properties (%chrome/browser/devtools/gclicommands.properties)
locale/browser/devtools/webconsole.properties (%chrome/browser/devtools/webconsole.properties)

View File

@ -0,0 +1,5 @@
/* 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/. */
%include ../../shared/devtools/shadereditor.inc.css

View File

@ -172,6 +172,7 @@ browser.jar:
skin/classic/browser/devtools/breadcrumbs/rtl-start-selected.png (devtools/breadcrumbs/rtl-start-selected.png)
skin/classic/browser/devtools/splitview.css (devtools/splitview.css)
skin/classic/browser/devtools/styleeditor.css (devtools/styleeditor.css)
* skin/classic/browser/devtools/shadereditor.css (devtools/shadereditor.css)
skin/classic/browser/devtools/debugger.css (devtools/debugger.css)
* skin/classic/browser/devtools/profiler.css (devtools/profiler.css)
skin/classic/browser/devtools/netmonitor.css (devtools/netmonitor.css)

View File

@ -0,0 +1,6 @@
/* 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/. */
%include ../shared.inc
%include ../../shared/devtools/shadereditor.inc.css

View File

@ -264,6 +264,7 @@ browser.jar:
skin/classic/browser/devtools/breadcrumbs/rtl-start-selected.png (devtools/breadcrumbs/rtl-start-selected.png)
skin/classic/browser/devtools/splitview.css (devtools/splitview.css)
skin/classic/browser/devtools/styleeditor.css (devtools/styleeditor.css)
* skin/classic/browser/devtools/shadereditor.css (devtools/shadereditor.css)
* skin/classic/browser/devtools/debugger.css (devtools/debugger.css)
* skin/classic/browser/devtools/profiler.css (devtools/profiler.css)
skin/classic/browser/devtools/netmonitor.css (devtools/netmonitor.css)

View File

@ -0,0 +1,107 @@
/* 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/. */
#body {
background: url(background-noise-toolbar.png), hsl(208,11%,27%);
}
#content {
background: #fff;
}
/* Reload and waiting notices */
.notice-container {
background: transparent;
margin-top: -50vh;
color: #fff;
}
#reload-notice {
font-size: 120%;
}
#waiting-notice {
font-size: 110%;
}
#waiting-notice::before {
display: inline-block;
content: "";
background: url("chrome://global/skin/icons/loading_16.png") center no-repeat;
width: 16px;
height: 16px;
-moz-margin-end: 6px;
}
#requests-menu-reload-notice-button {
min-height: 2em;
}
/* Shaders pane */
#shaders-pane {
min-width: 150px;
}
#shaders-pane + .devtools-side-splitter {
-moz-border-start-color: transparent;
}
.side-menu-widget-item-checkbox {
-moz-appearance: none;
-moz-margin-end: -6px;
padding: 0;
opacity: 0;
transition: opacity .15s ease-out 0s;
}
/* Only show the checkbox when the source is hovered over, is selected, or if it
* is not checked. */
.side-menu-widget-item:hover > .side-menu-widget-item-checkbox,
.side-menu-widget-item.selected > .side-menu-widget-item-checkbox,
.side-menu-widget-item-checkbox:not([checked]) {
opacity: 1;
transition: opacity .15s ease-out 0s;
}
.side-menu-widget-item-checkbox > .checkbox-check {
-moz-appearance: none;
background: none;
background-image: url("chrome://browser/skin/devtools/itemToggle.png");
background-repeat: no-repeat;
background-clip: content-box;
background-size: 32px 16px;
background-position: -16px 0;
width: 16px;
height: 16px;
border: 0;
}
.side-menu-widget-item-checkbox[checked] > .checkbox-check {
background-position: 0 0;
}
.side-menu-widget-item-checkbox:not([checked]) ~ .side-menu-widget-item-contents {
color: #888;
}
/* Shader source editors */
#editors-splitter {
-moz-border-start-color: rgb(61,69,76);
}
.editor-label {
background: url(background-noise-toolbar.png), hsl(208,11%,27%);
border-top: 1px solid #222426;
padding: 1px 12px;
color: #fff;
}
.editor-label[selected] {
background: linear-gradient(hsl(206,61%,40%), hsl(206,61%,31%)) repeat-x top left;
box-shadow: inset 0 1px 0 hsla(210,40%,83%,.15),
inset 0 -1px 0 hsla(210,40%,83%,.05);
}

View File

@ -0,0 +1,5 @@
/* 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/. */
%include ../../shared/devtools/shadereditor.inc.css

View File

@ -199,6 +199,7 @@ browser.jar:
skin/classic/browser/devtools/breadcrumbs/rtl-start-selected.png (devtools/breadcrumbs/rtl-start-selected.png)
skin/classic/browser/devtools/splitview.css (devtools/splitview.css)
skin/classic/browser/devtools/styleeditor.css (devtools/styleeditor.css)
* skin/classic/browser/devtools/shadereditor.css (devtools/shadereditor.css)
skin/classic/browser/devtools/debugger.css (devtools/debugger.css)
* skin/classic/browser/devtools/profiler.css (devtools/profiler.css)
skin/classic/browser/devtools/netmonitor.css (devtools/netmonitor.css)
@ -475,6 +476,7 @@ browser.jar:
skin/classic/aero/browser/devtools/breadcrumbs/rtl-start-selected.png (devtools/breadcrumbs/rtl-start-selected.png)
skin/classic/aero/browser/devtools/splitview.css (devtools/splitview.css)
skin/classic/aero/browser/devtools/styleeditor.css (devtools/styleeditor.css)
* skin/classic/aero/browser/devtools/shadereditor.css (devtools/shadereditor.css)
skin/classic/aero/browser/devtools/debugger.css (devtools/debugger.css)
* skin/classic/aero/browser/devtools/profiler.css (devtools/profiler.css)
skin/classic/aero/browser/devtools/netmonitor.css (devtools/netmonitor.css)

View File

@ -3931,6 +3931,10 @@
"kind": "boolean",
"description": "How many times has the devtool's Style Editor been opened?"
},
"DEVTOOLS_SHADEREDITOR_OPENED_BOOLEAN": {
"kind": "boolean",
"description": "How many times has the devtool's Shader Editor been opened?"
},
"DEVTOOLS_JSPROFILER_OPENED_BOOLEAN": {
"kind": "boolean",
"description": "How many times has the devtool's JS Profiler been opened?"
@ -4003,6 +4007,10 @@
"kind": "flag",
"description": "How many users have opened the devtool's Style Editor?"
},
"DEVTOOLS_SHADEREDITOR_OPENED_PER_USER_FLAG": {
"kind": "flag",
"description": "How many users have opened the devtool's Shader Editor?"
},
"DEVTOOLS_JSPROFILER_OPENED_PER_USER_FLAG": {
"kind": "flag",
"description": "How many users have opened the devtool's JS Profiler?"
@ -4103,6 +4111,12 @@
"n_buckets": 100,
"description": "How long has the style editor been active (seconds)"
},
"DEVTOOLS_SHADEREDITOR_TIME_ACTIVE_SECONDS": {
"kind": "exponential",
"high": "10000000",
"n_buckets": 100,
"description": "How long has the Shader Editor been active (seconds)"
},
"DEVTOOLS_JSPROFILER_TIME_ACTIVE_SECONDS": {
"kind": "exponential",
"high": "10000000",