tor-browser

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

test_utils.sub.js (8806B)


      1 var TestUtils = (function() {
      2  function randomString() {
      3    var result = "";
      4    for (var i = 0; i < 5; i++)
      5        result += String.fromCharCode(97 + Math.floor(Math.random() * 26));
      6    return result;
      7  };
      8 
      9  /**
     10   * Representation of one datatype.
     11   * @typedef Datatype
     12   * @type{object}
     13   * @property{string} name Name of the datatype.
     14   * @property{function():boolean} supported
     15   *     Whether this datatype is supported by this user agent.
     16   * @method{function():Void} add A function to add an instance of the datatype.
     17   * @method{function():boolean} isEmpty A function that tests whether
     18   *     the datatype's storage backend is empty.
     19   */
     20  var Datatype;
     21 
     22  var TestUtils = {};
     23 
     24  /**
     25   * Various storage backends that are part of the 'storage' datatype.
     26   * @param{Array.<Datatype>}
     27   */
     28  TestUtils.STORAGE = [
     29    {
     30      "name": "local storage",
     31      "supported": function() { return !!window.localStorage; },
     32      "add": function() {
     33        return new Promise(function(resolve, reject) {
     34          localStorage.setItem(randomString(), randomString());
     35          resolve();
     36        });
     37      },
     38      "isEmpty": function() {
     39        return new Promise(function(resolve, reject) {
     40          resolve(!localStorage.length);
     41        });
     42      }
     43    },
     44    {
     45      "name": "Indexed DB",
     46      "supported": function() { return !!window.indexedDB; },
     47      "add": function() {
     48        return new Promise(function(resolve, reject) {
     49          var request = window.indexedDB.open("database");
     50          request.onupgradeneeded = function() {
     51            request.result.createObjectStore("store");
     52          };
     53          request.onsuccess = function() {
     54            request.result.close();
     55            resolve();
     56          }
     57        });
     58      },
     59      "isEmpty": function() {
     60        return new Promise(function(resolve, reject) {
     61          var request = window.indexedDB.open("database");
     62          request.onsuccess = function() {
     63            var database = request.result;
     64            try {
     65              var transaction = database.transaction(["store"]);
     66              resolve(false);
     67            } catch(error) {
     68              // The database is empty. However, by testing that, we have also
     69              // created it, which means that |onupgradeneeded| in the "add"
     70              // method will not run the next time. Delete the database before
     71              // reporting that it was empty.
     72              var deletion = window.indexedDB.deleteDatabase("database");
     73              deletion.onsuccess = resolve.bind(this, true);
     74            } finally {
     75              database.close();
     76            }
     77          };
     78        });
     79      }
     80    },
     81    {
     82      // TODO(@msramek): We should also test the PERSISTENT filesystem, however,
     83      // that might require storage permissions.
     84      "name": "filesystems",
     85      "supported": function() {
     86        return window.requestFileSystem || window.webkitRequestFileSystem;
     87      },
     88      "add": function() {
     89        return new Promise(function(resolve, reject) {
     90          var onSuccess = function(fileSystem) {
     91            fileSystem.root.getFile('file', {"create": true}, resolve, resolve);
     92          }
     93          var onFailure = resolve;
     94 
     95          var requestFileSystem =
     96              window.requestFileSystem || window.webkitRequestFileSystem;
     97          requestFileSystem(window.TEMPORARY, 1 /* 1B */,
     98                            onSuccess, onFailure);
     99        });
    100      },
    101      "isEmpty": function() {
    102        return new Promise(function(resolve, reject) {
    103          var onSuccess = function(fileSystem) {
    104            fileSystem.root.getFile(
    105                'file', {},
    106                resolve.bind(this, false) /* opened successfully */,
    107                resolve.bind(this, true) /* failed to open */);
    108          }
    109          var onFailure = resolve.bind(this, true);
    110 
    111          var requestFileSystem =
    112              window.requestFileSystem || window.webkitRequestFileSystem;
    113          requestFileSystem(window.TEMPORARY, 1 /* 1B */,
    114                            onSuccess, onFailure);
    115        });
    116      }
    117    },
    118    {
    119      "name": "service workers",
    120      "supported": function() { return !!navigator.serviceWorker; },
    121      "add": function() {
    122        return navigator.serviceWorker.register(
    123            "support/service_worker.js",
    124            { scope: "support/page_using_service_worker.html"});
    125      },
    126      "isEmpty": function() {
    127        return new Promise(function(resolve, reject) {
    128          navigator.serviceWorker.getRegistrations()
    129              .then(function(registrations) {
    130                resolve(!registrations.length);
    131              });
    132        });
    133      }
    134    },
    135    {
    136      "name": "Storage Buckets",
    137      "supported": function() { return !!navigator.storageBuckets; },
    138      "add": function() {
    139        return navigator.storageBuckets.open('inbox_bucket');
    140      },
    141      "isEmpty": function() {
    142        return new Promise(async function(resolve, reject) {
    143          var keys = await navigator.storageBuckets.keys();
    144          resolve(!keys.includes('inbox_bucket'));
    145        });
    146      }
    147    },
    148  ].filter(function(backend) { return backend.supported(); });
    149 
    150  /**
    151   * All datatypes supported by Clear-Site-Data.
    152   * @param{Array.<Datatype>}
    153   */
    154  TestUtils.DATATYPES = [
    155    {
    156      "name": "cookies",
    157      "supported": function() { return typeof document.cookie == "string"; },
    158      "add": function() {
    159        return new Promise(function(resolve, reject) {
    160          document.cookie = randomString() + "=" + randomString();
    161          resolve();
    162        });
    163      },
    164      "isEmpty": function() {
    165        return new Promise(function(resolve, reject) {
    166          resolve(!document.cookie);
    167        });
    168      }
    169    },
    170    {
    171      "name": "storage",
    172      "supported": TestUtils.STORAGE[0].supported,
    173      "add": TestUtils.STORAGE[0].add,
    174      "isEmpty": TestUtils.STORAGE[0].isEmpty,
    175    }
    176  ].filter(function(datatype) { return datatype.supported(); });
    177 
    178  /**
    179   * All possible combinations of datatypes.
    180   * @property {Array.<Array.<Datatype>>}
    181   */
    182  TestUtils.COMBINATIONS = (function() {
    183    var combinations = [];
    184    for (var mask = 0; mask < (1 << TestUtils.DATATYPES.length); mask++) {
    185      var combination = [];
    186 
    187      for (var datatype = 0;
    188           datatype < TestUtils.DATATYPES.length; datatype++) {
    189        if (mask & (1 << datatype))
    190          combination.push(TestUtils.DATATYPES[datatype]);
    191      }
    192 
    193      combinations.push(combination);
    194    }
    195    return combinations;
    196  })();
    197 
    198  /**
    199   * Populates |datatypes| by calling the "add" method on each of them,
    200   * and verifies that they are nonempty.
    201   * @param {Array.<Datatype>} datatypes to be populated.
    202   * @private
    203   */
    204  function populate(datatypes) {
    205    return Promise.all(datatypes.map(function(datatype) {
    206      return new Promise(function(resolve, reject) {
    207        datatype.add().then(function() {
    208          datatype.isEmpty().then(function(isEmpty) {
    209            assert_false(
    210                isEmpty,
    211                datatype.name +
    212                    " has to be nonempty before the test starts.");
    213            resolve();
    214          });
    215        });
    216      });
    217    }));
    218  };
    219 
    220  /**
    221   * Ensures that all datatypes are nonempty. Should be called in the test
    222   * setup phase.
    223   */
    224  TestUtils.populateDatatypes = populate.bind(this, TestUtils.DATATYPES);
    225 
    226  /**
    227   * Ensures that all backends of the "storage" datatype are nonempty. Should
    228   * be called in the test setup phase.
    229   */
    230  TestUtils.populateStorage = populate.bind(this, TestUtils.STORAGE);
    231 
    232  /**
    233   * Get the support server URL that returns a Clear-Site-Data header
    234   * to clear |datatypes|.
    235   * @param{Array.<Datatype>} datatypes The list of datatypes to be deleted.
    236   * @return string The URL to be queried.
    237   */
    238  TestUtils.getClearSiteDataUrl = function(datatypes) {
    239    names = datatypes.map(function(e) { return e.name });
    240    return "support/echo-clear-site-data.py?" + names.join("&");
    241  }
    242 
    243  /**
    244   * @param{string} page_scheme Scheme of the page. "http" or "https".
    245   * @param{string} resource_scheme Scheme of the resource. "http" or "https".
    246   * @return The URL of a page that contains a resource requesting the deletion
    247   *     of storage.
    248   */
    249  TestUtils.getPageWithResourceUrl = function(page_scheme, resource_scheme) {
    250      if (page_scheme != "https" && page_scheme != "http")
    251        throw "Unsupported scheme: " + page_scheme;
    252      if (resource_scheme != "https" && resource_scheme != "http")
    253        throw "Unsupported scheme: " + resource_scheme;
    254      return page_scheme + "://{{domains[]}}:" +
    255          (page_scheme == "https" ? {{ports[https][0]}} : {{ports[http][0]}}) +
    256          "/clear-site-data/support/page_with_resource.sub.html?scheme=" +
    257          resource_scheme;
    258  }
    259 
    260  return TestUtils;
    261 })();