//
// Helper function for URI verification
//
static nsresult
InstallTriggerCheckLoadURIFromScript(JSContext *cx, const nsAString& uriStr)
{
    nsresult rv;
    nsCOMPtr<nsIScriptSecurityManager> secman(
        do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID,&rv));
    NS_ENSURE_SUCCESS(rv, rv);

    // get the script principal
    nsCOMPtr<nsIPrincipal> principal;
    rv = secman->GetSubjectPrincipal(getter_AddRefs(principal));
    NS_ENSURE_SUCCESS(rv, rv);
    if (!principal)
        return NS_ERROR_FAILURE;

    // convert the requested URL string to a URI
    // Note that we use a null base URI here, since that's what we use when we
    // actually convert the string into a URI to load.
    nsCOMPtr<nsIURI> uri;
    rv = NS_NewURI(getter_AddRefs(uri), uriStr);
    NS_ENSURE_SUCCESS(rv, rv);

    // are we allowed to load this one?
    rv = secman->CheckLoadURIWithPrincipal(principal, uri,
                    nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL);
    return rv;
}
NS_IMETHODIMP
sbSecurityMixin::GetCodebase(nsIURI **aCodebase) {
    NS_ENSURE_ARG_POINTER(aCodebase);

    // Get the current domain.
    nsresult rv;
    nsCOMPtr<nsIScriptSecurityManager> secman( do_GetService( NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv ) );
    NS_ENSURE_SUCCESS( rv, rv );
    nsCOMPtr<nsIPrincipal> principal;
    secman->GetSubjectPrincipal( getter_AddRefs(principal) );

    if (!principal) {
        LOG(("sbSecurityMixin::GetCodebase -- Error: No Subject Principal."));
        *aCodebase = nsnull;
        return NS_OK;
    }
    LOG(("sbSecurityMixin::GetCodebase -- Have Subject Principal."));

#ifdef PR_LOGGING
    nsCOMPtr<nsIPrincipal> systemPrincipal;
    secman->GetSystemPrincipal( getter_AddRefs(systemPrincipal) );

    if (principal == systemPrincipal) {
        LOG(("sbSecurityMixin::GetCodebase -- System Principal."));
    } else {
        LOG(("sbSecurityMixin::GetCodebase -- Not System Principal."));
    }
#endif

    nsCOMPtr<nsIURI> codebase;
    principal->GetDomain( getter_AddRefs(codebase) );

    if (!codebase) {
        LOG(("sbSecurityMixin::GetCodebase -- no codebase from domain, getting it from URI."));
        principal->GetURI( getter_AddRefs(codebase) );
    }

    *aCodebase = codebase;
    NS_IF_ADDREF(*aCodebase);
    return NS_OK;
}
//
// Native method Install
//
static JSBool
InstallTriggerGlobalInstall(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
  nsIDOMInstallTriggerGlobal *nativeThis = getTriggerNative(cx, obj);
  if (!nativeThis)
    return JS_FALSE;

  *rval = JSVAL_FALSE;

  // make sure XPInstall is enabled, return false if not
  nsIScriptGlobalObject *globalObject = nsnull;
  nsIScriptContext *scriptContext = GetScriptContextFromJSContext(cx);
  if (scriptContext)
    globalObject = scriptContext->GetGlobalObject();

  if (!globalObject)
      return JS_TRUE;

  nsCOMPtr<nsIScriptSecurityManager> secman(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID));
  if (!secman)
  {
    JS_ReportError(cx, "Could not the script security manager service.");
    return JS_FALSE;
  }
  // get the principal.  if it doesn't exist, die.
  nsCOMPtr<nsIPrincipal> principal;
  secman->GetSubjectPrincipal(getter_AddRefs(principal));
  if (!principal)
  {
    JS_ReportError(cx, "Could not get the Subject Principal during InstallTrigger.Install()");
    return JS_FALSE;
  }

  // get window.location to construct relative URLs
  nsCOMPtr<nsIURI> baseURL;
  JSObject* global = JS_GetGlobalObject(cx);
  if (global)
  {
    jsval v;
    if (JS_GetProperty(cx,global,"location",&v))
    {
      nsAutoString location;
      ConvertJSValToStr( location, cx, v );
      NS_NewURI(getter_AddRefs(baseURL), location);
    }
  }

  PRBool abortLoad = PR_FALSE;

  // parse associative array of installs
  if ( argc >= 1 && JSVAL_IS_OBJECT(argv[0]) && JSVAL_TO_OBJECT(argv[0]) )
  {
    nsXPITriggerInfo *trigger = new nsXPITriggerInfo();
    if (!trigger)
      return JS_FALSE;

    trigger->SetPrincipal(principal);

    JSIdArray *ida = JS_Enumerate( cx, JSVAL_TO_OBJECT(argv[0]) );
    if ( ida )
    {
      jsval v;
      const PRUnichar *name, *URL;
      const PRUnichar *iconURL = nsnull;

      for (int i = 0; i < ida->length && !abortLoad; i++ )
      {
        JS_IdToValue( cx, ida->vector[i], &v );
        JSString * str = JS_ValueToString( cx, v );
        if (!str)
        {
          abortLoad = PR_TRUE;
          break;
        }

        name = reinterpret_cast<const PRUnichar*>(JS_GetStringChars( str ));

        URL = iconURL = nsnull;
        JSAutoByteString hash;
        JS_GetUCProperty( cx, JSVAL_TO_OBJECT(argv[0]), reinterpret_cast<const jschar*>(name), nsCRT::strlen(name), &v );
        if ( JSVAL_IS_OBJECT(v) && JSVAL_TO_OBJECT(v) )
        {
          jsval v2;
          if (JS_GetProperty( cx, JSVAL_TO_OBJECT(v), "URL", &v2 ) && !JSVAL_IS_VOID(v2)) {
            JSString *str = JS_ValueToString(cx, v2);
            if (!str) {
              abortLoad = PR_TRUE;
              break;
            }
            URL = reinterpret_cast<const PRUnichar*>(JS_GetStringChars(str));
          }

          if (JS_GetProperty( cx, JSVAL_TO_OBJECT(v), "IconURL", &v2 ) && !JSVAL_IS_VOID(v2)) {
            JSString *str = JS_ValueToString(cx, v2);
            if (!str) {
              abortLoad = PR_TRUE;
              break;
            }
            iconURL = reinterpret_cast<const PRUnichar*>(JS_GetStringChars(str));
          }

          if (JS_GetProperty( cx, JSVAL_TO_OBJECT(v), "Hash", &v2) && !JSVAL_IS_VOID(v2)) {
            JSString *str = JS_ValueToString(cx, v2);
            if (!str || !hash.encode(cx, str)) {
              abortLoad = PR_TRUE;
              break;
            }
          }
        }
        else
        {
          JSString *str = JS_ValueToString(cx, v);
          if (!str) {
            abortLoad = PR_TRUE;
            break;
          }
          URL = reinterpret_cast<const PRUnichar*>(JS_GetStringChars(str));
        }

        if ( URL )
        {
            // Get relative URL to load
            nsAutoString xpiURL(URL);
            if (baseURL)
            {
                nsCAutoString resolvedURL;
                baseURL->Resolve(NS_ConvertUTF16toUTF8(xpiURL), resolvedURL);
                xpiURL = NS_ConvertUTF8toUTF16(resolvedURL);
            }

            nsAutoString icon(iconURL);
            if (iconURL && baseURL)
            {
                nsCAutoString resolvedIcon;
                baseURL->Resolve(NS_ConvertUTF16toUTF8(icon), resolvedIcon);
                icon = NS_ConvertUTF8toUTF16(resolvedIcon);
            }

            // Make sure we're allowed to load this URL and the icon URL
            nsresult rv = InstallTriggerCheckLoadURIFromScript(cx, xpiURL);
            if (NS_FAILED(rv))
                abortLoad = PR_TRUE;

            if (!abortLoad && iconURL)
            {
                rv = InstallTriggerCheckLoadURIFromScript(cx, icon);
                if (NS_FAILED(rv))
                    abortLoad = PR_TRUE;
            }

            if (!abortLoad)
            {
                // Add the install item to the trigger collection
                nsXPITriggerItem *item =
                    new nsXPITriggerItem( name, xpiURL.get(), icon.get(), hash );
                if ( item )
                {
                    trigger->Add( item );
                }
                else
                    abortLoad = PR_TRUE;
            }
        }
        else
            abortLoad = PR_TRUE;
      }
      JS_DestroyIdArray( cx, ida );
    }


    // pass on only if good stuff found
    if (!abortLoad && trigger->Size() > 0)
    {
        nsCOMPtr<nsIURI> checkuri;
        nsresult rv = nativeThis->GetOriginatingURI(globalObject,
                                                    getter_AddRefs(checkuri));
        if (NS_SUCCEEDED(rv))
        {
            nsCOMPtr<nsIDOMWindowInternal> win(do_QueryInterface(globalObject));
            nsCOMPtr<nsIXPIInstallInfo> installInfo =
                new nsXPIInstallInfo(win, checkuri, trigger, 0);
            if (installInfo)
            {
                // installInfo now owns triggers
                PRBool enabled = PR_FALSE;
                nativeThis->UpdateEnabled(checkuri, XPI_WHITELIST, &enabled);
                if (!enabled)
                {
                    nsCOMPtr<nsIObserverService> os =
                      mozilla::services::GetObserverService();
                    if (os)
                        os->NotifyObservers(installInfo,
                                            "xpinstall-install-blocked",
                                            nsnull);
                }
                else
                {
                    // save callback function if any (ignore bad args for now)
                    if ( argc >= 2 && JS_TypeOfValue(cx,argv[1]) == JSTYPE_FUNCTION )
                    {
                        trigger->SaveCallback( cx, argv[1] );
                    }

                    PRBool result;
                    nativeThis->StartInstall(installInfo, &result);
                    *rval = BOOLEAN_TO_JSVAL(result);
                }
                return JS_TRUE;
            }
        }
    }
    // didn't pass it on so we must delete trigger
    delete trigger;
  }

  JS_ReportError(cx, "Incorrect arguments to InstallTrigger.Install()");
  return JS_FALSE;
}