LCOV - code coverage report
Current view: directory - storage/src - mozStorageService.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 322 264 82.0 %
Date: 2012-06-02 Functions: 58 53 91.4 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
       2                 :  * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
       3                 :  * ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is Oracle Corporation code.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is
      19                 :  *  Oracle Corporation
      20                 :  * Portions created by the Initial Developer are Copyright (C) 2004
      21                 :  * the Initial Developer. All Rights Reserved.
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *   Vladimir Vukicevic <vladimir.vukicevic@oracle.com>
      25                 :  *   Brett Wilson <brettw@gmail.com>
      26                 :  *   Shawn Wilsher <me@shawnwilsher.com>
      27                 :  *   Drew Willcoxon <adw@mozilla.com>
      28                 :  *
      29                 :  * Alternatively, the contents of this file may be used under the terms of
      30                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      31                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      32                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      33                 :  * of those above. If you wish to allow use of your version of this file only
      34                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      35                 :  * use your version of this file under the terms of the MPL, indicate your
      36                 :  * decision by deleting the provisions above and replace them with the notice
      37                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      38                 :  * the provisions above, a recipient may use your version of this file under
      39                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      40                 :  *
      41                 :  * ***** END LICENSE BLOCK ***** */
      42                 : 
      43                 : #include "mozStorageService.h"
      44                 : #include "mozStorageConnection.h"
      45                 : #include "prinit.h"
      46                 : #include "pratom.h"
      47                 : #include "nsAutoPtr.h"
      48                 : #include "nsCollationCID.h"
      49                 : #include "nsEmbedCID.h"
      50                 : #include "nsThreadUtils.h"
      51                 : #include "mozStoragePrivateHelpers.h"
      52                 : #include "nsILocale.h"
      53                 : #include "nsILocaleService.h"
      54                 : #include "nsIXPConnect.h"
      55                 : #include "nsIObserverService.h"
      56                 : #include "mozilla/Services.h"
      57                 : #include "mozilla/Preferences.h"
      58                 : 
      59                 : #include "sqlite3.h"
      60                 : #include "test_quota.h"
      61                 : #include "test_quota.c"
      62                 : 
      63                 : #ifdef SQLITE_OS_WIN
      64                 : // "windows.h" was included and it can #define lots of things we care about...
      65                 : #undef CompareString
      66                 : #endif
      67                 : 
      68                 : #include "nsIPromptService.h"
      69                 : #include "nsIMemoryReporter.h"
      70                 : 
      71                 : #include "mozilla/FunctionTimer.h"
      72                 : #include "mozilla/Util.h"
      73                 : 
      74                 : namespace {
      75                 : 
      76                 : class QuotaCallbackData
      77                 : {
      78                 : public:
      79              50 :   QuotaCallbackData(mozIStorageQuotaCallback *aCallback,
      80                 :                     nsISupports *aUserData)
      81              50 :   : callback(aCallback), userData(aUserData)
      82                 :   {
      83              50 :     MOZ_COUNT_CTOR(QuotaCallbackData);
      84              50 :   }
      85                 : 
      86              50 :   ~QuotaCallbackData()
      87              50 :   {
      88              50 :     MOZ_COUNT_DTOR(QuotaCallbackData);
      89              50 :   }
      90                 : 
      91               0 :   static void Callback(const char *zFilename,
      92                 :                        sqlite3_int64 *piLimit,
      93                 :                        sqlite3_int64 iSize,
      94                 :                        void *pArg)
      95                 :   {
      96               0 :     NS_ASSERTION(zFilename && strlen(zFilename), "Null or empty filename!");
      97               0 :     NS_ASSERTION(piLimit, "Null pointer!");
      98                 : 
      99               0 :     QuotaCallbackData *data = static_cast<QuotaCallbackData*>(pArg);
     100               0 :     if (!data) {
     101                 :       // No callback specified, return immediately.
     102               0 :       return;
     103                 :     }
     104                 : 
     105               0 :     NS_ASSERTION(data->callback, "Should never have a null callback!");
     106                 : 
     107               0 :     nsDependentCString filename(zFilename);
     108                 : 
     109                 :     PRInt64 newLimit;
     110               0 :     if (NS_SUCCEEDED(data->callback->QuotaExceeded(filename, *piLimit,
     111                 :                                                    iSize, data->userData,
     112                 :                                                    &newLimit))) {
     113               0 :       *piLimit = newLimit;
     114                 :     }
     115                 :   }
     116                 : 
     117              50 :   static void Destroy(void *aUserData)
     118                 :   {
     119              50 :     delete static_cast<QuotaCallbackData*>(aUserData);
     120              50 :   }
     121                 : 
     122                 : private:
     123                 :   nsCOMPtr<mozIStorageQuotaCallback> callback;
     124                 :   nsCOMPtr<nsISupports> userData;
     125                 : };
     126                 : 
     127                 : } // anonymous namespace
     128                 : 
     129                 : ////////////////////////////////////////////////////////////////////////////////
     130                 : //// Defines
     131                 : 
     132                 : #define PREF_TS_SYNCHRONOUS "toolkit.storage.synchronous"
     133                 : #define PREF_TS_SYNCHRONOUS_DEFAULT 1
     134                 : 
     135                 : namespace mozilla {
     136                 : namespace storage {
     137                 : 
     138                 : ////////////////////////////////////////////////////////////////////////////////
     139                 : //// Memory Reporting
     140                 : 
     141                 : static PRInt64
     142               3 : GetStorageSQLiteMemoryUsed()
     143                 : {
     144               3 :   return ::sqlite3_memory_used();
     145                 : }
     146                 : 
     147                 : // We don't need an "explicit" reporter for total SQLite memory usage, because
     148                 : // the multi-reporter provides reports that add up to the total.  But it's
     149                 : // useful to have the total in the "Other Measurements" list in about:memory,
     150                 : // and more importantly, we also gather the total via telemetry.
     151             821 : NS_MEMORY_REPORTER_IMPLEMENT(StorageSQLite,
     152                 :     "storage-sqlite",
     153                 :     KIND_OTHER,
     154                 :     UNITS_BYTES,
     155                 :     GetStorageSQLiteMemoryUsed,
     156            6472 :     "Memory used by SQLite.")
     157                 : 
     158                 : class StorageSQLiteMultiReporter : public nsIMemoryMultiReporter
     159             794 : {
     160                 : private:
     161                 :   Service *mService;    // a weakref because Service contains a strongref to this
     162                 :   nsCString mStmtDesc;
     163                 :   nsCString mCacheDesc;
     164                 :   nsCString mSchemaDesc;
     165                 : 
     166                 : public:
     167                 :   NS_DECL_ISUPPORTS
     168                 : 
     169             803 :   StorageSQLiteMultiReporter(Service *aService) 
     170             803 :   : mService(aService)
     171                 :   {
     172            1606 :     NS_NAMED_LITERAL_CSTRING(mStmtDesc,
     173                 :       "Memory (approximate) used by all prepared statements used by "
     174                 :       "connections to this database.");
     175                 : 
     176            1606 :     NS_NAMED_LITERAL_CSTRING(mCacheDesc,
     177                 :       "Memory (approximate) used by all pager caches used by connections "
     178                 :       "to this database.");
     179                 : 
     180             803 :     NS_NAMED_LITERAL_CSTRING(mSchemaDesc,
     181                 :       "Memory (approximate) used to store the schema for all databases "
     182                 :       "associated with connections to this database.");
     183             803 :   }
     184                 : 
     185               0 :   NS_IMETHOD GetName(nsACString &aName)
     186                 :   {
     187               0 :       aName.AssignLiteral("storage-sqlite");
     188               0 :       return NS_OK;
     189                 :   }
     190                 : 
     191                 :   // Warning: To get a Connection's measurements requires holding its lock.
     192                 :   // There may be a delay getting the lock if another thread is accessing the
     193                 :   // Connection.  This isn't very nice if CollectReports is called from the
     194                 :   // main thread!  But at the time of writing this function is only called when
     195                 :   // about:memory is loaded (not, for example, when telemetry pings occur) and
     196                 :   // any delays in that case aren't so bad.
     197               3 :   NS_IMETHOD CollectReports(nsIMemoryMultiReporterCallback *aCb,
     198                 :                             nsISupports *aClosure)
     199                 :   {
     200                 :     nsresult rv;
     201               3 :     size_t totalConnSize = 0;
     202                 :     {
     203               6 :       nsTArray<nsRefPtr<Connection> > connections;
     204               3 :       mService->getConnections(connections);
     205                 : 
     206              12 :       for (PRUint32 i = 0; i < connections.Length(); i++) {
     207               9 :         nsRefPtr<Connection> &conn = connections[i];
     208                 : 
     209                 :         // Someone may have closed the Connection, in which case we skip it.
     210                 :         bool isReady;
     211               9 :         (void)conn->GetConnectionReady(&isReady);
     212               9 :         if (!isReady) {
     213               0 :             continue;
     214                 :         }
     215                 : 
     216              18 :         nsCString pathHead("explicit/storage/sqlite/");
     217               9 :         pathHead.Append(conn->getFilename());
     218               9 :         pathHead.AppendLiteral("/");
     219                 : 
     220              18 :         SQLiteMutexAutoLock lockedScope(conn->sharedDBMutex);
     221                 : 
     222                 :         rv = reportConn(aCb, aClosure, *conn.get(), pathHead,
     223               9 :                         NS_LITERAL_CSTRING("stmt"), mStmtDesc,
     224               9 :                         SQLITE_DBSTATUS_STMT_USED, &totalConnSize);
     225               9 :         NS_ENSURE_SUCCESS(rv, rv);
     226                 : 
     227                 :         rv = reportConn(aCb, aClosure, *conn.get(), pathHead,
     228               9 :                         NS_LITERAL_CSTRING("cache"), mCacheDesc,
     229               9 :                         SQLITE_DBSTATUS_CACHE_USED, &totalConnSize);
     230               9 :         NS_ENSURE_SUCCESS(rv, rv);
     231                 : 
     232                 :         rv = reportConn(aCb, aClosure, *conn.get(), pathHead,
     233               9 :                         NS_LITERAL_CSTRING("schema"), mSchemaDesc,
     234               9 :                         SQLITE_DBSTATUS_SCHEMA_USED, &totalConnSize);
     235               9 :         NS_ENSURE_SUCCESS(rv, rv);
     236                 :       }
     237                 :     }
     238                 : 
     239               3 :     PRInt64 other = ::sqlite3_memory_used() - totalConnSize;
     240                 : 
     241               3 :     rv = aCb->Callback(NS_LITERAL_CSTRING(""),
     242               3 :                        NS_LITERAL_CSTRING("explicit/storage/sqlite/other"),
     243                 :                        nsIMemoryReporter::KIND_HEAP,
     244                 :                        nsIMemoryReporter::UNITS_BYTES, other,
     245               3 :                        NS_LITERAL_CSTRING("All unclassified sqlite memory."),
     246               3 :                        aClosure);
     247               3 :     NS_ENSURE_SUCCESS(rv, rv);
     248                 : 
     249               3 :     return NS_OK;
     250                 :   }
     251                 : 
     252               3 :   NS_IMETHOD GetExplicitNonHeap(PRInt64 *aAmount)
     253                 :   {
     254                 :     // This reporter doesn't do any non-heap measurements.
     255               3 :     *aAmount = 0;
     256               3 :     return NS_OK;
     257                 :   }
     258                 : 
     259                 : private:
     260                 :   /**
     261                 :    * Passes a single SQLite memory statistic to a memory multi-reporter
     262                 :    * callback.
     263                 :    *
     264                 :    * @param aCallback
     265                 :    *        The callback.
     266                 :    * @param aClosure
     267                 :    *        The closure for the callback.
     268                 :    * @param aConn
     269                 :    *        The SQLite connection.
     270                 :    * @param aPathHead
     271                 :    *        Head of the path for the memory report.
     272                 :    * @param aKind
     273                 :    *        The memory report statistic kind, one of "stmt", "cache" or
     274                 :    *        "schema".
     275                 :    * @param aDesc
     276                 :    *        The memory report description.
     277                 :    * @param aOption
     278                 :    *        The SQLite constant for getting the measurement.
     279                 :    * @param aTotal
     280                 :    *        The accumulator for the measurement.
     281                 :    */
     282              27 :   nsresult reportConn(nsIMemoryMultiReporterCallback *aCb,
     283                 :                       nsISupports *aClosure,
     284                 :                       sqlite3 *aConn,
     285                 :                       const nsACString &aPathHead,
     286                 :                       const nsACString &aKind,
     287                 :                       const nsACString &aDesc,
     288                 :                       int aOption,
     289                 :                       size_t *aTotal)
     290                 :   {
     291              54 :     nsCString path(aPathHead);
     292              27 :     path.Append(aKind);
     293              27 :     path.AppendLiteral("-used");
     294                 : 
     295              27 :     int curr = 0, max = 0;
     296              27 :     int rc = ::sqlite3_db_status(aConn, aOption, &curr, &max, 0);
     297              27 :     nsresult rv = convertResultCode(rc);
     298              27 :     NS_ENSURE_SUCCESS(rv, rv);
     299                 : 
     300              27 :     rv = aCb->Callback(NS_LITERAL_CSTRING(""), path,
     301                 :                        nsIMemoryReporter::KIND_HEAP,
     302                 :                        nsIMemoryReporter::UNITS_BYTES, PRInt64(curr),
     303              27 :                        aDesc, aClosure);
     304              27 :     NS_ENSURE_SUCCESS(rv, rv);
     305              27 :     *aTotal += curr;
     306                 : 
     307              27 :     return NS_OK;
     308                 :   }
     309                 : };
     310                 : 
     311            6436 : NS_IMPL_THREADSAFE_ISUPPORTS1(
     312                 :   StorageSQLiteMultiReporter,
     313                 :   nsIMemoryMultiReporter
     314                 : )
     315                 : 
     316                 : ////////////////////////////////////////////////////////////////////////////////
     317                 : //// Helpers
     318                 : 
     319                 : class ServiceMainThreadInitializer : public nsRunnable
     320            3212 : {
     321                 : public:
     322             803 :   ServiceMainThreadInitializer(Service *aService,
     323                 :                                nsIObserver *aObserver,
     324                 :                                nsIXPConnect **aXPConnectPtr,
     325                 :                                PRInt32 *aSynchronousPrefValPtr)
     326                 :   : mService(aService)
     327                 :   , mObserver(aObserver)
     328                 :   , mXPConnectPtr(aXPConnectPtr)
     329             803 :   , mSynchronousPrefValPtr(aSynchronousPrefValPtr)
     330                 :   {
     331             803 :   }
     332                 : 
     333             803 :   NS_IMETHOD Run()
     334                 :   {
     335             803 :     NS_PRECONDITION(NS_IsMainThread(), "Must be running on the main thread!");
     336                 : 
     337                 :     // NOTE:  All code that can only run on the main thread and needs to be run
     338                 :     //        during initialization should be placed here.  During the off-
     339                 :     //        chance that storage is initialized on a background thread, this
     340                 :     //        will ensure everything that isn't threadsafe is initialized in
     341                 :     //        the right place.
     342                 : 
     343                 :     // Register for xpcom-shutdown so we can cleanup after ourselves.  The
     344                 :     // observer service can only be used on the main thread.
     345                 :     nsCOMPtr<nsIObserverService> os =
     346            1606 :       mozilla::services::GetObserverService();
     347             803 :     NS_ENSURE_TRUE(os, NS_ERROR_FAILURE);
     348             803 :     nsresult rv = os->AddObserver(mObserver, "xpcom-shutdown", false);
     349             803 :     NS_ENSURE_SUCCESS(rv, rv);
     350                 : 
     351                 :     // We cache XPConnect for our language helpers.  XPConnect can only be
     352                 :     // used on the main thread.
     353             803 :     (void)CallGetService(nsIXPConnect::GetCID(), mXPConnectPtr);
     354                 : 
     355                 :     // We need to obtain the toolkit.storage.synchronous preferences on the main
     356                 :     // thread because the preference service can only be accessed there.  This
     357                 :     // is cached in the service for all future Open[Unshared]Database calls.
     358                 :     PRInt32 synchronous =
     359             803 :       Preferences::GetInt(PREF_TS_SYNCHRONOUS, PREF_TS_SYNCHRONOUS_DEFAULT);
     360             803 :     ::PR_ATOMIC_SET(mSynchronousPrefValPtr, synchronous);
     361                 : 
     362                 :     // Create and register our SQLite memory reporters.  Registration can only
     363                 :     // happen on the main thread (otherwise you'll get cryptic crashes).
     364             803 :     mService->mStorageSQLiteReporter = new NS_MEMORY_REPORTER_NAME(StorageSQLite);
     365             803 :     mService->mStorageSQLiteMultiReporter = new StorageSQLiteMultiReporter(mService);
     366             803 :     (void)::NS_RegisterMemoryReporter(mService->mStorageSQLiteReporter);
     367             803 :     (void)::NS_RegisterMemoryMultiReporter(mService->mStorageSQLiteMultiReporter);
     368                 : 
     369             803 :     return NS_OK;
     370                 :   }
     371                 : 
     372                 : private:
     373                 :   Service *mService;
     374                 :   nsIObserver *mObserver;
     375                 :   nsIXPConnect **mXPConnectPtr;
     376                 :   PRInt32 *mSynchronousPrefValPtr;
     377                 : };
     378                 : 
     379                 : ////////////////////////////////////////////////////////////////////////////////
     380                 : //// Service
     381                 : 
     382           38856 : NS_IMPL_THREADSAFE_ISUPPORTS3(
     383                 :   Service,
     384                 :   mozIStorageService,
     385                 :   nsIObserver,
     386                 :   mozIStorageServiceQuotaManagement
     387                 : )
     388                 : 
     389                 : Service *Service::gService = nsnull;
     390                 : 
     391                 : Service *
     392             803 : Service::getSingleton()
     393                 : {
     394             803 :   if (gService) {
     395               0 :     NS_ADDREF(gService);
     396               0 :     return gService;
     397                 :   }
     398                 : 
     399                 :   // Ensure that we are using the same version of SQLite that we compiled with
     400                 :   // or newer.  Our configure check ensures we are using a new enough version
     401                 :   // at compile time.
     402             803 :   if (SQLITE_VERSION_NUMBER > ::sqlite3_libversion_number()) {
     403               0 :     nsCOMPtr<nsIPromptService> ps(do_GetService(NS_PROMPTSERVICE_CONTRACTID));
     404               0 :     if (ps) {
     405               0 :       nsAutoString title, message;
     406               0 :       title.AppendASCII("SQLite Version Error");
     407                 :       message.AppendASCII("The application has been updated, but your version "
     408                 :                           "of SQLite is too old and the application cannot "
     409               0 :                           "run.");
     410               0 :       (void)ps->Alert(nsnull, title.get(), message.get());
     411                 :     }
     412               0 :     ::PR_Abort();
     413                 :   }
     414                 : 
     415             803 :   gService = new Service();
     416             803 :   if (gService) {
     417             803 :     NS_ADDREF(gService);
     418             803 :     if (NS_FAILED(gService->initialize()))
     419               0 :       NS_RELEASE(gService);
     420                 :   }
     421                 : 
     422             803 :   return gService;
     423                 : }
     424                 : 
     425                 : nsIXPConnect *Service::sXPConnect = nsnull;
     426                 : 
     427                 : // static
     428                 : already_AddRefed<nsIXPConnect>
     429           14668 : Service::getXPConnect()
     430                 : {
     431           14668 :   NS_PRECONDITION(NS_IsMainThread(),
     432                 :                   "Must only get XPConnect on the main thread!");
     433           14668 :   NS_PRECONDITION(gService,
     434                 :                   "Can not get XPConnect without an instance of our service!");
     435                 : 
     436                 :   // If we've been shutdown, sXPConnect will be null.  To prevent leaks, we do
     437                 :   // not cache the service after this point.
     438           29336 :   nsCOMPtr<nsIXPConnect> xpc(sXPConnect);
     439           14668 :   if (!xpc)
     440               0 :     xpc = do_GetService(nsIXPConnect::GetCID());
     441           14668 :   NS_ASSERTION(xpc, "Could not get XPConnect!");
     442           14668 :   return xpc.forget();
     443                 : }
     444                 : 
     445                 : PRInt32 Service::sSynchronousPref;
     446                 : 
     447                 : // static
     448                 : PRInt32
     449            3267 : Service::getSynchronousPref()
     450                 : {
     451            3267 :   return sSynchronousPref;
     452                 : }
     453                 : 
     454             803 : Service::Service()
     455                 : : mMutex("Service::mMutex")
     456                 : , mSqliteVFS(nsnull)
     457                 : , mRegistrationMutex("Service::mRegistrationMutex")
     458                 : , mConnections()
     459                 : , mStorageSQLiteReporter(nsnull)
     460             803 : , mStorageSQLiteMultiReporter(nsnull)
     461                 : {
     462             803 : }
     463                 : 
     464            2382 : Service::~Service()
     465                 : {
     466             794 :   (void)::NS_UnregisterMemoryReporter(mStorageSQLiteReporter);
     467             794 :   (void)::NS_UnregisterMemoryMultiReporter(mStorageSQLiteMultiReporter);
     468                 : 
     469             794 :   int rc = sqlite3_vfs_unregister(mSqliteVFS);
     470             794 :   if (rc != SQLITE_OK)
     471               0 :     NS_WARNING("Failed to unregister sqlite vfs wrapper.");
     472                 : 
     473                 :   // Shutdown the sqlite3 API.  Warn if shutdown did not turn out okay, but
     474                 :   // there is nothing actionable we can do in that case.
     475             794 :   rc = ::sqlite3_quota_shutdown();
     476             794 :   if (rc != SQLITE_OK)
     477               0 :     NS_WARNING("sqlite3 did not shutdown cleanly.");
     478                 : 
     479             794 :   rc = ::sqlite3_shutdown();
     480             794 :   if (rc != SQLITE_OK)
     481               0 :     NS_WARNING("sqlite3 did not shutdown cleanly.");
     482                 : 
     483            1588 :   DebugOnly<bool> shutdownObserved = !sXPConnect;
     484             794 :   NS_ASSERTION(shutdownObserved, "Shutdown was not observed!");
     485                 : 
     486             794 :   gService = nsnull;
     487             794 :   delete mSqliteVFS;
     488             794 :   mSqliteVFS = nsnull;
     489            3176 : }
     490                 : 
     491                 : void
     492            3281 : Service::registerConnection(Connection *aConnection)
     493                 : {
     494            3281 :   mRegistrationMutex.AssertNotCurrentThreadOwns();
     495            6562 :   MutexAutoLock mutex(mRegistrationMutex);
     496            3281 :   (void)mConnections.AppendElement(aConnection);
     497            3281 : }
     498                 : 
     499                 : void
     500            3274 : Service::unregisterConnection(Connection *aConnection)
     501                 : {
     502                 :   // If this is the last Connection it might be the only thing keeping Service
     503                 :   // alive.  So ensure that Service is destroyed only after the Connection is
     504                 :   // cleanly unregistered and destroyed.
     505            6548 :   nsRefPtr<Service> kungFuDeathGrip(this);
     506                 :   {
     507            3274 :     mRegistrationMutex.AssertNotCurrentThreadOwns();
     508            6548 :     MutexAutoLock mutex(mRegistrationMutex);
     509            6548 :     DebugOnly<bool> removed = mConnections.RemoveElement(aConnection);
     510                 :     // Assert if we try to unregister a non-existent connection.
     511            3274 :     MOZ_ASSERT(removed);
     512                 :   }
     513            3274 : }
     514                 : 
     515                 : void
     516               3 : Service::getConnections(/* inout */ nsTArray<nsRefPtr<Connection> >& aConnections)
     517                 : {
     518               3 :   mRegistrationMutex.AssertNotCurrentThreadOwns();
     519               6 :   MutexAutoLock mutex(mRegistrationMutex);
     520               3 :   aConnections.Clear();
     521               3 :   aConnections.AppendElements(mConnections);
     522               3 : }
     523                 : 
     524                 : void
     525             803 : Service::shutdown()
     526                 : {
     527             803 :   NS_IF_RELEASE(sXPConnect);
     528             803 : }
     529                 : 
     530                 : sqlite3_vfs *ConstructTelemetryVFS();
     531                 : 
     532                 : #ifdef MOZ_MEMORY
     533                 : 
     534                 : #  if defined(XP_WIN) || defined(SOLARIS) || defined(ANDROID) || defined(XP_MACOSX)
     535                 : #    include "jemalloc.h"
     536                 : #  elif defined(XP_LINUX)
     537                 : // jemalloc is directly linked into firefox-bin; libxul doesn't link
     538                 : // with it.  So if we tried to use je_malloc_usable_size_in_advance directly
     539                 : // here, it wouldn't be defined.  Instead, we don't include the jemalloc header
     540                 : // and weakly link against je_malloc_usable_size_in_advance.
     541                 : extern "C" {
     542                 : extern size_t je_malloc_usable_size_in_advance(size_t size)
     543                 :   NS_VISIBILITY_DEFAULT __attribute__((weak));
     544                 : }
     545                 : #  endif  // XP_LINUX
     546                 : 
     547                 : namespace {
     548                 : 
     549                 : // By default, SQLite tracks the size of all its heap blocks by adding an extra
     550                 : // 8 bytes at the start of the block to hold the size.  Unfortunately, this
     551                 : // causes a lot of 2^N-sized allocations to be rounded up by jemalloc
     552                 : // allocator, wasting memory.  For example, a request for 1024 bytes has 8
     553                 : // bytes added, becoming a request for 1032 bytes, and jemalloc rounds this up
     554                 : // to 2048 bytes, wasting 1012 bytes.  (See bug 676189 for more details.)
     555                 : //
     556                 : // So we register jemalloc as the malloc implementation, which avoids this
     557                 : // 8-byte overhead, and thus a lot of waste.  This requires us to provide a
     558                 : // function, sqliteMemRoundup(), which computes the actual size that will be
     559                 : // allocated for a given request.  SQLite uses this function before all
     560                 : // allocations, and may be able to use any excess bytes caused by the rounding.
     561                 : //
     562                 : // Note: the wrappers for moz_malloc, moz_realloc and moz_malloc_usable_size
     563                 : // are necessary because the sqlite_mem_methods type signatures differ slightly
     564                 : // from the standard ones -- they use int instead of size_t.  But we don't need
     565                 : // a wrapper for moz_free.
     566                 : 
     567         6078342 : static void *sqliteMemMalloc(int n)
     568                 : {
     569         6078342 :   return ::moz_malloc(n);
     570                 : }
     571                 : 
     572          269031 : static void *sqliteMemRealloc(void *p, int n)
     573                 : {
     574          269031 :   return ::moz_realloc(p, n);
     575                 : }
     576                 : 
     577        15842376 : static int sqliteMemSize(void *p)
     578                 : {
     579        15842376 :   return ::moz_malloc_usable_size(p);
     580                 : }
     581                 : 
     582         7116781 : static int sqliteMemRoundup(int n)
     583                 : {
     584         7116781 :   n = je_malloc_usable_size_in_advance(n);
     585                 : 
     586                 :   // jemalloc can return blocks of size 2 and 4, but SQLite requires that all
     587                 :   // allocations be 8-aligned.  So we round up sub-8 requests to 8.  This
     588                 :   // wastes a small amount of memory but is obviously safe.
     589         7116780 :   return n <= 8 ? 8 : n;
     590                 : }
     591                 : 
     592             803 : static int sqliteMemInit(void *p)
     593                 : {
     594             803 :   return 0;
     595                 : }
     596                 : 
     597             794 : static void sqliteMemShutdown(void *p)
     598                 : {
     599             794 : }
     600                 : 
     601                 : const sqlite3_mem_methods memMethods = {
     602                 :   &sqliteMemMalloc,
     603                 :   &moz_free,
     604                 :   &sqliteMemRealloc,
     605                 :   &sqliteMemSize,
     606                 :   &sqliteMemRoundup,
     607                 :   &sqliteMemInit,
     608                 :   &sqliteMemShutdown,
     609                 :   NULL
     610                 : }; 
     611                 : 
     612                 : } // anonymous namespace
     613                 : 
     614                 : #endif  // MOZ_MEMORY
     615                 : 
     616                 : nsresult
     617             803 : Service::initialize()
     618                 : {
     619                 :   NS_TIME_FUNCTION;
     620                 : 
     621                 :   int rc;
     622                 : 
     623                 : #ifdef MOZ_MEMORY
     624             803 :   rc = ::sqlite3_config(SQLITE_CONFIG_MALLOC, &memMethods);
     625             803 :   if (rc != SQLITE_OK)
     626               0 :     return convertResultCode(rc);
     627                 : #endif
     628                 : 
     629                 :   // Explicitly initialize sqlite3.  Although this is implicitly called by
     630                 :   // various sqlite3 functions (and the sqlite3_open calls in our case),
     631                 :   // the documentation suggests calling this directly.  So we do.
     632             803 :   rc = ::sqlite3_initialize();
     633             803 :   if (rc != SQLITE_OK)
     634               0 :     return convertResultCode(rc);
     635                 : 
     636             803 :   mSqliteVFS = ConstructTelemetryVFS();
     637             803 :   if (mSqliteVFS) {
     638             803 :     rc = sqlite3_vfs_register(mSqliteVFS, 1);
     639             803 :     if (rc != SQLITE_OK)
     640               0 :       return convertResultCode(rc);
     641                 :   } else {
     642               0 :     NS_WARNING("Failed to register telemetry VFS");
     643                 :   }
     644             803 :   rc = ::sqlite3_quota_initialize("telemetry-vfs", 0);
     645             803 :   if (rc != SQLITE_OK)
     646               0 :     return convertResultCode(rc);
     647                 : 
     648                 :   // Set the default value for the toolkit.storage.synchronous pref.  It will be
     649                 :   // updated with the user preference on the main thread.
     650             803 :   sSynchronousPref = PREF_TS_SYNCHRONOUS_DEFAULT;
     651                 : 
     652                 :   // Run the things that need to run on the main thread there.
     653                 :   nsCOMPtr<nsIRunnable> event =
     654            1606 :     new ServiceMainThreadInitializer(this, this, &sXPConnect, &sSynchronousPref);
     655             803 :   if (event && ::NS_IsMainThread()) {
     656             752 :     (void)event->Run();
     657                 :   }
     658                 :   else {
     659              51 :     (void)::NS_DispatchToMainThread(event);
     660                 :   }
     661                 : 
     662             803 :   return NS_OK;
     663                 : }
     664                 : 
     665                 : int
     666            7098 : Service::localeCompareStrings(const nsAString &aStr1,
     667                 :                               const nsAString &aStr2,
     668                 :                               PRInt32 aComparisonStrength)
     669                 : {
     670                 :   // The implementation of nsICollation.CompareString() is platform-dependent.
     671                 :   // On Linux it's not thread-safe.  It may not be on Windows and OS X either,
     672                 :   // but it's more difficult to tell.  We therefore synchronize this method.
     673           14196 :   MutexAutoLock mutex(mMutex);
     674                 : 
     675            7098 :   nsICollation *coll = getLocaleCollation();
     676            7098 :   if (!coll) {
     677               0 :     NS_ERROR("Storage service has no collation");
     678               0 :     return 0;
     679                 :   }
     680                 : 
     681                 :   PRInt32 res;
     682            7098 :   nsresult rv = coll->CompareString(aComparisonStrength, aStr1, aStr2, &res);
     683            7098 :   if (NS_FAILED(rv)) {
     684               0 :     NS_ERROR("Collation compare string failed");
     685               0 :     return 0;
     686                 :   }
     687                 : 
     688            7098 :   return res;
     689                 : }
     690                 : 
     691                 : nsICollation *
     692            7098 : Service::getLocaleCollation()
     693                 : {
     694            7098 :   mMutex.AssertCurrentThreadOwns();
     695                 : 
     696            7098 :   if (mLocaleCollation)
     697            7097 :     return mLocaleCollation;
     698                 : 
     699               2 :   nsCOMPtr<nsILocaleService> svc(do_GetService(NS_LOCALESERVICE_CONTRACTID));
     700               1 :   if (!svc) {
     701               0 :     NS_WARNING("Could not get locale service");
     702               0 :     return nsnull;
     703                 :   }
     704                 : 
     705               2 :   nsCOMPtr<nsILocale> appLocale;
     706               1 :   nsresult rv = svc->GetApplicationLocale(getter_AddRefs(appLocale));
     707               1 :   if (NS_FAILED(rv)) {
     708               0 :     NS_WARNING("Could not get application locale");
     709               0 :     return nsnull;
     710                 :   }
     711                 : 
     712                 :   nsCOMPtr<nsICollationFactory> collFact =
     713               2 :     do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID);
     714               1 :   if (!collFact) {
     715               0 :     NS_WARNING("Could not create collation factory");
     716               0 :     return nsnull;
     717                 :   }
     718                 : 
     719               1 :   rv = collFact->CreateCollation(appLocale, getter_AddRefs(mLocaleCollation));
     720               1 :   if (NS_FAILED(rv)) {
     721               0 :     NS_WARNING("Could not create collation");
     722               0 :     return nsnull;
     723                 :   }
     724                 : 
     725               1 :   return mLocaleCollation;
     726                 : }
     727                 : 
     728                 : ////////////////////////////////////////////////////////////////////////////////
     729                 : //// mozIStorageService
     730                 : 
     731                 : #ifndef NS_APP_STORAGE_50_FILE
     732                 : #define NS_APP_STORAGE_50_FILE "UStor"
     733                 : #endif
     734                 : 
     735                 : NS_IMETHODIMP
     736              50 : Service::OpenSpecialDatabase(const char *aStorageKey,
     737                 :                              mozIStorageConnection **_connection)
     738                 : {
     739                 :   nsresult rv;
     740                 : 
     741             100 :   nsCOMPtr<nsIFile> storageFile;
     742              50 :   if (::strcmp(aStorageKey, "memory") == 0) {
     743                 :     // just fall through with NULL storageFile, this will cause the storage
     744                 :     // connection to use a memory DB.
     745                 :   }
     746               1 :   else if (::strcmp(aStorageKey, "profile") == 0) {
     747                 : 
     748                 :     rv = NS_GetSpecialDirectory(NS_APP_STORAGE_50_FILE,
     749               0 :                                 getter_AddRefs(storageFile));
     750               0 :     NS_ENSURE_SUCCESS(rv, rv);
     751                 : 
     752               0 :     nsString filename;
     753               0 :     storageFile->GetPath(filename);
     754               0 :     nsCString filename8 = NS_ConvertUTF16toUTF8(filename.get());
     755                 :     // fall through to DB initialization
     756                 :   }
     757                 :   else {
     758               1 :     return NS_ERROR_INVALID_ARG;
     759                 :   }
     760                 : 
     761              49 :   Connection *msc = new Connection(this, SQLITE_OPEN_READWRITE);
     762              49 :   NS_ENSURE_TRUE(msc, NS_ERROR_OUT_OF_MEMORY);
     763                 : 
     764              49 :   rv = msc->initialize(storageFile);
     765              49 :   NS_ENSURE_SUCCESS(rv, rv);
     766                 : 
     767              49 :   NS_ADDREF(*_connection = msc);
     768              49 :   return NS_OK;
     769                 : }
     770                 : 
     771                 : NS_IMETHODIMP
     772             844 : Service::OpenDatabase(nsIFile *aDatabaseFile,
     773                 :                       mozIStorageConnection **_connection)
     774                 : {
     775             844 :   NS_ENSURE_ARG(aDatabaseFile);
     776                 : 
     777                 : #ifdef NS_FUNCTION_TIMER
     778                 :   nsCString leafname;
     779                 :   (void)aDatabaseFile->GetNativeLeafName(leafname);
     780                 :   NS_TIME_FUNCTION_FMT("mozIStorageService::OpenDatabase(%s)", leafname.get());
     781                 : #endif
     782                 : 
     783                 :   // Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility
     784                 :   // reasons.
     785                 :   int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_SHAREDCACHE |
     786             843 :               SQLITE_OPEN_CREATE;
     787            1686 :   nsRefPtr<Connection> msc = new Connection(this, flags);
     788             843 :   NS_ENSURE_TRUE(msc, NS_ERROR_OUT_OF_MEMORY);
     789                 : 
     790             843 :   nsresult rv = msc->initialize(aDatabaseFile);
     791             843 :   NS_ENSURE_SUCCESS(rv, rv);
     792                 : 
     793             838 :   NS_ADDREF(*_connection = msc);
     794             838 :   return NS_OK;
     795                 : }
     796                 : 
     797                 : NS_IMETHODIMP
     798            1735 : Service::OpenUnsharedDatabase(nsIFile *aDatabaseFile,
     799                 :                               mozIStorageConnection **_connection)
     800                 : {
     801            1735 :   NS_ENSURE_ARG(aDatabaseFile);
     802                 : 
     803                 : #ifdef NS_FUNCTION_TIMER
     804                 :   nsCString leafname;
     805                 :   (void)aDatabaseFile->GetNativeLeafName(leafname);
     806                 :   NS_TIME_FUNCTION_FMT("mozIStorageService::OpenUnsharedDatabase(%s)",
     807                 :                        leafname.get());
     808                 : #endif
     809                 : 
     810                 :   // Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility
     811                 :   // reasons.
     812                 :   int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_PRIVATECACHE |
     813            1734 :               SQLITE_OPEN_CREATE;
     814            3468 :   nsRefPtr<Connection> msc = new Connection(this, flags);
     815            1734 :   NS_ENSURE_TRUE(msc, NS_ERROR_OUT_OF_MEMORY);
     816                 : 
     817            1734 :   nsresult rv = msc->initialize(aDatabaseFile);
     818            1734 :   NS_ENSURE_SUCCESS(rv, rv);
     819                 : 
     820            1725 :   NS_ADDREF(*_connection = msc);
     821            1725 :   return NS_OK;
     822                 : }
     823                 : 
     824                 : NS_IMETHODIMP
     825              15 : Service::BackupDatabaseFile(nsIFile *aDBFile,
     826                 :                             const nsAString &aBackupFileName,
     827                 :                             nsIFile *aBackupParentDirectory,
     828                 :                             nsIFile **backup)
     829                 : {
     830                 :   nsresult rv;
     831              30 :   nsCOMPtr<nsIFile> parentDir = aBackupParentDirectory;
     832              15 :   if (!parentDir) {
     833                 :     // This argument is optional, and defaults to the same parent directory
     834                 :     // as the current file.
     835               8 :     rv = aDBFile->GetParent(getter_AddRefs(parentDir));
     836               8 :     NS_ENSURE_SUCCESS(rv, rv);
     837                 :   }
     838                 : 
     839              30 :   nsCOMPtr<nsIFile> backupDB;
     840              15 :   rv = parentDir->Clone(getter_AddRefs(backupDB));
     841              15 :   NS_ENSURE_SUCCESS(rv, rv);
     842                 : 
     843              15 :   rv = backupDB->Append(aBackupFileName);
     844              15 :   NS_ENSURE_SUCCESS(rv, rv);
     845                 : 
     846              15 :   rv = backupDB->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
     847              15 :   NS_ENSURE_SUCCESS(rv, rv);
     848                 : 
     849              30 :   nsAutoString fileName;
     850              15 :   rv = backupDB->GetLeafName(fileName);
     851              15 :   NS_ENSURE_SUCCESS(rv, rv);
     852                 : 
     853              15 :   rv = backupDB->Remove(false);
     854              15 :   NS_ENSURE_SUCCESS(rv, rv);
     855                 : 
     856              15 :   backupDB.forget(backup);
     857                 : 
     858              15 :   return aDBFile->CopyTo(parentDir, fileName);
     859                 : }
     860                 : 
     861                 : ////////////////////////////////////////////////////////////////////////////////
     862                 : //// nsIObserver
     863                 : 
     864                 : NS_IMETHODIMP
     865             803 : Service::Observe(nsISupports *, const char *aTopic, const PRUnichar *)
     866                 : {
     867             803 :   if (strcmp(aTopic, "xpcom-shutdown") == 0)
     868             803 :     shutdown();
     869             803 :   return NS_OK;
     870                 : }
     871                 : 
     872                 : ////////////////////////////////////////////////////////////////////////////////
     873                 : //// mozIStorageServiceQuotaManagement
     874                 : 
     875                 : NS_IMETHODIMP
     876             605 : Service::OpenDatabaseWithVFS(nsIFile *aDatabaseFile,
     877                 :                              const nsACString &aVFSName,
     878                 :                              mozIStorageConnection **_connection)
     879                 : {
     880             605 :   NS_ENSURE_ARG(aDatabaseFile);
     881                 : 
     882                 : #ifdef NS_FUNCTION_TIMER
     883                 :   nsCString leafname;
     884                 :   (void)aDatabaseFile->GetNativeLeafName(leafname);
     885                 :   NS_TIME_FUNCTION_FMT("mozIStorageService::OpenDatabaseWithVFS(%s)",
     886                 :                        leafname.get());
     887                 : #endif
     888                 : 
     889                 :   // Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility
     890                 :   // reasons.
     891                 :   int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_SHAREDCACHE |
     892             605 :               SQLITE_OPEN_CREATE;
     893            1210 :   nsRefPtr<Connection> msc = new Connection(this, flags);
     894             605 :   NS_ENSURE_TRUE(msc, NS_ERROR_OUT_OF_MEMORY);
     895                 : 
     896                 :   nsresult rv = msc->initialize(aDatabaseFile,
     897             605 :                                 PromiseFlatCString(aVFSName).get());
     898             605 :   NS_ENSURE_SUCCESS(rv, rv);
     899                 : 
     900             605 :   NS_ADDREF(*_connection = msc);
     901             605 :   return NS_OK;
     902                 : }
     903                 : 
     904                 : NS_IMETHODIMP
     905              50 : Service::SetQuotaForFilenamePattern(const nsACString &aPattern,
     906                 :                                     PRInt64 aSizeLimit,
     907                 :                                     mozIStorageQuotaCallback *aCallback,
     908                 :                                     nsISupports *aUserData)
     909                 : {
     910              50 :   NS_ENSURE_FALSE(aPattern.IsEmpty(), NS_ERROR_INVALID_ARG);
     911                 : 
     912             100 :   nsAutoPtr<QuotaCallbackData> data;
     913              50 :   if (aSizeLimit && aCallback) {
     914              50 :     data = new QuotaCallbackData(aCallback, aUserData);
     915                 :   }
     916                 : 
     917              50 :   int rc = ::sqlite3_quota_set(PromiseFlatCString(aPattern).get(),
     918                 :                                aSizeLimit, QuotaCallbackData::Callback,
     919              50 :                                data, QuotaCallbackData::Destroy);
     920              50 :   NS_ENSURE_TRUE(rc == SQLITE_OK, convertResultCode(rc));
     921                 : 
     922              50 :   data.forget();
     923              50 :   return NS_OK;
     924                 : }
     925                 : 
     926                 : NS_IMETHODIMP
     927               0 : Service::UpdateQuotaInformationForFile(nsIFile *aFile)
     928                 : {
     929               0 :   NS_ENSURE_ARG_POINTER(aFile);
     930                 : 
     931               0 :   nsCString path;
     932               0 :   nsresult rv = aFile->GetNativePath(path);
     933               0 :   NS_ENSURE_SUCCESS(rv, rv);
     934                 : 
     935               0 :   int rc = ::sqlite3_quota_file(PromiseFlatCString(path).get());
     936               0 :   NS_ENSURE_TRUE(rc == SQLITE_OK, convertResultCode(rc));
     937                 : 
     938               0 :   return NS_OK;
     939                 : }
     940                 : 
     941                 : } // namespace storage
     942                 : } // namespace mozilla

Generated by: LCOV version 1.7