tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

webgl-conformance-tests.html (38896B)


      1 <!--
      2 Copyright (c) 2019 The Khronos Group Inc.
      3 Use of this source code is governed by an MIT-style license that can be
      4 found in the LICENSE.txt file.
      5 -->
      6 <!DOCTYPE html>
      7 <html>
      8 <head>
      9 <meta charset="utf-8">
     10 <!-- Prevents Chrome from offering to translate tests which generate
     11     random characters for things like attribute names -->
     12 <meta name="google" value="notranslate">
     13 <meta name="viewport" content="width=device-width">
     14 <title>WebGL Conformance Tests</title>
     15 <style>
     16  * {
     17    box-sizing: border-box;
     18  }
     19 
     20  body {
     21    border: 0;
     22    margin: 0;
     23    padding: 0;
     24    height: 100%;
     25    max-height:100%;
     26    font-family: Verdana, Arial, sans-serif;
     27    font-size: 0.8em;
     28  }
     29 
     30  input[type=button], select {
     31    padding: 2px 6px 2px 6px;
     32    margin: 0;
     33    border: 1px solid #888;
     34    border-radius: 2px;
     35    background: #f4f4f4;
     36  }
     37 
     38  a {
     39    color: #88F;
     40    text-decoration: none;
     41  }
     42 
     43  a:hover {
     44    border-bottom: 1px solid #66D;
     45  }
     46 
     47  label {
     48    white-space: nowrap;
     49  }
     50 
     51  #testlist {
     52    position:fixed;
     53    top:180px;
     54    left:0;
     55    right: calc(10% + 50px);
     56    bottom:0px;
     57    overflow:auto;
     58    min-height: 200px;
     59  }
     60 
     61  @media screen and (max-width: 500px) {
     62    #testlist {
     63      font-size: 80%;
     64    }
     65  }
     66 
     67  #header {
     68    position:absolute;
     69    top:0;
     70    left:0;
     71    width:100%;
     72    height: 160px;
     73    overflow: scroll;
     74    border-bottom: 1px solid #CCC;
     75  }
     76 
     77  #info {
     78    margin: 0 auto;
     79    max-width: 280px;
     80  }
     81  #logo {
     82    width: 68px;
     83    height: 40px;
     84  }
     85 
     86  #iframe-container {
     87    color: white;
     88    display: block;
     89    position: fixed;
     90    width: 90%;
     91    height: calc(100% - 170px);
     92    bottom: 0px;
     93    left: calc(90% - 50px);
     94    transition: left 0.15s;
     95  }
     96  #iframe-container.iframe-shown {
     97    left: 10%;
     98  }
     99  #iframe-toggle {
    100    display: inline-block;
    101    vertical-align: middle;
    102    width: 20px;
    103    height: 100%;
    104    padding: 0;
    105    -webkit-appearance: none;
    106  }
    107  #test-iframe {
    108    display: inline-block;
    109    vertical-align: middle;
    110    background: white;
    111    width: calc(100% - 20px);
    112    height: 100%;
    113    border: 1px solid black;
    114  }
    115 
    116  .folder {
    117    margin-bottom: 1.5em;
    118  }
    119 
    120  .folderHeader {
    121    white-space: nowrap;
    122    position: sticky;
    123    top: 0;
    124  }
    125  .folderHeaderInner {
    126    background: white;
    127    /* to hide checkboxes from parent headers */
    128    position: relative;
    129    left: -2em;
    130    padding-left: 2em;
    131  }
    132 
    133  .folderName {
    134    font-weight: bold;
    135  }
    136 
    137  .folderMessage {
    138    margin-left: 1em;
    139    font-size: 0.9em;
    140  }
    141 
    142  .pageHeader {
    143    white-space: nowrap;
    144  }
    145 
    146  .testpage {
    147    border-style: solid;
    148    border-color: #CCC;
    149    border-width: 0px 0 1px 0;
    150    background-color: #FFF;
    151    padding: 4px 0 4px 0;
    152 
    153    -webkit-transition: background-color 0.25s;
    154    -moz-transition: background-color 0.25s;
    155    transition: background-color 0.25s;
    156  }
    157 
    158  .testpage:first-child {
    159    border-width: 1px 0 1px 0;
    160  }
    161 
    162  .timeout { }
    163  .success { }
    164  .fail { }
    165  .testpagesuccess { background-color: #8F8; }
    166  .testpagefail { background-color: #F88; }
    167  .testpageskipped { background-color: #888; }
    168  .testpagetimeout { background-color: #FC8; }
    169  .nowebgl { font-weight: bold; color: red; }
    170  #error-wrap {
    171      float: left;
    172      position: relative;
    173      left: 50%;
    174  }
    175  #error {
    176     color: red;
    177     float: left;
    178     position: relative;
    179     left: -50%;
    180     text-align: left;
    181  }
    182  ul {
    183    list-style: none;
    184    padding-left: 1em;
    185  }
    186 </style>
    187 <script type="application/javascript" src="js/webgl-test-harness.js"></script>
    188 <script>
    189 "use strict";
    190 
    191 window.onbeforeunload = function() {
    192  // Prompt user before reloading
    193  return false;
    194 }
    195 
    196 var DEFAULT_CONFORMANCE_TEST_VERSION = "2.0.1 (beta)";
    197 
    198 var OPTIONS = {
    199  version: DEFAULT_CONFORMANCE_TEST_VERSION,
    200  frames: 1,
    201  allowSkip: 0,
    202  root: null,
    203  quiet: 0
    204 };
    205 
    206 var testVersions = [
    207  "1.0.4 (beta)",
    208  "2.0.1 (beta)"
    209 ];
    210 
    211 function start() {
    212 
    213  function log(msg) {
    214    if (window.console && window.console.log) {
    215      window.console.log(msg);
    216    }
    217  }
    218 
    219  function createStylesheet() {
    220    var style = document.createElement("style");
    221    style.appendChild(document.createTextNode(""));
    222    document.head.appendChild(style);
    223    return style.sheet;
    224  }
    225 
    226  function create3DContext(canvas, attrs, version) {
    227    if (!canvas) {
    228      canvas = document.createElement("canvas");
    229    }
    230    var context = null;
    231    var names;
    232    switch (version) {
    233      case 2:
    234        names = ["webgl2"]; break;
    235      default:
    236        names = ["webgl", "experimental-webgl"]; break;
    237    }
    238    for (var i = 0; i < names.length; ++i) {
    239      try {
    240        context = canvas.getContext(names[i], attrs);
    241      } catch (e) {
    242      }
    243      if (context) {
    244        break;
    245      }
    246    }
    247    return context;
    248  }
    249 
    250  var reportType = WebGLTestHarnessModule.TestHarness.reportType;
    251  var pageCount = 0;
    252  var folderCount = 0;
    253  var autoScrollEnabled = true; // Whether the user prefers to auto scroll
    254  var autoScroll = true; // Whether auto scroll is actually performed
    255  let quickTestMode = true;
    256 
    257  var Page = function(reporter, folder, testIndex, url) {
    258    this.reporter = reporter;
    259    this.folder = folder;
    260    this.url = url;
    261    this.totalTests = 0;
    262    this.totalSuccessful = 0;
    263    this.totalTimeouts = 0;
    264    this.totalSkipped = 0;
    265    this.totalFailed = 0;
    266    this.testIndex = testIndex;
    267    this.startTime = new Date();
    268    this.totalTime = 0;
    269    var that = this;
    270 
    271    this.elementId = "page" + pageCount++;
    272    var li = reporter.localDoc.createElement('li');
    273    li.id = this.elementId;
    274    var div = reporter.localDoc.createElement('div');
    275    div.classList.add('pageHeader');
    276    var check = reporter.localDoc.createElement('input');
    277    check.type = 'checkbox';
    278    check.checked = true;
    279    check.onclick = function() {
    280      if (this.checked) {
    281        that.folder.enableUp_();
    282      }
    283      else {
    284        that.folder.disableUp_();
    285      }
    286    };
    287    div.appendChild(check);
    288    var button = reporter.localDoc.createElement('input');
    289    button.type = 'button';
    290    button.id = this.elementId + "-button";
    291    button.value = 'run';
    292    button.onclick = function() {
    293      autoScroll = false;
    294      reporter.runTest(url);
    295    };
    296    if (reporter.noSelectedWebGLVersion) {
    297      button.disabled = true;
    298    }
    299    div.appendChild(button);
    300    var a = reporter.localDoc.createElement('a');
    301    a.href = WebGLTestHarnessModule.getURLWithOptions(url, {
    302      webglVersion: reporter.selectedWebGLVersion,
    303      quiet: OPTIONS.quiet,
    304      quick: quickTestMode ? 1 : 0,
    305    });
    306    a.target = "_blank";
    307    const folderName = that.folder.displayName;
    308    console.assert(folderName.startsWith("all/"));
    309    console.assert(url.startsWith(folderName.substring(4) + "/"));
    310    const urlWithoutFolder = url.substring(folderName.length - 4 + 1);
    311    var node = reporter.localDoc.createTextNode(urlWithoutFolder);
    312    a.appendChild(node);
    313    div.appendChild(a);
    314    li.setAttribute('class', 'testpage');
    315    li.appendChild(div);
    316    var ul = reporter.localDoc.createElement('ul');
    317    var node = reporter.localDoc.createTextNode('');
    318    li.appendChild(ul);
    319    div.appendChild(node);
    320    this.totalsElem = node;
    321    this.resultElem = ul;
    322    this.elem = li;
    323    this.check = check;
    324  };
    325 
    326  Page.prototype.checked = function() {
    327    return this.check.checked;
    328  }
    329 
    330  Page.prototype.addResult = function(msg, success, skipped) {
    331    ++this.totalTests;
    332    if (success === undefined) {
    333      ++this.totalTimeouts;
    334      var result = "timeout";
    335      var css = "timeout";
    336    } else if (success) {
    337      ++this.totalSuccessful;
    338      // don't report success.
    339      return;
    340    } else {
    341      ++this.totalFailed;
    342      if (skipped) {
    343        // Skipped tests are counted as both skips and failures (because we
    344        // don't want to accidentally accept a conformance submission with
    345        // skipped tests).
    346        ++this.totalSkipped;
    347      }
    348      var result = "failed";
    349      var css = "fail";
    350    }
    351 
    352    var node = this.reporter.localDoc.createTextNode(result + ': ' + msg);
    353    var li = this.reporter.localDoc.createElement('li');
    354    li.appendChild(node);
    355    li.setAttribute('class', css);
    356    this.resultElem.appendChild(li);
    357  };
    358 
    359  Page.prototype.startPage = function() {
    360    if (autoScroll && this.elem.scrollIntoView) {
    361      this.elem.scrollIntoView(false);
    362    }
    363    this.totalTests = 0;
    364    this.totalSuccessful = 0;
    365    this.totalSkipped = 0;
    366    this.totalFailed = 0;
    367    this.totalTimeouts = 0;
    368    this.totalTime = 0;
    369    // remove previous results.
    370    while (this.resultElem.hasChildNodes()) {
    371      this.resultElem.removeChild(this.resultElem.childNodes[0]);
    372    }
    373    this.totalsElem.textContent = '';
    374 
    375    var shouldRun = this.check.checked && this.folder.checked();
    376 
    377    if (shouldRun) {
    378      this.elem.classList.remove('testpagetimeout');
    379      this.elem.classList.remove('testpageskipped');
    380      this.elem.classList.remove('testpagefail');
    381      this.elem.classList.remove('testpagesuccess');
    382      this.startTime = Date.now();
    383    }
    384 
    385    return this.check.checked && this.folder.checked();
    386  };
    387 
    388  Page.prototype.firstTestIndex = function() {
    389    return this.testIndex;
    390  };
    391 
    392  Page.prototype.finishPage = function(success) {
    393    var shouldRun = this.check.checked && this.folder.checked();
    394    if (shouldRun) {
    395      this.totalTime = Date.now() - this.startTime;
    396    } else {
    397      this.totalTime = 0;
    398    }
    399 
    400    var passedMsg = ' (Passed: ' + this.totalSuccessful + '/' + this.totalTests;
    401    var skippedMsg = '';
    402    if (this.totalSkipped > 0) {
    403      skippedMsg = ' Skipped: ' + this.totalSkipped + '/' + this.totalTests;
    404    }
    405    var failedMsg = '';
    406    if (this.totalFailed > 0) {
    407      failedMsg = ' Failed: ' + this.totalFailed + '/' + this.totalTests;
    408    }
    409    var timeoutMsg = '';
    410    if (this.totalTimeouts > 0) {
    411      timeoutMsg = ' Timeout: ' + this.totalTimeouts + '/' + this.totalTests;
    412    }
    413    var msg = passedMsg + skippedMsg + failedMsg + timeoutMsg + ' in ' + this.totalTime.toFixed(1) + ' ms)';
    414 
    415    if (success === undefined) {
    416      var css = 'testpagetimeout';
    417      msg = '(*timeout*)';
    418      ++this.totalTests;
    419      ++this.totalTimeouts;
    420    } else if (this.totalSkipped) {
    421      var css = 'testpageskipped';
    422    } else if (this.totalSuccessful != this.totalTests) {
    423      var css = 'testpagefail';
    424    } else {
    425      var css = 'testpagesuccess';
    426    }
    427    this.elem.classList.add(css);
    428    this.totalsElem.textContent = msg;
    429    this.folder.pageFinished(this, success);
    430  };
    431 
    432  Page.prototype.enableTest = function(re) {
    433    if (this.url.match(re)) {
    434      this.check.checked = true;
    435      this.folder.enableUp_();
    436    }
    437  };
    438 
    439  Page.prototype.disableTest = function(re) {
    440    if (this.url.match(re)) {
    441      this.check.checked = false;
    442    }
    443  };
    444 
    445  Page.prototype.toJSON = function() {
    446    return {
    447      'subtests': this.totalTests,
    448      'successful': this.totalSuccessful,
    449      'skipped': this.totalSkipped,
    450      'failed': this.totalFailed,
    451      'timedOut': this.totalTimeouts,
    452      'totalTime': this.totalTime,
    453    };
    454  };
    455 
    456 
    457  var Folder = function(reporter, folder, depth, opt_name) {
    458    this.reporter = reporter;
    459    this.depth = depth;
    460    this.name = opt_name || "";
    461    this.displayName = this.name;
    462    if (folder && folder.displayName) {
    463      this.displayName = folder.displayName + '/' + this.displayName;
    464    }
    465    this.subFolders = {};
    466    this.pages = [];
    467    this.items = [];
    468    this.folder = folder;
    469    this.cachedTotalTime = 0;
    470    this.cachedTotalSuccessful = 0;
    471    this.cachedTotalSkipped = 0;
    472    this.cachedTotalTimeouts = 0;
    473    this.cachedTotalTests = 0;
    474    var that = this;
    475 
    476    var doc = reporter.localDoc;
    477    this.elementId = "folder" + folderCount++;
    478    var li = doc.createElement('li');
    479    li.id = this.elementId;
    480    li.classList.add("folder");
    481    var folderHeader = doc.createElement('div');
    482    folderHeader.classList.add('folderHeader');
    483    var folderHeaderInner = doc.createElement('div');
    484    folderHeaderInner.classList.add('folderHeaderInner');
    485    folderHeader.appendChild(folderHeaderInner);
    486    var check = doc.createElement('input');
    487    check.type = 'checkbox';
    488    check.checked = true;
    489    check.onclick = function() {
    490      if (this.checked) {
    491        that.enableTest(".*");
    492      }
    493      else {
    494        that.disableTest(".*", true);
    495      }
    496    };
    497    folderHeaderInner.appendChild(check);
    498    var button = doc.createElement('input');
    499    button.type = 'button';
    500    button.value = 'run';
    501    button.onclick = function() {
    502      autoScroll = autoScrollEnabled;
    503      that.run();
    504    };
    505    if (reporter.noSelectedWebGLVersion) {
    506      button.disabled = true;
    507    }
    508    folderHeaderInner.appendChild(button);
    509    var h = doc.createElement('span');
    510    h.classList.add('folderName');
    511    h.appendChild(doc.createTextNode(this.displayName));
    512    folderHeaderInner.appendChild(h);
    513    var m = doc.createElement('span');
    514    m.classList.add('folderMessage');
    515    this.msgNode = doc.createTextNode('');
    516    m.appendChild(this.msgNode);
    517    folderHeaderInner.appendChild(m);
    518    var ul = doc.createElement('ul');
    519    li.appendChild(folderHeader);
    520    li.appendChild(ul);
    521    this.childUL = ul;
    522    this.elem = li;
    523    this.check = check;
    524    this.folderHeader = folderHeader;
    525  };
    526 
    527  Folder.prototype.checked = function() {
    528    return this.check.checked &&
    529        (this.folder ? this.folder.checked() : true);
    530  };
    531 
    532  Folder.prototype.firstTestIndex = function() {
    533    return this.items[0].firstTestIndex();
    534  };
    535 
    536  Folder.prototype.numChildren = function() {
    537    var numChildren = 0;
    538    for (var name in this.subFolders) {
    539      numChildren += this.subFolders[name].numChildren();
    540    }
    541    return numChildren + this.pages.length;
    542  };
    543 
    544  Folder.prototype.totalTime = function() {
    545    // Check to see if the cached total time needs to be recomputed
    546    if (this.cachedTotalTime == -1) {
    547      this.cachedTotalTime = 0;
    548      for (var name in this.subFolders) {
    549        this.cachedTotalTime += this.subFolders[name].totalTime();
    550      }
    551      for (var ii = 0; ii < this.pages.length; ++ii) {
    552        this.cachedTotalTime += this.pages[ii].totalTime;
    553      }
    554    }
    555    return this.cachedTotalTime;
    556  };
    557 
    558  Folder.prototype.totalSuccessful = function() {
    559    if (this.cachedTotalSuccessful == -1) {
    560      this.cachedTotalSuccessful = 0;
    561      for (var name in this.subFolders) {
    562        this.cachedTotalSuccessful += this.subFolders[name].totalSuccessful();
    563      }
    564      for (var ii = 0; ii < this.pages.length; ++ii) {
    565        this.cachedTotalSuccessful += this.pages[ii].totalSuccessful;
    566      }
    567    }
    568    return this.cachedTotalSuccessful;
    569  };
    570 
    571  Folder.prototype.totalSkipped = function() {
    572    if (this.cachedTotalSkipped == -1) {
    573      this.cachedTotalSkipped = 0;
    574      for (var name in this.subFolders) {
    575        this.cachedTotalSkipped += this.subFolders[name].totalSkipped();
    576      }
    577      for (var ii = 0; ii < this.pages.length; ++ii) {
    578        this.cachedTotalSkipped += this.pages[ii].totalSkipped;
    579      }
    580    }
    581    return this.cachedTotalSkipped;
    582  };
    583 
    584  Folder.prototype.totalFailed = function() {
    585    if (this.cachedTotalFailed == -1) {
    586      this.cachedTotalFailed = 0;
    587      for (var name in this.subFolders) {
    588        this.cachedTotalFailed += this.subFolders[name].totalFailed();
    589      }
    590      for (var ii = 0; ii < this.pages.length; ++ii) {
    591        this.cachedTotalFailed += this.pages[ii].totalFailed;
    592      }
    593    }
    594    return this.cachedTotalFailed;
    595  };
    596 
    597  Folder.prototype.totalTimeouts = function() {
    598    if (this.cachedTotalTimeouts == -1) {
    599      this.cachedTotalTimeouts = 0;
    600      for (var name in this.subFolders) {
    601        this.cachedTotalTimeouts += this.subFolders[name].totalTimeouts();
    602      }
    603      for (var ii = 0; ii < this.pages.length; ++ii) {
    604        this.cachedTotalTimeouts += this.pages[ii].totalTimeouts;
    605      }
    606    }
    607    return this.cachedTotalTimeouts;
    608  };
    609 
    610  Folder.prototype.totalTests = function() {
    611    if (this.cachedTotalTests == -1) {
    612      this.cachedTotalTests = 0;
    613      for (var name in this.subFolders) {
    614        this.cachedTotalTests += this.subFolders[name].totalTests();
    615      }
    616      for (var ii = 0; ii < this.pages.length; ++ii) {
    617        this.cachedTotalTests += this.pages[ii].totalTests;
    618      }
    619    }
    620    return this.cachedTotalTests;
    621  };
    622 
    623  Folder.prototype.run = function() {
    624    this.msgNode.textContent = '';
    625    var firstTestIndex = this.firstTestIndex();
    626    var count = this.numChildren();
    627    log("run tests: " + firstTestIndex + " to " + (firstTestIndex + count - 1))
    628    testHarness.runTests({start: firstTestIndex, count: count});
    629  };
    630 
    631  Folder.prototype.pageFinished = function(page, success) {
    632    this.cachedTotalTime = -1;
    633    this.cachedTotalSuccessful = -1;
    634    this.cachedTotalSkipped = -1;
    635    this.cachedTotalFailed = -1;
    636    this.cachedTotalTimeouts = -1;
    637    this.cachedTotalTests = -1;
    638    var passedMsg = ' (Passed: ' + this.totalSuccessful() + '/' + this.totalTests();
    639    var skippedMsg = '';
    640    if (this.totalSkipped() > 0) {
    641      skippedMsg = ' Skipped: ' + this.totalSkipped() + '/' + this.totalTests();
    642    }
    643    var failedMsg = '';
    644    if (this.totalFailed() > 0) {
    645      failedMsg = ' Failed: ' + this.totalFailed() + '/' + this.totalTests();
    646    }
    647    var timeoutMsg = '';
    648    if (this.totalTimeouts() > 0) {
    649      timeoutMsg = ' Timeout: ' + this.totalTimeouts() + '/' + this.totalTests();
    650    }
    651    this.msgNode.textContent = passedMsg + skippedMsg + failedMsg + timeoutMsg + ' in ' + (this.totalTime() / 1000).toFixed(2) + ' seconds)';
    652    if (this.folder) {
    653      this.folder.pageFinished(page, success);
    654    }
    655  };
    656 
    657  Folder.prototype.getSubFolder = function(name) {
    658    var subFolder = this.subFolders[name];
    659    if (subFolder === undefined) {
    660      subFolder = new Folder(this.reporter, this, this.depth + 1, name);
    661      this.subFolders[name] = subFolder;
    662      this.items.push(subFolder);
    663      this.childUL.appendChild(subFolder.elem);
    664    }
    665    return subFolder;
    666  };
    667 
    668  Folder.prototype.getOrCreateFolder = function(url) {
    669    var parts = url.split('/');
    670    var folder = this;
    671    for (var pp = 0; pp < parts.length - 1; ++pp) {
    672      folder = folder.getSubFolder(parts[pp]);
    673    }
    674    return folder;
    675  };
    676 
    677  Folder.prototype.addPage = function(page) {
    678    this.pages.push(page);
    679    this.items.push(page);
    680    this.childUL.appendChild(page.elem);
    681    this.folderHeader.classList.add('hasPages');
    682  };
    683 
    684  Folder.prototype.disableTest = function(re, opt_forceRecurse) {
    685    var recurse = true;
    686    if (this.name.match(re)) {
    687      this.check.checked = false;
    688      recurse = opt_forceRecurse;
    689    }
    690    if (recurse) {
    691      for (var name in this.subFolders) {
    692        this.subFolders[name].disableTest(re, opt_forceRecurse);
    693      }
    694      for (var ii = 0; ii < this.pages.length; ++ii) {
    695        this.pages[ii].disableTest(re);
    696      }
    697    }
    698  };
    699 
    700  Folder.prototype.enableUp_ = function() {
    701    this.check.checked = true;
    702    var parent = this.folder;
    703    if (parent) {
    704      parent.enableUp_();
    705    }
    706  }
    707 
    708  Folder.prototype.disableUp_ = function() {
    709    var checked = false;
    710    for (var name in this.subFolders) {
    711      checked = this.subFolders[name].checked();
    712      if (checked) {
    713        break;
    714      }
    715    }
    716    for (var ii = 0; ii < this.pages.length && checked == false; ++ii) {
    717      checked = this.pages[ii].checked();
    718    }
    719    this.check.checked = checked;
    720    var parent = this.folder;
    721    if (parent) {
    722      parent.disableUp_();
    723    }
    724  }
    725 
    726  Folder.prototype.enableTest = function(re) {
    727    if (this.name.match(re)) {
    728      this.enableUp_();
    729    }
    730    for (var name in this.subFolders) {
    731      this.subFolders[name].enableTest(re);
    732    }
    733    for (var ii = 0; ii < this.pages.length; ++ii) {
    734      this.pages[ii].enableTest(re);
    735    }
    736  };
    737 
    738  var Reporter = function(iframes) {
    739    this.localDoc = document;
    740    this.resultElem = document.getElementById("results");
    741    this.fullResultsElem = document.getElementById("fullresults");
    742    var node = this.localDoc.createTextNode('');
    743    this.fullResultsElem.appendChild(node);
    744    this.fullResultsNode = node;
    745    this.iframes = iframes;
    746    this.currentPageElem = null;
    747    this.totalPages = 0;
    748    this.pagesByURL = {};
    749 
    750    // Check to see if WebGL is supported
    751    var canvas = document.createElement("canvas");
    752    var ctx = create3DContext(canvas, null, 1);
    753 
    754    // Check to see if WebGL2 is supported
    755    var canvas2 = document.createElement("canvas");
    756    var ctx2 = create3DContext(canvas2, null, 2);
    757 
    758    this.noSelectedWebGLVersion = false;
    759    this.selectedWebGLVersion = WebGLTestHarnessModule.getMajorVersion(OPTIONS.version);
    760    if (this.selectedWebGLVersion == 2 && !ctx2) {
    761        this.noSelectedWebGLVersion = true;
    762    } else if (this.selectedWebGLVersion == 1 && !ctx) {
    763        this.noSelectedWebGLVersion = true;
    764    }
    765 
    766    // If the WebGL2 context could be created use it to get context info
    767    if (ctx2) {
    768      ctx = ctx2;
    769    }
    770 
    771    this.noWebGL = !ctx;
    772 
    773    this.contextInfo = {};
    774    this.root = new Folder(this, null, 0, "all");
    775    this.resultElem.appendChild(this.root.elem);
    776    this.callbacks = { };
    777    this.startTime = new Date();
    778 
    779    if (ctx) {
    780      this.contextInfo["VENDOR"] = ctx.getParameter(ctx.VENDOR);
    781      this.contextInfo["VERSION"] = ctx.getParameter(ctx.VERSION);
    782      this.contextInfo["RENDERER"] = ctx.getParameter(ctx.RENDERER);
    783      this.contextInfo["RED_BITS"] = ctx.getParameter(ctx.RED_BITS);
    784      this.contextInfo["GREEN_BITS"] = ctx.getParameter(ctx.GREEN_BITS);
    785      this.contextInfo["BLUE_BITS"] = ctx.getParameter(ctx.BLUE_BITS);
    786      this.contextInfo["ALPHA_BITS"] = ctx.getParameter(ctx.ALPHA_BITS);
    787      this.contextInfo["DEPTH_BITS"] = ctx.getParameter(ctx.DEPTH_BITS);
    788      this.contextInfo["STENCIL_BITS"] = ctx.getParameter(ctx.STENCIL_BITS);
    789 
    790      var ext = ctx.getExtension("WEBGL_debug_renderer_info");
    791      if (ext) {
    792        this.contextInfo["UNMASKED_VENDOR"] = ctx.getParameter(ext.UNMASKED_VENDOR_WEBGL);
    793        this.contextInfo["UNMASKED_RENDERER"] = ctx.getParameter(ext.UNMASKED_RENDERER_WEBGL);
    794      }
    795    }
    796  };
    797 
    798  Reporter.prototype.enableTest = function(name) {
    799    this.root.enableTest(name);
    800  };
    801 
    802  Reporter.prototype.disableTest = function(name) {
    803    this.root.disableTest(name);
    804  };
    805 
    806  Reporter.prototype.disableAllTests = function() {
    807    this.root.disableTest(".*", true);
    808  };
    809 
    810  Reporter.prototype.addEventListener = function(type, func) {
    811    if (!this.callbacks[type]) {
    812      this.callbacks[type] = [];
    813    }
    814    this.callbacks[type].push(func);
    815  };
    816 
    817  Reporter.prototype.executeListenerEvents_ = function(type) {
    818    var callbacks = this.callbacks[type].slice(0);
    819    for (var ii = 0; ii < callbacks.length; ++ii) {
    820      setTimeout(callbacks[ii], 0);
    821    }
    822  };
    823 
    824  Reporter.prototype.runTest = function(url) {
    825    var page = this.pagesByURL[url];
    826    testHarness.runTests({start: page.firstTestIndex(), count: 1});
    827  };
    828 
    829  Reporter.prototype.getFolder = function(url) {
    830    return this.root.getOrCreateFolder(url);
    831  };
    832 
    833  Reporter.prototype.addPage = function(url) {
    834    var folder = this.getFolder(url);
    835    var page = new Page(this, folder, this.totalPages, url);
    836    folder.addPage(page);
    837    ++this.totalPages;
    838    this.pagesByURL[url] = page;
    839  };
    840 
    841  Reporter.prototype.startPage = function(url) {
    842    var page = this.pagesByURL[url];
    843    return page.startPage();
    844  };
    845 
    846  Reporter.prototype.addResult = function(url, msg, success, skipped) {
    847    var page = this.pagesByURL[url];
    848    page.addResult(msg, success, skipped);
    849  };
    850 
    851  Reporter.prototype.finishPage = function(url, success) {
    852    var page = this.pagesByURL[url];
    853    page.finishPage(success);
    854    if (OPTIONS.dumpShaders == 1) {
    855      var xhr = new XMLHttpRequest();
    856      xhr.open('POST', "/finishIndividualTest", true);
    857      xhr.send(null);
    858    }
    859  };
    860 
    861  Reporter.prototype.displayFinalResults = function(msg, success) {
    862    if (success) {
    863      var totalTests = 0;
    864      var testsSucceeded = 0;
    865      var testsFailed = 0;
    866      var testsSkipped = 0;
    867      var testsTimedOut = 0;
    868 
    869      var subtestsHit = 0;
    870      var subtestsSucceeded = 0;
    871      var subtestsTimedOut = 0;
    872      var subtestsSkipped = 0;
    873      var subtestsFailed = 0;
    874 
    875      var totalTime = Date.now() - this.startTime;
    876 
    877      for (var url in this.pagesByURL) {
    878        var page = this.pagesByURL[url];
    879        totalTests += 1;
    880        if (page.totalSkipped) {
    881          testsSkipped += 1;
    882        }
    883        if (page.totalFailed) {
    884          testsFailed += 1;
    885        } else if (page.totalTimeouts) {
    886          testsTimedOut += 1;
    887        } else if (page.totalSuccessful) {
    888          if (page.totalSuccessful != page.totalTests)
    889            throw successes_not_equal_total;
    890          testsSucceeded += 1;
    891        }
    892 
    893        subtestsHit += page.totalTests;
    894        subtestsSucceeded += page.totalSuccessful;
    895        subtestsTimedOut += page.totalTimeouts;
    896        subtestsSkipped += page.totalSkipped;
    897        subtestsFailed += page.totalFailed;
    898      }
    899 
    900      function ratio_str(x, y, name) {
    901        return x + '/' + y + ' ' + name + ' (' + (x / y * 100).toFixed(2) + '%)';
    902      }
    903      var testsSucceededRatio = ratio_str(testsSucceeded, totalTests, 'tests');
    904      var passedMsg = 'Passed ' + testsSucceededRatio + ', ' +
    905                      ratio_str(subtestsSucceeded, subtestsHit, 'subtests');
    906      var skippedMsg = '';
    907      if (testsSkipped > 0) {
    908        skippedMsg = ' Skipped ' + ratio_str(testsSkipped, totalTests, 'tests');
    909      }
    910      var failedMsg = '';
    911      if (testsFailed > 0) {
    912        failedMsg = ' Failed ' + ratio_str(testsFailed, totalTests, 'tests') + ', ' +
    913                    ratio_str(subtestsFailed, subtestsHit, 'subtests');
    914      }
    915      var timeoutMsg = '';
    916      if (testsTimedOut > 0) {
    917        timeoutMsg = ' Timeout ' + ratio_str(testsTimedOut, totalTests, 'tests');
    918      }
    919      var msg = passedMsg + skippedMsg + failedMsg + timeoutMsg;
    920      this.fullResultsNode.textContent = msg;
    921 
    922      // generate a text summary
    923      var tx = "";
    924      tx += "WebGL Conformance Test Results\n";
    925      tx += "Version " + OPTIONS.version + "\n";
    926      tx += "Generated on: " + (new Date()).toString() + "\n";
    927      tx += "\n";
    928      tx += "-------------------\n\n";
    929      tx += "User Agent: " + (navigator.userAgent ? navigator.userAgent : "(navigator.userAgent is null)") + "\n";
    930      tx += "WebGL VENDOR: " + this.contextInfo["VENDOR"] + "\n";
    931      tx += "WebGL VERSION: " + this.contextInfo["VERSION"] + "\n";
    932      tx += "WebGL RENDERER: " + this.contextInfo["RENDERER"] + "\n";
    933      tx += "Unmasked VENDOR: " + this.contextInfo["UNMASKED_VENDOR"] + "\n";
    934      tx += "Unmasked RENDERER: " + this.contextInfo["UNMASKED_RENDERER"] + "\n";
    935      tx += "WebGL R/G/B/A/Depth/Stencil bits (default config): " + this.contextInfo["RED_BITS"] + "/" + this.contextInfo["GREEN_BITS"] + "/" + this.contextInfo["BLUE_BITS"] + "/" + this.contextInfo["ALPHA_BITS"] + "/" + this.contextInfo["DEPTH_BITS"] + "/" + this.contextInfo["STENCIL_BITS"] + "\n";
    936      tx += "\n-------------------\n\n";
    937 
    938      var result;
    939      if (totalTests && testsSucceeded == totalTests) {
    940        result = 'PASS';
    941      } else {
    942        result = 'FAIL';
    943      }
    944      tx += "Test Summary: " + result + " (" + totalTests + " tests):\n";
    945      tx += subtestsHit + " subtests ran in " + (totalTime / 1000.0).toFixed(2) + " seconds\n";
    946      function record(what, tests, subtests) {
    947        tx += what + ": " + tests + " tests, " + subtests + " subtests\n";
    948      }
    949      record('PASSED', testsSucceeded, subtestsSucceeded);
    950      record('NOT PASSED', totalTests - testsSucceeded, subtestsHit - subtestsSucceeded);
    951 
    952      record('FAILED', testsFailed, subtestsFailed);
    953      record('TIMED OUT', testsTimedOut, subtestsTimedOut);
    954      record('SKIPPED', testsSkipped, subtestsSkipped);
    955 
    956      tx += "\n-------------------\n\n";
    957 
    958      const failureLines = [];
    959      const timeoutLines = [];
    960      const resultLines = [];
    961 
    962      for (var url in this.pagesByURL) {
    963        var page = this.pagesByURL[url];
    964        resultLines.push('    "' + url + '":' + JSON.stringify(page.toJSON()));
    965 
    966        if (page.totalFailed) {
    967          failureLines.push('    "' + url + '",');
    968        }
    969        if (page.totalTimeouts) {
    970          timeoutLines.push('    "' + url + '",');
    971        }
    972      }
    973 
    974      const lines = [].concat(
    975        [
    976          '{',
    977          '  "failures": [',
    978        ],
    979        failureLines,
    980        [
    981          '  ],',
    982          '  "timeouts": [',
    983        ],
    984        timeoutLines,
    985        [
    986          '  ],',
    987          '  "results": {',
    988        ],
    989        resultLines,
    990        [
    991        '  },',
    992        '}',
    993        ]
    994      );
    995 
    996      tx += lines.join('\n');
    997 
    998      var r = document.getElementById("testResultsAsText");
    999      while (r.firstChild) r.removeChild(r.firstChild);
   1000      r.appendChild(document.createTextNode(tx));
   1001      document.getElementById("showTextSummary").disabled = false;
   1002      document.getElementById("dlTextSummary").disabled = false;
   1003 
   1004      this.postResultsToServer(tx);
   1005    } else {
   1006      var e = document.getElementById("error");
   1007      e.innerHTML = msg;
   1008      this.postResultsToServer(msg);
   1009    }
   1010  };
   1011 
   1012  Reporter.prototype.postTestStartToServer = function(resultText) {
   1013    this.startTime = Date.now();
   1014    if(OPTIONS.postResults == undefined || OPTIONS.postResults == 0) {
   1015      return;
   1016    }
   1017 
   1018    var xhr = new XMLHttpRequest();
   1019    xhr.open('POST', "/start", true);
   1020    xhr.send(null);
   1021  };
   1022 
   1023  Reporter.prototype.postResultsToServer = function(resultText) {
   1024    if(OPTIONS.postResults == undefined || OPTIONS.postResults == 0) {
   1025      return;
   1026    }
   1027 
   1028    var xhr = new XMLHttpRequest();
   1029    xhr.open('POST', "/finish", true);
   1030    xhr.setRequestHeader("Content-Type", "text/plain");
   1031    xhr.send(resultText);
   1032  };
   1033 
   1034  Reporter.prototype.ready = function() {
   1035    var loading = document.getElementById("loading");
   1036    loading.style.display = "none";
   1037    if (!this.noSelectedWebGLVersion) {
   1038      var button = document.getElementById("runTestsButton");
   1039      button.disabled = false;
   1040      this.executeListenerEvents_("ready");
   1041    }
   1042  };
   1043 
   1044  Reporter.prototype.reportFunc = function(type, url, msg, success, skipped) {
   1045    switch (type) {
   1046      case reportType.ADD_PAGE:
   1047        return this.addPage(msg);
   1048      case reportType.READY:
   1049        return this.ready();
   1050      case reportType.START_PAGE:
   1051        return this.startPage(url);
   1052      case reportType.TEST_RESULT:
   1053        return this.addResult(url, msg, success, skipped);
   1054      case reportType.FINISH_PAGE:
   1055        return this.finishPage(url, success);
   1056      case reportType.FINISHED_ALL_TESTS:
   1057        return this.displayFinalResults(msg, success);
   1058      default:
   1059        throw 'unhandled';
   1060        break;
   1061    };
   1062  };
   1063 
   1064  var getURLOptions = function(obj) {
   1065    var s = window.location.href;
   1066    var q = s.indexOf("?");
   1067    var e = s.indexOf("#");
   1068    if (e < 0) {
   1069      e = s.length;
   1070    }
   1071    var query = s.substring(q + 1, e);
   1072    var pairs = query.split("&");
   1073    for (var ii = 0; ii < pairs.length; ++ii) {
   1074      var keyValue = pairs[ii].split("=");
   1075      var key = keyValue[0];
   1076      var value = decodeURIComponent(keyValue[1]);
   1077      obj[key] = value;
   1078    }
   1079  };
   1080 
   1081  getURLOptions(OPTIONS);
   1082 
   1083  var makeVersionSelect = function(currentVersion) {
   1084    var versionSelect = document.getElementById("testVersion");
   1085    var foundCurrentVersion = false;
   1086    var numericCurrentVersion = currentVersion.replace(/[^\d.]/g, '');
   1087 
   1088    for (var i in testVersions) {
   1089      var version = testVersions[i];
   1090      var numericVersion = version.replace(/[^\d.]/g, '');
   1091      var option = document.createElement("option");
   1092      option.setAttribute('value', numericVersion);
   1093      option.innerHTML = version;
   1094 
   1095      if (numericVersion == numericCurrentVersion) {
   1096        foundCurrentVersion = true;
   1097        option.selected = true;
   1098      }
   1099 
   1100      versionSelect.appendChild(option);
   1101    }
   1102 
   1103    // If the version requested by the query string isn't in the list add it.
   1104    if (!foundCurrentVersion) {
   1105      var option = document.createElement("option");
   1106      option.setAttribute('value', numericCurrentVersion);
   1107      option.innerHTML = currentVersion + " (unknown)";
   1108      option.selected = true;
   1109 
   1110      versionSelect.appendChild(option);
   1111    }
   1112 
   1113    versionSelect.addEventListener('change', function(ev) {
   1114      window.location.href = "?version=" + versionSelect.value;
   1115    }, false);
   1116  }
   1117 
   1118  makeVersionSelect(OPTIONS.version);
   1119 
   1120  // Make iframes
   1121  var iframes = [document.getElementById("test-iframe")];
   1122 
   1123  var testPath = "00_test_list.txt";
   1124  if (OPTIONS.root) {
   1125    testPath = OPTIONS.root + "/" + testPath;
   1126  }
   1127 
   1128  var reporter = new Reporter(iframes);
   1129  var testHarness = new WebGLTestHarnessModule.TestHarness(
   1130      iframes,
   1131      testPath,
   1132      function(type, url, msg, success, skipped) {
   1133        return reporter.reportFunc(type, url, msg, success, skipped);
   1134      },
   1135      OPTIONS);
   1136  reporter.addEventListener("ready", function() {
   1137    // Set which tests to include.
   1138    if (OPTIONS.include) {
   1139      reporter.disableAllTests();
   1140      var includes = OPTIONS.include.split(",")
   1141      for (var ii = 0; ii < includes.length; ++ii) {
   1142        reporter.enableTest(new RegExp(includes[ii]));
   1143      }
   1144    }
   1145    // Remove tests based on skip=re1,re2 in URL.
   1146    if (OPTIONS.skip) {
   1147      var skips = OPTIONS.skip.split(",")
   1148      for (var ii = 0; ii < skips.length; ++ii) {
   1149        reporter.disableTest(new RegExp(skips[ii]));
   1150      }
   1151    }
   1152    // Auto run the tests if the run=1 in URL
   1153    if (OPTIONS.run != undefined && OPTIONS.run != 0) {
   1154      reporter.postTestStartToServer();
   1155      testHarness.runTests();
   1156    }
   1157  });
   1158  window.webglTestHarness = testHarness;
   1159  var button = document.getElementById("runTestsButton");
   1160  button.disabled = true;
   1161  button.onclick = function() {
   1162    autoScroll = autoScrollEnabled;
   1163    reporter.postTestStartToServer();
   1164    testHarness.runTests();
   1165  };
   1166  var autoScrollCheckbox = document.getElementById("autoScrollCheckbox");
   1167  autoScrollCheckbox.checked = autoScrollEnabled;
   1168  autoScrollCheckbox.onclick = function() {
   1169    autoScrollEnabled = autoScrollCheckbox.checked;
   1170    autoScroll = autoScrollEnabled;
   1171  };
   1172 
   1173  var hidePassedSheet = createStylesheet();
   1174  var hidePassedCheckbox = document.getElementById("hidePassedCheckbox");
   1175  hidePassedCheckbox.checked = false;
   1176  hidePassedCheckbox.onclick = function() {
   1177    var hidePassedTests = hidePassedCheckbox.checked;
   1178    if (hidePassedTests) {
   1179        hidePassedSheet.insertRule(".testpagesuccess { display: none; }", 0);
   1180    } else {
   1181        hidePassedSheet.deleteRule(0);
   1182    }
   1183  };
   1184 
   1185  var quickTestModeCheckbox = document.getElementById("quickTestModeCheckbox");
   1186  quickTestModeCheckbox.checked = quickTestMode;
   1187  quickTestModeCheckbox.onclick = function() {
   1188    quickTestMode = quickTestModeCheckbox.checked;
   1189  };
   1190 
   1191  var textbutton = document.getElementById("showTextSummary");
   1192  textbutton.onclick = function() {
   1193    log("click");
   1194    var htmldiv = document.getElementById("testResultsHTML");
   1195    var textdiv = document.getElementById("testResultsText");
   1196    if (textdiv.style.display == "none") {
   1197      textdiv.style.display = "block";
   1198      htmldiv.style.display = "none";
   1199      textbutton.setAttribute("value", "display html summary");
   1200    } else {
   1201      textdiv.style.display = "none";
   1202      htmldiv.style.display = "block";
   1203      textbutton.setAttribute("value", "display text summary");
   1204    }
   1205  };
   1206 
   1207  function download(filename, text) {
   1208    var element = document.createElement("a");
   1209    element.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(text));
   1210    element.setAttribute("download", filename);
   1211    element.style.display = "none";
   1212    document.body.appendChild(element);
   1213    element.click();
   1214    document.body.removeChild(element);
   1215  }
   1216  var dltextbutton = document.getElementById("dlTextSummary");
   1217  dltextbutton.onclick = function() {
   1218    var textdiv = document.getElementById("testResultsText");
   1219    download("webgl-conformance-" + OPTIONS.version + ".txt", textdiv.innerText.trim());
   1220  };
   1221 
   1222  if (reporter.noSelectedWebGLVersion) {
   1223    button.disabled = true;
   1224  }
   1225  if (reporter.noWebGL) {
   1226    var elem = document.getElementById("nowebgl");
   1227    elem.style.display = "";
   1228    reporter.postResultsToServer("Browser does not appear to support WebGL");
   1229  } else if (reporter.noSelectedWebGLVersion) {
   1230    var elem = document.getElementById("noselectedwebgl");
   1231    elem.style.display = "";
   1232    reporter.postResultsToServer("Browser does not appear to support the selected version of WebGL");
   1233  }
   1234 
   1235  const iframeContainer = document.getElementById("iframe-container");
   1236  const iframeToggle = document.getElementById("iframe-toggle");
   1237  iframeToggle.value = iframeToggle.getAttribute("data-value-hidden");
   1238  iframeToggle.onclick = function() {
   1239    const expanded = iframeToggle.myExpanded = !iframeToggle.myExpanded;
   1240    if (expanded) {
   1241      iframeContainer.classList.add("iframe-shown");
   1242      iframeToggle.value = iframeToggle.getAttribute("data-value-shown");
   1243    } else {
   1244      iframeContainer.classList.remove("iframe-shown");
   1245      iframeToggle.value = iframeToggle.getAttribute("data-value-hidden");
   1246    }
   1247  };
   1248 }
   1249 </script>
   1250 </head>
   1251 <body onload="start()">
   1252 
   1253 <div id="testlist">
   1254  <div id="testResultsHTML">
   1255    <ul id="results">
   1256    </ul>
   1257  </div>
   1258  <div style="display: none;" id="testResultsText">
   1259    <pre id="testResultsAsText"></pre>
   1260  </div>
   1261 </div> <!-- end of container -->
   1262 
   1263 <div id="iframe-container">
   1264  <input type="button" data-value-hidden="◄" data-value-shown="►" id="iframe-toggle" aria-hidden="true"
   1265  ><iframe id="test-iframe"></iframe>
   1266 </div>
   1267 
   1268 <div id="header">
   1269  <div id="info">
   1270    <div style="text-align:center">
   1271      <img src="resources/webgl-logo.png" alt="WebGL" id="logo"/>
   1272      <br/>
   1273      Conformance Test Runner
   1274    </div>
   1275    Version
   1276    <select id="testVersion">
   1277    </select>
   1278    <a href="../../conformance-suites/">(older versions?)</a>
   1279    <br/>
   1280    <input type="button" value="run tests" id="runTestsButton"/>
   1281    <label for="autoScrollCheckbox"><input type="checkbox" id="autoScrollCheckbox"/>auto scroll</label>
   1282    <label for="hidePassedCheckbox"><input type="checkbox" id="hidePassedCheckbox"/>hide passed</label>
   1283    <label for="quickTestModeCheckbox"><input type="checkbox" id="quickTestModeCheckbox"/>quick test mode</label>
   1284    <br/>
   1285    <input type="button" disabled value="show text summary" id="showTextSummary"/>
   1286    <input type="button" disabled value="download text" id="dlTextSummary"/>
   1287    <div id="nowebgl" class="nowebgl" style="display: none;">
   1288      This browser does not appear to support WebGL
   1289    </div>
   1290    <div id="noselectedwebgl" class="nowebgl" style="display: none;">
   1291      This browser does not appear to support the selected version of WebGL
   1292    </div>
   1293    <div id="loading">
   1294      Loading Tests...
   1295    </div>
   1296    <div id="fullresults">
   1297    </div>
   1298  </div>
   1299  <div id="error-wrap">
   1300    <pre id="error"></pre>
   1301  </div>
   1302 </div> <!-- end of header -->
   1303 
   1304 </body>
   1305 </html>