Bug 1184607 P7.5 Add RequestRedirect to Cache API schema with migration. r=ehsan
authorBen Kelly <ben@wanderview.com>
Mon, 31 Aug 2015 14:26:30 -0700
changeset 280146 5f09a49ac1a95b600a4ff1a15ab4d6a611c12782
parent 280145 4ab4edf1f7307452c3a773c00283e048bf40690b
child 280147 84cb3ede78ef027ca4064673d5add80bc23af3e0
push idunknown
push userunknown
push dateunknown
reviewersehsan
bugs1184607
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1184607 P7.5 Add RequestRedirect to Cache API schema with migration. r=ehsan
dom/cache/CacheTypes.ipdlh
dom/cache/DBSchema.cpp
dom/cache/IPCUtils.h
dom/cache/TypeUtils.cpp
--- a/dom/cache/CacheTypes.ipdlh
+++ b/dom/cache/CacheTypes.ipdlh
@@ -8,16 +8,17 @@ include protocol PCacheStreamControl;
 include InputStreamParams;
 include ChannelInfo;
 include PBackgroundSharedTypes;
 
 using HeadersGuardEnum from "mozilla/dom/cache/IPCUtils.h";
 using RequestCredentials from "mozilla/dom/cache/IPCUtils.h";
 using RequestMode from "mozilla/dom/cache/IPCUtils.h";
 using RequestCache from "mozilla/dom/cache/IPCUtils.h";
+using RequestRedirect from "mozilla/dom/cache/IPCUtils.h";
 using ResponseType from "mozilla/dom/cache/IPCUtils.h";
 using mozilla::void_t from "ipc/IPCMessageUtils.h";
 using struct nsID from "nsID.h";
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
@@ -59,16 +60,17 @@ struct CacheRequest
   HeadersEntry[] headers;
   HeadersGuardEnum headersGuard;
   nsString referrer;
   RequestMode mode;
   RequestCredentials credentials;
   CacheReadStreamOrVoid body;
   uint32_t contentPolicyType;
   RequestCache requestCache;
+  RequestRedirect requestRedirect;
 };
 
 union CacheRequestOrVoid
 {
   void_t;
   CacheRequest;
 };
 
