browser/devtools/shadereditor/test/head.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* Any copyright is dedicated to the Public Domain.
     2    http://creativecommons.org/publicdomain/zero/1.0/ */
     3 "use strict";
     5 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
     7 let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
     9 let gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log");
    10 // To enable logging for try runs, just set the pref to true.
    11 Services.prefs.setBoolPref("devtools.debugger.log", false);
    13 let { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
    14 let { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
    15 let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
    16 let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
    17 let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
    18 let { DebuggerClient } = Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {});
    20 let { WebGLFront } = devtools.require("devtools/server/actors/webgl");
    21 let TiltGL = devtools.require("devtools/tilt/tilt-gl");
    22 let TargetFactory = devtools.TargetFactory;
    23 let Toolbox = devtools.Toolbox;
    25 const EXAMPLE_URL = "http://example.com/browser/browser/devtools/shadereditor/test/";
    26 const SIMPLE_CANVAS_URL = EXAMPLE_URL + "doc_simple-canvas.html";
    27 const SHADER_ORDER_URL = EXAMPLE_URL + "doc_shader-order.html";
    28 const MULTIPLE_CONTEXTS_URL = EXAMPLE_URL + "doc_multiple-contexts.html";
    29 const OVERLAPPING_GEOMETRY_CANVAS_URL = EXAMPLE_URL + "doc_overlapping-geometry.html";
    30 const BLENDED_GEOMETRY_CANVAS_URL = EXAMPLE_URL + "doc_blended-geometry.html";
    32 // All tests are asynchronous.
    33 waitForExplicitFinish();
    35 let gToolEnabled = Services.prefs.getBoolPref("devtools.shadereditor.enabled");
    37 registerCleanupFunction(() => {
    38   info("finish() was called, cleaning up...");
    39   Services.prefs.setBoolPref("devtools.debugger.log", gEnableLogging);
    40   Services.prefs.setBoolPref("devtools.shadereditor.enabled", gToolEnabled);
    42   // These tests use a lot of memory due to GL contexts, so force a GC to help
    43   // fragmentation.
    44   info("Forcing GC after shadereditor test.");
    45   Cu.forceGC();
    46 });
    48 function addTab(aUrl, aWindow) {
    49   info("Adding tab: " + aUrl);
    51   let deferred = promise.defer();
    52   let targetWindow = aWindow || window;
    53   let targetBrowser = targetWindow.gBrowser;
    55   targetWindow.focus();
    56   let tab = targetBrowser.selectedTab = targetBrowser.addTab(aUrl);
    57   let linkedBrowser = tab.linkedBrowser;
    59   linkedBrowser.addEventListener("load", function onLoad() {
    60     linkedBrowser.removeEventListener("load", onLoad, true);
    61     info("Tab added and finished loading: " + aUrl);
    62     deferred.resolve(tab);
    63   }, true);
    65   return deferred.promise;
    66 }
    68 function removeTab(aTab, aWindow) {
    69   info("Removing tab.");
    71   let deferred = promise.defer();
    72   let targetWindow = aWindow || window;
    73   let targetBrowser = targetWindow.gBrowser;
    74   let tabContainer = targetBrowser.tabContainer;
    76   tabContainer.addEventListener("TabClose", function onClose(aEvent) {
    77     tabContainer.removeEventListener("TabClose", onClose, false);
    78     info("Tab removed and finished closing.");
    79     deferred.resolve();
    80   }, false);
    82   targetBrowser.removeTab(aTab);
    83   return deferred.promise;
    84 }
    86 function handleError(aError) {
    87   ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
    88   finish();
    89 }
    91 function ifWebGLSupported() {
    92   ok(false, "You need to define a 'ifWebGLSupported' function.");
    93   finish();
    94 }
    96 function ifWebGLUnsupported() {
    97   todo(false, "Skipping test because WebGL isn't supported.");
    98   finish();
    99 }
   101 function test() {
   102   let generator = isWebGLSupported() ? ifWebGLSupported : ifWebGLUnsupported;
   103   Task.spawn(generator).then(null, handleError);
   104 }
   106 function createCanvas() {
   107   return document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
   108 }
   110 function isWebGLSupported() {
   111   let supported =
   112     !TiltGL.isWebGLForceEnabled() &&
   113      TiltGL.isWebGLSupported() &&
   114      TiltGL.create3DContext(createCanvas());
   116   info("Apparently, WebGL is" + (supported ? "" : " not") + " supported.");
   117   return supported;
   118 }
   120 function once(aTarget, aEventName, aUseCapture = false) {
   121   info("Waiting for event: '" + aEventName + "' on " + aTarget + ".");
   123   let deferred = promise.defer();
   125   for (let [add, remove] of [
   126     ["on", "off"], // Use event emitter before DOM events for consistency
   127     ["addEventListener", "removeEventListener"],
   128     ["addListener", "removeListener"]
   129   ]) {
   130     if ((add in aTarget) && (remove in aTarget)) {
   131       aTarget[add](aEventName, function onEvent(...aArgs) {
   132         aTarget[remove](aEventName, onEvent, aUseCapture);
   133         deferred.resolve(...aArgs);
   134       }, aUseCapture);
   135       break;
   136     }
   137   }
   139   return deferred.promise;
   140 }
   142 // Hack around `once`, as that only resolves to a single (first) argument
   143 // and discards the rest. `onceSpread` is similar, except resolves to an
   144 // array of all of the arguments in the handler. These should be consolidated
   145 // into the same function, but many tests will need to be changed.
   146 function onceSpread(aTarget, aEvent) {
   147   let deferred = promise.defer();
   148   aTarget.once(aEvent, (...args) => deferred.resolve(args));
   149   return deferred.promise;
   150 }
   152 function observe(aNotificationName, aOwnsWeak = false) {
   153   info("Waiting for observer notification: '" + aNotificationName + ".");
   155   let deferred = promise.defer();
   157   Services.obs.addObserver(function onNotification(...aArgs) {
   158     Services.obs.removeObserver(onNotification, aNotificationName);
   159     deferred.resolve.apply(deferred, aArgs);
   160   }, aNotificationName, aOwnsWeak);
   162   return deferred.promise;
   163 }
   165 function waitForFrame(aDebuggee) {
   166   let deferred = promise.defer();
   167   aDebuggee.requestAnimationFrame(deferred.resolve);
   168   return deferred.promise;
   169 }
   171 function isApprox(aFirst, aSecond, aMargin = 1) {
   172   return Math.abs(aFirst - aSecond) <= aMargin;
   173 }
   175 function isApproxColor(aFirst, aSecond, aMargin) {
   176   return isApprox(aFirst.r, aSecond.r, aMargin) &&
   177     isApprox(aFirst.g, aSecond.g, aMargin) &&
   178     isApprox(aFirst.b, aSecond.b, aMargin) &&
   179     isApprox(aFirst.a, aSecond.a, aMargin);
   180 }
   182 function getPixels(aDebuggee, aSelector = "canvas") {
   183   let canvas = aDebuggee.document.querySelector(aSelector);
   184   let gl = canvas.getContext("webgl");
   186   let { width, height } = canvas;
   187   let buffer = new aDebuggee.Uint8Array(width * height * 4);
   188   gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buffer);
   190   info("Retrieved pixels: " + width + "x" + height);
   191   return [buffer, width, height];
   192 }
   194 function getPixel(aDebuggee, aPosition, aSelector = "canvas") {
   195   let canvas = aDebuggee.document.querySelector(aSelector);
   196   let gl = canvas.getContext("webgl");
   198   let { width, height } = canvas;
   199   let { x, y } = aPosition;
   200   let buffer = new aDebuggee.Uint8Array(4);
   201   gl.readPixels(x, height - y - 1, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, buffer);
   203   let pixel = { r: buffer[0], g: buffer[1], b: buffer[2], a: buffer[3] };
   205   info("Retrieved pixel: " + pixel.toSource() + " at " + aPosition.toSource());
   206   return pixel;
   207 }
   209 function ensurePixelIs(aDebuggee, aPosition, aColor, aWaitFlag = false, aSelector = "canvas") {
   210   let pixel = getPixel(aDebuggee, aPosition, aSelector);
   211   if (isApproxColor(pixel, aColor)) {
   212     ok(true, "Expected pixel is shown at: " + aPosition.toSource());
   213     return promise.resolve(null);
   214   }
   215   if (aWaitFlag) {
   216     return Task.spawn(function() {
   217       yield waitForFrame(aDebuggee);
   218       yield ensurePixelIs(aDebuggee, aPosition, aColor, aWaitFlag, aSelector);
   219     });
   220   }
   221   ok(false, "Expected pixel was not already shown at: " + aPosition.toSource());
   222   return promise.reject(null);
   223 }
   225 function navigateInHistory(aTarget, aDirection, aWaitForTargetEvent = "navigate") {
   226   executeSoon(() => content.history[aDirection]());
   227   return once(aTarget, aWaitForTargetEvent);
   228 }
   230 function navigate(aTarget, aUrl, aWaitForTargetEvent = "navigate") {
   231   executeSoon(() => aTarget.activeTab.navigateTo(aUrl));
   232   return once(aTarget, aWaitForTargetEvent);
   233 }
   235 function reload(aTarget, aWaitForTargetEvent = "navigate") {
   236   executeSoon(() => aTarget.activeTab.reload());
   237   return once(aTarget, aWaitForTargetEvent);
   238 }
   240 function initBackend(aUrl) {
   241   info("Initializing a shader editor front.");
   243   if (!DebuggerServer.initialized) {
   244     DebuggerServer.init(() => true);
   245     DebuggerServer.addBrowserActors();
   246   }
   248   return Task.spawn(function*() {
   249     let tab = yield addTab(aUrl);
   250     let target = TargetFactory.forTab(tab);
   251     let debuggee = target.window.wrappedJSObject;
   253     yield target.makeRemote();
   255     let front = new WebGLFront(target.client, target.form);
   256     return [target, debuggee, front];
   257   });
   258 }
   260 function initShaderEditor(aUrl) {
   261   info("Initializing a shader editor pane.");
   263   return Task.spawn(function*() {
   264     let tab = yield addTab(aUrl);
   265     let target = TargetFactory.forTab(tab);
   266     let debuggee = target.window.wrappedJSObject;
   268     yield target.makeRemote();
   270     Services.prefs.setBoolPref("devtools.shadereditor.enabled", true);
   271     let toolbox = yield gDevTools.showToolbox(target, "shadereditor");
   272     let panel = toolbox.getCurrentPanel();
   273     return [target, debuggee, panel];
   274   });
   275 }
   277 function teardown(aPanel) {
   278   info("Destroying the specified shader editor.");
   280   return promise.all([
   281     once(aPanel, "destroyed"),
   282     removeTab(aPanel.target.tab)
   283   ]);
   284 }
   286 // Due to `program-linked` events firing synchronously, we cannot
   287 // just yield/chain them together, as then we miss all actors after the
   288 // first event since they're fired consecutively. This allows us to capture
   289 // all actors and returns an array containing them.
   290 //
   291 // Takes a `front` object that is an event emitter, the number of
   292 // programs that should be listened to and waited on, and an optional
   293 // `onAdd` function that calls with the entire actors array on program link
   294 function getPrograms(front, count, onAdd) {
   295   let actors = [];
   296   let deferred = promise.defer();
   297   front.on("program-linked", function onLink (actor) {
   298     if (actors.length !== count) {
   299       actors.push(actor);
   300       if (typeof onAdd === 'function') onAdd(actors)
   301     }
   302     if (actors.length === count) {
   303       front.off("program-linked", onLink);
   304       deferred.resolve(actors);
   305     }
   306   });
   307   return deferred.promise;
   308 }

mercurial