chromium/chrome/test/data/webrtc/indexeddb.js

/**
 * Copyright 2016 The Chromium Authors
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

/** @private */
var kDatabaseName = 'WebRTC-Database';

/**
 * The one and only |IDBDatabase| in this page.
 * @private
 */
var gDatabase = null;

/**
 * Set by |generateAndCloneCertificate|.
 */
var gCertificate = null;
var gCertificateClone = null;

// Public interface to tests.

function openDatabase() {
  if (gDatabase !== null)
    throw new Error('The database is already open.');
  var reqOpen = indexedDB.open(kDatabaseName);
  reqOpen.onupgradeneeded = function() {
    // This happens before |onsuccess| if the database is new or its version is
    // updated. Create object stores.
    var db = reqOpen.result;
    var certStore = db.createObjectStore('certificates', { keyPath: 'id' });
  };
  return new Promise((resolve, reject) => {
    reqOpen.onsuccess = function() {
      if (gDatabase !== null)
        return reject(new Error('The database is already open.'));
      gDatabase = reqOpen.result;
      return resolve(logAndReturn('ok-database-opened'));
    }
    reqOpen.onerror = function() {
      reject(new Error('The database could not be opened. Error: ' +
        reqOpen.error));
    }
  });
}

function closeDatabase() {
  if (gDatabase === null)
    throw new Error('The database is already closed.');
  gDatabase.close();
  gDatabase = null;
  return logAndReturn('ok-database-closed');
}

function deleteDatabase() {
  if (gDatabase !== null)
    throw new Error('The database should be closed before deleting.');
  var reqDelete = indexedDB.deleteDatabase(kDatabaseName);
  return new Promise((resolve, reject) => {
    reqDelete.onsuccess = function () {
      resolve(logAndReturn('ok-database-deleted'));
    };
    reqDelete.onerror = function () {
      reject(new Error('The database could not be deleted. Error: '
        + reqDelete.error));
    };
  });
}

/**
 * Generates a certificate and clones it by saving and loading it to the
 * database (requires database to be open, see |openDatabase|). After returning
 * successfully to the test, the global variables |gCertificate| and
 * |gCertificateClone| have been set.
 * @param {!Object} keygenAlgorithm An |AlgorithmIdentifier| to be used as
 * parameter to |RTCPeerConnection.generateCertificate|. The resulting
 * certificate will be used by the peer connection.
 */
function generateAndCloneCertificate(keygenAlgorithm) {
  return RTCPeerConnection.generateCertificate(keygenAlgorithm).then(
      function(certificate) {
        gCertificate = certificate;
        if (gCertificate.getFingerprints().length == 0)
          throw new Error('getFingerprints() is empty.');
        for (let i = 0; i < gCertificate.getFingerprints().length; ++i) {
          if (gCertificate.getFingerprints()[i].algorithm != 'sha-256')
            throw new Error('Unexpected fingerprint algorithm.');
          if (gCertificate.getFingerprints()[i].value.length == 0)
            throw new Error('Unexpected fingerprint value.');
        }
        return cloneCertificate_(gCertificate).then(
            function(clone) {
              let cloneIsEqual = (clone.getFingerprints().length ==
                                  gCertificate.getFingerprints().length);
              if (cloneIsEqual) {
                for (let i = 0; i < clone.getFingerprints().length; ++i) {
                  if (clone.getFingerprints()[i].algorithm !=
                      gCertificate.getFingerprints()[i].algorithm ||
                      clone.getFingerprints()[i].value !=
                      gCertificate.getFingerprints()[i].value) {
                    cloneIsEqual = false;
                    break;
                  }
                }
              }
              if (!cloneIsEqual) {
                throw new Error('The cloned certificate\'s fingerprints does ' +
                               'not match the original certificate.');
              }

              gCertificateClone = clone;
              return logAndReturn('ok-generated-and-cloned');
            },
            function() {
              throw new Error('Error cloning certificate.');
            });
      },
      function() {
        throw new Error('Certificate generation failed. keygenAlgorithm: ' +
            JSON.stringify(keygenAlgorithm));
      });
}

// Internals.

/** @private */
function saveCertificate_(certificate) {
  return new Promise(function(resolve, reject) {
    if (gDatabase === null)
      throw new Error('The database is not open.');

    var certTrans = gDatabase.transaction('certificates', 'readwrite');
    var certStore = certTrans.objectStore('certificates');
    var certPut = certStore.put({
      id:0,
      cert:certificate
    });

    certPut.onsuccess = function() {
      resolve();
    };
    certPut.onerror = function() {
      reject(certPut.error);
    };
  });
}

/** @private */
function loadCertificate_() {
  return new Promise(function(resolve, reject) {
    if (gDatabase === null)
      throw new Error('The database is not open.');

    var certTrans = gDatabase.transaction('certificates', 'readonly');
    var certStore = certTrans.objectStore('certificates');

    var reqGet = certStore.get(0);
    reqGet.onsuccess = function() {
      var match = reqGet.result;
      if (match !== undefined) {
        resolve(match.cert);
      } else {
        resolve(null);
      }
    };
    reqGet.onerror = function() {
      reject(reqGet.error);
    };
  });
}

/** @private */
function cloneCertificate_(certificate) {
  return saveCertificate_(certificate)
    .then(loadCertificate_)
    .then(function(clone) {
      // Save + load successful.
      if (clone === null)
        new Error('loadCertificate returned a null certificate.');
      return clone;
    });
}