Back out 3 changesets (bug 1204596) for b2g test_fetch_cors.html failures
authorPhil Ringnalda <philringnalda@gmail.com>
Wed, 16 Sep 2015 20:51:17 -0700
changeset 297278 004e30faaea89ac7f4118f71fda916eebb0fa136
parent 297277 21235635ebdec9bdfa6d65b71285f0fdb7b1bce8
child 297279 0c3593c96d650714464682f38d49ed935cfd0f48
push id962
push userjlund@mozilla.com
push dateFri, 04 Dec 2015 23:28:54 +0000
treeherdermozilla-release@23a2d286e80f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1204596
milestone43.0a1
backs out2662a1ad4cad84a6209c8116ad714d6a0c1bb302
cfc4c4ecbbf520970834fe7cd6e2c017bcd86852
d10c6f32ce461a3aef76868eaa78de009145657d
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
Back out 3 changesets (bug 1204596) for b2g test_fetch_cors.html failures Backed out changeset 2662a1ad4cad (bug 1204596) Backed out changeset cfc4c4ecbbf5 (bug 1204596) Backed out changeset d10c6f32ce46 (bug 1204596)
dom/cache/DBSchema.cpp
dom/cache/test/xpcshell/test_migration.js
dom/fetch/ChannelInfo.cpp
dom/fetch/ChannelInfo.h
dom/fetch/ChannelInfo.ipdlh
dom/tests/mochitest/fetch/fetch_test_framework.js
dom/tests/mochitest/fetch/reroute.html
dom/tests/mochitest/fetch/reroute.js
dom/tests/mochitest/fetch/test_fetch_basic_http.js
dom/tests/mochitest/fetch/test_fetch_cors.js
dom/workers/test/serviceworkers/fetch/origin/https/origin_test.js
dom/workers/test/serviceworkers/fetch/origin/origin_test.js
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/HttpBaseChannel.h
--- a/dom/cache/DBSchema.cpp
+++ b/dom/cache/DBSchema.cpp
@@ -32,17 +32,17 @@ namespace dom {
 namespace cache {
 namespace db {
 
 const int32_t kFirstShippedSchemaVersion = 15;
 
 namespace {
 
 // Update this whenever the DB schema is changed.
-const int32_t kLatestSchemaVersion = 17;
+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
@@ -97,16 +97,20 @@ const char* const kTableEntries =
     "response_type INTEGER NOT NULL, "
     "response_url TEXT NOT NULL, "
     "response_status INTEGER NOT NULL, "
     "response_status_text TEXT NOT NULL, "
     "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, "
 
     // 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
@@ -333,61 +337,16 @@ static nsresult CreateAndBindKeyStatemen
                                           const nsAString& aKey,
                                           mozIStorageStatement** aStateOut);
 static nsresult HashCString(nsICryptoHash* aCrypto, const nsACString& aIn,
                             nsACString& aOut);
 nsresult Validate(mozIStorageConnection* aConn);
 nsresult Migrate(mozIStorageConnection* aConn);
 } // namespace
 
-class MOZ_RAII AutoDisableForeignKeyChecking
-{
-public:
-  explicit AutoDisableForeignKeyChecking(mozIStorageConnection* aConn)
-    : mConn(aConn)
-    , mForeignKeyCheckingDisabled(false)
-  {
-    nsCOMPtr<mozIStorageStatement> state;
-    nsresult rv = mConn->CreateStatement(NS_LITERAL_CSTRING(
-      "PRAGMA foreign_keys;"
-    ), getter_AddRefs(state));
-    if (NS_WARN_IF(NS_FAILED(rv))) { return; }
-
-    bool hasMoreData = false;
-    rv = state->ExecuteStep(&hasMoreData);
-    if (NS_WARN_IF(NS_FAILED(rv))) { return; }
-
-    int32_t mode;
-    rv = state->GetInt32(0, &mode);
-    if (NS_WARN_IF(NS_FAILED(rv))) { return; }
-
-    if (mode) {
-      nsresult rv = mConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-        "PRAGMA foreign_keys = OFF;"
-      ));
-      if (NS_WARN_IF(NS_FAILED(rv))) { return; }
-      mForeignKeyCheckingDisabled = true;
-    }
-  }
-
-  ~AutoDisableForeignKeyChecking()
-  {
-    if (mForeignKeyCheckingDisabled) {
-      nsresult rv = mConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-        "PRAGMA foreign_keys = ON;"
-      ));
-      if (NS_WARN_IF(NS_FAILED(rv))) { return; }
-    }
-  }
-
-private:
-  nsCOMPtr<mozIStorageConnection> mConn;
-  bool mForeignKeyCheckingDisabled;
-};
-
 nsresult
 CreateOrMigrateSchema(mozIStorageConnection* aConn)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aConn);
 
   int32_t schemaVersion;
   nsresult rv = aConn->GetSchemaVersion(&schemaVersion);