--- a/dom/cache/DBSchema.cpp
+++ b/dom/cache/DBSchema.cpp
@@ -31,17 +31,18 @@ namespace mozilla {
 namespace dom {
 namespace cache {
 namespace db {
 
 const int32_t kFirstShippedSchemaVersion = 15;
 
 namespace {
 
-const int32_t kLatestSchemaVersion = 15;
+// Update this whenever the DB schema is changed.
+const int32_t kLatestSchemaVersion = 16;
 
 // ---------
 // The following constants define the SQL schema.  These are defined in the
 // same order the SQL should be executed in CreateOrMigrateSchema().  They are
 // broken out as constants for convenient use in validation and migration.
 // ---------
 
 // The caches table is the single source of truth about what Cache
@@ -100,17 +101,21 @@ const char* const kTableEntries =
     "response_headers_guard INTEGER NOT NULL, "
     "response_body_id TEXT NULL, "
     "response_security_info_id INTEGER NULL REFERENCES security_info(id), "
     "response_principal_info TEXT NOT NULL, "
     "response_redirected INTEGER NOT NULL, "
     // Note that response_redirected_url is either going to be empty, or
     // it's going to be a URL different than response_url.
     "response_redirected_url TEXT NOT NULL, "
-    "cache_id INTEGER NOT NULL REFERENCES caches(id) ON DELETE CASCADE"
+    "cache_id INTEGER NOT NULL REFERENCES caches(id) ON DELETE CASCADE, "
+
+    // New columns must be added at the end of table to migrate and
+    // validate properly.
+    "request_redirect INTEGER NOT NULL"
   ")";
 
 // Create an index to support the QueryCache() matching algorithm.  This
 // needs to quickly find entries in a given Cache that match the request
 // URL.  The url query is separated in order to support the ignoreSearch
 // option.  Finally, we index hashes of the URL values instead of the
 // actual strings to avoid excessive disk bloat.  The index will duplicate
 // the contents of the columsn in the index.  The hash index will prune
@@ -202,22 +207,28 @@ static_assert(int(RequestCredentials::Om
 static_assert(int(RequestCache::Default) == 0 &&
               int(RequestCache::No_store) == 1 &&
               int(RequestCache::Reload) == 2 &&
               int(RequestCache::No_cache) == 3 &&
               int(RequestCache::Force_cache) == 4 &&
               int(RequestCache::Only_if_cached) == 5 &&
               int(RequestCache::EndGuard_) == 6,
               "RequestCache values are as expected");
+static_assert(int(RequestRedirect::Follow) == 0 &&
+              int(RequestRedirect::Error) == 1 &&
+              int(RequestRedirect::Manual) == 2 &&
+              int(RequestRedirect::EndGuard_) == 3,
+              "RequestRedirect values are as expected");
 static_assert(int(ResponseType::Basic) == 0 &&
               int(ResponseType::Cors) == 1 &&
               int(ResponseType::Default) == 2 &&
               int(ResponseType::Error) == 3 &&
               int(ResponseType::Opaque) == 4 &&
-              int(ResponseType::EndGuard_) == 5,
+              int(ResponseType::Opaqueredirect) == 5 &&
+              int(ResponseType::EndGuard_) == 6,
               "ResponseType values are as expected");
 
 // If the static_asserts below fails, it means that you have changed the
 // Namespace enum in a way that may be incompatible with the existing data
 // stored in the DOM Cache.  You would need to update the Cache database schema
 // accordingly and adjust the failing static_assert.
 static_assert(DEFAULT_NAMESPACE == 0 &&
               CHROME_ONLY_NAMESPACE == 1 &&
@@ -355,17 +366,18 @@ CreateOrMigrateSchema(mozIStorageConnect
 
   if (schemaVersion) {
     // A schema exists, but its not the current version.  Attempt to
     // migrate it to our new schema.
     rv = Migrate(aConn);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     // Migrations happen infrequently and reflect a chance in DB structure.
-    // This is a good time to rebuild the database.
+    // This is a good time to rebuild the database.  It also helps catch
+    // if a new migration is incorrect by fast failing on the corruption.
     needVacuum = true;
 
   } else {
     // There is no schema installed.  Create the database from scratch.
     rv = aConn->ExecuteSimpleSQL(nsDependentCString(kTableCaches));
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     rv = aConn->ExecuteSimpleSQL(nsDependentCString(kTableSecurityInfo));
@@ -1575,16 +1587,17 @@ InsertEntry(mozIStorageConnection* aConn
       "request_url_query, "
       "request_url_query_hash, "
       "request_referrer, "
       "request_headers_guard, "
       "request_mode, "
       "request_credentials, "
       "request_contentpolicytype, "
       "request_cache, "
+      "request_redirect, "
       "request_body_id, "
       "response_type, "
       "response_url, "
       "response_status, "
       "response_status_text, "
       "response_headers_guard, "
       "response_body_id, "
       "response_security_info_id, "
@@ -1599,16 +1612,17 @@ InsertEntry(mozIStorageConnection* aConn
       ":request_url_query, "
       ":request_url_query_hash, "
       ":request_referrer, "
       ":request_headers_guard, "
       ":request_mode, "
       ":request_credentials, "
       ":request_contentpolicytype, "
       ":request_cache, "
+      ":request_redirect, "
       ":request_body_id, "
       ":response_type, "
       ":response_url, "
       ":response_status, "
       ":response_status_text, "
       ":response_headers_guard, "
       ":response_body_id, "
       ":response_security_info_id, "
@@ -1667,16 +1681,19 @@ InsertEntry(mozIStorageConnection* aConn
   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("request_contentpolicytype"),
     static_cast<int32_t>(aRequest.contentPolicyType()));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("request_cache"),
     static_cast<int32_t>(aRequest.requestCache()));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
+  rv = state->BindInt32ByName(NS_LITERAL_CSTRING("request_redirect"),
+    static_cast<int32_t>(aRequest.requestRedirect()));
+
   rv = BindId(state, NS_LITERAL_CSTRING("request_body_id"), aRequestBodyId);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("response_type"),
                               static_cast<int32_t>(aResponse.type()));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("response_url"),
@@ -1946,16 +1963,17 @@ ReadRequest(mozIStorageConnection* aConn
       "request_url_no_query, "
       "request_url_query, "
       "request_referrer, "
       "request_headers_guard, "
       "request_mode, "
       "request_credentials, "
       "request_contentpolicytype, "
       "request_cache, "
+      "request_redirect, "
       "request_body_id "
     "FROM entries "
     "WHERE id=:id;"
   ), getter_AddRefs(state));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("id"), aEntryId);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
@@ -2000,23 +2018,29 @@ ReadRequest(mozIStorageConnection* aConn
     static_cast<nsContentPolicyType>(requestContentPolicyType);
 
   int32_t requestCache;
   rv = state->GetInt32(8, &requestCache);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   aSavedRequestOut->mValue.requestCache() =
     static_cast<RequestCache>(requestCache);
 
+  int32_t requestRedirect;
+  rv = state->GetInt32(9, &requestRedirect);
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+  aSavedRequestOut->mValue.requestRedirect() =
+    static_cast<RequestRedirect>(requestRedirect);
+
   bool nullBody = false;
-  rv = state->GetIsNull(9, &nullBody);
+  rv = state->GetIsNull(10, &nullBody);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   aSavedRequestOut->mHasBodyId = !nullBody;
 
   if (aSavedRequestOut->mHasBodyId) {
-    rv = ExtractId(state, 9, &aSavedRequestOut->mBodyId);
+    rv = ExtractId(state, 10, &aSavedRequestOut->mBodyId);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   }
 
   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT "
       "name, "
       "value "
     "FROM request_headers "
@@ -2355,19 +2379,21 @@ struct Migration
     , mFunc(aFunc)
   { }
   int32_t mFromVersion;
   MigrationFunc mFunc;
 };
 
 // Declare migration functions here.  Each function should upgrade
 // the version by a single increment.  Don't skip versions.
+nsresult MigrateFrom15To16(mozIStorageConnection* aConn);
 
 // Configure migration functions to run for the given starting version.
 Migration sMigrationList[] = {
+  Migration(15, MigrateFrom15To16),
 };
 
 uint32_t sMigrationListLength = sizeof(sMigrationList) / sizeof(Migration);
 
 nsresult
 Migrate(mozIStorageConnection* aConn)
 {
   MOZ_ASSERT(!NS_IsMainThread());
@@ -2397,14 +2423,62 @@ Migrate(mozIStorageConnection* aConn)
     MOZ_ASSERT(currentVersion > lastVersion);
   }
 
   MOZ_ASSERT(currentVersion == kLatestSchemaVersion);
 
   return rv;
 }
 
+nsresult MigrateFrom15To16(mozIStorageConnection* aConn)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_ASSERT(aConn);
+
+  // Add the request_redirect column with a default value of "follow".  Note,
+  // we only use a default value here because its required by ALTER TABLE and
+  // we need to apply the default "follow" to existing records in the table.
+  // We don't actually want to keep the default in the schema for future
+  // INSERTs.
+  nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "ALTER TABLE entries "
+    "ADD COLUMN request_redirect INTEGER NOT NULL DEFAULT 0"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  // Now overwrite the master SQL for the entries table to remove the column
+  // default value.  This is also necessary for our Validate() method to
+  // pass on this database.
+  rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "PRAGMA writable_schema = ON"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  nsCOMPtr<mozIStorageStatement> state;
+  rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
+    "UPDATE sqlite_master SET sql=:sql WHERE name='entries'"
+  ), getter_AddRefs(state));
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("sql"),
+                                   nsDependentCString(kTableEntries));
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  rv = state->Execute();
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  rv = aConn->SetSchemaVersion(16);
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "PRAGMA writable_schema = OFF"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  return rv;
+}
+
 } // anonymous namespace
 
 } // namespace db
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/IPCUtils.h
+++ b/dom/cache/IPCUtils.h
@@ -34,16 +34,21 @@ namespace IPC {
                                     mozilla::dom::RequestCredentials::Omit,
                                     mozilla::dom::RequestCredentials::EndGuard_> {};
   template<>
   struct ParamTraits<mozilla::dom::RequestCache> :
     public ContiguousEnumSerializer<mozilla::dom::RequestCache,
                                     mozilla::dom::RequestCache::Default,
                                     mozilla::dom::RequestCache::EndGuard_> {};
   template<>
+  struct ParamTraits<mozilla::dom::RequestRedirect> :
+    public ContiguousEnumSerializer<mozilla::dom::RequestRedirect,
+                                    mozilla::dom::RequestRedirect::Follow,
+                                    mozilla::dom::RequestRedirect::EndGuard_> {};
+  template<>
   struct ParamTraits<mozilla::dom::ResponseType> :
     public ContiguousEnumSerializer<mozilla::dom::ResponseType,
                                     mozilla::dom::ResponseType::Basic,
                                     mozilla::dom::ResponseType::EndGuard_> {};
   template<>
   struct ParamTraits<mozilla::dom::cache::Namespace> :
     public ContiguousEnumSerializer<mozilla::dom::cache::Namespace,
                                     mozilla::dom::cache::DEFAULT_NAMESPACE,
--- a/dom/cache/TypeUtils.cpp
+++ b/dom/cache/TypeUtils.cpp
@@ -174,16 +174,17 @@ TypeUtils::ToCacheRequest(CacheRequest& 
   nsRefPtr<InternalHeaders> headers = aIn->Headers();
   MOZ_ASSERT(headers);
   ToHeadersEntryList(aOut.headers(), headers);
   aOut.headersGuard() = headers->Guard();
   aOut.mode() = aIn->Mode();
   aOut.credentials() = aIn->GetCredentialsMode();
   aOut.contentPolicyType() = aIn->ContentPolicyType();
   aOut.requestCache() = aIn->GetCacheMode();
+  aOut.requestRedirect() = aIn->GetRedirectMode();
 
   if (aBodyAction == IgnoreBody) {
     aOut.body() = void_t();
     return;
   }
 
   // BodyUsed flag is checked and set previously in ToInternalRequest()
 
@@ -207,18 +208,18 @@ TypeUtils::ToCacheResponseWithoutBody(Ca
     // Pass all Response URL schemes through... The spec only requires we take
     // action on invalid schemes for Request objects.
     ProcessURL(aOut.url(), nullptr, nullptr, nullptr, aRv);
     if (aRv.Failed()) {
       return;
     }
   }
 
-  aOut.status() = aIn.GetStatus();
-  aOut.statusText() = aIn.GetStatusText();
+  aOut.status() = aIn.GetUnfilteredStatus();
+  aOut.statusText() = aIn.GetUnfilteredStatusText();
   nsRefPtr<InternalHeaders> headers = aIn.UnfilteredHeaders();
   MOZ_ASSERT(headers);
   if (HasVaryStar(headers)) {
     aRv.ThrowTypeError(MSG_RESPONSE_HAS_VARY_STAR);
     return;
   }
   ToHeadersEntryList(aOut.headers(), headers);
   aOut.headersGuard() = headers->Guard();
@@ -240,17 +241,17 @@ TypeUtils::ToCacheResponse(CacheResponse
 
   nsRefPtr<InternalResponse> ir = aIn.GetInternalResponse();
   ToCacheResponseWithoutBody(aOut, *ir, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
   nsCOMPtr<nsIInputStream> stream;
-  ir->GetInternalBody(getter_AddRefs(stream));
+  ir->GetUnfilteredBody(getter_AddRefs(stream));
   if (stream) {
     aIn.SetBodyUsed();
   }
 
   SerializeCacheStream(stream, &aOut.body(), aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
@@ -299,26 +300,29 @@ TypeUtils::ToResponse(const CacheRespons
     ir->SetPrincipalInfo(Move(info));
   }
 
   nsCOMPtr<nsIInputStream> stream = ReadStream::Create(aIn.body());
   ir->SetBody(stream);
 
   switch (aIn.type())
   {
+    case ResponseType::Basic:
+      ir = ir->BasicResponse();
+      break;
+    case ResponseType::Cors:
+      ir = ir->CORSResponse();
+      break;
     case ResponseType::Default:
       break;
     case ResponseType::Opaque:
       ir = ir->OpaqueResponse();
       break;
-    case ResponseType::Basic:
-      ir = ir->BasicResponse();
-      break;
-    case ResponseType::Cors:
-      ir = ir->CORSResponse();
+    case ResponseType::Opaqueredirect:
+      ir = ir->OpaqueRedirectResponse();
       break;
     default:
       MOZ_CRASH("Unexpected ResponseType!");
   }
   MOZ_ASSERT(ir);
 
   nsRefPtr<Response> ref = new Response(GetGlobalObject(), ir);
   return ref.forget();
@@ -335,16 +339,17 @@ TypeUtils::ToInternalRequest(const Cache
   url.Append(aIn.urlQuery());
   internalRequest->SetURL(url);
 
   internalRequest->SetReferrer(aIn.referrer());
   internalRequest->SetMode(aIn.mode());
   internalRequest->SetCredentialsMode(aIn.credentials());
   internalRequest->SetContentPolicyType(aIn.contentPolicyType());
   internalRequest->SetCacheMode(aIn.requestCache());
+  internalRequest->SetRedirectMode(aIn.requestRedirect());
 
   nsRefPtr<InternalHeaders> internalHeaders =
     ToInternalHeaders(aIn.headers(), aIn.headersGuard());
   ErrorResult result;
   internalRequest->Headers()->SetGuard(aIn.headersGuard(), result);
   MOZ_ASSERT(!result.Failed());
   internalRequest->Headers()->Fill(*internalHeaders, result);
   MOZ_ASSERT(!result.Failed());