LCOV - code coverage report
Current view: top level - dom/storage - StorageDBUpdater.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 26 192 13.5 %
Date: 2017-07-14 16:53:18 Functions: 2 22 9.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "LocalStorageManager.h"
       8             : #include "StorageUtils.h"
       9             : 
      10             : #include "mozIStorageBindingParamsArray.h"
      11             : #include "mozIStorageBindingParams.h"
      12             : #include "mozIStorageValueArray.h"
      13             : #include "mozIStorageFunction.h"
      14             : #include "mozilla/BasePrincipal.h"
      15             : #include "nsVariant.h"
      16             : #include "mozilla/Services.h"
      17             : #include "mozilla/Tokenizer.h"
      18             : 
      19             : // Current version of the database schema
      20             : #define CURRENT_SCHEMA_VERSION 2
      21             : 
      22             : namespace mozilla {
      23             : namespace dom {
      24             : 
      25             : using namespace StorageUtils;
      26             : 
      27             : namespace {
      28             : 
      29           0 : class nsReverseStringSQLFunction final : public mozIStorageFunction
      30             : {
      31           0 :   ~nsReverseStringSQLFunction() {}
      32             : 
      33             :   NS_DECL_ISUPPORTS
      34             :   NS_DECL_MOZISTORAGEFUNCTION
      35             : };
      36             : 
      37           0 : NS_IMPL_ISUPPORTS(nsReverseStringSQLFunction, mozIStorageFunction)
      38             : 
      39             : NS_IMETHODIMP
      40           0 : nsReverseStringSQLFunction::OnFunctionCall(
      41             :     mozIStorageValueArray* aFunctionArguments, nsIVariant** aResult)
      42             : {
      43             :   nsresult rv;
      44             : 
      45           0 :   nsAutoCString stringToReverse;
      46           0 :   rv = aFunctionArguments->GetUTF8String(0, stringToReverse);
      47           0 :   NS_ENSURE_SUCCESS(rv, rv);
      48             : 
      49           0 :   nsAutoCString result;
      50           0 :   ReverseString(stringToReverse, result);
      51             : 
      52           0 :   RefPtr<nsVariant> outVar(new nsVariant());
      53           0 :   rv = outVar->SetAsAUTF8String(result);
      54           0 :   NS_ENSURE_SUCCESS(rv, rv);
      55             : 
      56           0 :   outVar.forget(aResult);
      57           0 :   return NS_OK;
      58             : }
      59             : 
      60             : // "scope" to "origin attributes suffix" and "origin key" convertor
      61             : 
      62           0 : class ExtractOriginData : protected mozilla::Tokenizer
      63             : {
      64             : public:
      65           0 :   ExtractOriginData(const nsACString& scope, nsACString& suffix,
      66             :                     nsACString& origin)
      67           0 :     : mozilla::Tokenizer(scope)
      68             :   {
      69             :     using mozilla::OriginAttributes;
      70             : 
      71             :     // Parse optional appId:isInIsolatedMozBrowserElement: string, in case
      72             :     // we don't find it, the scope is our new origin key and suffix
      73             :     // is empty.
      74           0 :     suffix.Truncate();
      75           0 :     origin.Assign(scope);
      76             : 
      77             :     // Bail out if it isn't appId.
      78             :     uint32_t appId;
      79           0 :     if (!ReadInteger(&appId)) {
      80           0 :       return;
      81             :     }
      82             : 
      83             :     // Should be followed by a colon.
      84           0 :     if (!CheckChar(':')) {
      85           0 :       return;
      86             :     }
      87             : 
      88             :     // Bail out if it isn't 'isolatedBrowserFlag'.
      89           0 :     nsDependentCSubstring isolatedBrowserFlag;
      90           0 :     if (!ReadWord(isolatedBrowserFlag)) {
      91           0 :       return;
      92             :     }
      93             : 
      94           0 :     bool inIsolatedMozBrowser = isolatedBrowserFlag == "t";
      95           0 :     bool notInIsolatedBrowser = isolatedBrowserFlag == "f";
      96           0 :     if (!inIsolatedMozBrowser && !notInIsolatedBrowser) {
      97           0 :       return;
      98             :     }
      99             : 
     100             :     // Should be followed by a colon.
     101           0 :     if (!CheckChar(':')) {
     102           0 :       return;
     103             :     }
     104             : 
     105             :     // OK, we have found appId and inIsolatedMozBrowser flag, create the suffix
     106             :     // from it and take the rest as the origin key.
     107             : 
     108             :     // If the profile went through schema 1 -> schema 0 -> schema 1 switching
     109             :     // we may have stored the full attributes origin suffix when there were
     110             :     // more than just appId and inIsolatedMozBrowser set on storage principal's
     111             :     // OriginAttributes.
     112             :     //
     113             :     // To preserve full uniqueness we store this suffix to the scope key.
     114             :     // Schema 0 code will just ignore it while keeping the scoping unique.
     115             :     //
     116             :     // The whole scope string is in one of the following forms (when we are
     117             :     // here):
     118             :     //
     119             :     // "1001:f:^appId=1001&inBrowser=false&addonId=101:gro.allizom.rxd.:https:443"
     120             :     // "1001:f:gro.allizom.rxd.:https:443"
     121             :     //         |
     122             :     //         +- the parser cursor position.
     123             :     //
     124             :     // If there is '^', the full origin attributes suffix follows.  We search
     125             :     // for ':' since it is the delimiter used in the scope string and is never
     126             :     // contained in the origin attributes suffix.  Remaining string after
     127             :     // the comma is the reversed-domain+schema+port tuple.
     128           0 :     Record();
     129           0 :     if (CheckChar('^')) {
     130           0 :       Token t;
     131           0 :       while (Next(t)) {
     132           0 :         if (t.Equals(Token::Char(':'))) {
     133           0 :           Claim(suffix);
     134           0 :           break;
     135             :         }
     136             :       }
     137             :     } else {
     138           0 :       OriginAttributes attrs(appId, inIsolatedMozBrowser);
     139           0 :       attrs.CreateSuffix(suffix);
     140             :     }
     141             : 
     142             :     // Consume the rest of the input as "origin".
     143           0 :     origin.Assign(Substring(mCursor, mEnd));
     144             :   }
     145             : };
     146             : 
     147             : class GetOriginParticular final : public mozIStorageFunction
     148             : {
     149             : public:
     150             :   enum EParticular {
     151             :     ORIGIN_ATTRIBUTES_SUFFIX,
     152             :     ORIGIN_KEY
     153             :   };
     154             : 
     155           0 :   explicit GetOriginParticular(EParticular aParticular)
     156           0 :     : mParticular(aParticular) {}
     157             : 
     158             : private:
     159             :   GetOriginParticular() = delete;
     160           0 :   ~GetOriginParticular() {}
     161             : 
     162             :   EParticular mParticular;
     163             : 
     164             :   NS_DECL_ISUPPORTS
     165             :   NS_DECL_MOZISTORAGEFUNCTION
     166             : };
     167             : 
     168           0 : NS_IMPL_ISUPPORTS(GetOriginParticular, mozIStorageFunction)
     169             : 
     170             : NS_IMETHODIMP
     171           0 : GetOriginParticular::OnFunctionCall(
     172             :     mozIStorageValueArray* aFunctionArguments, nsIVariant** aResult)
     173             : {
     174             :   nsresult rv;
     175             : 
     176           0 :   nsAutoCString scope;
     177           0 :   rv = aFunctionArguments->GetUTF8String(0, scope);
     178           0 :   NS_ENSURE_SUCCESS(rv, rv);
     179             : 
     180           0 :   nsAutoCString suffix, origin;
     181           0 :   ExtractOriginData(scope, suffix, origin);
     182             : 
     183           0 :   nsCOMPtr<nsIWritableVariant> outVar(new nsVariant());
     184             : 
     185           0 :   switch (mParticular) {
     186             :   case EParticular::ORIGIN_ATTRIBUTES_SUFFIX:
     187           0 :     rv = outVar->SetAsAUTF8String(suffix);
     188           0 :     break;
     189             :   case EParticular::ORIGIN_KEY:
     190           0 :     rv = outVar->SetAsAUTF8String(origin);
     191           0 :     break;
     192             :   }
     193             : 
     194           0 :   NS_ENSURE_SUCCESS(rv, rv);
     195             : 
     196           0 :   outVar.forget(aResult);
     197           0 :   return NS_OK;
     198             : }
     199             : 
     200             : class StripOriginAddonId final : public mozIStorageFunction
     201             : {
     202             : public:
     203           0 :   explicit StripOriginAddonId() {}
     204             : 
     205             : private:
     206           0 :   ~StripOriginAddonId() {}
     207             : 
     208             :   NS_DECL_ISUPPORTS
     209             :   NS_DECL_MOZISTORAGEFUNCTION
     210             : };
     211             : 
     212           0 : NS_IMPL_ISUPPORTS(StripOriginAddonId, mozIStorageFunction)
     213             : 
     214             : NS_IMETHODIMP
     215           0 : StripOriginAddonId::OnFunctionCall(
     216             :     mozIStorageValueArray* aFunctionArguments, nsIVariant** aResult)
     217             : {
     218             :   nsresult rv;
     219             : 
     220           0 :   nsAutoCString suffix;
     221           0 :   rv = aFunctionArguments->GetUTF8String(0, suffix);
     222           0 :   NS_ENSURE_SUCCESS(rv, rv);
     223             : 
     224             :   // Deserialize and re-serialize to automatically drop any obsolete origin
     225             :   // attributes.
     226           0 :   OriginAttributes oa;
     227           0 :   bool ok = oa.PopulateFromSuffix(suffix);
     228           0 :   NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
     229             : 
     230           0 :   nsAutoCString newSuffix;
     231           0 :   oa.CreateSuffix(newSuffix);
     232             : 
     233           0 :   nsCOMPtr<nsIWritableVariant> outVar = new nsVariant();
     234           0 :   rv = outVar->SetAsAUTF8String(newSuffix);
     235           0 :   NS_ENSURE_SUCCESS(rv, rv);
     236             : 
     237           0 :   outVar.forget(aResult);
     238           0 :   return NS_OK;
     239             : }
     240             : 
     241             : } // namespace
     242             : 
     243             : namespace StorageDBUpdater {
     244             : 
     245           1 : nsresult CreateSchema1Tables(mozIStorageConnection *aWorkerConnection)
     246             : {
     247             :   nsresult rv;
     248             : 
     249           3 :   rv = aWorkerConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     250             :           "CREATE TABLE IF NOT EXISTS webappsstore2 ("
     251             :           "originAttributes TEXT, "
     252             :           "originKey TEXT, "
     253             :           "scope TEXT, " // Only for schema0 downgrade compatibility
     254             :           "key TEXT, "
     255           3 :           "value TEXT)"));
     256           1 :   NS_ENSURE_SUCCESS(rv, rv);
     257             : 
     258           3 :   rv = aWorkerConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     259             :         "CREATE UNIQUE INDEX IF NOT EXISTS origin_key_index"
     260           3 :         " ON webappsstore2(originAttributes, originKey, key)"));
     261           1 :   NS_ENSURE_SUCCESS(rv, rv);
     262             : 
     263           1 :   return NS_OK;
     264             : }
     265             : 
     266           1 : nsresult Update(mozIStorageConnection *aWorkerConnection)
     267             : {
     268             :   nsresult rv;
     269             : 
     270           2 :   mozStorageTransaction transaction(aWorkerConnection, false);
     271             : 
     272           1 :   bool doVacuum = false;
     273             : 
     274             :   int32_t schemaVer;
     275           1 :   rv = aWorkerConnection->GetSchemaVersion(&schemaVer);
     276           1 :   NS_ENSURE_SUCCESS(rv, rv);
     277             : 
     278             :   // downgrade (v0) -> upgrade (v1+) specific code
     279           1 :   if (schemaVer >= 1) {
     280             :     bool schema0IndexExists;
     281           3 :     rv = aWorkerConnection->IndexExists(NS_LITERAL_CSTRING("scope_key_index"),
     282           3 :                                         &schema0IndexExists);
     283           1 :     NS_ENSURE_SUCCESS(rv, rv);
     284             : 
     285           1 :     if (schema0IndexExists) {
     286             :       // If this index exists, the database (already updated to schema >1)
     287             :       // has been run again on schema 0 code.  That recreated that index
     288             :       // and might store some new rows while updating only the 'scope' column.
     289             :       // For such added rows we must fill the new 'origin*' columns correctly
     290             :       // otherwise there would be a data loss.  The safest way to do it is to
     291             :       // simply run the whole update to schema 1 again.
     292           0 :       schemaVer = 0;
     293             :     }
     294             :   }
     295             : 
     296           1 :   switch (schemaVer) {
     297             :   case 0: {
     298             :     bool webappsstore2Exists, webappsstoreExists, moz_webappsstoreExists;
     299             : 
     300           0 :     rv = aWorkerConnection->TableExists(NS_LITERAL_CSTRING("webappsstore2"),
     301           0 :                                         &webappsstore2Exists);
     302           0 :     NS_ENSURE_SUCCESS(rv, rv);
     303           0 :     rv = aWorkerConnection->TableExists(NS_LITERAL_CSTRING("webappsstore"),
     304           0 :                                         &webappsstoreExists);
     305           0 :     NS_ENSURE_SUCCESS(rv, rv);
     306           0 :     rv = aWorkerConnection->TableExists(NS_LITERAL_CSTRING("moz_webappsstore"),
     307           0 :                                         &moz_webappsstoreExists);
     308           0 :     NS_ENSURE_SUCCESS(rv, rv);
     309             : 
     310           0 :     if (!webappsstore2Exists && !webappsstoreExists &&
     311           0 :         !moz_webappsstoreExists) {
     312             :       // The database is empty, this is the first start.  Just create the schema
     313             :       // table and break to the next version to update to, i.e. bypass update
     314             :       // from the old version.
     315             : 
     316           0 :       rv = CreateSchema1Tables(aWorkerConnection);
     317           0 :       NS_ENSURE_SUCCESS(rv, rv);
     318             : 
     319           0 :       rv = aWorkerConnection->SetSchemaVersion(CURRENT_SCHEMA_VERSION);
     320           0 :       NS_ENSURE_SUCCESS(rv, rv);
     321             : 
     322           0 :       break;
     323             :     }
     324             : 
     325           0 :     doVacuum = true;
     326             : 
     327             :     // Ensure Gecko 1.9.1 storage table
     328           0 :     rv = aWorkerConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     329             :            "CREATE TABLE IF NOT EXISTS webappsstore2 ("
     330             :            "scope TEXT, "
     331             :            "key TEXT, "
     332             :            "value TEXT, "
     333             :            "secure INTEGER, "
     334           0 :            "owner TEXT)"));
     335           0 :     NS_ENSURE_SUCCESS(rv, rv);
     336             : 
     337           0 :     rv = aWorkerConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     338             :           "CREATE UNIQUE INDEX IF NOT EXISTS scope_key_index"
     339           0 :           " ON webappsstore2(scope, key)"));
     340           0 :     NS_ENSURE_SUCCESS(rv, rv);
     341             : 
     342           0 :     nsCOMPtr<mozIStorageFunction> function1(new nsReverseStringSQLFunction());
     343           0 :     NS_ENSURE_TRUE(function1, NS_ERROR_OUT_OF_MEMORY);
     344             : 
     345           0 :     rv = aWorkerConnection->CreateFunction(NS_LITERAL_CSTRING("REVERSESTRING"),
     346           0 :                                            1, function1);
     347           0 :     NS_ENSURE_SUCCESS(rv, rv);
     348             : 
     349             :     // Check if there is storage of Gecko 1.9.0 and if so, upgrade that storage
     350             :     // to actual webappsstore2 table and drop the obsolete table. First process
     351             :     // this newer table upgrade to priority potential duplicates from older
     352             :     // storage table.
     353           0 :     if (webappsstoreExists) {
     354           0 :       rv = aWorkerConnection->ExecuteSimpleSQL(
     355           0 :         NS_LITERAL_CSTRING("INSERT OR IGNORE INTO "
     356             :                            "webappsstore2(scope, key, value, secure, owner) "
     357             :                            "SELECT REVERSESTRING(domain) || '.:', key, value, secure, owner "
     358           0 :                            "FROM webappsstore"));
     359           0 :       NS_ENSURE_SUCCESS(rv, rv);
     360             : 
     361           0 :       rv = aWorkerConnection->ExecuteSimpleSQL(
     362           0 :         NS_LITERAL_CSTRING("DROP TABLE webappsstore"));
     363           0 :       NS_ENSURE_SUCCESS(rv, rv);
     364             :     }
     365             : 
     366             :     // Check if there is storage of Gecko 1.8 and if so, upgrade that storage
     367             :     // to actual webappsstore2 table and drop the obsolete table. Potential
     368             :     // duplicates will be ignored.
     369           0 :     if (moz_webappsstoreExists) {
     370           0 :       rv = aWorkerConnection->ExecuteSimpleSQL(
     371           0 :         NS_LITERAL_CSTRING("INSERT OR IGNORE INTO "
     372             :                            "webappsstore2(scope, key, value, secure, owner) "
     373             :                            "SELECT REVERSESTRING(domain) || '.:', key, value, secure, domain "
     374           0 :                            "FROM moz_webappsstore"));
     375           0 :       NS_ENSURE_SUCCESS(rv, rv);
     376             : 
     377           0 :       rv = aWorkerConnection->ExecuteSimpleSQL(
     378           0 :         NS_LITERAL_CSTRING("DROP TABLE moz_webappsstore"));
     379           0 :       NS_ENSURE_SUCCESS(rv, rv);
     380             :     }
     381             : 
     382           0 :     aWorkerConnection->RemoveFunction(NS_LITERAL_CSTRING("REVERSESTRING"));
     383             : 
     384             :     // Update the scoping to match the new implememntation: split to oa suffix
     385             :     // and origin key First rename the old table, we want to remove some columns
     386             :     // no longer needed, but even before that drop all indexes from it (CREATE
     387             :     // IF NOT EXISTS for index on the new table would falsely find the index!)
     388           0 :     rv = aWorkerConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     389           0 :           "DROP INDEX IF EXISTS webappsstore2.origin_key_index"));
     390           0 :     NS_ENSURE_SUCCESS(rv, rv);
     391             : 
     392           0 :     rv = aWorkerConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     393           0 :           "DROP INDEX IF EXISTS webappsstore2.scope_key_index"));
     394           0 :     NS_ENSURE_SUCCESS(rv, rv);
     395             : 
     396           0 :     rv = aWorkerConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     397           0 :           "ALTER TABLE webappsstore2 RENAME TO webappsstore2_old"));
     398           0 :     NS_ENSURE_SUCCESS(rv, rv);
     399             : 
     400             :     nsCOMPtr<mozIStorageFunction> oaSuffixFunc(
     401           0 :       new GetOriginParticular(GetOriginParticular::ORIGIN_ATTRIBUTES_SUFFIX));
     402           0 :     rv = aWorkerConnection->CreateFunction(NS_LITERAL_CSTRING("GET_ORIGIN_SUFFIX"),
     403           0 :                                            1, oaSuffixFunc);
     404           0 :     NS_ENSURE_SUCCESS(rv, rv);
     405             : 
     406             :     nsCOMPtr<mozIStorageFunction> originKeyFunc(
     407           0 :       new GetOriginParticular(GetOriginParticular::ORIGIN_KEY));
     408           0 :     rv = aWorkerConnection->CreateFunction(NS_LITERAL_CSTRING("GET_ORIGIN_KEY"),
     409           0 :                                            1, originKeyFunc);
     410           0 :     NS_ENSURE_SUCCESS(rv, rv);
     411             : 
     412             :     // Here we ensure this schema tables when we are updating.
     413           0 :     rv = CreateSchema1Tables(aWorkerConnection);
     414           0 :     NS_ENSURE_SUCCESS(rv, rv);
     415             : 
     416           0 :     rv = aWorkerConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     417             :           "INSERT OR IGNORE INTO "
     418             :           "webappsstore2 (originAttributes, originKey, scope, key, value) "
     419             :           "SELECT GET_ORIGIN_SUFFIX(scope), GET_ORIGIN_KEY(scope), scope, key, value "
     420           0 :           "FROM webappsstore2_old"));
     421           0 :     NS_ENSURE_SUCCESS(rv, rv);
     422             : 
     423           0 :     rv = aWorkerConnection->ExecuteSimpleSQL(
     424           0 :       NS_LITERAL_CSTRING("DROP TABLE webappsstore2_old"));
     425           0 :     NS_ENSURE_SUCCESS(rv, rv);
     426             : 
     427           0 :     aWorkerConnection->RemoveFunction(NS_LITERAL_CSTRING("GET_ORIGIN_SUFFIX"));
     428           0 :     aWorkerConnection->RemoveFunction(NS_LITERAL_CSTRING("GET_ORIGIN_KEY"));
     429             : 
     430           0 :     rv = aWorkerConnection->SetSchemaVersion(1);
     431           0 :     NS_ENSURE_SUCCESS(rv, rv);
     432             : 
     433             :     MOZ_FALLTHROUGH;
     434             :   }
     435             :   case 1: {
     436             :     nsCOMPtr<mozIStorageFunction> oaStripAddonId(
     437           0 :       new StripOriginAddonId());
     438           0 :     rv = aWorkerConnection->CreateFunction(NS_LITERAL_CSTRING("STRIP_ADDON_ID"), 1, oaStripAddonId);
     439           0 :     NS_ENSURE_SUCCESS(rv, rv);
     440             : 
     441           0 :     rv = aWorkerConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     442             :           "UPDATE webappsstore2 "
     443             :           "SET originAttributes = STRIP_ADDON_ID(originAttributes) "
     444           0 :           "WHERE originAttributes LIKE '^%'"));
     445           0 :     NS_ENSURE_SUCCESS(rv, rv);
     446             : 
     447           0 :     aWorkerConnection->RemoveFunction(NS_LITERAL_CSTRING("STRIP_ADDON_ID"));
     448             : 
     449           0 :     rv = aWorkerConnection->SetSchemaVersion(2);
     450           0 :     NS_ENSURE_SUCCESS(rv, rv);
     451             : 
     452             :     MOZ_FALLTHROUGH;
     453             :   }
     454             :   case CURRENT_SCHEMA_VERSION:
     455             :     // Ensure the tables and indexes are up.  This is mostly a no-op
     456             :     // in common scenarios.
     457           1 :     rv = CreateSchema1Tables(aWorkerConnection);
     458           1 :     NS_ENSURE_SUCCESS(rv, rv);
     459             : 
     460             :     // Nothing more to do here, this is the current schema version
     461           1 :     break;
     462             : 
     463             :   default:
     464           0 :     MOZ_ASSERT(false);
     465             :     break;
     466             :   } // switch
     467             : 
     468           1 :   rv = transaction.Commit();
     469           1 :   NS_ENSURE_SUCCESS(rv, rv);
     470             : 
     471           1 :   if (doVacuum) {
     472             :     // In some cases this can make the disk file of the database significantly
     473             :     // smaller.  VACUUM cannot be executed inside a transaction.
     474           0 :     rv = aWorkerConnection->ExecuteSimpleSQL(
     475           0 :       NS_LITERAL_CSTRING("VACUUM"));
     476           0 :     NS_ENSURE_SUCCESS(rv, rv);
     477             :   }
     478             : 
     479           1 :   return NS_OK;
     480             : }
     481             : 
     482             : } // namespace StorageDBUpdater
     483             : } // namespace dom
     484             : } // namespace mozilla

Generated by: LCOV version 1.13