@@ -397,33 +356,31 @@ CreateOrMigrateSchema(mozIStorageConnect
     // We already have the correct schema version.  Validate it matches
     // our expected schema and then proceed.
     rv = Validate(aConn);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     return rv;
   }
 
-  // Turn off checking foreign keys before starting a transaction, and restore
-  // it once we're done.
-  AutoDisableForeignKeyChecking restoreForeignKeyChecking(aConn);
   mozStorageTransaction trans(aConn, false,
                               mozIStorageConnection::TRANSACTION_IMMEDIATE);
   bool needVacuum = false;
 
   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.  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));
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
@@ -1641,16 +1598,18 @@ InsertEntry(mozIStorageConnection* aConn
       "response_type, "
       "response_url, "
       "response_status, "
       "response_status_text, "
       "response_headers_guard, "
       "response_body_id, "
       "response_security_info_id, "
       "response_principal_info, "
+      "response_redirected, "
+      "response_redirected_url, "
       "cache_id "
     ") VALUES ("
       ":request_method, "
       ":request_url_no_query, "
       ":request_url_no_query_hash, "
       ":request_url_query, "
       ":request_url_query_hash, "
       ":request_referrer, "
@@ -1664,16 +1623,18 @@ InsertEntry(mozIStorageConnection* aConn
       ":response_type, "
       ":response_url, "
       ":response_status, "
       ":response_status_text, "
       ":response_headers_guard, "
       ":response_body_id, "
       ":response_security_info_id, "
       ":response_principal_info, "
+      ":response_redirected, "
+      ":response_redirected_url, "
       ":cache_id "
     ");"
   ), getter_AddRefs(state));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("request_method"),
                                    aRequest.method());
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
@@ -1780,16 +1741,24 @@ InsertEntry(mozIStorageConnection* aConn
     attrs.CreateSuffix(suffix);
     serializedInfo.Append(suffix);
   }
 
   rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("response_principal_info"),
                                    serializedInfo);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
+  rv = state->BindInt32ByName(NS_LITERAL_CSTRING("response_redirected"),
+                              aResponse.channelInfo().redirected() ? 1 : 0);
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("response_redirected_url"),
+                                   aResponse.channelInfo().redirectedURI());
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
   rv = state->BindInt64ByName(NS_LITERAL_CSTRING("cache_id"), aCacheId);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = state->Execute();
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT last_insert_rowid()"
@@ -1872,16 +1841,18 @@ ReadResponse(mozIStorageConnection* aCon
     "SELECT "
       "entries.response_type, "
       "entries.response_url, "
       "entries.response_status, "
       "entries.response_status_text, "
       "entries.response_headers_guard, "
       "entries.response_body_id, "
       "entries.response_principal_info, "
+      "entries.response_redirected, "
+      "entries.response_redirected_url, "
       "security_info.data "
     "FROM entries "
     "LEFT OUTER JOIN security_info "
     "ON entries.response_security_info_id=security_info.id "
     "WHERE entries.id=:id;"
   ), getter_AddRefs(state));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
