/** Test for Bug 815105 **/
/*
 * gData is an array of object that tests using this framework must pass in
 * The current tests only pass in a single element array. Each test in
 * gData is executed by the framework for a given file
 *
 * Fields in gData object
 * perms   (required) Array of Strings
 *         list of permissions that this test will need. See
 *         http://mxr.mozilla.org/mozilla-central/source/dom/apps/src/PermissionsTable.jsm
 *         These permissions are added after a sanity check and removed at
 *         test conclusion
 *
 * obj     (required for default verifier) String
 *         The name of the window.navigator object used for accessing the
 *         WebAPI during the tests
 *
 * webidl  (required for default verifier) String
 * idl     (required for default verifier) String
 *         Only one of webidl / idl is required
 *         The IDL describing the navigator object. The returned object
 *         during tests /must/ be an instanceof this
 *
 * skip    (optional) Array of Strings
 *         A list of navigator.userAgent's to skip the second part of tests
 *         on. The tests still verify that you can't get obj on those
 *         platforms without permissions, however it is expected that adding
 *         the permission still won't allow access to those objects
 *
 * settings (optional) Array of preference tuples
 *         A list of settings that need to be set before this API is
 *         enabled. Note the settings are set before the sanity check is
 *         performed. If an API gates access only by preferences, then it
 *         will fail the initial test
 *
 * verifier (optional) Function
 *         A function used to test whether a WebAPI is accessible or not.
 *         The function takes a success and failure callback which both
 *         accept a msg argument. msg is surfaced up to the top level tests
 *         A default verifier is provided which only attempts to access
 *         the navigator object.
 *
 * needParentPerm (optional) Boolean
 *         Whether or not the parent frame requires these permissions as
 *         well. Otherwise the test process may be killed.
 */

SimpleTest.waitForExplicitFinish();
var expand = SpecialPowers.Cu.import("resource://gre/modules/PermissionsTable.jsm").expandPermissions;
const permTable = SpecialPowers.Cu.import("resource://gre/modules/PermissionsTable.jsm").PermissionsTable;

const TEST_DOMAIN = "http://example.org";
const SHIM_PATH = "/tests/dom/permission/tests/file_shim.html"
var gContent = document.getElementById('content');

//var gData; defined in external files
var gCurrentTest = 0;
var gRemainingTests;
var pendingTests = {};

function PermTest(aData) {
  var self = this;
  var skip = aData.skip || false;
  this.step = 0;
  this.data = aData;
  this.isSkip = skip &&
                skip.some(function (el) {
                          return navigator.
                                 userAgent.toLowerCase().
                                 indexOf(el.toLowerCase()) != -1;
                        });

  this.setupParent = false;
  this.perms = expandPermissions(aData.perm);
  this.id = gCurrentTest++;
  this.iframe = null;

  // keep a reference to this for eventhandler
  pendingTests[this.id] = this;

  this.createFrame = function() {
    if (self.iframe) {
      gContent.removeChild(self.iframe);
    }
    var iframe = document.createElement('iframe');
    iframe.setAttribute('id', 'testframe' + self.step + self.perms)
    iframe.setAttribute('remote', true);
    iframe.src = TEST_DOMAIN + SHIM_PATH;
    iframe.addEventListener('load', function _iframeLoad() {
      iframe.removeEventListener('load', _iframeLoad);

      // check permissions are correct
      var allow = (self.step == 0 ? false : true);
      self.perms.forEach(function (el) {
        try {
        var res = SpecialPowers.hasPermission(el, SpecialPowers.wrap(iframe)
                                                  .contentDocument);
        is(res, allow, (allow ? "Has " : "Doesn't have ") + el);
        } catch(e) {
          ok(false, "failed " + e);
        }
      });

      var msg = {
        id: self.id,
        step: self.step++,
        testdata: self.data,
      }
      // start the tests
      iframe.contentWindow.postMessage(msg, "*");
    });

    self.iframe = iframe;
    gContent.appendChild(iframe);
  }

  this.next = function () {
    switch(self.step) {
    case 0:
      self.createFrame();
    break;
    case 1:
      // add permissions
      addPermissions(self.perms, SpecialPowers.
                                 wrap(self.iframe).
                                 contentDocument,
                     self.createFrame.bind(self));
    break;
    case 2:
      if (self.iframe) {
        gContent.removeChild(self.iframe);
      }
      checkFinish();
    break;
    default:
      ok(false, "Should not be reached");
    break
    }
  }

  this.start = function() {
    // some permissions need parent to have permission as well
    if (!self.setupParent && self.data.needParentPerm &&
        !SpecialPowers.isMainProcess()) {
      self.setupParent = true;
      addPermissions(self.perms, window.document, self.start.bind(self));
    } else if (self.data.settings && self.data.settings.length) {
      SpecialPowers.pushPrefEnv({'set': self.data.settings.slice(0)},
                                self.next.bind(self));
    } else {
      self.next();
    }
  }
}

function addPermissions(aPerms, aDoc, aCallback) {
  var permList = [];
  aPerms.forEach(function (el) {
    var obj = {'type': el,
               'allow': 1,
               'context': aDoc};
    permList.push(obj);
  });
  SpecialPowers.pushPermissions(permList, aCallback);
}

function expandPermissions(aPerms) {
  var perms = [];
  aPerms.forEach(function(el) {
    var access = permTable[el].access ? "readwrite" : null;
    var expanded = SpecialPowers.unwrap(expand(el, access));
    perms = perms.concat(expanded.slice(0));
  });

  return perms;
}

function msgHandler(evt) {
  var data = evt.data;
  var test = pendingTests[data.id];

  /*
   * step 2 of tests should fail on
   * platforms which are skipped
   */
  if (test.isSkip && test.step == 2) {
    todo(data.result, data.msg);
  } else {
    ok(data.result, data.msg);
  }

  if (test) {
    test.next();
  } else {
    ok(false, "Received unknown id " + data.id);
    checkFinish();
  }
}

function checkFinish() {
  if (--gRemainingTests) {
    gTestRunner.next();
  } else {
    window.removeEventListener('message', msgHandler);
    SimpleTest.finish();
  }
}

function runTest() {
  gRemainingTests = Object.keys(gData).length;

  for (var test in gData) {
    var test = new PermTest(gData[test]);
    test.start();
    yield undefined;
  }
}

var gTestRunner = runTest();

window.addEventListener('load', function() { gTestRunner.next(); }, false);
window.addEventListener('message', msgHandler, false);
