extensions/cookie/nsPermissionManager.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/extensions/cookie/nsPermissionManager.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1943 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "mozilla/Attributes.h"
    1.10 +#include "mozilla/DebugOnly.h"
    1.11 +
    1.12 +#include "mozilla/dom/ContentParent.h"
    1.13 +#include "mozilla/dom/ContentChild.h"
    1.14 +#include "mozilla/unused.h"
    1.15 +#include "nsPermissionManager.h"
    1.16 +#include "nsPermission.h"
    1.17 +#include "nsCRT.h"
    1.18 +#include "nsNetUtil.h"
    1.19 +#include "nsCOMArray.h"
    1.20 +#include "nsArrayEnumerator.h"
    1.21 +#include "nsTArray.h"
    1.22 +#include "nsReadableUtils.h"
    1.23 +#include "nsILineInputStream.h"
    1.24 +#include "nsIIDNService.h"
    1.25 +#include "nsAppDirectoryServiceDefs.h"
    1.26 +#include "prprf.h"
    1.27 +#include "mozilla/storage.h"
    1.28 +#include "mozilla/Attributes.h"
    1.29 +#include "nsXULAppAPI.h"
    1.30 +#include "nsIPrincipal.h"
    1.31 +#include "nsContentUtils.h"
    1.32 +#include "nsIScriptSecurityManager.h"
    1.33 +#include "nsIAppsService.h"
    1.34 +#include "mozIApplication.h"
    1.35 +#include "nsIEffectiveTLDService.h"
    1.36 +#include "nsPIDOMWindow.h"
    1.37 +#include "nsIDocument.h"
    1.38 +#include "nsCOMPtr.h"
    1.39 +#include "nsIPrefService.h"
    1.40 +#include "nsIPrefBranch.h"
    1.41 +#include "nsIPrefBranch2.h"
    1.42 +#include "mozilla/net/NeckoMessageUtils.h"
    1.43 +
    1.44 +static nsPermissionManager *gPermissionManager = nullptr;
    1.45 +
    1.46 +using mozilla::dom::ContentParent;
    1.47 +using mozilla::dom::ContentChild;
    1.48 +using mozilla::unused; // ha!
    1.49 +
    1.50 +static bool
    1.51 +IsChildProcess()
    1.52 +{
    1.53 +  return XRE_GetProcessType() == GeckoProcessType_Content;
    1.54 +}
    1.55 +
    1.56 +/**
    1.57 + * @returns The child process object, or if we are not in the child
    1.58 + *          process, nullptr.
    1.59 + */
    1.60 +static ContentChild*
    1.61 +ChildProcess()
    1.62 +{
    1.63 +  if (IsChildProcess()) {
    1.64 +    ContentChild* cpc = ContentChild::GetSingleton();
    1.65 +    if (!cpc)
    1.66 +      NS_RUNTIMEABORT("Content Process is nullptr!");
    1.67 +    return cpc;
    1.68 +  }
    1.69 +
    1.70 +  return nullptr;
    1.71 +}
    1.72 +
    1.73 +
    1.74 +#define ENSURE_NOT_CHILD_PROCESS_(onError) \
    1.75 +  PR_BEGIN_MACRO \
    1.76 +  if (IsChildProcess()) { \
    1.77 +    NS_ERROR("Cannot perform action in content process!"); \
    1.78 +    onError \
    1.79 +  } \
    1.80 +  PR_END_MACRO
    1.81 +
    1.82 +#define ENSURE_NOT_CHILD_PROCESS \
    1.83 +  ENSURE_NOT_CHILD_PROCESS_({ return NS_ERROR_NOT_AVAILABLE; })
    1.84 +
    1.85 +#define ENSURE_NOT_CHILD_PROCESS_NORET \
    1.86 +  ENSURE_NOT_CHILD_PROCESS_(;)
    1.87 +
    1.88 +////////////////////////////////////////////////////////////////////////////////
    1.89 +
    1.90 +namespace {
    1.91 +
    1.92 +nsresult
    1.93 +GetPrincipal(const nsACString& aHost, uint32_t aAppId, bool aIsInBrowserElement,
    1.94 +             nsIPrincipal** aPrincipal)
    1.95 +{
    1.96 +  nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
    1.97 +  NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
    1.98 +
    1.99 +  nsCOMPtr<nsIURI> uri;
   1.100 +  nsresult rv = NS_NewURI(getter_AddRefs(uri), aHost);
   1.101 +  if (NS_FAILED(rv)) {
   1.102 +    // NOTE: most callers will end up here because we don't append "http://" for
   1.103 +    // hosts. It's fine to arbitrary use "http://" because, for those entries,
   1.104 +    // we will actually just use the host. If we end up here, but the host looks
   1.105 +    // like an email address, we use mailto: instead.
   1.106 +    nsCString scheme;
   1.107 +    if (aHost.FindChar('@') == -1)
   1.108 +      scheme = NS_LITERAL_CSTRING("http://");
   1.109 +    else
   1.110 +      scheme = NS_LITERAL_CSTRING("mailto:");
   1.111 +    rv = NS_NewURI(getter_AddRefs(uri), scheme + aHost);
   1.112 +    NS_ENSURE_SUCCESS(rv, rv);
   1.113 +  }
   1.114 +
   1.115 +  return secMan->GetAppCodebasePrincipal(uri, aAppId, aIsInBrowserElement, aPrincipal);
   1.116 +}
   1.117 +
   1.118 +nsresult
   1.119 +GetPrincipal(nsIURI* aURI, nsIPrincipal** aPrincipal)
   1.120 +{
   1.121 +  nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
   1.122 +  NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
   1.123 +
   1.124 +  return secMan->GetNoAppCodebasePrincipal(aURI, aPrincipal);
   1.125 +}
   1.126 +
   1.127 +nsresult
   1.128 +GetPrincipal(const nsACString& aHost, nsIPrincipal** aPrincipal)
   1.129 +{
   1.130 +  return GetPrincipal(aHost, nsIScriptSecurityManager::NO_APP_ID, false, aPrincipal);
   1.131 +}
   1.132 +
   1.133 +nsresult
   1.134 +GetHostForPrincipal(nsIPrincipal* aPrincipal, nsACString& aHost)
   1.135 +{
   1.136 +  nsCOMPtr<nsIURI> uri;
   1.137 +  nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
   1.138 +  NS_ENSURE_SUCCESS(rv, rv);
   1.139 +
   1.140 +  uri = NS_GetInnermostURI(uri);
   1.141 +  NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
   1.142 +
   1.143 +  rv = uri->GetAsciiHost(aHost);
   1.144 +  if (NS_SUCCEEDED(rv) && !aHost.IsEmpty()) {
   1.145 +    return NS_OK;
   1.146 +  }
   1.147 +
   1.148 +  // For the mailto scheme, we use the path of the URI. We have to chop off the
   1.149 +  // query part if one exists, so we eliminate everything after a ?.
   1.150 +  bool isMailTo = false;
   1.151 +  if (NS_SUCCEEDED(uri->SchemeIs("mailto", &isMailTo)) && isMailTo) {
   1.152 +    rv = uri->GetPath(aHost);
   1.153 +    NS_ENSURE_SUCCESS(rv, rv);
   1.154 +
   1.155 +    int32_t spart = aHost.FindChar('?', 0);
   1.156 +    if (spart >= 0) {
   1.157 +      aHost.Cut(spart, aHost.Length() - spart);
   1.158 +    }
   1.159 +    return NS_OK;
   1.160 +  }
   1.161 +
   1.162 +  // Some entries like "file://" uses the origin.
   1.163 +  rv = aPrincipal->GetOrigin(getter_Copies(aHost));
   1.164 +  if (NS_SUCCEEDED(rv) && !aHost.IsEmpty()) {
   1.165 +    return NS_OK;
   1.166 +  }
   1.167 +
   1.168 +  return NS_ERROR_UNEXPECTED;
   1.169 +}
   1.170 +
   1.171 +nsCString
   1.172 +GetNextSubDomainForHost(const nsACString& aHost)
   1.173 +{
   1.174 +  nsCOMPtr<nsIEffectiveTLDService> tldService =
   1.175 +    do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
   1.176 +  if (!tldService) {
   1.177 +    NS_ERROR("Should have a tld service!");
   1.178 +    return EmptyCString();
   1.179 +  }
   1.180 +
   1.181 +  nsCString subDomain;
   1.182 +  nsresult rv = tldService->GetNextSubDomain(aHost, subDomain);
   1.183 +  // We can fail if there is no more subdomain or if the host can't have a
   1.184 +  // subdomain.
   1.185 +  if (NS_FAILED(rv)) {
   1.186 +    return EmptyCString();
   1.187 +  }
   1.188 +
   1.189 +  return subDomain;
   1.190 +}
   1.191 +
   1.192 +class AppClearDataObserver MOZ_FINAL : public nsIObserver {
   1.193 +public:
   1.194 +  NS_DECL_ISUPPORTS
   1.195 +
   1.196 +  // nsIObserver implementation.
   1.197 +  NS_IMETHODIMP
   1.198 +  Observe(nsISupports *aSubject, const char *aTopic, const char16_t *data)
   1.199 +  {
   1.200 +    MOZ_ASSERT(!nsCRT::strcmp(aTopic, "webapps-clear-data"));
   1.201 +
   1.202 +    nsCOMPtr<mozIApplicationClearPrivateDataParams> params =
   1.203 +      do_QueryInterface(aSubject);
   1.204 +    if (!params) {
   1.205 +      NS_ERROR("'webapps-clear-data' notification's subject should be a mozIApplicationClearPrivateDataParams");
   1.206 +      return NS_ERROR_UNEXPECTED;
   1.207 +    }
   1.208 +
   1.209 +    uint32_t appId;
   1.210 +    nsresult rv = params->GetAppId(&appId);
   1.211 +    NS_ENSURE_SUCCESS(rv, rv);
   1.212 +
   1.213 +    bool browserOnly;
   1.214 +    rv = params->GetBrowserOnly(&browserOnly);
   1.215 +    NS_ENSURE_SUCCESS(rv, rv);
   1.216 +
   1.217 +    nsCOMPtr<nsIPermissionManager> permManager = do_GetService("@mozilla.org/permissionmanager;1");
   1.218 +    return permManager->RemovePermissionsForApp(appId, browserOnly);
   1.219 +  }
   1.220 +};
   1.221 +
   1.222 +NS_IMPL_ISUPPORTS(AppClearDataObserver, nsIObserver)
   1.223 +
   1.224 +static bool
   1.225 +IsExpandedPrincipal(nsIPrincipal* aPrincipal)
   1.226 +{
   1.227 +  nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal);
   1.228 +  return !!ep;
   1.229 +}
   1.230 +
   1.231 +} // anonymous namespace
   1.232 +
   1.233 +////////////////////////////////////////////////////////////////////////////////
   1.234 +
   1.235 +nsPermissionManager::PermissionKey::PermissionKey(nsIPrincipal* aPrincipal)
   1.236 +{
   1.237 +  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(GetHostForPrincipal(aPrincipal, mHost)));
   1.238 +  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aPrincipal->GetAppId(&mAppId)));
   1.239 +  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aPrincipal->GetIsInBrowserElement(&mIsInBrowserElement)));
   1.240 +}
   1.241 +
   1.242 +/**
   1.243 + * Simple callback used by |AsyncClose| to trigger a treatment once
   1.244 + * the database is closed.
   1.245 + *
   1.246 + * Note: Beware that, if you hold onto a |CloseDatabaseListener| from a
   1.247 + * |nsPermissionManager|, this will create a cycle.
   1.248 + *
   1.249 + * Note: Once the callback has been called this DeleteFromMozHostListener cannot
   1.250 + * be reused.
   1.251 + */
   1.252 +class CloseDatabaseListener MOZ_FINAL : public mozIStorageCompletionCallback
   1.253 +{
   1.254 +public:
   1.255 +  NS_DECL_ISUPPORTS
   1.256 +  NS_DECL_MOZISTORAGECOMPLETIONCALLBACK
   1.257 +  /**
   1.258 +   * @param aManager The owning manager.
   1.259 +   * @param aRebuildOnSuccess If |true|, reinitialize the database once
   1.260 +   * it has been closed. Otherwise, do nothing such.
   1.261 +   */
   1.262 +  CloseDatabaseListener(nsPermissionManager* aManager,
   1.263 +                        bool aRebuildOnSuccess);
   1.264 +
   1.265 +protected:
   1.266 +  nsRefPtr<nsPermissionManager> mManager;
   1.267 +  bool mRebuildOnSuccess;
   1.268 +};
   1.269 +
   1.270 +NS_IMPL_ISUPPORTS(CloseDatabaseListener, mozIStorageCompletionCallback)
   1.271 +
   1.272 +CloseDatabaseListener::CloseDatabaseListener(nsPermissionManager* aManager,
   1.273 +                                             bool aRebuildOnSuccess)
   1.274 +  : mManager(aManager)
   1.275 +  , mRebuildOnSuccess(aRebuildOnSuccess)
   1.276 +{
   1.277 +}
   1.278 +
   1.279 +NS_IMETHODIMP
   1.280 +CloseDatabaseListener::Complete(nsresult, nsISupports*)
   1.281 +{
   1.282 +  // Help breaking cycles
   1.283 +  nsRefPtr<nsPermissionManager> manager = mManager.forget();
   1.284 +  if (mRebuildOnSuccess && !manager->mIsShuttingDown) {
   1.285 +    return manager->InitDB(true);
   1.286 +  }
   1.287 +  return NS_OK;
   1.288 +}
   1.289 +
   1.290 +
   1.291 +/**
   1.292 + * Simple callback used by |RemoveAllInternal| to trigger closing
   1.293 + * the database and reinitializing it.
   1.294 + *
   1.295 + * Note: Beware that, if you hold onto a |DeleteFromMozHostListener| from a
   1.296 + * |nsPermissionManager|, this will create a cycle.
   1.297 + *
   1.298 + * Note: Once the callback has been called this DeleteFromMozHostListener cannot
   1.299 + * be reused.
   1.300 + */
   1.301 +class DeleteFromMozHostListener MOZ_FINAL : public mozIStorageStatementCallback
   1.302 +{
   1.303 +public:
   1.304 +  NS_DECL_ISUPPORTS
   1.305 +  NS_DECL_MOZISTORAGESTATEMENTCALLBACK
   1.306 +
   1.307 +  /**
   1.308 +   * @param aManager The owning manager.
   1.309 +   */
   1.310 +  DeleteFromMozHostListener(nsPermissionManager* aManager);
   1.311 +
   1.312 +protected:
   1.313 +  nsRefPtr<nsPermissionManager> mManager;
   1.314 +};
   1.315 +
   1.316 +NS_IMPL_ISUPPORTS(DeleteFromMozHostListener, mozIStorageStatementCallback)
   1.317 +
   1.318 +DeleteFromMozHostListener::
   1.319 +DeleteFromMozHostListener(nsPermissionManager* aManager)
   1.320 +  : mManager(aManager)
   1.321 +{
   1.322 +}
   1.323 +
   1.324 +NS_IMETHODIMP DeleteFromMozHostListener::HandleResult(mozIStorageResultSet *)
   1.325 +{
   1.326 +  MOZ_CRASH("Should not get any results");
   1.327 +}
   1.328 +
   1.329 +NS_IMETHODIMP DeleteFromMozHostListener::HandleError(mozIStorageError *)
   1.330 +{
   1.331 +  // Errors are handled in |HandleCompletion|
   1.332 +  return NS_OK;
   1.333 +}
   1.334 +
   1.335 +NS_IMETHODIMP DeleteFromMozHostListener::HandleCompletion(uint16_t aReason)
   1.336 +{
   1.337 +  // Help breaking cycles
   1.338 +  nsRefPtr<nsPermissionManager> manager = mManager.forget();
   1.339 +
   1.340 +  if (aReason == REASON_ERROR) {
   1.341 +    manager->CloseDB(true);
   1.342 +  }
   1.343 +
   1.344 +  return NS_OK;
   1.345 +}
   1.346 +
   1.347 +/* static */ void
   1.348 +nsPermissionManager::AppClearDataObserverInit()
   1.349 +{
   1.350 +  nsCOMPtr<nsIObserverService> observerService = do_GetService("@mozilla.org/observer-service;1");
   1.351 +  observerService->AddObserver(new AppClearDataObserver(), "webapps-clear-data", /* holdsWeak= */ false);
   1.352 +}
   1.353 +
   1.354 +////////////////////////////////////////////////////////////////////////////////
   1.355 +// nsPermissionManager Implementation
   1.356 +
   1.357 +static const char kPermissionsFileName[] = "permissions.sqlite";
   1.358 +#define HOSTS_SCHEMA_VERSION 3
   1.359 +
   1.360 +static const char kHostpermFileName[] = "hostperm.1";
   1.361 +
   1.362 +static const char kPermissionChangeNotification[] = PERM_CHANGE_NOTIFICATION;
   1.363 +
   1.364 +NS_IMPL_ISUPPORTS(nsPermissionManager, nsIPermissionManager, nsIObserver, nsISupportsWeakReference)
   1.365 +
   1.366 +nsPermissionManager::nsPermissionManager()
   1.367 + : mLargestID(0)
   1.368 + , mIsShuttingDown(false)
   1.369 +{
   1.370 +}
   1.371 +
   1.372 +nsPermissionManager::~nsPermissionManager()
   1.373 +{
   1.374 +  RemoveAllFromMemory();
   1.375 +  gPermissionManager = nullptr;
   1.376 +}
   1.377 +
   1.378 +// static
   1.379 +nsIPermissionManager*
   1.380 +nsPermissionManager::GetXPCOMSingleton()
   1.381 +{
   1.382 +  if (gPermissionManager) {
   1.383 +    NS_ADDREF(gPermissionManager);
   1.384 +    return gPermissionManager;
   1.385 +  }
   1.386 +
   1.387 +  // Create a new singleton nsPermissionManager.
   1.388 +  // We AddRef only once since XPCOM has rules about the ordering of module
   1.389 +  // teardowns - by the time our module destructor is called, it's too late to
   1.390 +  // Release our members, since GC cycles have already been completed and
   1.391 +  // would result in serious leaks.
   1.392 +  // See bug 209571.
   1.393 +  gPermissionManager = new nsPermissionManager();
   1.394 +  if (gPermissionManager) {
   1.395 +    NS_ADDREF(gPermissionManager);
   1.396 +    if (NS_FAILED(gPermissionManager->Init())) {
   1.397 +      NS_RELEASE(gPermissionManager);
   1.398 +    }
   1.399 +  }
   1.400 +
   1.401 +  return gPermissionManager;
   1.402 +}
   1.403 +
   1.404 +nsresult
   1.405 +nsPermissionManager::Init()
   1.406 +{
   1.407 +  nsresult rv;
   1.408 +
   1.409 +  mObserverService = do_GetService("@mozilla.org/observer-service;1", &rv);
   1.410 +  if (NS_SUCCEEDED(rv)) {
   1.411 +    mObserverService->AddObserver(this, "profile-before-change", true);
   1.412 +    mObserverService->AddObserver(this, "profile-do-change", true);
   1.413 +  }
   1.414 +
   1.415 +
   1.416 +  nsCOMPtr<nsIPrefBranch2> pbi = do_GetService(NS_PREFSERVICE_CONTRACTID);
   1.417 +  if (pbi) {
   1.418 +    pbi->AddObserver("permissions.", this, PR_FALSE);
   1.419 +  }
   1.420 +
   1.421 +  if (IsChildProcess()) {
   1.422 +    // Get the permissions from the parent process
   1.423 +    InfallibleTArray<IPC::Permission> perms;
   1.424 +    ChildProcess()->SendReadPermissions(&perms);
   1.425 +
   1.426 +    for (uint32_t i = 0; i < perms.Length(); i++) {
   1.427 +      const IPC::Permission &perm = perms[i];
   1.428 +
   1.429 +      nsCOMPtr<nsIPrincipal> principal;
   1.430 +      rv = GetPrincipal(perm.host, perm.appId, perm.isInBrowserElement, getter_AddRefs(principal));
   1.431 +      NS_ENSURE_SUCCESS(rv, rv);
   1.432 +
   1.433 +      AddInternal(principal, perm.type, perm.capability, 0, perm.expireType,
   1.434 +                  perm.expireTime, eNotify, eNoDBOperation);
   1.435 +    }
   1.436 +
   1.437 +    // Stop here; we don't need the DB in the child process
   1.438 +    return NS_OK;
   1.439 +  }
   1.440 +
   1.441 +  // ignore failure here, since it's non-fatal (we can run fine without
   1.442 +  // persistent storage - e.g. if there's no profile).
   1.443 +  // XXX should we tell the user about this?
   1.444 +  InitDB(false);
   1.445 +
   1.446 +  return NS_OK;
   1.447 +}
   1.448 +
   1.449 +nsresult
   1.450 +nsPermissionManager::InitDB(bool aRemoveFile)
   1.451 +{
   1.452 +  nsCOMPtr<nsIFile> permissionsFile;
   1.453 +  nsresult rv = NS_GetSpecialDirectory(NS_APP_PERMISSION_PARENT_DIR, getter_AddRefs(permissionsFile));
   1.454 +  if (NS_FAILED(rv)) {
   1.455 +    rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(permissionsFile));
   1.456 +  }
   1.457 +  NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
   1.458 +
   1.459 +  rv = permissionsFile->AppendNative(NS_LITERAL_CSTRING(kPermissionsFileName));
   1.460 +  NS_ENSURE_SUCCESS(rv, rv);
   1.461 +
   1.462 +  if (aRemoveFile) {
   1.463 +    bool exists = false;
   1.464 +    rv = permissionsFile->Exists(&exists);
   1.465 +    NS_ENSURE_SUCCESS(rv, rv);
   1.466 +    if (exists) {
   1.467 +      rv = permissionsFile->Remove(false);
   1.468 +      NS_ENSURE_SUCCESS(rv, rv);
   1.469 +    }
   1.470 +  }
   1.471 +
   1.472 +  nsCOMPtr<mozIStorageService> storage = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
   1.473 +  if (!storage)
   1.474 +    return NS_ERROR_UNEXPECTED;
   1.475 +
   1.476 +  bool memory_db = false;
   1.477 +  nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
   1.478 +  if (prefs) {
   1.479 +    prefs->GetBoolPref("permissions.memory_only", &memory_db); 
   1.480 +  }
   1.481 +  
   1.482 +  // cache a connection to the hosts database
   1.483 +  if (memory_db) {
   1.484 +    rv = storage->OpenSpecialDatabase("memory", getter_AddRefs(mDBConn));
   1.485 +  } else {
   1.486 +    rv = storage->OpenDatabase(permissionsFile, getter_AddRefs(mDBConn));
   1.487 +  }
   1.488 +  NS_ENSURE_SUCCESS(rv, rv);
   1.489 +
   1.490 +  bool ready;
   1.491 +  mDBConn->GetConnectionReady(&ready);
   1.492 +  if (!ready) {
   1.493 +    // delete and try again
   1.494 +    rv = permissionsFile->Remove(false);
   1.495 +    NS_ENSURE_SUCCESS(rv, rv);
   1.496 +
   1.497 +    if (memory_db) {
   1.498 +      rv = storage->OpenSpecialDatabase("memory", getter_AddRefs(mDBConn));
   1.499 +    } else {
   1.500 +      rv = storage->OpenDatabase(permissionsFile, getter_AddRefs(mDBConn));
   1.501 +    }
   1.502 +    NS_ENSURE_SUCCESS(rv, rv);
   1.503 +
   1.504 +    mDBConn->GetConnectionReady(&ready);
   1.505 +    if (!ready)
   1.506 +      return NS_ERROR_UNEXPECTED;
   1.507 +  }
   1.508 +
   1.509 +  bool tableExists = false;
   1.510 +  mDBConn->TableExists(NS_LITERAL_CSTRING("moz_hosts"), &tableExists);
   1.511 +  if (!tableExists) {
   1.512 +    rv = CreateTable();
   1.513 +    NS_ENSURE_SUCCESS(rv, rv);
   1.514 +
   1.515 +  } else {
   1.516 +    // table already exists; check the schema version before reading
   1.517 +    int32_t dbSchemaVersion;
   1.518 +    rv = mDBConn->GetSchemaVersion(&dbSchemaVersion);
   1.519 +    NS_ENSURE_SUCCESS(rv, rv);
   1.520 +
   1.521 +    switch (dbSchemaVersion) {
   1.522 +    // upgrading.
   1.523 +    // every time you increment the database schema, you need to implement
   1.524 +    // the upgrading code from the previous version to the new one.
   1.525 +    // fall through to current version
   1.526 +
   1.527 +    case 1:
   1.528 +      {
   1.529 +        // previous non-expiry version of database.  Upgrade it by adding the
   1.530 +        // expiration columns
   1.531 +        rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   1.532 +              "ALTER TABLE moz_hosts ADD expireType INTEGER"));
   1.533 +        NS_ENSURE_SUCCESS(rv, rv);
   1.534 +
   1.535 +        rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   1.536 +              "ALTER TABLE moz_hosts ADD expireTime INTEGER"));
   1.537 +        NS_ENSURE_SUCCESS(rv, rv);
   1.538 +      }
   1.539 +
   1.540 +      // fall through to the next upgrade
   1.541 +
   1.542 +    // TODO: we want to make default version as version 2 in order to fix bug 784875.
   1.543 +    case 0:
   1.544 +    case 2:
   1.545 +      {
   1.546 +        // Add appId/isInBrowserElement fields.
   1.547 +        rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   1.548 +              "ALTER TABLE moz_hosts ADD appId INTEGER"));
   1.549 +        NS_ENSURE_SUCCESS(rv, rv);
   1.550 +
   1.551 +        rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   1.552 +              "ALTER TABLE moz_hosts ADD isInBrowserElement INTEGER"));
   1.553 +        NS_ENSURE_SUCCESS(rv, rv);
   1.554 +
   1.555 +        rv = mDBConn->SetSchemaVersion(HOSTS_SCHEMA_VERSION);
   1.556 +        NS_ENSURE_SUCCESS(rv, rv);
   1.557 +      }
   1.558 +
   1.559 +      // fall through to the next upgrade
   1.560 +
   1.561 +    // current version.
   1.562 +    case HOSTS_SCHEMA_VERSION:
   1.563 +      break;
   1.564 +
   1.565 +    // downgrading.
   1.566 +    // if columns have been added to the table, we can still use the ones we
   1.567 +    // understand safely. if columns have been deleted or altered, just
   1.568 +    // blow away the table and start from scratch! if you change the way
   1.569 +    // a column is interpreted, make sure you also change its name so this
   1.570 +    // check will catch it.
   1.571 +    default:
   1.572 +      {
   1.573 +        // check if all the expected columns exist
   1.574 +        nsCOMPtr<mozIStorageStatement> stmt;
   1.575 +        rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
   1.576 +          "SELECT host, type, permission, expireType, expireTime, appId, isInBrowserElement FROM moz_hosts"),
   1.577 +          getter_AddRefs(stmt));
   1.578 +        if (NS_SUCCEEDED(rv))
   1.579 +          break;
   1.580 +
   1.581 +        // our columns aren't there - drop the table!
   1.582 +        rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DROP TABLE moz_hosts"));
   1.583 +        NS_ENSURE_SUCCESS(rv, rv);
   1.584 +
   1.585 +        rv = CreateTable();
   1.586 +        NS_ENSURE_SUCCESS(rv, rv);
   1.587 +      }
   1.588 +      break;
   1.589 +    }
   1.590 +  }
   1.591 +
   1.592 +  // make operations on the table asynchronous, for performance
   1.593 +  mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA synchronous = OFF"));
   1.594 +
   1.595 +  // cache frequently used statements (for insertion, deletion, and updating)
   1.596 +  rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
   1.597 +    "INSERT INTO moz_hosts "
   1.598 +    "(id, host, type, permission, expireType, expireTime, appId, isInBrowserElement) "
   1.599 +    "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)"), getter_AddRefs(mStmtInsert));
   1.600 +  NS_ENSURE_SUCCESS(rv, rv);
   1.601 +
   1.602 +  rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
   1.603 +    "DELETE FROM moz_hosts "
   1.604 +    "WHERE id = ?1"), getter_AddRefs(mStmtDelete));
   1.605 +  NS_ENSURE_SUCCESS(rv, rv);
   1.606 +
   1.607 +  rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
   1.608 +    "UPDATE moz_hosts "
   1.609 +    "SET permission = ?2, expireType= ?3, expireTime = ?4 WHERE id = ?1"),
   1.610 +    getter_AddRefs(mStmtUpdate));
   1.611 +  NS_ENSURE_SUCCESS(rv, rv);
   1.612 +
   1.613 +  // check whether to import or just read in the db
   1.614 +  if (tableExists)
   1.615 +    return Read();
   1.616 +
   1.617 +  return Import();
   1.618 +}
   1.619 +
   1.620 +// sets the schema version and creates the moz_hosts table.
   1.621 +nsresult
   1.622 +nsPermissionManager::CreateTable()
   1.623 +{
   1.624 +  // set the schema version, before creating the table
   1.625 +  nsresult rv = mDBConn->SetSchemaVersion(HOSTS_SCHEMA_VERSION);
   1.626 +  if (NS_FAILED(rv)) return rv;
   1.627 +
   1.628 +  // create the table
   1.629 +  // SQL also lives in automation.py.in. If you change this SQL change that
   1.630 +  // one too.
   1.631 +  return mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   1.632 +    "CREATE TABLE moz_hosts ("
   1.633 +      " id INTEGER PRIMARY KEY"
   1.634 +      ",host TEXT"
   1.635 +      ",type TEXT"
   1.636 +      ",permission INTEGER"
   1.637 +      ",expireType INTEGER"
   1.638 +      ",expireTime INTEGER"
   1.639 +      ",appId INTEGER"
   1.640 +      ",isInBrowserElement INTEGER"
   1.641 +    ")"));
   1.642 +}
   1.643 +
   1.644 +NS_IMETHODIMP
   1.645 +nsPermissionManager::Add(nsIURI     *aURI,
   1.646 +                         const char *aType,
   1.647 +                         uint32_t    aPermission,
   1.648 +                         uint32_t    aExpireType,
   1.649 +                         int64_t     aExpireTime)
   1.650 +{
   1.651 +  NS_ENSURE_ARG_POINTER(aURI);
   1.652 +
   1.653 +  nsCOMPtr<nsIPrincipal> principal;
   1.654 +  nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
   1.655 +  NS_ENSURE_SUCCESS(rv, rv);
   1.656 +
   1.657 +  return AddFromPrincipal(principal, aType, aPermission, aExpireType, aExpireTime);
   1.658 +}
   1.659 +
   1.660 +NS_IMETHODIMP
   1.661 +nsPermissionManager::AddFromPrincipal(nsIPrincipal* aPrincipal,
   1.662 +                                      const char* aType, uint32_t aPermission,
   1.663 +                                      uint32_t aExpireType, int64_t aExpireTime)
   1.664 +{
   1.665 +  ENSURE_NOT_CHILD_PROCESS;
   1.666 +  NS_ENSURE_ARG_POINTER(aPrincipal);
   1.667 +  NS_ENSURE_ARG_POINTER(aType);
   1.668 +  NS_ENSURE_TRUE(aExpireType == nsIPermissionManager::EXPIRE_NEVER ||
   1.669 +                 aExpireType == nsIPermissionManager::EXPIRE_TIME ||
   1.670 +                 aExpireType == nsIPermissionManager::EXPIRE_SESSION,
   1.671 +                 NS_ERROR_INVALID_ARG);
   1.672 +
   1.673 +  // Skip addition if the permission is already expired. Note that EXPIRE_SESSION only
   1.674 +  // honors expireTime if it is nonzero.
   1.675 +  if ((aExpireType == nsIPermissionManager::EXPIRE_TIME ||
   1.676 +       (aExpireType == nsIPermissionManager::EXPIRE_SESSION && aExpireTime != 0)) &&
   1.677 +      aExpireTime <= (PR_Now() / 1000)) {
   1.678 +    return NS_OK;
   1.679 +  }
   1.680 +
   1.681 +  // We don't add the system principal because it actually has no URI and we
   1.682 +  // always allow action for them.
   1.683 +  if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
   1.684 +    return NS_OK;
   1.685 +  }
   1.686 +
   1.687 +  // Permissions may not be added to expanded principals.
   1.688 +  if (IsExpandedPrincipal(aPrincipal)) {
   1.689 +    return NS_ERROR_INVALID_ARG;
   1.690 +  }
   1.691 +
   1.692 +  return AddInternal(aPrincipal, nsDependentCString(aType), aPermission, 0,
   1.693 +                     aExpireType, aExpireTime, eNotify, eWriteToDB);
   1.694 +}
   1.695 +
   1.696 +nsresult
   1.697 +nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
   1.698 +                                 const nsAFlatCString &aType,
   1.699 +                                 uint32_t              aPermission,
   1.700 +                                 int64_t               aID,
   1.701 +                                 uint32_t              aExpireType,
   1.702 +                                 int64_t               aExpireTime,
   1.703 +                                 NotifyOperationType   aNotifyOperation,
   1.704 +                                 DBOperationType       aDBOperation)
   1.705 +{
   1.706 +  nsAutoCString host;
   1.707 +  nsresult rv = GetHostForPrincipal(aPrincipal, host);
   1.708 +  NS_ENSURE_SUCCESS(rv, rv);
   1.709 +
   1.710 +  if (!IsChildProcess()) {
   1.711 +    uint32_t appId;
   1.712 +    rv = aPrincipal->GetAppId(&appId);
   1.713 +    NS_ENSURE_SUCCESS(rv, rv);
   1.714 +
   1.715 +    bool isInBrowserElement;
   1.716 +    rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
   1.717 +    NS_ENSURE_SUCCESS(rv, rv);
   1.718 +
   1.719 +    IPC::Permission permission(host, appId, isInBrowserElement, aType,
   1.720 +                               aPermission, aExpireType, aExpireTime);
   1.721 +
   1.722 +    nsTArray<ContentParent*> cplist;
   1.723 +    ContentParent::GetAll(cplist);
   1.724 +    for (uint32_t i = 0; i < cplist.Length(); ++i) {
   1.725 +      ContentParent* cp = cplist[i];
   1.726 +      if (cp->NeedsPermissionsUpdate())
   1.727 +        unused << cp->SendAddPermission(permission);
   1.728 +    }
   1.729 +  }
   1.730 +
   1.731 +  // look up the type index
   1.732 +  int32_t typeIndex = GetTypeIndex(aType.get(), true);
   1.733 +  NS_ENSURE_TRUE(typeIndex != -1, NS_ERROR_OUT_OF_MEMORY);
   1.734 +
   1.735 +  // When an entry already exists, PutEntry will return that, instead
   1.736 +  // of adding a new one
   1.737 +  nsRefPtr<PermissionKey> key = new PermissionKey(aPrincipal);
   1.738 +  PermissionHashKey* entry = mPermissionTable.PutEntry(key);
   1.739 +  if (!entry) return NS_ERROR_FAILURE;
   1.740 +  if (!entry->GetKey()) {
   1.741 +    mPermissionTable.RawRemoveEntry(entry);
   1.742 +    return NS_ERROR_OUT_OF_MEMORY;
   1.743 +  }
   1.744 +
   1.745 +  // figure out the transaction type, and get any existing permission value
   1.746 +  OperationType op;
   1.747 +  int32_t index = entry->GetPermissionIndex(typeIndex);
   1.748 +  if (index == -1) {
   1.749 +    if (aPermission == nsIPermissionManager::UNKNOWN_ACTION)
   1.750 +      op = eOperationNone;
   1.751 +    else
   1.752 +      op = eOperationAdding;
   1.753 +
   1.754 +  } else {
   1.755 +    PermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
   1.756 +
   1.757 +    // remove the permission if the permission is UNKNOWN, update the
   1.758 +    // permission if its value or expire type have changed OR if the time has
   1.759 +    // changed and the expire type is time, otherwise, don't modify.  There's
   1.760 +    // no need to modify a permission that doesn't expire with time when the
   1.761 +    // only thing changed is the expire time.
   1.762 +    if (aPermission == oldPermissionEntry.mPermission &&
   1.763 +        aExpireType == oldPermissionEntry.mExpireType &&
   1.764 +        (aExpireType == nsIPermissionManager::EXPIRE_NEVER ||
   1.765 +         aExpireTime == oldPermissionEntry.mExpireTime))
   1.766 +      op = eOperationNone;
   1.767 +    else if (aPermission == nsIPermissionManager::UNKNOWN_ACTION)
   1.768 +      op = eOperationRemoving;
   1.769 +    else
   1.770 +      op = eOperationChanging;
   1.771 +  }
   1.772 +
   1.773 +  // do the work for adding, deleting, or changing a permission:
   1.774 +  // update the in-memory list, write to the db, and notify consumers.
   1.775 +  int64_t id;
   1.776 +  switch (op) {
   1.777 +  case eOperationNone:
   1.778 +    {
   1.779 +      // nothing to do
   1.780 +      return NS_OK;
   1.781 +    }
   1.782 +
   1.783 +  case eOperationAdding:
   1.784 +    {
   1.785 +      if (aDBOperation == eWriteToDB) {
   1.786 +        // we'll be writing to the database - generate a known unique id
   1.787 +        id = ++mLargestID;
   1.788 +      } else {
   1.789 +        // we're reading from the database - use the id already assigned
   1.790 +        id = aID;
   1.791 +      }
   1.792 +
   1.793 +      entry->GetPermissions().AppendElement(PermissionEntry(id, typeIndex, aPermission, aExpireType, aExpireTime));
   1.794 +
   1.795 +      if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION) {
   1.796 +        uint32_t appId;
   1.797 +        rv = aPrincipal->GetAppId(&appId);
   1.798 +        NS_ENSURE_SUCCESS(rv, rv);
   1.799 +
   1.800 +        bool isInBrowserElement;
   1.801 +        rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
   1.802 +        NS_ENSURE_SUCCESS(rv, rv);
   1.803 +
   1.804 +        UpdateDB(op, mStmtInsert, id, host, aType, aPermission, aExpireType, aExpireTime, appId, isInBrowserElement);
   1.805 +      }
   1.806 +
   1.807 +      if (aNotifyOperation == eNotify) {
   1.808 +        NotifyObserversWithPermission(host,
   1.809 +                                      entry->GetKey()->mAppId,
   1.810 +                                      entry->GetKey()->mIsInBrowserElement,
   1.811 +                                      mTypeArray[typeIndex],
   1.812 +                                      aPermission,
   1.813 +                                      aExpireType,
   1.814 +                                      aExpireTime,
   1.815 +                                      MOZ_UTF16("added"));
   1.816 +      }
   1.817 +
   1.818 +      break;
   1.819 +    }
   1.820 +
   1.821 +  case eOperationRemoving:
   1.822 +    {
   1.823 +      PermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
   1.824 +      id = oldPermissionEntry.mID;
   1.825 +      entry->GetPermissions().RemoveElementAt(index);
   1.826 +
   1.827 +      if (aDBOperation == eWriteToDB)
   1.828 +        // We care only about the id here so we pass dummy values for all other
   1.829 +        // parameters.
   1.830 +        UpdateDB(op, mStmtDelete, id, EmptyCString(), EmptyCString(), 0,
   1.831 +                 nsIPermissionManager::EXPIRE_NEVER, 0, 0, false);
   1.832 +
   1.833 +      if (aNotifyOperation == eNotify) {
   1.834 +        NotifyObserversWithPermission(host,
   1.835 +                                      entry->GetKey()->mAppId,
   1.836 +                                      entry->GetKey()->mIsInBrowserElement,
   1.837 +                                      mTypeArray[typeIndex],
   1.838 +                                      oldPermissionEntry.mPermission,
   1.839 +                                      oldPermissionEntry.mExpireType,
   1.840 +                                      oldPermissionEntry.mExpireTime,
   1.841 +                                      MOZ_UTF16("deleted"));
   1.842 +      }
   1.843 +
   1.844 +      // If there are no more permissions stored for that entry, clear it.
   1.845 +      if (entry->GetPermissions().IsEmpty()) {
   1.846 +        mPermissionTable.RawRemoveEntry(entry);
   1.847 +      }
   1.848 +
   1.849 +      break;
   1.850 +    }
   1.851 +
   1.852 +  case eOperationChanging:
   1.853 +    {
   1.854 +      id = entry->GetPermissions()[index].mID;
   1.855 +
   1.856 +      // If the new expireType is EXPIRE_SESSION, then we have to keep a
   1.857 +      // copy of the previous permission/expireType values. This cached value will be
   1.858 +      // used when restoring the permissions of an app.
   1.859 +      if (entry->GetPermissions()[index].mExpireType != nsIPermissionManager::EXPIRE_SESSION &&
   1.860 +          aExpireType == nsIPermissionManager::EXPIRE_SESSION) {
   1.861 +        entry->GetPermissions()[index].mNonSessionPermission = entry->GetPermissions()[index].mPermission;
   1.862 +        entry->GetPermissions()[index].mNonSessionExpireType = entry->GetPermissions()[index].mExpireType;
   1.863 +        entry->GetPermissions()[index].mNonSessionExpireTime = entry->GetPermissions()[index].mExpireTime;
   1.864 +      } else if (aExpireType != nsIPermissionManager::EXPIRE_SESSION) {
   1.865 +        entry->GetPermissions()[index].mNonSessionPermission = aPermission;
   1.866 +        entry->GetPermissions()[index].mNonSessionExpireType = aExpireType;
   1.867 +        entry->GetPermissions()[index].mNonSessionExpireTime = aExpireTime;
   1.868 +      }
   1.869 +
   1.870 +      entry->GetPermissions()[index].mPermission = aPermission;
   1.871 +      entry->GetPermissions()[index].mExpireType = aExpireType;
   1.872 +      entry->GetPermissions()[index].mExpireTime = aExpireTime;
   1.873 +
   1.874 +      if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION)
   1.875 +        // We care only about the id, the permission and expireType/expireTime here.
   1.876 +        // We pass dummy values for all other parameters.
   1.877 +        UpdateDB(op, mStmtUpdate, id, EmptyCString(), EmptyCString(),
   1.878 +                 aPermission, aExpireType, aExpireTime, 0, false);
   1.879 +
   1.880 +      if (aNotifyOperation == eNotify) {
   1.881 +        NotifyObserversWithPermission(host,
   1.882 +                                      entry->GetKey()->mAppId,
   1.883 +                                      entry->GetKey()->mIsInBrowserElement,
   1.884 +                                      mTypeArray[typeIndex],
   1.885 +                                      aPermission,
   1.886 +                                      aExpireType,
   1.887 +                                      aExpireTime,
   1.888 +                                      MOZ_UTF16("changed"));
   1.889 +      }
   1.890 +
   1.891 +      break;
   1.892 +    }
   1.893 +  }
   1.894 +
   1.895 +  return NS_OK;
   1.896 +}
   1.897 +
   1.898 +NS_IMETHODIMP
   1.899 +nsPermissionManager::Remove(const nsACString &aHost,
   1.900 +                            const char       *aType)
   1.901 +{
   1.902 +  nsCOMPtr<nsIPrincipal> principal;
   1.903 +  nsresult rv = GetPrincipal(aHost, getter_AddRefs(principal));
   1.904 +  NS_ENSURE_SUCCESS(rv, rv);
   1.905 +
   1.906 +  return RemoveFromPrincipal(principal, aType);
   1.907 +}
   1.908 +
   1.909 +NS_IMETHODIMP
   1.910 +nsPermissionManager::RemoveFromPrincipal(nsIPrincipal* aPrincipal,
   1.911 +                                         const char* aType)
   1.912 +{
   1.913 +  ENSURE_NOT_CHILD_PROCESS;
   1.914 +  NS_ENSURE_ARG_POINTER(aPrincipal);
   1.915 +  NS_ENSURE_ARG_POINTER(aType);
   1.916 +
   1.917 +  // System principals are never added to the database, no need to remove them.
   1.918 +  if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
   1.919 +    return NS_OK;
   1.920 +  }
   1.921 +
   1.922 +  // Permissions may not be added to expanded principals.
   1.923 +  if (IsExpandedPrincipal(aPrincipal)) {
   1.924 +    return NS_ERROR_INVALID_ARG;
   1.925 +  }
   1.926 +
   1.927 +  // AddInternal() handles removal, just let it do the work
   1.928 +  return AddInternal(aPrincipal,
   1.929 +                     nsDependentCString(aType),
   1.930 +                     nsIPermissionManager::UNKNOWN_ACTION,
   1.931 +                     0,
   1.932 +                     nsIPermissionManager::EXPIRE_NEVER,
   1.933 +                     0,
   1.934 +                     eNotify,
   1.935 +                     eWriteToDB);
   1.936 +}
   1.937 +
   1.938 +NS_IMETHODIMP
   1.939 +nsPermissionManager::RemoveAll()
   1.940 +{
   1.941 +  ENSURE_NOT_CHILD_PROCESS;
   1.942 +  return RemoveAllInternal(true);
   1.943 +}
   1.944 +
   1.945 +void
   1.946 +nsPermissionManager::CloseDB(bool aRebuildOnSuccess)
   1.947 +{
   1.948 +  // Null the statements, this will finalize them.
   1.949 +  mStmtInsert = nullptr;
   1.950 +  mStmtDelete = nullptr;
   1.951 +  mStmtUpdate = nullptr;
   1.952 +  if (mDBConn) {
   1.953 +    mozIStorageCompletionCallback* cb = new CloseDatabaseListener(this,
   1.954 +           aRebuildOnSuccess);
   1.955 +    mozilla::DebugOnly<nsresult> rv = mDBConn->AsyncClose(cb);
   1.956 +    MOZ_ASSERT(NS_SUCCEEDED(rv));
   1.957 +    mDBConn = nullptr; // Avoid race conditions
   1.958 +  }
   1.959 +}
   1.960 +
   1.961 +nsresult
   1.962 +nsPermissionManager::RemoveAllInternal(bool aNotifyObservers)
   1.963 +{
   1.964 +  // Remove from memory and notify immediately. Since the in-memory
   1.965 +  // database is authoritative, we do not need confirmation from the
   1.966 +  // on-disk database to notify observers.
   1.967 +  RemoveAllFromMemory();
   1.968 +  if (aNotifyObservers) {
   1.969 +    NotifyObservers(nullptr, MOZ_UTF16("cleared"));
   1.970 +  }
   1.971 +
   1.972 +  // clear the db
   1.973 +  if (mDBConn) {
   1.974 +    nsCOMPtr<mozIStorageAsyncStatement> removeStmt;
   1.975 +    nsresult rv = mDBConn->
   1.976 +      CreateAsyncStatement(NS_LITERAL_CSTRING(
   1.977 +         "DELETE FROM moz_hosts"
   1.978 +      ), getter_AddRefs(removeStmt));
   1.979 +    MOZ_ASSERT(NS_SUCCEEDED(rv));
   1.980 +    if (!removeStmt) {
   1.981 +      return NS_ERROR_UNEXPECTED;
   1.982 +    }
   1.983 +    nsCOMPtr<mozIStoragePendingStatement> pending;
   1.984 +    mozIStorageStatementCallback* cb = new DeleteFromMozHostListener(this);
   1.985 +    rv = removeStmt->ExecuteAsync(cb, getter_AddRefs(pending));
   1.986 +    MOZ_ASSERT(NS_SUCCEEDED(rv));
   1.987 +
   1.988 +    return rv;
   1.989 +  }
   1.990 +
   1.991 +  return NS_OK;
   1.992 +}
   1.993 +
   1.994 +NS_IMETHODIMP
   1.995 +nsPermissionManager::TestExactPermission(nsIURI     *aURI,
   1.996 +                                         const char *aType,
   1.997 +                                         uint32_t   *aPermission)
   1.998 +{
   1.999 +  nsCOMPtr<nsIPrincipal> principal;
  1.1000 +  nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
  1.1001 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1002 +
  1.1003 +  return TestExactPermissionFromPrincipal(principal, aType, aPermission);
  1.1004 +}
  1.1005 +
  1.1006 +NS_IMETHODIMP
  1.1007 +nsPermissionManager::TestExactPermissionFromPrincipal(nsIPrincipal* aPrincipal,
  1.1008 +                                                      const char* aType,
  1.1009 +                                                      uint32_t* aPermission)
  1.1010 +{
  1.1011 +  return CommonTestPermission(aPrincipal, aType, aPermission, true, true);
  1.1012 +}
  1.1013 +
  1.1014 +NS_IMETHODIMP
  1.1015 +nsPermissionManager::TestExactPermanentPermission(nsIPrincipal* aPrincipal,
  1.1016 +                                                  const char* aType,
  1.1017 +                                                  uint32_t* aPermission)
  1.1018 +{
  1.1019 +  return CommonTestPermission(aPrincipal, aType, aPermission, true, false);
  1.1020 +}
  1.1021 +
  1.1022 +NS_IMETHODIMP
  1.1023 +nsPermissionManager::TestPermission(nsIURI     *aURI,
  1.1024 +                                    const char *aType,
  1.1025 +                                    uint32_t   *aPermission)
  1.1026 +{
  1.1027 +  nsCOMPtr<nsIPrincipal> principal;
  1.1028 +  nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
  1.1029 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1030 +
  1.1031 +  return TestPermissionFromPrincipal(principal, aType, aPermission);
  1.1032 +}
  1.1033 +
  1.1034 +NS_IMETHODIMP
  1.1035 +nsPermissionManager::TestPermissionFromWindow(nsIDOMWindow* aWindow,
  1.1036 +                                              const char* aType,
  1.1037 +                                              uint32_t* aPermission)
  1.1038 +{
  1.1039 +  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
  1.1040 +  NS_ENSURE_TRUE(window, NS_NOINTERFACE);
  1.1041 +
  1.1042 +  nsPIDOMWindow* innerWindow = window->IsInnerWindow() ?
  1.1043 +    window.get() :
  1.1044 +    window->GetCurrentInnerWindow();
  1.1045 +
  1.1046 +  // Get the document for security check
  1.1047 +  nsCOMPtr<nsIDocument> document = innerWindow->GetExtantDoc();
  1.1048 +  NS_ENSURE_TRUE(document, NS_NOINTERFACE);
  1.1049 +
  1.1050 +  nsCOMPtr<nsIPrincipal> principal = document->NodePrincipal();
  1.1051 +  return TestPermissionFromPrincipal(principal, aType, aPermission);
  1.1052 +}
  1.1053 +
  1.1054 +NS_IMETHODIMP
  1.1055 +nsPermissionManager::TestPermissionFromPrincipal(nsIPrincipal* aPrincipal,
  1.1056 +                                                 const char* aType,
  1.1057 +                                                 uint32_t* aPermission)
  1.1058 +{
  1.1059 +  return CommonTestPermission(aPrincipal, aType, aPermission, false, true);
  1.1060 +}
  1.1061 +
  1.1062 +NS_IMETHODIMP
  1.1063 +nsPermissionManager::GetPermissionObject(nsIPrincipal* aPrincipal,
  1.1064 +                                         const char* aType,
  1.1065 +                                         bool aExactHostMatch,
  1.1066 +                                         nsIPermission** aResult)
  1.1067 +{
  1.1068 +  NS_ENSURE_ARG_POINTER(aPrincipal);
  1.1069 +  NS_ENSURE_ARG_POINTER(aType);
  1.1070 +
  1.1071 +  *aResult = nullptr;
  1.1072 +
  1.1073 +  if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
  1.1074 +    return NS_OK;
  1.1075 +  }
  1.1076 +
  1.1077 +  // Querying the permission object of an nsEP is non-sensical.
  1.1078 +  if (IsExpandedPrincipal(aPrincipal)) {
  1.1079 +    return NS_ERROR_INVALID_ARG;
  1.1080 +  }
  1.1081 +
  1.1082 +  nsAutoCString host;
  1.1083 +  nsresult rv = GetHostForPrincipal(aPrincipal, host);
  1.1084 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1085 +
  1.1086 +  int32_t typeIndex = GetTypeIndex(aType, false);
  1.1087 +  // If type == -1, the type isn't known,
  1.1088 +  // so just return NS_OK
  1.1089 +  if (typeIndex == -1) return NS_OK;
  1.1090 +
  1.1091 +  uint32_t appId;
  1.1092 +  rv = aPrincipal->GetAppId(&appId);
  1.1093 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1094 +
  1.1095 +  bool isInBrowserElement;
  1.1096 +  rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
  1.1097 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1098 +
  1.1099 +  PermissionHashKey* entry = GetPermissionHashKey(host, appId, isInBrowserElement,
  1.1100 +                                                  typeIndex, aExactHostMatch);
  1.1101 +  if (!entry) {
  1.1102 +    return NS_OK;
  1.1103 +  }
  1.1104 +
  1.1105 +  // We don't call GetPermission(typeIndex) because that returns a fake
  1.1106 +  // UNKNOWN_ACTION entry if there is no match.
  1.1107 +  int32_t idx = entry->GetPermissionIndex(typeIndex);
  1.1108 +  if (-1 == idx) {
  1.1109 +    return NS_OK;
  1.1110 +  }
  1.1111 +
  1.1112 +  PermissionEntry& perm = entry->GetPermissions()[idx];
  1.1113 +  nsCOMPtr<nsIPermission> r = new nsPermission(entry->GetKey()->mHost,
  1.1114 +                                               entry->GetKey()->mAppId,
  1.1115 +                                               entry->GetKey()->mIsInBrowserElement,
  1.1116 +                                               mTypeArray.ElementAt(perm.mType),
  1.1117 +                                               perm.mPermission,
  1.1118 +                                               perm.mExpireType,
  1.1119 +                                               perm.mExpireTime);
  1.1120 +  r.forget(aResult);
  1.1121 +  return NS_OK;
  1.1122 +}
  1.1123 +
  1.1124 +nsresult
  1.1125 +nsPermissionManager::CommonTestPermission(nsIPrincipal* aPrincipal,
  1.1126 +                                          const char *aType,
  1.1127 +                                          uint32_t   *aPermission,
  1.1128 +                                          bool        aExactHostMatch,
  1.1129 +                                          bool        aIncludingSession)
  1.1130 +{
  1.1131 +  NS_ENSURE_ARG_POINTER(aPrincipal);
  1.1132 +  NS_ENSURE_ARG_POINTER(aType);
  1.1133 +
  1.1134 +  if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
  1.1135 +    *aPermission = nsIPermissionManager::ALLOW_ACTION;
  1.1136 +    return NS_OK;
  1.1137 +  }
  1.1138 +
  1.1139 +  // Set the default.
  1.1140 +  *aPermission = nsIPermissionManager::UNKNOWN_ACTION;
  1.1141 +
  1.1142 +  // For expanded principals, we want to iterate over the whitelist and see
  1.1143 +  // if the permission is granted for any of them.
  1.1144 +  nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal);
  1.1145 +  if (ep) {
  1.1146 +    nsTArray<nsCOMPtr<nsIPrincipal>>* whitelist;
  1.1147 +    nsresult rv = ep->GetWhiteList(&whitelist);
  1.1148 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1149 +
  1.1150 +    for (size_t i = 0; i < whitelist->Length(); ++i) {
  1.1151 +      uint32_t perm;
  1.1152 +      rv = CommonTestPermission(whitelist->ElementAt(i), aType, &perm, aExactHostMatch,
  1.1153 +                                aIncludingSession);
  1.1154 +      NS_ENSURE_SUCCESS(rv, rv);
  1.1155 +      if (perm == nsIPermissionManager::ALLOW_ACTION) {
  1.1156 +        *aPermission = perm;
  1.1157 +        return NS_OK;
  1.1158 +      } else if (perm == nsIPermissionManager::PROMPT_ACTION) {
  1.1159 +        // Store it, but keep going to see if we can do better.
  1.1160 +        *aPermission = perm;
  1.1161 +      }
  1.1162 +    }
  1.1163 +
  1.1164 +    return NS_OK;
  1.1165 +  }
  1.1166 +
  1.1167 +  nsAutoCString host;
  1.1168 +  nsresult rv = GetHostForPrincipal(aPrincipal, host);
  1.1169 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1170 +
  1.1171 +  int32_t typeIndex = GetTypeIndex(aType, false);
  1.1172 +  // If type == -1, the type isn't known,
  1.1173 +  // so just return NS_OK
  1.1174 +  if (typeIndex == -1) return NS_OK;
  1.1175 +
  1.1176 +  uint32_t appId;
  1.1177 +  rv = aPrincipal->GetAppId(&appId);
  1.1178 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1179 +
  1.1180 +  bool isInBrowserElement;
  1.1181 +  rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
  1.1182 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1183 +
  1.1184 +  PermissionHashKey* entry = GetPermissionHashKey(host, appId, isInBrowserElement,
  1.1185 +                                                  typeIndex, aExactHostMatch);
  1.1186 +  if (!entry ||
  1.1187 +      (!aIncludingSession &&
  1.1188 +       entry->GetPermission(typeIndex).mNonSessionExpireType ==
  1.1189 +         nsIPermissionManager::EXPIRE_SESSION)) {
  1.1190 +    return NS_OK;
  1.1191 +  }
  1.1192 +
  1.1193 +  *aPermission = aIncludingSession
  1.1194 +                   ? entry->GetPermission(typeIndex).mPermission
  1.1195 +                   : entry->GetPermission(typeIndex).mNonSessionPermission;
  1.1196 +
  1.1197 +  return NS_OK;
  1.1198 +}
  1.1199 +
  1.1200 +// Returns PermissionHashKey for a given { host, appId, isInBrowserElement } tuple.
  1.1201 +// This is not simply using PermissionKey because we will walk-up domains in
  1.1202 +// case of |host| contains sub-domains.
  1.1203 +// Returns null if nothing found.
  1.1204 +// Also accepts host on the format "<foo>". This will perform an exact match
  1.1205 +// lookup as the string doesn't contain any dots.
  1.1206 +nsPermissionManager::PermissionHashKey*
  1.1207 +nsPermissionManager::GetPermissionHashKey(const nsACString& aHost,
  1.1208 +                                          uint32_t aAppId,
  1.1209 +                                          bool aIsInBrowserElement,
  1.1210 +                                          uint32_t aType,
  1.1211 +                                          bool aExactHostMatch)
  1.1212 +{
  1.1213 +  PermissionHashKey* entry = nullptr;
  1.1214 +
  1.1215 +  nsRefPtr<PermissionKey> key = new PermissionKey(aHost, aAppId, aIsInBrowserElement);
  1.1216 +  entry = mPermissionTable.GetEntry(key);
  1.1217 +
  1.1218 +  if (entry) {
  1.1219 +    PermissionEntry permEntry = entry->GetPermission(aType);
  1.1220 +
  1.1221 +    // if the entry is expired, remove and keep looking for others.
  1.1222 +    // Note that EXPIRE_SESSION only honors expireTime if it is nonzero.
  1.1223 +    if ((permEntry.mExpireType == nsIPermissionManager::EXPIRE_TIME ||
  1.1224 +         (permEntry.mExpireType == nsIPermissionManager::EXPIRE_SESSION &&
  1.1225 +          permEntry.mExpireTime != 0)) &&
  1.1226 +        permEntry.mExpireTime <= (PR_Now() / 1000)) {
  1.1227 +      nsCOMPtr<nsIPrincipal> principal;
  1.1228 +      if (NS_FAILED(GetPrincipal(aHost, aAppId, aIsInBrowserElement, getter_AddRefs(principal)))) {
  1.1229 +        return nullptr;
  1.1230 +      }
  1.1231 +
  1.1232 +      entry = nullptr;
  1.1233 +      RemoveFromPrincipal(principal, mTypeArray[aType].get());
  1.1234 +    } else if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) {
  1.1235 +      entry = nullptr;
  1.1236 +    }
  1.1237 +  }
  1.1238 +
  1.1239 +  if (entry) {
  1.1240 +    return entry;
  1.1241 +  }
  1.1242 +
  1.1243 +  // If we haven't found an entry, depending on the host, we could try a bit
  1.1244 +  // harder.
  1.1245 +  // If this is a file:// URI, we can check for the presence of the magic entry
  1.1246 +  // <file> which gives permission to all file://. This hack might disappear,
  1.1247 +  // see bug 817007. Note that we don't require aExactHostMatch to be true for
  1.1248 +  // that to keep retro-compatibility.
  1.1249 +  // If this is not a file:// URI, and that aExactHostMatch wasn't true, we can
  1.1250 +  // check if the base domain has a permission entry.
  1.1251 +
  1.1252 +  if (StringBeginsWith(aHost, NS_LITERAL_CSTRING("file://"))) {
  1.1253 +    return GetPermissionHashKey(NS_LITERAL_CSTRING("<file>"), aAppId, aIsInBrowserElement, aType, true);
  1.1254 +  }
  1.1255 +
  1.1256 +  if (!aExactHostMatch) {
  1.1257 +    nsCString domain = GetNextSubDomainForHost(aHost);
  1.1258 +    if (!domain.IsEmpty()) {
  1.1259 +      return GetPermissionHashKey(domain, aAppId, aIsInBrowserElement, aType, aExactHostMatch);
  1.1260 +    }
  1.1261 +  }
  1.1262 +
  1.1263 +  // No entry, really...
  1.1264 +  return nullptr;
  1.1265 +}
  1.1266 +
  1.1267 +// helper struct for passing arguments into hash enumeration callback.
  1.1268 +struct nsGetEnumeratorData
  1.1269 +{
  1.1270 +  nsGetEnumeratorData(nsCOMArray<nsIPermission> *aArray, const nsTArray<nsCString> *aTypes)
  1.1271 +   : array(aArray)
  1.1272 +   , types(aTypes) {}
  1.1273 +
  1.1274 +  nsCOMArray<nsIPermission> *array;
  1.1275 +  const nsTArray<nsCString> *types;
  1.1276 +};
  1.1277 +
  1.1278 +static PLDHashOperator
  1.1279 +AddPermissionsToList(nsPermissionManager::PermissionHashKey* entry, void *arg)
  1.1280 +{
  1.1281 +  nsGetEnumeratorData *data = static_cast<nsGetEnumeratorData *>(arg);
  1.1282 +
  1.1283 +  for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) {
  1.1284 +    nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i];
  1.1285 +
  1.1286 +    nsPermission *perm = new nsPermission(entry->GetKey()->mHost,
  1.1287 +                                          entry->GetKey()->mAppId,
  1.1288 +                                          entry->GetKey()->mIsInBrowserElement,
  1.1289 +                                          data->types->ElementAt(permEntry.mType),
  1.1290 +                                          permEntry.mPermission,
  1.1291 +                                          permEntry.mExpireType,
  1.1292 +                                          permEntry.mExpireTime);
  1.1293 +
  1.1294 +    data->array->AppendObject(perm);
  1.1295 +  }
  1.1296 +
  1.1297 +  return PL_DHASH_NEXT;
  1.1298 +}
  1.1299 +
  1.1300 +NS_IMETHODIMP nsPermissionManager::GetEnumerator(nsISimpleEnumerator **aEnum)
  1.1301 +{
  1.1302 +  // roll an nsCOMArray of all our permissions, then hand out an enumerator
  1.1303 +  nsCOMArray<nsIPermission> array;
  1.1304 +  nsGetEnumeratorData data(&array, &mTypeArray);
  1.1305 +
  1.1306 +  mPermissionTable.EnumerateEntries(AddPermissionsToList, &data);
  1.1307 +
  1.1308 +  return NS_NewArrayEnumerator(aEnum, array);
  1.1309 +}
  1.1310 +
  1.1311 +NS_IMETHODIMP nsPermissionManager::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *someData)
  1.1312 +{
  1.1313 +  ENSURE_NOT_CHILD_PROCESS;
  1.1314 +
  1.1315 +  if (nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
  1.1316 +    if (!nsCRT::strcmp(someData, NS_LITERAL_STRING("permissions.memory_only").get())) {
  1.1317 +      // XXX: Should we remove the file? Probably not..
  1.1318 +      InitDB(PR_FALSE);
  1.1319 +    }
  1.1320 +  } else if (!nsCRT::strcmp(aTopic, "profile-before-change")) {
  1.1321 +    // The profile is about to change,
  1.1322 +    // or is going away because the application is shutting down.
  1.1323 +    mIsShuttingDown = true;
  1.1324 +    if (!nsCRT::strcmp(someData, MOZ_UTF16("shutdown-cleanse"))) {
  1.1325 +      // Clear the permissions file and close the db asynchronously
  1.1326 +      RemoveAllInternal(false);
  1.1327 +    } else {
  1.1328 +      RemoveAllFromMemory();
  1.1329 +      CloseDB(false);
  1.1330 +    }
  1.1331 +  }
  1.1332 +  else if (!nsCRT::strcmp(aTopic, "profile-do-change")) {
  1.1333 +    // the profile has already changed; init the db from the new location
  1.1334 +    InitDB(false);
  1.1335 +  }
  1.1336 +
  1.1337 +  return NS_OK;
  1.1338 +}
  1.1339 +
  1.1340 +PLDHashOperator
  1.1341 +nsPermissionManager::GetPermissionsForApp(nsPermissionManager::PermissionHashKey* entry, void* arg)
  1.1342 +{
  1.1343 +  GetPermissionsForAppStruct* data = static_cast<GetPermissionsForAppStruct*>(arg);
  1.1344 +
  1.1345 +  for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) {
  1.1346 +    nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i];
  1.1347 +
  1.1348 +    if (entry->GetKey()->mAppId != data->appId ||
  1.1349 +        (data->browserOnly && !entry->GetKey()->mIsInBrowserElement)) {
  1.1350 +      continue;
  1.1351 +    }
  1.1352 +
  1.1353 +    data->permissions.AppendObject(new nsPermission(entry->GetKey()->mHost,
  1.1354 +                                                    entry->GetKey()->mAppId,
  1.1355 +                                                    entry->GetKey()->mIsInBrowserElement,
  1.1356 +                                                    gPermissionManager->mTypeArray.ElementAt(permEntry.mType),
  1.1357 +                                                    permEntry.mPermission,
  1.1358 +                                                    permEntry.mExpireType,
  1.1359 +                                                    permEntry.mExpireTime));
  1.1360 +  }
  1.1361 +
  1.1362 +  return PL_DHASH_NEXT;
  1.1363 +}
  1.1364 +
  1.1365 +NS_IMETHODIMP
  1.1366 +nsPermissionManager::RemovePermissionsForApp(uint32_t aAppId, bool aBrowserOnly)
  1.1367 +{
  1.1368 +  ENSURE_NOT_CHILD_PROCESS;
  1.1369 +  NS_ENSURE_ARG(aAppId != nsIScriptSecurityManager::NO_APP_ID);
  1.1370 +
  1.1371 +  // We begin by removing all the permissions from the DB.
  1.1372 +  // After clearing the DB, we call AddInternal() to make sure that all
  1.1373 +  // processes are aware of this change and the representation of the DB in
  1.1374 +  // memory is updated.
  1.1375 +  // We have to get all permissions associated with an application and then
  1.1376 +  // remove those because doing so in EnumerateEntries() would fail because
  1.1377 +  // we might happen to actually delete entries from the list.
  1.1378 +
  1.1379 +  nsAutoCString sql;
  1.1380 +  sql.AppendLiteral("DELETE FROM moz_hosts WHERE appId=");
  1.1381 +  sql.AppendInt(aAppId);
  1.1382 +
  1.1383 +  if (aBrowserOnly) {
  1.1384 +    sql.AppendLiteral(" AND isInBrowserElement=1");
  1.1385 +  }
  1.1386 +
  1.1387 +  nsCOMPtr<mozIStorageAsyncStatement> removeStmt;
  1.1388 +  nsresult rv = mDBConn->CreateAsyncStatement(sql, getter_AddRefs(removeStmt));
  1.1389 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1390 +
  1.1391 +  nsCOMPtr<mozIStoragePendingStatement> pending;
  1.1392 +  rv = removeStmt->ExecuteAsync(nullptr, getter_AddRefs(pending));
  1.1393 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1394 +
  1.1395 +  GetPermissionsForAppStruct data(aAppId, aBrowserOnly);
  1.1396 +  mPermissionTable.EnumerateEntries(GetPermissionsForApp, &data);
  1.1397 +
  1.1398 +  for (int32_t i=0; i<data.permissions.Count(); ++i) {
  1.1399 +    nsAutoCString host;
  1.1400 +    bool isInBrowserElement;
  1.1401 +    nsAutoCString type;
  1.1402 +
  1.1403 +    data.permissions[i]->GetHost(host);
  1.1404 +    data.permissions[i]->GetIsInBrowserElement(&isInBrowserElement);
  1.1405 +    data.permissions[i]->GetType(type);
  1.1406 +
  1.1407 +    nsCOMPtr<nsIPrincipal> principal;
  1.1408 +    if (NS_FAILED(GetPrincipal(host, aAppId, isInBrowserElement,
  1.1409 +                               getter_AddRefs(principal)))) {
  1.1410 +      NS_ERROR("GetPrincipal() failed!");
  1.1411 +      continue;
  1.1412 +    }
  1.1413 +
  1.1414 +    AddInternal(principal,
  1.1415 +                type,
  1.1416 +                nsIPermissionManager::UNKNOWN_ACTION,
  1.1417 +                0,
  1.1418 +                nsIPermissionManager::EXPIRE_NEVER,
  1.1419 +                0,
  1.1420 +                nsPermissionManager::eNotify,
  1.1421 +                nsPermissionManager::eNoDBOperation);
  1.1422 +  }
  1.1423 +
  1.1424 +  return NS_OK;
  1.1425 +}
  1.1426 +
  1.1427 +PLDHashOperator
  1.1428 +nsPermissionManager::RemoveExpiredPermissionsForAppEnumerator(
  1.1429 +  nsPermissionManager::PermissionHashKey* entry, void* arg)
  1.1430 +{
  1.1431 +  uint32_t* appId = static_cast<uint32_t*>(arg);
  1.1432 +
  1.1433 +  for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) {
  1.1434 +    if (entry->GetKey()->mAppId != *appId) {
  1.1435 +      continue;
  1.1436 +    }
  1.1437 +
  1.1438 +    nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i];
  1.1439 +    if (permEntry.mExpireType != nsIPermissionManager::EXPIRE_SESSION) {
  1.1440 +      continue;
  1.1441 +    }
  1.1442 +
  1.1443 +    if (permEntry.mNonSessionExpireType == nsIPermissionManager::EXPIRE_SESSION) {
  1.1444 +      PermissionEntry oldPermissionEntry = entry->GetPermissions()[i];
  1.1445 +
  1.1446 +      entry->GetPermissions().RemoveElementAt(i);
  1.1447 +
  1.1448 +      gPermissionManager->NotifyObserversWithPermission(entry->GetKey()->mHost,
  1.1449 +                                                        entry->GetKey()->mAppId,
  1.1450 +                                                        entry->GetKey()->mIsInBrowserElement,
  1.1451 +                                                        gPermissionManager->mTypeArray.ElementAt(oldPermissionEntry.mType),
  1.1452 +                                                        oldPermissionEntry.mPermission,
  1.1453 +                                                        oldPermissionEntry.mExpireType,
  1.1454 +                                                        oldPermissionEntry.mExpireTime,
  1.1455 +                                                        MOZ_UTF16("deleted"));
  1.1456 +      --i;
  1.1457 +      continue;
  1.1458 +    }
  1.1459 +
  1.1460 +    permEntry.mPermission = permEntry.mNonSessionPermission;
  1.1461 +    permEntry.mExpireType = permEntry.mNonSessionExpireType;
  1.1462 +    permEntry.mExpireTime = permEntry.mNonSessionExpireTime;
  1.1463 +
  1.1464 +    gPermissionManager->NotifyObserversWithPermission(entry->GetKey()->mHost,
  1.1465 +                                                      entry->GetKey()->mAppId,
  1.1466 +                                                      entry->GetKey()->mIsInBrowserElement,
  1.1467 +                                                      gPermissionManager->mTypeArray.ElementAt(permEntry.mType),
  1.1468 +                                                      permEntry.mPermission,
  1.1469 +                                                      permEntry.mExpireType,
  1.1470 +                                                      permEntry.mExpireTime,
  1.1471 +                                                      MOZ_UTF16("changed"));
  1.1472 +  }
  1.1473 +
  1.1474 +  return PL_DHASH_NEXT;
  1.1475 +}
  1.1476 +
  1.1477 +nsresult
  1.1478 +nsPermissionManager::RemoveExpiredPermissionsForApp(uint32_t aAppId)
  1.1479 +{
  1.1480 +  ENSURE_NOT_CHILD_PROCESS;
  1.1481 +
  1.1482 +  if (aAppId != nsIScriptSecurityManager::NO_APP_ID) {
  1.1483 +    mPermissionTable.EnumerateEntries(RemoveExpiredPermissionsForAppEnumerator, &aAppId);
  1.1484 +  }
  1.1485 +
  1.1486 +  return NS_OK;
  1.1487 +}
  1.1488 +
  1.1489 +//*****************************************************************************
  1.1490 +//*** nsPermissionManager private methods
  1.1491 +//*****************************************************************************
  1.1492 +
  1.1493 +nsresult
  1.1494 +nsPermissionManager::RemoveAllFromMemory()
  1.1495 +{
  1.1496 +  mLargestID = 0;
  1.1497 +  mTypeArray.Clear();
  1.1498 +  mPermissionTable.Clear();
  1.1499 +
  1.1500 +  return NS_OK;
  1.1501 +}
  1.1502 +
  1.1503 +// Returns -1 on failure
  1.1504 +int32_t
  1.1505 +nsPermissionManager::GetTypeIndex(const char *aType,
  1.1506 +                                  bool        aAdd)
  1.1507 +{
  1.1508 +  for (uint32_t i = 0; i < mTypeArray.Length(); ++i)
  1.1509 +    if (mTypeArray[i].Equals(aType))
  1.1510 +      return i;
  1.1511 +
  1.1512 +  if (!aAdd) {
  1.1513 +    // Not found, but that is ok - we were just looking.
  1.1514 +    return -1;
  1.1515 +  }
  1.1516 +
  1.1517 +  // This type was not registered before.
  1.1518 +  // append it to the array, without copy-constructing the string
  1.1519 +  nsCString *elem = mTypeArray.AppendElement();
  1.1520 +  if (!elem)
  1.1521 +    return -1;
  1.1522 +
  1.1523 +  elem->Assign(aType);
  1.1524 +  return mTypeArray.Length() - 1;
  1.1525 +}
  1.1526 +
  1.1527 +// wrapper function for mangling (host,type,perm,expireType,expireTime)
  1.1528 +// set into an nsIPermission.
  1.1529 +void
  1.1530 +nsPermissionManager::NotifyObserversWithPermission(const nsACString &aHost,
  1.1531 +                                                   uint32_t          aAppId,
  1.1532 +                                                   bool              aIsInBrowserElement,
  1.1533 +                                                   const nsCString  &aType,
  1.1534 +                                                   uint32_t          aPermission,
  1.1535 +                                                   uint32_t          aExpireType,
  1.1536 +                                                   int64_t           aExpireTime,
  1.1537 +                                                   const char16_t  *aData)
  1.1538 +{
  1.1539 +  nsCOMPtr<nsIPermission> permission =
  1.1540 +    new nsPermission(aHost, aAppId, aIsInBrowserElement, aType, aPermission,
  1.1541 +                     aExpireType, aExpireTime);
  1.1542 +  if (permission)
  1.1543 +    NotifyObservers(permission, aData);
  1.1544 +}
  1.1545 +
  1.1546 +// notify observers that the permission list changed. there are four possible
  1.1547 +// values for aData:
  1.1548 +// "deleted" means a permission was deleted. aPermission is the deleted permission.
  1.1549 +// "added"   means a permission was added. aPermission is the added permission.
  1.1550 +// "changed" means a permission was altered. aPermission is the new permission.
  1.1551 +// "cleared" means the entire permission list was cleared. aPermission is null.
  1.1552 +void
  1.1553 +nsPermissionManager::NotifyObservers(nsIPermission   *aPermission,
  1.1554 +                                     const char16_t *aData)
  1.1555 +{
  1.1556 +  if (mObserverService)
  1.1557 +    mObserverService->NotifyObservers(aPermission,
  1.1558 +                                      kPermissionChangeNotification,
  1.1559 +                                      aData);
  1.1560 +}
  1.1561 +
  1.1562 +nsresult
  1.1563 +nsPermissionManager::Read()
  1.1564 +{
  1.1565 +  ENSURE_NOT_CHILD_PROCESS;
  1.1566 +
  1.1567 +  nsresult rv;
  1.1568 +
  1.1569 +  // delete expired permissions before we read in the db
  1.1570 +  {
  1.1571 +    // this deletion has its own scope so the write lock is released when done.
  1.1572 +    nsCOMPtr<mozIStorageStatement> stmtDeleteExpired;
  1.1573 +    rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
  1.1574 +          "DELETE FROM moz_hosts WHERE expireType = ?1 AND expireTime <= ?2"),
  1.1575 +          getter_AddRefs(stmtDeleteExpired));
  1.1576 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1577 +
  1.1578 +    rv = stmtDeleteExpired->BindInt32ByIndex(0, nsIPermissionManager::EXPIRE_TIME);
  1.1579 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1580 +
  1.1581 +    rv = stmtDeleteExpired->BindInt64ByIndex(1, PR_Now() / 1000);
  1.1582 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1583 +
  1.1584 +    bool hasResult;
  1.1585 +    rv = stmtDeleteExpired->ExecuteStep(&hasResult);
  1.1586 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1587 +  }
  1.1588 +
  1.1589 +  nsCOMPtr<mozIStorageStatement> stmt;
  1.1590 +  rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
  1.1591 +    "SELECT id, host, type, permission, expireType, expireTime, appId, isInBrowserElement "
  1.1592 +    "FROM moz_hosts"), getter_AddRefs(stmt));
  1.1593 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1594 +
  1.1595 +  int64_t id;
  1.1596 +  nsAutoCString host, type;
  1.1597 +  uint32_t permission;
  1.1598 +  uint32_t expireType;
  1.1599 +  int64_t expireTime;
  1.1600 +  uint32_t appId;
  1.1601 +  bool isInBrowserElement;
  1.1602 +  bool hasResult;
  1.1603 +  bool readError = false;
  1.1604 +
  1.1605 +  while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
  1.1606 +    // explicitly set our entry id counter for use in AddInternal(),
  1.1607 +    // and keep track of the largest id so we know where to pick up.
  1.1608 +    id = stmt->AsInt64(0);
  1.1609 +    if (id > mLargestID)
  1.1610 +      mLargestID = id;
  1.1611 +
  1.1612 +    rv = stmt->GetUTF8String(1, host);
  1.1613 +    if (NS_FAILED(rv)) {
  1.1614 +      readError = true;
  1.1615 +      continue;
  1.1616 +    }
  1.1617 +
  1.1618 +    rv = stmt->GetUTF8String(2, type);
  1.1619 +    if (NS_FAILED(rv)) {
  1.1620 +      readError = true;
  1.1621 +      continue;
  1.1622 +    }
  1.1623 +
  1.1624 +    permission = stmt->AsInt32(3);
  1.1625 +    expireType = stmt->AsInt32(4);
  1.1626 +
  1.1627 +    // convert into int64_t value (milliseconds)
  1.1628 +    expireTime = stmt->AsInt64(5);
  1.1629 +
  1.1630 +    if (stmt->AsInt64(6) < 0) {
  1.1631 +      readError = true;
  1.1632 +      continue;
  1.1633 +    }
  1.1634 +    appId = static_cast<uint32_t>(stmt->AsInt64(6));
  1.1635 +    isInBrowserElement = static_cast<bool>(stmt->AsInt32(7));
  1.1636 +
  1.1637 +    nsCOMPtr<nsIPrincipal> principal;
  1.1638 +    nsresult rv = GetPrincipal(host, appId, isInBrowserElement, getter_AddRefs(principal));
  1.1639 +    if (NS_FAILED(rv)) {
  1.1640 +      readError = true;
  1.1641 +      continue;
  1.1642 +    }
  1.1643 +
  1.1644 +    rv = AddInternal(principal, type, permission, id, expireType, expireTime,
  1.1645 +                     eDontNotify, eNoDBOperation);
  1.1646 +    if (NS_FAILED(rv)) {
  1.1647 +      readError = true;
  1.1648 +      continue;
  1.1649 +    }
  1.1650 +  }
  1.1651 +
  1.1652 +  if (readError) {
  1.1653 +    NS_ERROR("Error occured while reading the permissions database!");
  1.1654 +    return NS_ERROR_FAILURE;
  1.1655 +  }
  1.1656 +
  1.1657 +  return NS_OK;
  1.1658 +}
  1.1659 +
  1.1660 +static const char kMatchTypeHost[] = "host";
  1.1661 +
  1.1662 +nsresult
  1.1663 +nsPermissionManager::Import()
  1.1664 +{
  1.1665 +  ENSURE_NOT_CHILD_PROCESS;
  1.1666 +
  1.1667 +  nsresult rv;
  1.1668 +
  1.1669 +  nsCOMPtr<nsIFile> permissionsFile;
  1.1670 +  rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(permissionsFile));
  1.1671 +  if (NS_FAILED(rv)) return rv;
  1.1672 +
  1.1673 +  rv = permissionsFile->AppendNative(NS_LITERAL_CSTRING(kHostpermFileName));
  1.1674 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1675 +
  1.1676 +  nsCOMPtr<nsIInputStream> fileInputStream;
  1.1677 +  rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream),
  1.1678 +                                  permissionsFile);
  1.1679 +  if (NS_FAILED(rv)) return rv;
  1.1680 +
  1.1681 +  nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(fileInputStream, &rv);
  1.1682 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1683 +
  1.1684 +  // start a transaction on the storage db, to optimize insertions.
  1.1685 +  // transaction will automically commit on completion
  1.1686 +  mozStorageTransaction transaction(mDBConn, true);
  1.1687 +
  1.1688 +  /* format is:
  1.1689 +   * matchtype \t type \t permission \t host
  1.1690 +   * Only "host" is supported for matchtype
  1.1691 +   * type is a string that identifies the type of permission (e.g. "cookie")
  1.1692 +   * permission is an integer between 1 and 15
  1.1693 +   */
  1.1694 +
  1.1695 +  nsAutoCString buffer;
  1.1696 +  bool isMore = true;
  1.1697 +  while (isMore && NS_SUCCEEDED(lineInputStream->ReadLine(buffer, &isMore))) {
  1.1698 +    if (buffer.IsEmpty() || buffer.First() == '#') {
  1.1699 +      continue;
  1.1700 +    }
  1.1701 +
  1.1702 +    nsTArray<nsCString> lineArray;
  1.1703 +
  1.1704 +    // Split the line at tabs
  1.1705 +    ParseString(buffer, '\t', lineArray);
  1.1706 +
  1.1707 +    if (lineArray[0].EqualsLiteral(kMatchTypeHost) &&
  1.1708 +        lineArray.Length() == 4) {
  1.1709 +
  1.1710 +      nsresult error;
  1.1711 +      uint32_t permission = lineArray[2].ToInteger(&error);
  1.1712 +      if (NS_FAILED(error))
  1.1713 +        continue;
  1.1714 +
  1.1715 +      // hosts might be encoded in UTF8; switch them to ACE to be consistent
  1.1716 +      if (!IsASCII(lineArray[3])) {
  1.1717 +        rv = NormalizeToACE(lineArray[3]);
  1.1718 +        if (NS_FAILED(rv))
  1.1719 +          continue;
  1.1720 +      }
  1.1721 +
  1.1722 +      nsCOMPtr<nsIPrincipal> principal;
  1.1723 +      nsresult rv = GetPrincipal(lineArray[3], getter_AddRefs(principal));
  1.1724 +      NS_ENSURE_SUCCESS(rv, rv);
  1.1725 +
  1.1726 +      rv = AddInternal(principal, lineArray[1], permission, 0,
  1.1727 +                       nsIPermissionManager::EXPIRE_NEVER, 0, eDontNotify, eWriteToDB);
  1.1728 +      NS_ENSURE_SUCCESS(rv, rv);
  1.1729 +    }
  1.1730 +  }
  1.1731 +
  1.1732 +  // we're done importing - delete the old file
  1.1733 +  permissionsFile->Remove(false);
  1.1734 +
  1.1735 +  return NS_OK;
  1.1736 +}
  1.1737 +
  1.1738 +nsresult
  1.1739 +nsPermissionManager::NormalizeToACE(nsCString &aHost)
  1.1740 +{
  1.1741 +  // lazily init the IDN service
  1.1742 +  if (!mIDNService) {
  1.1743 +    nsresult rv;
  1.1744 +    mIDNService = do_GetService(NS_IDNSERVICE_CONTRACTID, &rv);
  1.1745 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1746 +  }
  1.1747 +
  1.1748 +  return mIDNService->ConvertUTF8toACE(aHost, aHost);
  1.1749 +}
  1.1750 +
  1.1751 +void
  1.1752 +nsPermissionManager::UpdateDB(OperationType aOp,
  1.1753 +                              mozIStorageAsyncStatement* aStmt,
  1.1754 +                              int64_t aID,
  1.1755 +                              const nsACString &aHost,
  1.1756 +                              const nsACString &aType,
  1.1757 +                              uint32_t aPermission,
  1.1758 +                              uint32_t aExpireType,
  1.1759 +                              int64_t aExpireTime,
  1.1760 +                              uint32_t aAppId,
  1.1761 +                              bool aIsInBrowserElement)
  1.1762 +{
  1.1763 +  ENSURE_NOT_CHILD_PROCESS_NORET;
  1.1764 +
  1.1765 +  nsresult rv;
  1.1766 +
  1.1767 +  // no statement is ok - just means we don't have a profile
  1.1768 +  if (!aStmt)
  1.1769 +    return;
  1.1770 +
  1.1771 +  switch (aOp) {
  1.1772 +  case eOperationAdding:
  1.1773 +    {
  1.1774 +      rv = aStmt->BindInt64ByIndex(0, aID);
  1.1775 +      if (NS_FAILED(rv)) break;
  1.1776 +
  1.1777 +      rv = aStmt->BindUTF8StringByIndex(1, aHost);
  1.1778 +      if (NS_FAILED(rv)) break;
  1.1779 +
  1.1780 +      rv = aStmt->BindUTF8StringByIndex(2, aType);
  1.1781 +      if (NS_FAILED(rv)) break;
  1.1782 +
  1.1783 +      rv = aStmt->BindInt32ByIndex(3, aPermission);
  1.1784 +      if (NS_FAILED(rv)) break;
  1.1785 +
  1.1786 +      rv = aStmt->BindInt32ByIndex(4, aExpireType);
  1.1787 +      if (NS_FAILED(rv)) break;
  1.1788 +
  1.1789 +      rv = aStmt->BindInt64ByIndex(5, aExpireTime);
  1.1790 +      if (NS_FAILED(rv)) break;
  1.1791 +
  1.1792 +      rv = aStmt->BindInt64ByIndex(6, aAppId);
  1.1793 +      if (NS_FAILED(rv)) break;
  1.1794 +
  1.1795 +      rv = aStmt->BindInt64ByIndex(7, aIsInBrowserElement);
  1.1796 +      break;
  1.1797 +    }
  1.1798 +
  1.1799 +  case eOperationRemoving:
  1.1800 +    {
  1.1801 +      rv = aStmt->BindInt64ByIndex(0, aID);
  1.1802 +      break;
  1.1803 +    }
  1.1804 +
  1.1805 +  case eOperationChanging:
  1.1806 +    {
  1.1807 +      rv = aStmt->BindInt64ByIndex(0, aID);
  1.1808 +      if (NS_FAILED(rv)) break;
  1.1809 +
  1.1810 +      rv = aStmt->BindInt32ByIndex(1, aPermission);
  1.1811 +      if (NS_FAILED(rv)) break;
  1.1812 +
  1.1813 +      rv = aStmt->BindInt32ByIndex(2, aExpireType);
  1.1814 +      if (NS_FAILED(rv)) break;
  1.1815 +
  1.1816 +      rv = aStmt->BindInt64ByIndex(3, aExpireTime);
  1.1817 +      break;
  1.1818 +    }
  1.1819 +
  1.1820 +  default:
  1.1821 +    {
  1.1822 +      NS_NOTREACHED("need a valid operation in UpdateDB()!");
  1.1823 +      rv = NS_ERROR_UNEXPECTED;
  1.1824 +      break;
  1.1825 +    }
  1.1826 +  }
  1.1827 +
  1.1828 +  if (NS_FAILED(rv)) {
  1.1829 +    NS_WARNING("db change failed!");
  1.1830 +    return;
  1.1831 +  }
  1.1832 +
  1.1833 +  nsCOMPtr<mozIStoragePendingStatement> pending;
  1.1834 +  rv = aStmt->ExecuteAsync(nullptr, getter_AddRefs(pending));
  1.1835 +  MOZ_ASSERT(NS_SUCCEEDED(rv));
  1.1836 +}
  1.1837 +
  1.1838 +NS_IMETHODIMP
  1.1839 +nsPermissionManager::AddrefAppId(uint32_t aAppId)
  1.1840 +{
  1.1841 +  if (aAppId == nsIScriptSecurityManager::NO_APP_ID) {
  1.1842 +    return NS_OK;
  1.1843 +  }
  1.1844 +
  1.1845 +  bool found = false;
  1.1846 +  for (uint32_t i = 0; i < mAppIdRefcounts.Length(); ++i) {
  1.1847 +    if (mAppIdRefcounts[i].mAppId == aAppId) {
  1.1848 +      ++mAppIdRefcounts[i].mCounter;
  1.1849 +      found = true;
  1.1850 +      break;
  1.1851 +    }
  1.1852 +  }
  1.1853 +
  1.1854 +  if (!found) {
  1.1855 +    ApplicationCounter app = { aAppId, 1 };
  1.1856 +    mAppIdRefcounts.AppendElement(app);
  1.1857 +  }
  1.1858 +
  1.1859 +  return NS_OK;
  1.1860 +}
  1.1861 +
  1.1862 +NS_IMETHODIMP
  1.1863 +nsPermissionManager::ReleaseAppId(uint32_t aAppId)
  1.1864 +{
  1.1865 +  // An app has been released, maybe we have to reset its session.
  1.1866 +
  1.1867 +  if (aAppId == nsIScriptSecurityManager::NO_APP_ID) {
  1.1868 +    return NS_OK;
  1.1869 +  }
  1.1870 +
  1.1871 +  for (uint32_t i = 0; i < mAppIdRefcounts.Length(); ++i) {
  1.1872 +    if (mAppIdRefcounts[i].mAppId == aAppId) {
  1.1873 +      --mAppIdRefcounts[i].mCounter;
  1.1874 +
  1.1875 +      if (!mAppIdRefcounts[i].mCounter) {
  1.1876 +        mAppIdRefcounts.RemoveElementAt(i);
  1.1877 +        return RemoveExpiredPermissionsForApp(aAppId);
  1.1878 +      }
  1.1879 +
  1.1880 +      break;
  1.1881 +    }
  1.1882 +  }
  1.1883 +
  1.1884 +  return NS_OK;
  1.1885 +}
  1.1886 +
  1.1887 +NS_IMETHODIMP
  1.1888 +nsPermissionManager::UpdateExpireTime(nsIPrincipal* aPrincipal,
  1.1889 +                                     const char* aType,
  1.1890 +                                     bool aExactHostMatch,
  1.1891 +                                     uint64_t aSessionExpireTime,
  1.1892 +                                     uint64_t aPersistentExpireTime)
  1.1893 +{
  1.1894 +  NS_ENSURE_ARG_POINTER(aPrincipal);
  1.1895 +  NS_ENSURE_ARG_POINTER(aType);
  1.1896 +
  1.1897 +  uint64_t nowms = PR_Now() / 1000;
  1.1898 +  if (aSessionExpireTime < nowms || aPersistentExpireTime < nowms) {
  1.1899 +    return NS_ERROR_INVALID_ARG;
  1.1900 +  }
  1.1901 +
  1.1902 +  if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
  1.1903 +    return NS_OK;
  1.1904 +  }
  1.1905 +
  1.1906 +  // Setting the expire time of an nsEP is non-sensical.
  1.1907 +  if (IsExpandedPrincipal(aPrincipal)) {
  1.1908 +    return NS_ERROR_INVALID_ARG;
  1.1909 +  }
  1.1910 +
  1.1911 +  nsAutoCString host;
  1.1912 +  nsresult rv = GetHostForPrincipal(aPrincipal, host);
  1.1913 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1914 +
  1.1915 +  int32_t typeIndex = GetTypeIndex(aType, false);
  1.1916 +  // If type == -1, the type isn't known,
  1.1917 +  // so just return NS_OK
  1.1918 +  if (typeIndex == -1) return NS_OK;
  1.1919 +
  1.1920 +  uint32_t appId;
  1.1921 +  rv = aPrincipal->GetAppId(&appId);
  1.1922 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1923 +
  1.1924 +  bool isInBrowserElement;
  1.1925 +  rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
  1.1926 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1927 +
  1.1928 +  PermissionHashKey* entry = GetPermissionHashKey(host, appId, isInBrowserElement,
  1.1929 +                                                  typeIndex, aExactHostMatch);
  1.1930 +  if (!entry) {
  1.1931 +    return NS_OK;
  1.1932 +  }
  1.1933 +
  1.1934 +  int32_t idx = entry->GetPermissionIndex(typeIndex);
  1.1935 +  if (-1 == idx) {
  1.1936 +    return NS_OK;
  1.1937 +  }
  1.1938 +
  1.1939 +  PermissionEntry& perm = entry->GetPermissions()[idx];
  1.1940 +  if (perm.mExpireType == EXPIRE_TIME) {
  1.1941 +    perm.mExpireTime = aPersistentExpireTime;
  1.1942 +  } else if (perm.mExpireType == EXPIRE_SESSION && perm.mExpireTime != 0) {
  1.1943 +    perm.mExpireTime = aSessionExpireTime;
  1.1944 +  }
  1.1945 +  return NS_OK;
  1.1946 +}

mercurial