@@ -1936,17 +1907,24 @@ ReadResponse(mozIStorageConnection* aCon
       NS_WARNING("Something went wrong parsing a serialized principal!");
       return NS_ERROR_FAILURE;
     }
 
     aSavedResponseOut->mValue.principalInfo() =
       mozilla::ipc::ContentPrincipalInfo(attrs.mAppId, attrs.mInBrowser, originNoSuffix);
   }
 
-  rv = state->GetBlobAsUTF8String(7, aSavedResponseOut->mValue.channelInfo().securityInfo());
+  int32_t redirected;
+  rv = state->GetInt32(7, &redirected);
+  aSavedResponseOut->mValue.channelInfo().redirected() = !!redirected;
+
+  rv = state->GetUTF8String(8, aSavedResponseOut->mValue.channelInfo().redirectedURI());
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  rv = state->GetBlobAsUTF8String(9, aSavedResponseOut->mValue.channelInfo().securityInfo());
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT "
       "name, "
       "value "
     "FROM response_headers "
     "WHERE entry_id=:entry_id;"
@@ -2403,22 +2381,20 @@ struct Migration
   { }
   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);
-nsresult MigrateFrom16To17(mozIStorageConnection* aConn);
 
 // Configure migration functions to run for the given starting version.
 Migration sMigrationList[] = {
   Migration(15, MigrateFrom15To16),
-  Migration(16, MigrateFrom16To17),
 };
 
 uint32_t sMigrationListLength = sizeof(sMigrationList) / sizeof(Migration);
 
 nsresult
 Migrate(mozIStorageConnection* aConn)
 {
   MOZ_ASSERT(!NS_IsMainThread());
@@ -2448,20 +2424,36 @@ Migrate(mozIStorageConnection* aConn)
     MOZ_ASSERT(currentVersion > lastVersion);
   }
 
   MOZ_ASSERT(currentVersion == kLatestSchemaVersion);
 
   return rv;
 }
 
-nsresult
-RewriteEntriesSchema(mozIStorageConnection* aConn)
+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));
@@ -2469,191 +2461,25 @@ RewriteEntriesSchema(mozIStorageConnecti
 
   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;
 }
 
