| var TestUtils = (function() { |
| function randomString() { |
| var result = ""; |
| for (var i = 0; i < 5; i++) |
| result += String.fromCharCode(97 + Math.floor(Math.random() * 26)); |
| return result; |
| }; |
| |
| /** |
| * Representation of one datatype. |
| * @typedef Datatype |
| * @type{object} |
| * @property{string} name Name of the datatype. |
| * @property{function():boolean} supported |
| * Whether this datatype is supported by this user agent. |
| * @method{function():Void} add A function to add an instance of the datatype. |
| * @method{function():boolean} isEmpty A function that tests whether |
| * the datatype's storage backend is empty. |
| */ |
| var Datatype; |
| |
| var TestUtils = {}; |
| |
| /** |
| * Various storage backends that are part of the 'storage' datatype. |
| * @param{Array.<Datatype>} |
| */ |
| TestUtils.STORAGE = [ |
| { |
| "name": "local storage", |
| "supported": function() { !!window.localStorage; }, |
| "add": function() { |
| return new Promise(function(resolve, reject) { |
| localStorage.setItem(randomString(), randomString()); |
| resolve(); |
| }); |
| }, |
| "isEmpty": function() { |
| return new Promise(function(resolve, reject) { |
| resolve(!localStorage.length); |
| }); |
| } |
| }, |
| { |
| "name": "Indexed DB", |
| "supported": function() { return !!window.indexedDB; }, |
| "add": function() { |
| return new Promise(function(resolve, reject) { |
| var request = window.indexedDB.open("database"); |
| request.onupgradeneeded = function() { |
| request.result.createObjectStore("store"); |
| }; |
| request.onsuccess = function() { |
| request.result.close(); |
| resolve(); |
| } |
| }); |
| }, |
| "isEmpty": function() { |
| return new Promise(function(resolve, reject) { |
| var request = window.indexedDB.open("database"); |
| request.onsuccess = function() { |
| var database = request.result; |
| try { |
| var transaction = database.transaction(["store"]); |
| resolve(false); |
| } catch(error) { |
| // The database is empty. However, by testing that, we have also |
| // created it, which means that |onupgradeneeded| in the "add" |
| // method will not run the next time. Delete the database before |
| // reporting that it was empty. |
| var deletion = window.indexedDB.deleteDatabase("database"); |
| deletion.onsuccess = resolve.bind(this, true); |
| } finally { |
| database.close(); |
| } |
| }; |
| }); |
| } |
| }, |
| { |
| // TODO(@msramek): We should also test the PERSISTENT filesystem, however, |
| // that might require storage permissions. |
| "name": "filesystems", |
| "supported": function() { |
| return window.requestFileSystem || window.webkitRequestFileSystem; |
| }, |
| "add": function() { |
| return new Promise(function(resolve, reject) { |
| var onSuccess = function(fileSystem) { |
| fileSystem.root.getFile('file', {"create": true}, resolve, resolve); |
| } |
| var onFailure = resolve; |
| |
| var requestFileSystem = |
| window.requestFileSystem || window.webkitRequestFileSystem; |
| requestFileSystem(window.TEMPORARY, 1 /* 1B */, |
| onSuccess, onFailure); |
| }); |
| }, |
| "isEmpty": function() { |
| return new Promise(function(resolve, reject) { |
| var onSuccess = function(fileSystem) { |
| fileSystem.root.getFile( |
| 'file', {}, |
| resolve.bind(this, false) /* opened successfully */, |
| resolve.bind(this, true) /* failed to open */); |
| } |
| var onFailure = resolve.bind(this, true); |
| |
| var requestFileSystem = |
| window.requestFileSystem || window.webkitRequestFileSystem; |
| requestFileSystem(window.TEMPORARY, 1 /* 1B */, |
| onSuccess, onFailure); |
| }); |
| } |
| }, |
| { |
| "name": "service workers", |
| "supported": function() { return !!navigator.serviceWorker; }, |
| "add": function() { |
| return navigator.serviceWorker.register( |
| "support/service_worker.js", |
| { scope: "support/"}); |
| }, |
| "isEmpty": function() { |
| return new Promise(function(resolve, reject) { |
| navigator.serviceWorker.getRegistrations() |
| .then(function(registrations) { |
| resolve(!registrations.length); |
| }); |
| }); |
| } |
| }, |
| { |
| "name": "WebSQL", |
| "supported": function() { return !!window.openDatabase; }, |
| "add": function() { |
| return new Promise(function(resolve, reject) { |
| var database = window.openDatabase( |
| "database", "1.0", "database", 1024 /* 1 kB */); |
| database.transaction(function(context) { |
| context.executeSql("CREATE TABLE IF NOT EXISTS data (column)"); |
| context.executeSql( |
| "INSERT INTO data (column) VALUES (1)", [], resolve); |
| }); |
| }); |
| }, |
| "isEmpty": function() { |
| return new Promise(function(resolve, reject) { |
| var database = window.openDatabase( |
| "database", "1.0", "database", 1024 /* 1 kB */); |
| database.transaction(function(context) { |
| context.executeSql("CREATE TABLE IF NOT EXISTS data (column)"); |
| context.executeSql( |
| "SELECT * FROM data", [], |
| function(transaction, result) { |
| resolve(!result.rows.length); |
| }); |
| }); |
| }); |
| } |
| } |
| ].filter(function(backend) { return backend.supported(); }); |
| |
| /** |
| * All datatypes supported by Clear-Site-Data. |
| * @param{Array.<Datatype>} |
| */ |
| TestUtils.DATATYPES = [ |
| { |
| "name": "cookies", |
| "supported": function() { return typeof document.cookie == "string"; }, |
| "add": function() { |
| return new Promise(function(resolve, reject) { |
| document.cookie = randomString() + "=" + randomString(); |
| resolve(); |
| }); |
| }, |
| "isEmpty": function() { |
| return new Promise(function(resolve, reject) { |
| resolve(!document.cookie); |
| }); |
| } |
| }, |
| { |
| "name": "storage", |
| "supported": TestUtils.STORAGE[0].supported, |
| "add": TestUtils.STORAGE[0].add, |
| "isEmpty": TestUtils.STORAGE[0].isEmpty, |
| } |
| ].filter(function(datatype) { return datatype.supported(); }); |
| |
| /** |
| * All possible combinations of datatypes. |
| * @property {Array.<Array.<Datatype>>} |
| */ |
| TestUtils.COMBINATIONS = (function() { |
| var combinations = []; |
| for (var mask = 0; mask < (1 << TestUtils.DATATYPES.length); mask++) { |
| var combination = []; |
| |
| for (var datatype = 0; |
| datatype < TestUtils.DATATYPES.length; datatype++) { |
| if (mask & (1 << datatype)) |
| combination.push(TestUtils.DATATYPES[datatype]); |
| } |
| |
| combinations.push(combination); |
| } |
| return combinations; |
| })(); |
| |
| /** |
| * Populates |datatypes| by calling the "add" method on each of them, |
| * and verifies that they are nonempty. |
| * @param {Array.<Datatype>} datatypes to be populated. |
| * @private |
| */ |
| function populate(datatypes) { |
| return Promise.all(datatypes.map(function(datatype) { |
| return new Promise(function(resolve, reject) { |
| datatype.add().then(function() { |
| datatype.isEmpty().then(function(isEmpty) { |
| assert_false( |
| isEmpty, |
| datatype.name + |
| " has to be nonempty before the test starts."); |
| resolve(); |
| }); |
| }); |
| }); |
| })); |
| }; |
| |
| /** |
| * Ensures that all datatypes are nonempty. Should be called in the test |
| * setup phase. |
| */ |
| TestUtils.populateDatatypes = populate.bind(this, TestUtils.DATATYPES); |
| |
| /** |
| * Ensures that all backends of the "storage" datatype are nonempty. Should |
| * be called in the test setup phase. |
| */ |
| TestUtils.populateStorage = populate.bind(this, TestUtils.STORAGE); |
| |
| /** |
| * Get the support server URL that returns a Clear-Site-Data header |
| * to clear |datatypes|. |
| * @param{Array.<Datatype>} datatypes The list of datatypes to be deleted. |
| * @return string The URL to be queried. |
| */ |
| TestUtils.getClearSiteDataUrl = function(datatypes) { |
| names = datatypes.map(function(e) { return e.name }); |
| return "support/echo-clear-site-data.py?" + names.join("&"); |
| } |
| |
| /** |
| * @param{string} page_scheme Scheme of the page. "http" or "https". |
| * @param{string} resource_scheme Scheme of the resource. "http" or "https". |
| * @return The URL of a page that contains a resource requesting the deletion |
| * of storage. |
| */ |
| TestUtils.getPageWithResourceUrl = function(page_scheme, resource_scheme) { |
| if (page_scheme != "https" && page_scheme != "http") |
| throw "Unsupported scheme: " + page_scheme; |
| if (resource_scheme != "https" && resource_scheme != "http") |
| throw "Unsupported scheme: " + resource_scheme; |
| return page_scheme + "://{{domains[]}}:" + |
| (page_scheme == "https" ? {{ports[https][0]}} : {{ports[http][0]}}) + |
| "/clear-site-data/support/page_with_resource.sub.html?scheme=" + |
| resource_scheme; |
| } |
| |
| return TestUtils; |
| })(); |