/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;

Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");

XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
                                   "@mozilla.org/childprocessmessagemanager;1",
                                   "nsISyncMessageSender");

function debug(aMsg) {
  //dump("-- ActivityProxy " + Date.now() + " : " + aMsg + "\n");
}

/**
  * nsIActivityProxy implementation
  * We keep a reference to the C++ Activity object, and
  * communicate with the Message Manager to know when to
  * fire events on it.
  */
function ActivityProxy() {
  debug("ActivityProxy");
  this.activity = null;
  let inParent = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime)
                   .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
  debug("inParent: " + inParent);
  Cu.import(inParent ? "resource://gre/modules/Webapps.jsm"
                     : "resource://gre/modules/AppsServiceChild.jsm");
}

ActivityProxy.prototype = {
  startActivity: function actProxy_startActivity(aActivity, aOptions, aWindow,
                                                 aChildID) {
    debug("startActivity");

    this.window = aWindow;
    this.activity = aActivity;
    this.id = Cc["@mozilla.org/uuid-generator;1"]
                .getService(Ci.nsIUUIDGenerator)
                .generateUUID().toString();
    // Retrieve the app's manifest url from the principal, so that we can
    // later notify when the activity handler called postResult or postError
    let principal = aWindow.document.nodePrincipal;
    let appId = principal.appId;
    let manifestURL = (appId != Ci.nsIScriptSecurityManager.NO_APP_ID &&
                       appId != Ci.nsIScriptSecurityManager.UNKNOWN_APP_ID)
                        ? DOMApplicationRegistry.getManifestURLByLocalId(appId)
                        : null;

    // Only let certified apps enumerate providers for this filter.
    if (aOptions.getFilterResults === true &&
        principal.appStatus != Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
      Services.DOMRequest.fireErrorAsync(this.activity, "SecurityError");
      Services.obs.notifyObservers(null, "Activity:Error", null);
      return;
    }

    // Only let certified app to initiate this activitiy.
    if (aOptions.name === 'internal-system-engineering-mode' &&
        principal.appStatus != Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
      Services.DOMRequest.fireErrorAsync(this.activity, "SecurityError");
      Services.obs.notifyObservers(null, "Activity:Error", null);
      return;
    }

    // Check the activities that are restricted to be used in dev mode.
    let devMode = false;
    let isDevModeActivity = false;
    try {
      devMode = Services.prefs.getBoolPref("dom.apps.developer_mode");
      isDevModeActivity =
        Services.prefs.getCharPref("dom.activities.developer_mode_only")
                      .split(",").indexOf(aOptions.name) !== -1;

    } catch(e) {}
    if (isDevModeActivity && !devMode) {
      Services.DOMRequest.fireErrorAsync(this.activity, "SecurityError");
      Services.obs.notifyObservers(null, "Activity:Error", null);
      return;
    }

    cpmm.addMessageListener("Activity:FireSuccess", this);
    cpmm.addMessageListener("Activity:FireError", this);

    cpmm.sendAsyncMessage("Activity:Start",
      {
        id: this.id,
        options: {
          name: aOptions.name,
          data: aOptions.data
        },
        getFilterResults: aOptions.getFilterResults,
        manifestURL: manifestURL,
        pageURL: aWindow.document.location.href,
        childID: aChildID });
  },

  receiveMessage: function actProxy_receiveMessage(aMessage) {
    debug("Got message: " + aMessage.name);
    let msg = aMessage.json;
    if (msg.id != this.id)
      return;
    debug("msg=" + JSON.stringify(msg));

    switch(aMessage.name) {
      case "Activity:FireSuccess":
        debug("FireSuccess");
        Services.DOMRequest.fireSuccess(this.activity,
                                        Cu.cloneInto(msg.result, this.window));
        Services.obs.notifyObservers(null, "Activity:Success", null);
        break;
      case "Activity:FireError":
        debug("FireError");
        Services.DOMRequest.fireError(this.activity, msg.error);
        Services.obs.notifyObservers(null, "Activity:Error", null);
        break;
    }
    // We can only get one FireSuccess / FireError message, so cleanup as soon as possible.
    this.cleanup();
  },

  cleanup: function actProxy_cleanup() {
    debug("cleanup");
    if (cpmm && !this.cleanedUp) {
      cpmm.removeMessageListener("Activity:FireSuccess", this);
      cpmm.removeMessageListener("Activity:FireError", this);
    }
    this.cleanedUp = true;
  },

  classID: Components.ID("{ba9bd5cb-76a0-4ecf-a7b3-d2f7c43c5949}"),
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIActivityProxy])
}

this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ActivityProxy]);