-nsresult MigrateFrom15To16(mozIStorageConnection* aConn)
-{
-  MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
-
-  mozStorageTransaction trans(aConn, true,
-                              mozIStorageConnection::TRANSACTION_IMMEDIATE);
-
-  // 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 = RewriteEntriesSchema(aConn);
-  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-
-  rv = aConn->SetSchemaVersion(16);
-  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-
-  return rv;
-}
-
-nsresult
-MigrateFrom16To17(mozIStorageConnection* aConn)
-{
-  MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
-
-  // This migration path removes the response_redirected and
-  // response_redirected_url columns from the entries table.  sqlite doesn't
-  // support removing a column from a table using ALTER TABLE, so we need to
-  // create a new table without those columns, fill it up with the existing
-  // data, and then drop the original table and rename the new one to the old
-  // one.
-
-  // Create a new_entries table with the new fields as of version 17.
-  nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-    "CREATE TABLE new_entries ("
-      "id INTEGER NOT NULL PRIMARY KEY, "
-      "request_method TEXT NOT NULL, "
-      "request_url_no_query TEXT NOT NULL, "
-      "request_url_no_query_hash BLOB NOT NULL, "
-      "request_url_query TEXT NOT NULL, "
-      "request_url_query_hash BLOB NOT NULL, "
-      "request_referrer TEXT NOT NULL, "
-      "request_headers_guard INTEGER NOT NULL, "
-      "request_mode INTEGER NOT NULL, "
-      "request_credentials INTEGER NOT NULL, "
-      "request_contentpolicytype INTEGER NOT NULL, "
-      "request_cache INTEGER NOT NULL, "
-      "request_body_id TEXT NULL, "
-      "response_type INTEGER NOT NULL, "
-      "response_url TEXT NOT NULL, "
-      "response_status INTEGER NOT NULL, "
-      "response_status_text TEXT NOT NULL, "
-      "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, "
-      "cache_id INTEGER NOT NULL REFERENCES caches(id) ON DELETE CASCADE, "
-      "request_redirect INTEGER NOT NULL"
-    ")"
-  ));
-  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-
-  // Copy all of the data to the newly created table.
-  rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-    "INSERT INTO new_entries ("
-      "id, "
-      "request_method, "
-      "request_url_no_query, "
-      "request_url_no_query_hash, "
-      "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, "
-      "response_principal_info, "
-      "cache_id "
-    ") SELECT "
-      "id, "
-      "request_method, "
-      "request_url_no_query, "
-      "request_url_no_query_hash, "
-      "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, "
-      "response_principal_info, "
-      "cache_id "
-    "FROM entries;"
-  ));
-  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-
-  // Remove the old table.
-  rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-    "DROP TABLE entries;"
-  ));
-  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-
-  // Rename new_entries to entries.
-  rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-    "ALTER TABLE new_entries RENAME to entries;"
-  ));
-  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-
-  // Now, recreate our indices.
-  rv = aConn->ExecuteSimpleSQL(nsDependentCString(kIndexEntriesRequest));
-  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-
-  // Revalidate the foreign key constraints, and ensure that there are no
-  // violations.
-  nsCOMPtr<mozIStorageStatement> state;
-  rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
-    "PRAGMA foreign_key_check;"
-  ), getter_AddRefs(state));
-  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-
-  bool hasMoreData = false;
-  rv = state->ExecuteStep(&hasMoreData);
-  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-  if (NS_WARN_IF(hasMoreData)) { return NS_ERROR_FAILURE; }
-
-  // Finally, rewrite the schema for the entries database, otherwise the
-  // returned SQL string from sqlite will wrap the name of the table in quotes,
-  // breaking the checks in Validate().
-  rv = RewriteEntriesSchema(aConn);
-  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-
-  rv = aConn->SetSchemaVersion(17);
-  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-
-  return rv;
-}
-
 } // anonymous namespace
 
 } // namespace db
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/test/xpcshell/test_migration.js
+++ b/dom/cache/test/xpcshell/test_migration.js
@@ -22,19 +22,17 @@ function run_test() {
       ok(request.redirect === 'follow', 'request.redirect should default to "follow"');
     });
     return Promise.all(requestList.map(function(request) {
       return cache.match(request);
     }));
   }).then(function(responseList) {
     ok(responseList.length > 0, 'should have at least one response in cache');
     responseList.forEach(function(response) {
-      ok(response, 'each response in list should be non-null');
-      do_check_eq(response.headers.get('Content-Type'), 'text/plain;charset=UTF-8',
-                  'the response should have the correct header');
+      ok(response, 'each request in list should be non-null');
     });
   }).then(function() {
     do_test_finished();
   }).catch(function(e) {
     ok(false, 'caught exception ' + e);
     do_test_finished();
   });
 }
--- a/dom/fetch/ChannelInfo.cpp
+++ b/dom/fetch/ChannelInfo.cpp
@@ -26,53 +26,76 @@ ChannelInfo::InitFromDocument(nsIDocumen
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!mInited, "Cannot initialize the object twice");
 
   nsCOMPtr<nsISupports> securityInfo = aDoc->GetSecurityInfo();
   if (securityInfo) {
     SetSecurityInfo(securityInfo);
   }
 
+  // mRedirected flag and mRedirectedURISpec are only important for maintaining
+  // the channel's redirected status.  If the ChannelInfo is initialized from
+  // a document, that document has already asked the channel from which it was
+  // loaded about the current channel URI, so it won't matter if a future
+  // ResurrectInfoOnChannel() call misses whether the channel was redirected.
+  mRedirected = false;
   mInited = true;
 }
 
 void
 ChannelInfo::InitFromChannel(nsIChannel* aChannel)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!mInited, "Cannot initialize the object twice");
 
   nsCOMPtr<nsISupports> securityInfo;
   aChannel->GetSecurityInfo(getter_AddRefs(securityInfo));
   if (securityInfo) {
     SetSecurityInfo(securityInfo);
   }
 
