dom/quota/CheckQuotaHelper.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/quota/CheckQuotaHelper.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,283 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=2 et sw=2 tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "CheckQuotaHelper.h"
    1.11 +
    1.12 +#include "nsIDOMWindow.h"
    1.13 +#include "nsIObserverService.h"
    1.14 +#include "nsIPermissionManager.h"
    1.15 +#include "nsIPrincipal.h"
    1.16 +#include "nsIScriptObjectPrincipal.h"
    1.17 +#include "nsIURI.h"
    1.18 +#include "nsPIDOMWindow.h"
    1.19 +
    1.20 +#include "mozilla/dom/quota/QuotaManager.h"
    1.21 +#include "mozilla/Services.h"
    1.22 +#include "nsContentUtils.h"
    1.23 +#include "nsNetUtil.h"
    1.24 +#include "nsThreadUtils.h"
    1.25 +#include "nsXULAppAPI.h"
    1.26 +
    1.27 +#define TOPIC_QUOTA_PROMPT "indexedDB-quota-prompt"
    1.28 +#define TOPIC_QUOTA_RESPONSE "indexedDB-quota-response"
    1.29 +#define TOPIC_QUOTA_CANCEL "indexedDB-quota-cancel"
    1.30 +
    1.31 +USING_QUOTA_NAMESPACE
    1.32 +using namespace mozilla::services;
    1.33 +using mozilla::MutexAutoLock;
    1.34 +
    1.35 +namespace {
    1.36 +
    1.37 +inline
    1.38 +uint32_t
    1.39 +GetQuotaPermissionFromWindow(nsIDOMWindow* aWindow)
    1.40 +{
    1.41 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
    1.42 +
    1.43 +  nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(aWindow));
    1.44 +  NS_ENSURE_TRUE(sop, nsIPermissionManager::DENY_ACTION);
    1.45 +
    1.46 +  return CheckQuotaHelper::GetQuotaPermission(sop->GetPrincipal());
    1.47 +}
    1.48 +
    1.49 +} // anonymous namespace
    1.50 +
    1.51 +CheckQuotaHelper::CheckQuotaHelper(nsPIDOMWindow* aWindow,
    1.52 +                                   mozilla::Mutex& aMutex)
    1.53 +: mWindow(aWindow),
    1.54 +  mMutex(aMutex),
    1.55 +  mCondVar(mMutex, "CheckQuotaHelper::mCondVar"),
    1.56 +  mPromptResult(0),
    1.57 +  mWaiting(true),
    1.58 +  mHasPrompted(false)
    1.59 +{
    1.60 +  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
    1.61 +  mMutex.AssertCurrentThreadOwns();
    1.62 +}
    1.63 +
    1.64 +bool
    1.65 +CheckQuotaHelper::PromptAndReturnQuotaIsDisabled()
    1.66 +{
    1.67 +  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
    1.68 +  mMutex.AssertCurrentThreadOwns();
    1.69 +
    1.70 +  while (mWaiting) {
    1.71 +    mCondVar.Wait();
    1.72 +  }
    1.73 +
    1.74 +  NS_ASSERTION(!mWindow, "This should always be null here!");
    1.75 +
    1.76 +  NS_ASSERTION(mPromptResult == nsIPermissionManager::ALLOW_ACTION ||
    1.77 +               mPromptResult == nsIPermissionManager::DENY_ACTION ||
    1.78 +               mPromptResult == nsIPermissionManager::UNKNOWN_ACTION,
    1.79 +               "Unknown permission!");
    1.80 +
    1.81 +  return mPromptResult == nsIPermissionManager::ALLOW_ACTION;
    1.82 +}
    1.83 +
    1.84 +void
    1.85 +CheckQuotaHelper::Cancel()
    1.86 +{
    1.87 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
    1.88 +  mMutex.AssertCurrentThreadOwns();
    1.89 +
    1.90 +  if (mWaiting && !mHasPrompted) {
    1.91 +    MutexAutoUnlock unlock(mMutex);
    1.92 +
    1.93 +    // First close any prompts that are open for this window.
    1.94 +    nsCOMPtr<nsIObserverService> obs = GetObserverService();
    1.95 +    NS_WARN_IF_FALSE(obs, "Failed to get observer service!");
    1.96 +    if (obs && NS_FAILED(obs->NotifyObservers(static_cast<nsIRunnable*>(this),
    1.97 +                                              TOPIC_QUOTA_CANCEL, nullptr))) {
    1.98 +      NS_WARNING("Failed to notify observers!");
    1.99 +    }
   1.100 +
   1.101 +    // If that didn't trigger an Observe callback (maybe the window had already
   1.102 +    // died?) then go ahead and do it manually.
   1.103 +    if (!mHasPrompted) {
   1.104 +      nsAutoString response;
   1.105 +      response.AppendInt(nsIPermissionManager::UNKNOWN_ACTION);
   1.106 +
   1.107 +      if (NS_SUCCEEDED(Observe(nullptr, TOPIC_QUOTA_RESPONSE, response.get()))) {
   1.108 +        NS_ASSERTION(mHasPrompted, "Should have set this in Observe!");
   1.109 +      }
   1.110 +      else {
   1.111 +        NS_WARNING("Failed to notify!");
   1.112 +      }
   1.113 +    }
   1.114 +  }
   1.115 +}
   1.116 +
   1.117 +// static
   1.118 +uint32_t
   1.119 +CheckQuotaHelper::GetQuotaPermission(nsIPrincipal* aPrincipal)
   1.120 +{
   1.121 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   1.122 +  NS_ASSERTION(aPrincipal, "Null principal!");
   1.123 +
   1.124 +  NS_ASSERTION(!nsContentUtils::IsSystemPrincipal(aPrincipal),
   1.125 +               "Chrome windows shouldn't track quota!");
   1.126 +
   1.127 +  nsCOMPtr<nsIPermissionManager> pm =
   1.128 +    do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
   1.129 +  NS_ENSURE_TRUE(pm, nsIPermissionManager::DENY_ACTION);
   1.130 +
   1.131 +  uint32_t permission;
   1.132 +  nsresult rv = pm->TestPermissionFromPrincipal(aPrincipal,
   1.133 +                                                PERMISSION_STORAGE_UNLIMITED,
   1.134 +                                                &permission);
   1.135 +  NS_ENSURE_SUCCESS(rv, nsIPermissionManager::DENY_ACTION);
   1.136 +
   1.137 +  return permission;
   1.138 +}
   1.139 +
   1.140 +NS_IMPL_ISUPPORTS(CheckQuotaHelper, nsIRunnable,
   1.141 +                  nsIInterfaceRequestor,
   1.142 +                  nsIObserver)
   1.143 +
   1.144 +NS_IMETHODIMP
   1.145 +CheckQuotaHelper::Run()
   1.146 +{
   1.147 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   1.148 +
   1.149 +  nsresult rv = NS_OK;
   1.150 +
   1.151 +  if (NS_SUCCEEDED(rv)) {
   1.152 +    if (!mHasPrompted) {
   1.153 +      mPromptResult = GetQuotaPermissionFromWindow(mWindow);
   1.154 +    }
   1.155 +
   1.156 +    if (mHasPrompted) {
   1.157 +      // Add permissions to the database, but only if we are in the parent
   1.158 +      // process (if we are in the child process, we have already
   1.159 +      // set the permission when the prompt was shown in the parent, as
   1.160 +      // we cannot set the permission from the child).
   1.161 +      if (mPromptResult != nsIPermissionManager::UNKNOWN_ACTION &&
   1.162 +          XRE_GetProcessType() == GeckoProcessType_Default) {
   1.163 +        nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(mWindow);
   1.164 +        NS_ENSURE_TRUE(sop, NS_ERROR_FAILURE);
   1.165 +
   1.166 +        nsCOMPtr<nsIPermissionManager> permissionManager =
   1.167 +          do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
   1.168 +        NS_ENSURE_STATE(permissionManager);
   1.169 +
   1.170 +        rv = permissionManager->AddFromPrincipal(sop->GetPrincipal(),
   1.171 +                                                 PERMISSION_STORAGE_UNLIMITED,
   1.172 +                                                 mPromptResult,
   1.173 +                                                 nsIPermissionManager::EXPIRE_NEVER, 0);
   1.174 +        NS_ENSURE_SUCCESS(rv, rv);
   1.175 +      }
   1.176 +    }
   1.177 +    else if (mPromptResult == nsIPermissionManager::UNKNOWN_ACTION) {
   1.178 +      uint32_t quota = QuotaManager::GetStorageQuotaMB();
   1.179 +
   1.180 +      nsString quotaString;
   1.181 +      quotaString.AppendInt(quota);
   1.182 +
   1.183 +      nsCOMPtr<nsIObserverService> obs = GetObserverService();
   1.184 +      NS_ENSURE_STATE(obs);
   1.185 +
   1.186 +      // We have to watch to make sure that the window doesn't go away without
   1.187 +      // responding to us. Otherwise our database threads will hang.
   1.188 +      rv = obs->AddObserver(this, DOM_WINDOW_DESTROYED_TOPIC, false);
   1.189 +      NS_ENSURE_SUCCESS(rv, rv);
   1.190 +
   1.191 +      rv = obs->NotifyObservers(static_cast<nsIRunnable*>(this),
   1.192 +                                TOPIC_QUOTA_PROMPT, quotaString.get());
   1.193 +      NS_ENSURE_SUCCESS(rv, rv);
   1.194 +
   1.195 +      return NS_OK;
   1.196 +    }
   1.197 +  }
   1.198 +
   1.199 +  MutexAutoLock lock(mMutex);
   1.200 +
   1.201 +  NS_ASSERTION(mWaiting, "Huh?!");
   1.202 +
   1.203 +  // This should never be used again.
   1.204 +  mWindow = nullptr;
   1.205 +
   1.206 +  mWaiting = false;
   1.207 +  mCondVar.NotifyAll();
   1.208 +
   1.209 +  return NS_OK;
   1.210 +}
   1.211 +
   1.212 +NS_IMETHODIMP
   1.213 +CheckQuotaHelper::GetInterface(const nsIID& aIID,
   1.214 +                               void** aResult)
   1.215 +{
   1.216 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   1.217 +
   1.218 +  if (aIID.Equals(NS_GET_IID(nsIObserver))) {
   1.219 +    return QueryInterface(aIID, aResult);
   1.220 +  }
   1.221 +
   1.222 +  if (aIID.Equals(NS_GET_IID(nsIDOMWindow))) {
   1.223 +    return mWindow->QueryInterface(aIID, aResult);
   1.224 +  }
   1.225 +
   1.226 +  *aResult = nullptr;
   1.227 +  return NS_ERROR_NOT_AVAILABLE;
   1.228 +}
   1.229 +
   1.230 +NS_IMETHODIMP
   1.231 +CheckQuotaHelper::Observe(nsISupports* aSubject,
   1.232 +                          const char* aTopic,
   1.233 +                          const char16_t* aData)
   1.234 +{
   1.235 +  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   1.236 +
   1.237 +  nsresult rv;
   1.238 +
   1.239 +  if (!strcmp(aTopic, TOPIC_QUOTA_RESPONSE)) {
   1.240 +    if (!mHasPrompted) {
   1.241 +      mHasPrompted = true;
   1.242 +
   1.243 +      mPromptResult = nsDependentString(aData).ToInteger(&rv);
   1.244 +      NS_ENSURE_SUCCESS(rv, rv);
   1.245 +
   1.246 +      rv = NS_DispatchToCurrentThread(this);
   1.247 +      NS_ENSURE_SUCCESS(rv, rv);
   1.248 +
   1.249 +      // We no longer care about the window here.
   1.250 +      nsCOMPtr<nsIObserverService> obs = GetObserverService();
   1.251 +      NS_ENSURE_STATE(obs);
   1.252 +
   1.253 +      rv = obs->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC);
   1.254 +      NS_ENSURE_SUCCESS(rv, rv);
   1.255 +    }
   1.256 +    return NS_OK;
   1.257 +  }
   1.258 +
   1.259 +  if (!strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC)) {
   1.260 +    NS_ASSERTION(!mHasPrompted, "Should have removed observer before now!");
   1.261 +    NS_ASSERTION(mWindow, "This should never be null!");
   1.262 +
   1.263 +    nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aSubject));
   1.264 +    NS_ENSURE_STATE(window);
   1.265 +
   1.266 +    if (mWindow->GetSerial() == window->GetSerial()) {
   1.267 +      // This is our window, dying, without responding to our prompt! Fake one.
   1.268 +      mHasPrompted = true;
   1.269 +      mPromptResult = nsIPermissionManager::UNKNOWN_ACTION;
   1.270 +
   1.271 +      rv = NS_DispatchToCurrentThread(this);
   1.272 +      NS_ENSURE_SUCCESS(rv, rv);
   1.273 +
   1.274 +      // We no longer care about the window here.
   1.275 +      nsCOMPtr<nsIObserverService> obs = GetObserverService();
   1.276 +      NS_ENSURE_STATE(obs);
   1.277 +
   1.278 +      rv = obs->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC);
   1.279 +      NS_ENSURE_SUCCESS(rv, rv);
   1.280 +    }
   1.281 +    return NS_OK;
   1.282 +  }
   1.283 +
   1.284 +  NS_NOTREACHED("Unexpected topic!");
   1.285 +  return NS_ERROR_UNEXPECTED;
   1.286 +}

mercurial