Backed out 4 changesets (bug 1204596) for Android test_fetch_basic_http_sw_reroute.html failures a=backout
authorWes Kocher <wkocher@mozilla.com>
Tue, 06 Oct 2015 10:41:24 -0700
changeset 296282 83e1183fff6645e0b706fd7bfe0b1f36f5e108ea
parent 296281 0ee0ccd92605d502e04921fc671ade08f7d6440e
child 296283 35c9d4fa88063bab60bf49543382e4d70c937e9c
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout
bugs1204596
milestone43.0a2
backs out0ee0ccd92605d502e04921fc671ade08f7d6440e
35b2edcb498b63f52124aeb792da395adb188314
daa0e457650e98f7fd9060eab3df0430f26820e1
33a968e122bc207129970f57ea11a45127f0bd78
Backed out 4 changesets (bug 1204596) for Android test_fetch_basic_http_sw_reroute.html failures a=backout Backed out changeset 0ee0ccd92605 (bug 1204596) Backed out changeset 35b2edcb498b (bug 1204596) Backed out changeset daa0e457650e (bug 1204596) Backed out changeset 33a968e122bc (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/mochitest.ini
dom/tests/mochitest/fetch/reroute.html
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/mochitest.ini
+++ b/dom/tests/mochitest/fetch/mochitest.ini
@@ -24,17 +24,16 @@ skip-if = buildapp == 'b2g' # Bug 113768
 [test_fetch_app_protocol.html]
 [test_fetch_basic.html]
 [test_fetch_basic_sw_reroute.html]
 skip-if = buildapp == 'b2g' # Bug 1137683
 [test_fetch_basic_http.html]
 [test_fetch_basic_http_sw_reroute.html]
 skip-if = buildapp == 'b2g' # Bug 1137683
 [test_fetch_cors.html]
-skip-if = buildapp == 'b2g' || (toolkit == 'android' && debug) # Bug 1210552 && 1210282
 [test_fetch_cors_sw_reroute.html]
 skip-if = buildapp == 'b2g' # Bug 1137683
 [test_formdataparsing.html]
 [test_formdataparsing_sw_reroute.html]
 skip-if = buildapp == 'b2g' # Bug 1137683
 [test_request.html]
 [test_request_context.html]
 [test_request_sw_reroute.html]
--- 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/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);