+  nsLoadFlags loadFlags = 0;
+  aChannel->GetLoadFlags(&loadFlags);
+  mRedirected = (loadFlags & nsIChannel::LOAD_REPLACE);
+  if (mRedirected) {
+    // Save the spec and not the nsIURI object itself, since those objects are
+    // not thread-safe, and releasing them somewhere other than the main thread
+    // is not possible.
+    nsCOMPtr<nsIURI> redirectedURI;
+    aChannel->GetURI(getter_AddRefs(redirectedURI));
+    if (redirectedURI) {
+      redirectedURI->GetSpec(mRedirectedURISpec);
+    }
+  }
+
   mInited = true;
 }
 
 void
 ChannelInfo::InitFromChromeGlobal(nsIGlobalObject* aGlobal)
 {
   MOZ_ASSERT(!mInited, "Cannot initialize the object twice");
   MOZ_ASSERT(aGlobal);
 
   MOZ_RELEASE_ASSERT(
     nsContentUtils::IsSystemPrincipal(aGlobal->PrincipalOrNull()));
 
   mSecurityInfo.Truncate();
+  mRedirected = false;
   mInited = true;
 }
 
 void
 ChannelInfo::InitFromIPCChannelInfo(const mozilla::ipc::IPCChannelInfo& aChannelInfo)
 {
   MOZ_ASSERT(!mInited, "Cannot initialize the object twice");
 
   mSecurityInfo = aChannelInfo.securityInfo();
+  mRedirectedURISpec = aChannelInfo.redirectedURI();
+  mRedirected = aChannelInfo.redirected();
 
   mInited = true;
 }
 
 void
 ChannelInfo::SetSecurityInfo(nsISupports* aSecurityInfo)
 {
   MOZ_ASSERT(mSecurityInfo.IsEmpty(), "security info should only be set once");
@@ -114,25 +137,54 @@ ChannelInfo::ResurrectInfoOnChannel(nsIC
       if (NS_WARN_IF(!jarChannel)) {
         return NS_ERROR_FAILURE;
       }
       static_cast<nsJARChannel*>(jarChannel.get())->
         OverrideSecurityInfo(infoObj);
     }
   }
 
+  if (mRedirected) {
+    nsLoadFlags flags = 0;
+    aChannel->GetLoadFlags(&flags);
+    flags |= nsIChannel::LOAD_REPLACE;
+    aChannel->SetLoadFlags(flags);
+
+    nsCOMPtr<nsIURI> redirectedURI;
+    nsresult rv = NS_NewURI(getter_AddRefs(redirectedURI),
+                            mRedirectedURISpec);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+    if (httpChannel) {
+      net::HttpBaseChannel* httpBaseChannel =
+        static_cast<net::HttpBaseChannel*>(httpChannel.get());
+      rv = httpBaseChannel->OverrideURI(redirectedURI);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+    } else {
+      if (NS_WARN_IF(!jarChannel)) {
+        return NS_ERROR_FAILURE;
+      }
+      static_cast<nsJARChannel*>(jarChannel.get())->OverrideURI(redirectedURI);
+    }
+  }
+
   return NS_OK;
 }
 
 mozilla::ipc::IPCChannelInfo
 ChannelInfo::AsIPCChannelInfo() const
 {
   // This may be called when mInited is false, for example if we try to store
   // a synthesized Response object into the Cache.  Uninitialized and empty
   // ChannelInfo objects are indistinguishable at the IPC level, so this is
   // fine.
 
   IPCChannelInfo ipcInfo;
 
   ipcInfo.securityInfo() = mSecurityInfo;
+  ipcInfo.redirectedURI() = mRedirectedURISpec;
+  ipcInfo.redirected() = mRedirected;
 
   return ipcInfo;
 }
--- a/dom/fetch/ChannelInfo.h
+++ b/dom/fetch/ChannelInfo.h
@@ -41,30 +41,35 @@ namespace dom {
 // initialized.  There are assertions ensuring these invariants.
 class ChannelInfo final
 {
 public:
   typedef mozilla::ipc::IPCChannelInfo IPCChannelInfo;
 
   ChannelInfo()
     : mInited(false)
+    , mRedirected(false)
   {
   }
 
   ChannelInfo(const ChannelInfo& aRHS)
     : mSecurityInfo(aRHS.mSecurityInfo)
+    , mRedirectedURISpec(aRHS.mRedirectedURISpec)
     , mInited(aRHS.mInited)
+    , mRedirected(aRHS.mRedirected)
   {
   }
 
   ChannelInfo&
   operator=(const ChannelInfo& aRHS)
   {
     mSecurityInfo = aRHS.mSecurityInfo;
+    mRedirectedURISpec = aRHS.mRedirectedURISpec;
     mInited = aRHS.mInited;
+    mRedirected = aRHS.mRedirected;
     return *this;
   }
 
   void InitFromDocument(nsIDocument* aDoc);
   void InitFromChannel(nsIChannel* aChannel);
   void InitFromChromeGlobal(nsIGlobalObject* aGlobal);
   void InitFromIPCChannelInfo(const IPCChannelInfo& aChannelInfo);
 
@@ -79,15 +84,17 @@ public:
 
   IPCChannelInfo AsIPCChannelInfo() const;
 
 private:
   void SetSecurityInfo(nsISupports* aSecurityInfo);
 
 private:
   nsCString mSecurityInfo;
+  nsCString mRedirectedURISpec;
   bool mInited;
+  bool mRedirected;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_ChannelInfo_h
--- a/dom/fetch/ChannelInfo.ipdlh
+++ b/dom/fetch/ChannelInfo.ipdlh
@@ -3,12 +3,14 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 namespace mozilla {
 namespace ipc {
 
 struct IPCChannelInfo
 {
   nsCString securityInfo;
+  nsCString redirectedURI;
+  bool redirected;
 };
 
 } // namespace ipc
 } // namespace mozilla
--- a/dom/tests/mochitest/fetch/fetch_test_framework.js
+++ b/dom/tests/mochitest/fetch/fetch_test_framework.js
@@ -1,14 +1,9 @@
 function testScript(script) {
-  // reroute.html should have set this variable if a service worker is present!
-  if (!("isSWPresent" in window)) {
-    window.isSWPresent = false;
-  }
-
   function setupPrefs() {
     return new Promise(function(resolve, reject) {
       SpecialPowers.pushPrefEnv({
         "set": [["dom.requestcontext.enabled", true],
                 ["dom.serviceWorkers.enabled", true],
                 ["dom.serviceWorkers.testing.enabled", true],
                 ["dom.serviceWorkers.exemptFromPerDomainMax", true]]
       }, resolve);
--- a/dom/tests/mochitest/fetch/reroute.html
+++ b/dom/tests/mochitest/fetch/reroute.html
@@ -1,11 +1,10 @@
 <!DOCTYPE html>
 <script>
 ["SimpleTest", "ok", "info", "is", "$"]
   .forEach((v) => window[v] = window.parent[v]);
 </script>
 <script type="text/javascript" src="utils.js"> </script>
 <script type="text/javascript" src="fetch_test_framework.js"> </script>
 <script>
-window.isSWPresent = true;
 testScript(location.search.substring(1) + ".js");
 </script>
--- a/dom/tests/mochitest/fetch/reroute.js
+++ b/dom/tests/mochitest/fetch/reroute.js
@@ -3,16 +3,17 @@ onfetch = function(e) {
     // Silently rewrite the referrer so the referrer test passes since the
     // document/worker isn't aware of this service worker.
     var url = e.request.url.substring(0, e.request.url.indexOf('?'));
     url += '?headers=' + ({ 'Referer': self.location.href }).toSource();
 
     e.respondWith(fetch(url, {
       method: e.request.method,
       headers: e.request.headers,
+      body: e.request.body,
       mode: e.request.mode,
       credentials: e.request.credentials,
       cache: e.request.cache,
     }));
     return;
   }
   e.respondWith(fetch(e.request));
 };
--- a/dom/tests/mochitest/fetch/test_fetch_basic_http.js
+++ b/dom/tests/mochitest/fetch/test_fetch_basic_http.js
@@ -7,18 +7,18 @@ var passFiles = [['file_XHR_pass1.xml', 
 
 function testURL() {
   var promises = [];
   passFiles.forEach(function(entry) {
     var p = fetch(path + entry[0]).then(function(res) {
       ok(res.type !== "error", "Response should not be an error for " + entry[0]);
       is(res.status, entry[2], "Status should match expected for " + entry[0]);
       is(res.statusText, entry[3], "Status text should match expected for " + entry[0]);
-      // This file redirects to pass2, but that is invisible if a SW is present.
-      if (entry[0] != "file_XHR_pass3.txt" || isSWPresent)
+      // This file redirects to pass2
+      if (entry[0] != "file_XHR_pass3.txt")
         ok(res.url.endsWith(path + entry[0]), "Response url should match request for simple fetch for " + entry[0]);
       else
         ok(res.url.endsWith(path + "file_XHR_pass2.txt"), "Response url should match request for simple fetch for " + entry[0]);
       is(res.headers.get('content-type'), entry[4], "Response should have content-type for " + entry[0]);
     });
     promises.push(p);
   });
 
@@ -44,18 +44,17 @@ function testURLFail() {
 function testRequestGET() {
   var promises = [];
   passFiles.forEach(function(entry) {
     var req = new Request(path + entry[0], { method: entry[1] });
     var p = fetch(req).then(function(res) {
       ok(res.type !== "error", "Response should not be an error for " + entry[0]);
       is(res.status, entry[2], "Status should match expected for " + entry[0]);
       is(res.statusText, entry[3], "Status text should match expected for " + entry[0]);
-      // This file redirects to pass2, but that is invisible if a SW is present.
-      if (entry[0] != "file_XHR_pass3.txt" || isSWPresent)
+      if (entry[0] != "file_XHR_pass3.txt")
         ok(res.url.endsWith(path + entry[0]), "Response url should match request for simple fetch for " + entry[0]);
       else
         ok(res.url.endsWith(path + "file_XHR_pass2.txt"), "Response url should match request for simple fetch for " + entry[0]);
       is(res.headers.get('content-type'), entry[4], "Response should have content-type for " + entry[0]);
     });
     promises.push(p);
   });
 
--- a/dom/tests/mochitest/fetch/test_fetch_cors.js
+++ b/dom/tests/mochitest/fetch/test_fetch_cors.js
@@ -1272,27 +1272,17 @@ function testRedirects() {
     var request = new Request(req.url, { method: req.method,
                                          headers: req.headers,
                                          body: req.body });
     fetches.push((function(request, test) {
       return fetch(request).then(function(res) {
         ok(test.pass, "Expected test to pass for " + test.toSource());
         is(res.status, 200, "wrong status in test for " + test.toSource());
         is(res.statusText, "OK", "wrong status text for " + test.toSource());
-        var reqHost = (new URL(req.url)).host;
-        // If there is a service worker present, the redirections will be
-        // transparent, assuming that the original request is to the current
-        // site and would be intercepted.
-        if (isSWPresent) {
-          if (reqHost === location.host) {
-            is((new URL(res.url)).host, reqHost, "Response URL should be original URL with a SW present");
-          }
-        } else {
-          is((new URL(res.url)).host, (new URL(test.hops[test.hops.length-1].server)).host, "Response URL should be redirected URL");
-        }
+        is((new URL(res.url)).host, (new URL(test.hops[test.hops.length-1].server)).host, "Response URL should be redirected URL");
         return res.text().then(function(v) {
           is(v, "<res>hello pass</res>\n",
              "wrong responseText in test for " + test.toSource());
         });
       }, function(e) {
         ok(!test.pass, "Expected test failure for " + test.toSource());
         ok(e instanceof TypeError, "Exception should be TypeError for " + test.toSource());
       });
--- a/dom/workers/test/serviceworkers/fetch/origin/https/origin_test.js
+++ b/dom/workers/test/serviceworkers/fetch/origin/https/origin_test.js
@@ -1,15 +1,15 @@
 var prefix = "/tests/dom/workers/test/serviceworkers/fetch/origin/https/";
 
 self.addEventListener("install", function(event) {
   event.waitUntil(
     self.caches.open("origin-cache")
       .then(c => {
-        return c.add(new Request(prefix + 'index-https.sjs', {redirect: "manual"}));
+        return c.add(prefix + 'index-https.sjs');
       })
   );
 });
 
 self.addEventListener("fetch", function(event) {
   if (event.request.url.indexOf("index-cached-https.sjs") >= 0) {
     event.respondWith(
       self.caches.open("origin-cache")
--- a/dom/workers/test/serviceworkers/fetch/origin/origin_test.js
+++ b/dom/workers/test/serviceworkers/fetch/origin/origin_test.js
@@ -1,18 +1,18 @@
 var prefix = "/tests/dom/workers/test/serviceworkers/fetch/origin/";
 
 self.addEventListener("install", function(event) {
   event.waitUntil(
     self.caches.open("origin-cache")
       .then(c => {
         return Promise.all(
           [
-            c.add(new Request(prefix + 'index.sjs', {redirect: "manual"})),
-            c.add(new Request(prefix + 'index-to-https.sjs', {redirect: "manual"}))
+            c.add(prefix + 'index.sjs'),
+            c.add(prefix + 'index-to-https.sjs')
           ]
         );
       })
   );
 });
 
 self.addEventListener("fetch", function(event) {
   if (event.request.url.indexOf("index-cached.sjs") >= 0) {
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -1556,16 +1556,38 @@ HttpBaseChannel::OverrideSecurityInfo(ns
          "[this=%p]\n", this));
     return NS_ERROR_UNEXPECTED;
   }
 
   mSecurityInfo = aSecurityInfo;
   return NS_OK;
 }
 
+nsresult
+HttpBaseChannel::OverrideURI(nsIURI* aRedirectedURI)
+{
+  MOZ_ASSERT(mLoadFlags & LOAD_REPLACE,
+             "This can only happen if the LOAD_REPLACE flag is set");
+  MOZ_ASSERT(ShouldIntercept(),
+             "This can only be called on channels that can be intercepted");
+  if (!(mLoadFlags & LOAD_REPLACE)) {
+    LOG(("HttpBaseChannel::OverrideURI LOAD_REPLACE flag not set! [this=%p]\n",
+         this));
+    return NS_ERROR_UNEXPECTED;
+  }
+  if (!mResponseCouldBeSynthesized) {
+    LOG(("HttpBaseChannel::OverrideURI channel cannot be intercepted! "
+         "[this=%p]\n", this));
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  mURI = aRedirectedURI;
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 HttpBaseChannel::IsNoStoreResponse(bool *value)
 {
   if (!mResponseHead)
     return NS_ERROR_NOT_AVAILABLE;
   *value = mResponseHead->NoStore();
   return NS_OK;
 }
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -251,16 +251,17 @@ public:
 
     nsHttpResponseHead * GetResponseHead() const { return mResponseHead; }
     nsHttpRequestHead * GetRequestHead() { return &mRequestHead; }
 
     const NetAddr& GetSelfAddr() { return mSelfAddr; }
     const NetAddr& GetPeerAddr() { return mPeerAddr; }
 
     nsresult OverrideSecurityInfo(nsISupports* aSecurityInfo);
+    nsresult OverrideURI(nsIURI* aRedirectedURI);
 
 public: /* Necko internal use only... */
     bool IsNavigation();
 
     // Return whether upon a redirect code of httpStatus for method, the
     // request method should be rewritten to GET.
     static bool ShouldRewriteRedirectToGET(uint32_t httpStatus,
                                            nsHttpRequestHead::ParsedMethodType method);