Bug 1264178 - Part 1: Expose URL fragment to request but not response. r=bkelly
authorTom Tung <ttung@mozilla.com>
Mon, 07 Nov 2016 10:16:34 +0800
changeset 351838 7245abb243756fdc0b13a09417db08376904620e
parent 351837 74a75900ef5e03e86ccd5661002f4bc4392116cf
child 351839 3363a134f720f49c2f75cc953a297e5981cfea1f
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-esr52@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbkelly
bugs1264178
milestone52.0a1
Bug 1264178 - Part 1: Expose URL fragment to request but not response. r=bkelly
dom/cache/CacheTypes.ipdlh
dom/cache/DBSchema.cpp
dom/cache/TypeUtils.cpp
dom/cache/test/mochitest/test_cache_match_request.js
dom/fetch/FetchDriver.cpp
dom/fetch/InternalRequest.cpp
dom/fetch/InternalRequest.h
dom/fetch/Request.cpp
dom/flyweb/HttpServer.cpp
dom/tests/mochitest/fetch/test_request.js
dom/workers/ServiceWorkerPrivate.cpp
testing/web-platform/meta/fetch/api/request/request-init-003.sub.html.ini
testing/web-platform/tests/service-workers/service-worker/fetch-event.https.html
testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-test-worker.js
--- a/dom/cache/CacheTypes.ipdlh
+++ b/dom/cache/CacheTypes.ipdlh
@@ -45,22 +45,22 @@ union CacheReadStreamOrVoid
   CacheReadStream;
 };
 
 struct HeadersEntry
 {
   nsCString name;
   nsCString value;
 };
-
 struct CacheRequest
 {
   nsCString method;
   nsCString urlWithoutQuery;
   nsCString urlQuery;
+  nsCString urlFragment;
   HeadersEntry[] headers;
   HeadersGuardEnum headersGuard;
   nsString referrer;
   ReferrerPolicy referrerPolicy;
   RequestMode mode;
   RequestCredentials credentials;
   CacheReadStreamOrVoid body;
   uint32_t contentPolicyType;
--- a/dom/cache/DBSchema.cpp
+++ b/dom/cache/DBSchema.cpp
@@ -27,30 +27,25 @@
 #include "nsNetCID.h"
 #include "nsPrintfCString.h"
 #include "nsTArray.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 namespace db {
-
 const int32_t kFirstShippedSchemaVersion = 15;
-
 namespace {
-
 // Update this whenever the DB schema is changed.
-const int32_t kLatestSchemaVersion = 23;
-
+const int32_t kLatestSchemaVersion = 24;
 // ---------
 // 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
 // objects exist for the origin.  The contents of the Cache are stored
 // in the entries table that references back to caches.
 //
 // The caches table is also referenced from storage.  Rows in storage
 // represent named Cache objects.  There are cases, however, where
 // a Cache can still exist, but not be in a named Storage.  For example,
 // when content is still using the Cache after CacheStorage::Delete()
@@ -98,24 +93,23 @@ const char* const kTableEntries =
     "response_type INTEGER 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, "
     "request_referrer_policy INTEGER NOT NULL, "
-    "request_integrity TEXT NOT NULL"
+    "request_integrity TEXT NOT NULL, "
+    "request_url_fragment TEXT NOT NULL"
     // New columns must be added at the end of table to migrate and
     // validate properly.
   ")";
-
 // 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
 // the vast majority of values from the query result so that normal
 // scanning only has to be done on a few values to find an exact URL match.
@@ -1650,16 +1644,17 @@ InsertEntry(mozIStorageConnection* aConn
   nsCOMPtr<mozIStorageStatement> state;
   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "INSERT INTO entries ("
       "request_method, "
       "request_url_no_query, "
       "request_url_no_query_hash, "
       "request_url_query, "
       "request_url_query_hash, "
+      "request_url_fragment, "
       "request_referrer, "
       "request_referrer_policy, "
       "request_headers_guard, "
       "request_mode, "
       "request_credentials, "
       "request_contentpolicytype, "
       "request_cache, "
       "request_redirect, "
@@ -1674,16 +1669,17 @@ InsertEntry(mozIStorageConnection* aConn
       "response_principal_info, "
       "cache_id "
     ") VALUES ("
       ":request_method, "
       ":request_url_no_query, "
       ":request_url_no_query_hash, "
       ":request_url_query, "
       ":request_url_query_hash, "
+      ":request_url_fragment, "
       ":request_referrer, "
       ":request_referrer_policy, "
       ":request_headers_guard, "
       ":request_mode, "
       ":request_credentials, "
       ":request_contentpolicytype, "
       ":request_cache, "
       ":request_redirect, "
@@ -1719,29 +1715,29 @@ InsertEntry(mozIStorageConnection* aConn
 
   rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("request_url_query"),
                                    aRequest.urlQuery());
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   nsAutoCString urlQueryHash;
   rv = HashCString(crypto, aRequest.urlQuery(), urlQueryHash);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-
   rv = state->BindUTF8StringAsBlobByName(
     NS_LITERAL_CSTRING("request_url_query_hash"), urlQueryHash);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+  rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("request_url_fragment"),
+                                   aRequest.urlFragment());
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = state->BindStringByName(NS_LITERAL_CSTRING("request_referrer"),
                                aRequest.referrer());
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-
   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("request_referrer_policy"),
                               static_cast<int32_t>(aRequest.referrerPolicy()));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-
   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("request_headers_guard"),
     static_cast<int32_t>(aRequest.headersGuard()));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("request_mode"),
                               static_cast<int32_t>(aRequest.mode()));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
@@ -2038,23 +2034,23 @@ ReadResponse(mozIStorageConnection* aCon
 
 nsresult
 ReadRequest(mozIStorageConnection* aConn, EntryId aEntryId,
             SavedRequest* aSavedRequestOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aConn);
   MOZ_ASSERT(aSavedRequestOut);
-
   nsCOMPtr<mozIStorageStatement> state;
   nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT "
       "request_method, "
       "request_url_no_query, "
       "request_url_query, "
+      "request_url_fragment, "
       "request_referrer, "
       "request_referrer_policy, "
       "request_headers_guard, "
       "request_mode, "
       "request_credentials, "
       "request_contentpolicytype, "
       "request_cache, "
       "request_redirect, "
@@ -2069,80 +2065,69 @@ ReadRequest(mozIStorageConnection* aConn
   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; }
 
   rv = state->GetUTF8String(0, aSavedRequestOut->mValue.method());
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-
   rv = state->GetUTF8String(1, aSavedRequestOut->mValue.urlWithoutQuery());
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-
   rv = state->GetUTF8String(2, aSavedRequestOut->mValue.urlQuery());
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-
-  rv = state->GetString(3, aSavedRequestOut->mValue.referrer());
+  rv = state->GetUTF8String(3, aSavedRequestOut->mValue.urlFragment());
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+  rv = state->GetString(4, aSavedRequestOut->mValue.referrer());
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   int32_t referrerPolicy;
-  rv = state->GetInt32(4, &referrerPolicy);
+  rv = state->GetInt32(5, &referrerPolicy);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   aSavedRequestOut->mValue.referrerPolicy() =
     static_cast<ReferrerPolicy>(referrerPolicy);
-
   int32_t guard;
-  rv = state->GetInt32(5, &guard);
+  rv = state->GetInt32(6, &guard);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   aSavedRequestOut->mValue.headersGuard() =
     static_cast<HeadersGuardEnum>(guard);
-
   int32_t mode;
-  rv = state->GetInt32(6, &mode);
+  rv = state->GetInt32(7, &mode);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   aSavedRequestOut->mValue.mode() = static_cast<RequestMode>(mode);
-
   int32_t credentials;
-  rv = state->GetInt32(7, &credentials);
+  rv = state->GetInt32(8, &credentials);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   aSavedRequestOut->mValue.credentials() =
     static_cast<RequestCredentials>(credentials);
-
   int32_t requestContentPolicyType;
-  rv = state->GetInt32(8, &requestContentPolicyType);
+  rv = state->GetInt32(9, &requestContentPolicyType);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   aSavedRequestOut->mValue.contentPolicyType() =
     static_cast<nsContentPolicyType>(requestContentPolicyType);
-
   int32_t requestCache;
-  rv = state->GetInt32(9, &requestCache);
+  rv = state->GetInt32(10, &requestCache);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   aSavedRequestOut->mValue.requestCache() =
     static_cast<RequestCache>(requestCache);
-
   int32_t requestRedirect;
-  rv = state->GetInt32(10, &requestRedirect);
+  rv = state->GetInt32(11, &requestRedirect);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   aSavedRequestOut->mValue.requestRedirect() =
     static_cast<RequestRedirect>(requestRedirect);
-
-  rv = state->GetString(11, aSavedRequestOut->mValue.integrity());
+  rv = state->GetString(12, aSavedRequestOut->mValue.integrity());
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-
   bool nullBody = false;
-  rv = state->GetIsNull(12, &nullBody);
+  rv = state->GetIsNull(13, &nullBody);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   aSavedRequestOut->mHasBodyId = !nullBody;
-
   if (aSavedRequestOut->mHasBodyId) {
-    rv = ExtractId(state, 12, &aSavedRequestOut->mBodyId);
+    rv = ExtractId(state, 13, &aSavedRequestOut->mBodyId);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   }
-
   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT "
       "name, "
       "value "
     "FROM request_headers "
     "WHERE entry_id=:entry_id;"
   ), getter_AddRefs(state));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
@@ -2487,31 +2472,30 @@ struct Migration
 nsresult MigrateFrom15To16(mozIStorageConnection* aConn, bool& aRewriteSchema);
 nsresult MigrateFrom16To17(mozIStorageConnection* aConn, bool& aRewriteSchema);
 nsresult MigrateFrom17To18(mozIStorageConnection* aConn, bool& aRewriteSchema);
 nsresult MigrateFrom18To19(mozIStorageConnection* aConn, bool& aRewriteSchema);
 nsresult MigrateFrom19To20(mozIStorageConnection* aConn, bool& aRewriteSchema);
 nsresult MigrateFrom20To21(mozIStorageConnection* aConn, bool& aRewriteSchema);
 nsresult MigrateFrom21To22(mozIStorageConnection* aConn, bool& aRewriteSchema);
 nsresult MigrateFrom22To23(mozIStorageConnection* aConn, bool& aRewriteSchema);
-
+nsresult MigrateFrom23To24(mozIStorageConnection* aConn, bool& aRewriteSchema);
 // Configure migration functions to run for the given starting version.
 Migration sMigrationList[] = {
   Migration(15, MigrateFrom15To16),
   Migration(16, MigrateFrom16To17),
   Migration(17, MigrateFrom17To18),
   Migration(18, MigrateFrom18To19),
   Migration(19, MigrateFrom19To20),
   Migration(20, MigrateFrom20To21),
   Migration(21, MigrateFrom21To22),
   Migration(22, MigrateFrom22To23),
+  Migration(23, MigrateFrom23To24),
 };
-
 uint32_t sMigrationListLength = sizeof(sMigrationList) / sizeof(Migration);
-
 nsresult
 RewriteEntriesSchema(mozIStorageConnection* aConn)
 {
   nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "PRAGMA writable_schema = ON"
   ));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
@@ -2996,21 +2980,37 @@ nsresult MigrateFrom21To22(mozIStorageCo
 
 nsresult MigrateFrom22To23(mozIStorageConnection* aConn, bool& aRewriteSchema)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aConn);
 
   // The only change between 22 and 23 was a different snappy compression
   // format, but it's backwards-compatible.
-
   nsresult rv = aConn->SetSchemaVersion(23);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+  return rv;
+}
+nsresult MigrateFrom23To24(mozIStorageConnection* aConn, bool& aRewriteSchema)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_ASSERT(aConn);
+
+  // Add the request_url_fragment column.
+  nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "ALTER TABLE entries "
+    "ADD COLUMN request_url_fragment TEXT NOT NULL DEFAULT ''"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  rv = aConn->SetSchemaVersion(24);
+  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+
+  aRewriteSchema = true;
 
   return rv;
 }
 
 } // anonymous namespace
-
 } // namespace db
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/TypeUtils.cpp
+++ b/dom/cache/TypeUtils.cpp
@@ -117,40 +117,35 @@ TypeUtils::ToInternalRequest(const Ownin
 
 void
 TypeUtils::ToCacheRequest(CacheRequest& aOut, InternalRequest* aIn,
                           BodyAction aBodyAction, SchemeAction aSchemeAction,
                           nsTArray<UniquePtr<AutoIPCStream>>& aStreamCleanupList,
                           ErrorResult& aRv)
 {
   MOZ_ASSERT(aIn);
-
   aIn->GetMethod(aOut.method());
-
-  nsAutoCString url;
-  aIn->GetURL(url);
-
+  nsCString url(aIn->GetURLWithoutFragment());
   bool schemeValid;
   ProcessURL(url, &schemeValid, &aOut.urlWithoutQuery(), &aOut.urlQuery(), aRv);
   if (aRv.Failed()) {
     return;
   }
-
   if (!schemeValid) {
     if (aSchemeAction == TypeErrorOnInvalidScheme) {
       NS_ConvertUTF8toUTF16 urlUTF16(url);
       aRv.ThrowTypeError<MSG_INVALID_URL_SCHEME>(NS_LITERAL_STRING("Request"),
                                                  urlUTF16);
       return;
     }
   }
+  aOut.urlFragment() = aIn->GetFragment();
 
   aIn->GetReferrer(aOut.referrer());
   aOut.referrerPolicy() = aIn->ReferrerPolicy_();
-
   RefPtr<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();
@@ -301,27 +296,24 @@ TypeUtils::ToResponse(const CacheRespons
     default:
       MOZ_CRASH("Unexpected ResponseType!");
   }
   MOZ_ASSERT(ir);
 
   RefPtr<Response> ref = new Response(GetGlobalObject(), ir);
   return ref.forget();
 }
-
 already_AddRefed<InternalRequest>
 TypeUtils::ToInternalRequest(const CacheRequest& aIn)
 {
   nsAutoCString url(aIn.urlWithoutQuery());
   url.Append(aIn.urlQuery());
-
-  RefPtr<InternalRequest> internalRequest = new InternalRequest(url);
-
+  RefPtr<InternalRequest> internalRequest =
+    new InternalRequest(url, aIn.urlFragment());
   internalRequest->SetMethod(aIn.method());
-
   internalRequest->SetReferrer(aIn.referrer());
   internalRequest->SetReferrerPolicy(aIn.referrerPolicy());
   internalRequest->SetMode(aIn.mode());
   internalRequest->SetCredentialsMode(aIn.credentials());
   internalRequest->SetContentPolicyType(aIn.contentPolicyType());
   internalRequest->SetCacheMode(aIn.requestCache());
   internalRequest->SetRedirectMode(aIn.requestRedirect());
   internalRequest->SetIntegrity(aIn.integrity());
--- a/dom/cache/test/mochitest/test_cache_match_request.js
+++ b/dom/cache/test/mochitest/test_cache_match_request.js
@@ -20,36 +20,66 @@ function checkResponse(r, expectedBody) 
      "Both responses should have the same status text");
   return r.text().then(function(text) {
     // Avoid dumping out the large response text to the log if they're equal.
     if (text !== expectedBody) {
       is(text, responseText, "The response body should be correct");
     }
   });
 }
+function checkFragment(cache, request, requestWithDifferentFragment) {
+  var req1 = new Request(request);
+  var req2 = new Request(requestWithDifferentFragment);
+  var res1 = new Response("Hello World1");
+  var res2 = new Response("Hello World2");
+
+  // Ensure both request and requestWithDifferentFragment have fragment.
+  ok(req1.url.includes('#fragment'), "Request URL should keep the fragment.");
+  ok(req2.url.includes('#other'), "Request URL should keep the fragment.");
+
+  cache.put(req1, res1).then(() => cache.put(req2, res2))
+  .then(function () {
+    // Test for ensuring we get the fragment from cache request.
+    cache.keys(req2).then(function(keys) {
+      keys.forEach(function(r, index, array) {
+        is(req2.url, r.url, "Request URL should be the same");
+      });
+    })
+    return cache.match(req1);
+  }).then(function (resp) {
+    return resp.text();
+  }).then(function (body) {
+    // Test for ensuring only the last response will be cached.
+    is(body, "Hello World2", "Response should be the same as last one");
+    cache.delete(req1);
+    cache.delete(req2);
+  });
+  return Promise.resolve();
+}
 
 fetch(new Request(request)).then(function(r) {
   response = r;
   return response.text();
 }).then(function(text) {
   responseText = text;
   return testRequest(request, unknownRequest, requestWithAltQS,
                      request.url.replace("#fragment", "#other"));
 }).then(function() {
   return testRequest(request.url, unknownRequest.url, requestWithAltQS.url,
                      request.url.replace("#fragment", "#other"));
 }).then(function() {
   testDone();
 });
-
 // The request argument can either be a URL string, or a Request object.
 function testRequest(request, unknownRequest, requestWithAlternateQueryString,
                      requestWithDifferentFragment) {
   return caches.open(name).then(function(cache) {
     c = cache;
+    return checkFragment(c, request, requestWithDifferentFragment);
+  }).then(function() {
     return c.add(request);
   }).then(function() {
     return Promise.all(
       ["HEAD", "POST", "PUT", "DELETE", "OPTIONS"]
         .map(function(method) {
           var r = new Request(request, {method: method});
           return c.add(r)
             .then(function() {
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -370,29 +370,25 @@ FetchDriver::HttpFetch()
   }
 
   rv = chan->AsyncOpen2(this);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Step 4 onwards of "HTTP Fetch" is handled internally by Necko.
   return NS_OK;
 }
-
 already_AddRefed<InternalResponse>
 FetchDriver::BeginAndGetFilteredResponse(InternalResponse* aResponse,
                                          bool aFoundOpaqueRedirect)
 {
   MOZ_ASSERT(aResponse);
-
   AutoTArray<nsCString, 4> reqURLList;
-  mRequest->GetURLList(reqURLList);
-
+  mRequest->GetURLListWithoutFragment(reqURLList);
   MOZ_ASSERT(!reqURLList.IsEmpty());
   aResponse->SetURLList(reqURLList);
-
   RefPtr<InternalResponse> filteredResponse;
   if (aFoundOpaqueRedirect) {
     filteredResponse = aResponse->OpaqueRedirectResponse();
   } else {
     switch (mRequest->GetResponseTainting()) {
       case LoadTainting::Basic:
         filteredResponse = aResponse->BasicResponse();
         break;
@@ -803,25 +799,28 @@ FetchDriver::AsyncOnChannelRedirect(nsIC
   nsCOMPtr<nsIURI> uri;
   MOZ_ALWAYS_SUCCEEDS(aNewChannel->GetURI(getter_AddRefs(uri)));
 
   nsCOMPtr<nsIURI> uriClone;
   nsresult rv = uri->CloneIgnoringRef(getter_AddRefs(uriClone));
   if(NS_WARN_IF(NS_FAILED(rv))){
     return rv;
   }
-
   nsCString spec;
   rv = uriClone->GetSpec(spec);
   if(NS_WARN_IF(NS_FAILED(rv))){
     return rv;
   }
+  nsCString fragment;
+  rv = uri->GetRef(fragment);
+  if(NS_WARN_IF(NS_FAILED(rv))){
+    return rv;
+  }
 
-  mRequest->AddURL(spec);
-
+  mRequest->AddURL(spec, fragment);
   NS_ConvertUTF8toUTF16 tRPHeaderValue(tRPHeaderCValue);
   // updates request’s associated referrer policy according to the
   // Referrer-Policy header (if any).
   if (!tRPHeaderValue.IsEmpty()) {
     net::ReferrerPolicy net_referrerPolicy =
       nsContentUtils::GetReferrerPolicyFromHeader(tRPHeaderValue);
     if (net_referrerPolicy != net::RP_Unset) {
       ReferrerPolicy referrerPolicy = mRequest->ReferrerPolicy_();
--- a/dom/fetch/InternalRequest.cpp
+++ b/dom/fetch/InternalRequest.cpp
@@ -14,28 +14,26 @@
 #include "mozilla/dom/FetchTypes.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/workers/Workers.h"
 
 #include "WorkerPrivate.h"
 
 namespace mozilla {
 namespace dom {
-
 // The global is used to extract the principal.
 already_AddRefed<InternalRequest>
 InternalRequest::GetRequestConstructorCopy(nsIGlobalObject* aGlobal, ErrorResult& aRv) const
 {
   MOZ_RELEASE_ASSERT(!mURLList.IsEmpty(), "Internal Request's urlList should not be empty when copied from constructor.");
-
-  RefPtr<InternalRequest> copy = new InternalRequest(mURLList.LastElement());
+  RefPtr<InternalRequest> copy = new InternalRequest(mURLList.LastElement(),
+                                                     mFragment);
   copy->SetMethod(mMethod);
   copy->mHeaders = new InternalHeaders(*mHeaders);
   copy->SetUnsafeRequest();
-
   copy->mBodyStream = mBodyStream;
   copy->mForceOriginHeader = true;
   // The "client" is not stored in our implementation. Fetch API users should
   // use the appropriate window/document/principal and other Gecko security
   // mechanisms as appropriate.
   copy->mSameOriginDataURL = true;
   copy->mPreserveContentCodings = true;
   copy->mReferrer = mReferrer;
@@ -70,21 +68,20 @@ InternalRequest::Clone()
   nsresult rv = NS_CloneInputStream(mBodyStream, getter_AddRefs(clonedBody),
                                     getter_AddRefs(replacementBody));
   if (NS_WARN_IF(NS_FAILED(rv))) { return nullptr; }
 
   clone->mBodyStream.swap(clonedBody);
   if (replacementBody) {
     mBodyStream.swap(replacementBody);
   }
-
   return clone.forget();
 }
-
-InternalRequest::InternalRequest(const nsACString& aURL)
+InternalRequest::InternalRequest(const nsACString& aURL,
+                                 const nsACString& aFragment)
   : mMethod("GET")
   , mHeaders(new InternalHeaders(HeadersGuardEnum::None))
   , mContentPolicyType(nsIContentPolicy::TYPE_FETCH)
   , mReferrer(NS_LITERAL_STRING(kFETCH_CLIENT_REFERRER_STR))
   , mReferrerPolicy(ReferrerPolicy::_empty)
   , mEnvironmentReferrerPolicy(net::RP_Default)
   , mMode(RequestMode::No_cors)
   , mCredentialsMode(RequestCredentials::Omit)
@@ -100,20 +97,20 @@ InternalRequest::InternalRequest(const n
     // specification does not handle this yet.
   , mSameOriginDataURL(true)
   , mSkipServiceWorker(false)
   , mSynchronous(false)
   , mUnsafeRequest(false)
   , mUseURLCredentials(false)
 {
   MOZ_ASSERT(!aURL.IsEmpty());
-  AddURL(aURL);
+  AddURL(aURL, aFragment);
 }
-
 InternalRequest::InternalRequest(const nsACString& aURL,
+                                 const nsACString& aFragment,
                                  const nsACString& aMethod,
                                  already_AddRefed<InternalHeaders> aHeaders,
                                  RequestCache aCacheMode,
                                  RequestMode aMode,
                                  RequestRedirect aRequestRedirect,
                                  RequestCredentials aRequestCredentials,
                                  const nsAString& aReferrer,
                                  ReferrerPolicy aReferrerPolicy,
@@ -137,33 +134,33 @@ InternalRequest::InternalRequest(const n
     // FIXME See the above comment in the default constructor.
   , mSameOriginDataURL(true)
   , mSkipServiceWorker(false)
   , mSynchronous(false)
   , mUnsafeRequest(false)
   , mUseURLCredentials(false)
 {
   MOZ_ASSERT(!aURL.IsEmpty());
-  AddURL(aURL);
+  AddURL(aURL, aFragment);
 }
-
 InternalRequest::InternalRequest(const InternalRequest& aOther)
   : mMethod(aOther.mMethod)
   , mURLList(aOther.mURLList)
   , mHeaders(new InternalHeaders(*aOther.mHeaders))
   , mContentPolicyType(aOther.mContentPolicyType)
   , mReferrer(aOther.mReferrer)
   , mReferrerPolicy(aOther.mReferrerPolicy)
   , mEnvironmentReferrerPolicy(aOther.mEnvironmentReferrerPolicy)
   , mMode(aOther.mMode)
   , mCredentialsMode(aOther.mCredentialsMode)
   , mResponseTainting(aOther.mResponseTainting)
   , mCacheMode(aOther.mCacheMode)
   , mRedirectMode(aOther.mRedirectMode)
   , mIntegrity(aOther.mIntegrity)
+  , mFragment(aOther.mFragment)
   , mAuthenticationFlag(aOther.mAuthenticationFlag)
   , mForceOriginHeader(aOther.mForceOriginHeader)
   , mPreserveContentCodings(aOther.mPreserveContentCodings)
   , mSameOriginDataURL(aOther.mSameOriginDataURL)
   , mSkipServiceWorker(aOther.mSkipServiceWorker)
   , mSynchronous(aOther.mSynchronous)
   , mUnsafeRequest(aOther.mUnsafeRequest)
   , mUseURLCredentials(aOther.mUseURLCredentials)
--- a/dom/fetch/InternalRequest.h
+++ b/dom/fetch/InternalRequest.h
@@ -82,27 +82,24 @@ namespace dom {
  * TODO: Add a content type for favicon
  * TODO: Add a content type for download
  */
 
 class Request;
 class IPCInternalRequest;
 
 #define kFETCH_CLIENT_REFERRER_STR "about:client"
-
 class InternalRequest final
 {
   friend class Request;
-
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(InternalRequest)
-
-  explicit InternalRequest(const nsACString& aURL);
-
+  InternalRequest(const nsACString& aURL, const nsACString& aFragment);
   InternalRequest(const nsACString& aURL,
+                  const nsACString& aFragment,
                   const nsACString& aMethod,
                   already_AddRefed<InternalHeaders> aHeaders,
                   RequestCache aCacheMode,
                   RequestMode aMode,
                   RequestRedirect aRequestRedirect,
                   RequestCredentials aRequestCredentials,
                   const nsAString& aReferrer,
                   ReferrerPolicy aReferrerPolicy,
@@ -129,47 +126,59 @@ public:
 
   bool
   HasSimpleMethod() const
   {
     return mMethod.LowerCaseEqualsASCII("get") ||
            mMethod.LowerCaseEqualsASCII("post") ||
            mMethod.LowerCaseEqualsASCII("head");
   }
-
-  // GetURL should get the request's current url. A request has an associated
-  // current url. It is a pointer to the last fetch URL in request's url list.
+  // GetURL should get the request's current url with fragment. A request has
+  // an associated current url. It is a pointer to the last fetch URL in
+  // request's url list.
   void
   GetURL(nsACString& aURL) const
   {
-    MOZ_RELEASE_ASSERT(!mURLList.IsEmpty(), "Internal Request's urlList should not be empty.");
-
-    aURL.Assign(mURLList.LastElement());
+    aURL.Assign(GetURLWithoutFragment());
+    if (GetFragment().IsEmpty()) {
+      return;
+    }
+    aURL.Append(NS_LITERAL_CSTRING("#"));
+    aURL.Append(GetFragment());
   }
 
+  const nsCString&
+  GetURLWithoutFragment() const
+  {
+    MOZ_RELEASE_ASSERT(!mURLList.IsEmpty(),
+                       "Internal Request's urlList should not be empty.");
+
+    return mURLList.LastElement();
+  }
   // AddURL should append the url into url list.
-  // Normally we strip the fragment from the URL in Request::Constructor.
-  // If internal code is directly constructing this object they must
-  // strip the fragment first.  Since these should be well formed URLs we
-  // can use a simple check for a fragment here.  The full parser is
-  // difficult to use off the main thread.
+  // Normally we strip the fragment from the URL in Request::Constructor and
+  // pass the fragment as the second argument into it.
+  // If a fragment is present in the URL it must be stripped and passed in
+  // separately.
   void
-  AddURL(const nsACString& aURL)
+  AddURL(const nsACString& aURL, const nsACString& aFragment)
   {
     MOZ_ASSERT(!aURL.IsEmpty());
+    MOZ_ASSERT(!aURL.Contains('#'));
+
     mURLList.AppendElement(aURL);
-    MOZ_ASSERT(mURLList.LastElement().Find(NS_LITERAL_CSTRING("#")) == kNotFound);
+
+    mFragment.Assign(aFragment);
   }
-
+  // Get the URL list without their fragments.
   void
-  GetURLList(nsTArray<nsCString>& aURLList)
+  GetURLListWithoutFragment(nsTArray<nsCString>& aURLList)
   {
     aURLList.Assign(mURLList);
   }
-
   void
   GetReferrer(nsAString& aReferrer) const
   {
     aReferrer.Assign(mReferrer);
   }
 
   void
   SetReferrer(const nsAString& aReferrer)
@@ -316,30 +325,33 @@ public:
     mRedirectMode = aRedirectMode;
   }
 
   const nsString&
   GetIntegrity() const
   {
     return mIntegrity;
   }
-
   void
   SetIntegrity(const nsAString& aIntegrity)
   {
     MOZ_ASSERT(mIntegrity.IsEmpty());
     mIntegrity.Assign(aIntegrity);
   }
+  const nsCString&
+  GetFragment() const
+  {
+    return mFragment;
+  }
 
   nsContentPolicyType
   ContentPolicyType() const
   {
     return mContentPolicyType;
   }
-
   void
   SetContentPolicyType(nsContentPolicyType aContentPolicyType);
 
   void
   OverrideContentPolicyType(nsContentPolicyType aContentPolicyType);
 
   RequestContext
   Context() const
@@ -486,25 +498,23 @@ private:
   ReferrerPolicy mReferrerPolicy;
 
   // This will be used for request created from Window or Worker contexts
   // In case there's no Referrer Policy in Request, this will be passed to
   // channel.
   // The Environment Referrer Policy should be net::ReferrerPolicy so that it
   // could be associated with nsIHttpChannel.
   net::ReferrerPolicy mEnvironmentReferrerPolicy;
-
   RequestMode mMode;
   RequestCredentials mCredentialsMode;
   MOZ_INIT_OUTSIDE_CTOR LoadTainting mResponseTainting;
   RequestCache mCacheMode;
   RequestRedirect mRedirectMode;
-
   nsString mIntegrity;
-
+  nsCString mFragment;
   MOZ_INIT_OUTSIDE_CTOR bool mAuthenticationFlag;
   MOZ_INIT_OUTSIDE_CTOR bool mForceOriginHeader;
   MOZ_INIT_OUTSIDE_CTOR bool mPreserveContentCodings;
   MOZ_INIT_OUTSIDE_CTOR bool mSameOriginDataURL;
   MOZ_INIT_OUTSIDE_CTOR bool mSkipServiceWorker;
   MOZ_INIT_OUTSIDE_CTOR bool mSynchronous;
   MOZ_INIT_OUTSIDE_CTOR bool mUnsafeRequest;
   MOZ_INIT_OUTSIDE_CTOR bool mUseURLCredentials;
--- a/dom/fetch/Request.cpp
+++ b/dom/fetch/Request.cpp
@@ -84,147 +84,158 @@ ParseURLFromDocument(nsIDocument* aDocum
   nsCOMPtr<nsIURI> baseURI = aDocument->GetBaseURI();
   nsCOMPtr<nsIURI> resolvedURI;
   aRv = NS_NewURI(getter_AddRefs(resolvedURI), aInput, nullptr, baseURI);
   if (NS_WARN_IF(aRv.Failed())) {
     aRv.ThrowTypeError<MSG_INVALID_URL>(aInput);
   }
   return resolvedURI.forget();
 }
-
 void
 GetRequestURLFromDocument(nsIDocument* aDocument, const nsAString& aInput,
-                          nsAString& aRequestURL, ErrorResult& aRv)
+                          nsAString& aRequestURL, nsACString& aURLfragment,
+                          ErrorResult& aRv)
 {
   nsCOMPtr<nsIURI> resolvedURI = ParseURLFromDocument(aDocument, aInput, aRv);
   if (aRv.Failed()) {
     return;
   }
-
   // This fails with URIs with weird protocols, even when they are valid,
   // so we ignore the failure
   nsAutoCString credentials;
   Unused << resolvedURI->GetUserPass(credentials);
   if (!credentials.IsEmpty()) {
     aRv.ThrowTypeError<MSG_URL_HAS_CREDENTIALS>(aInput);
     return;
   }
 
   nsCOMPtr<nsIURI> resolvedURIClone;
   // We use CloneIgnoringRef to strip away the fragment even if the original URI
   // is immutable.
   aRv = resolvedURI->CloneIgnoringRef(getter_AddRefs(resolvedURIClone));
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
-
   nsAutoCString spec;
   aRv = resolvedURIClone->GetSpec(spec);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
+  CopyUTF8toUTF16(spec, aRequestURL);
 
-  CopyUTF8toUTF16(spec, aRequestURL);
+  // Get the fragment from nsIURI.
+  aRv = resolvedURI->GetRef(aURLfragment);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
+  }
 }
-
 already_AddRefed<nsIURI>
 ParseURLFromChrome(const nsAString& aInput, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
-
   nsCOMPtr<nsIURI> uri;
   aRv = NS_NewURI(getter_AddRefs(uri), aInput, nullptr, nullptr);
   if (NS_WARN_IF(aRv.Failed())) {
     aRv.ThrowTypeError<MSG_INVALID_URL>(aInput);
   }
   return uri.forget();
 }
-
 void
 GetRequestURLFromChrome(const nsAString& aInput, nsAString& aRequestURL,
-                        ErrorResult& aRv)
+                        nsACString& aURLfragment, ErrorResult& aRv)
 {
   nsCOMPtr<nsIURI> uri = ParseURLFromChrome(aInput, aRv);
   if (aRv.Failed()) {
     return;
   }
-
   // This fails with URIs with weird protocols, even when they are valid,
   // so we ignore the failure
   nsAutoCString credentials;
   Unused << uri->GetUserPass(credentials);
   if (!credentials.IsEmpty()) {
     aRv.ThrowTypeError<MSG_URL_HAS_CREDENTIALS>(aInput);
     return;
   }
 
   nsCOMPtr<nsIURI> uriClone;
   // We use CloneIgnoringRef to strip away the fragment even if the original URI
   // is immutable.
   aRv = uri->CloneIgnoringRef(getter_AddRefs(uriClone));
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
-
   nsAutoCString spec;
   aRv = uriClone->GetSpec(spec);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
+  CopyUTF8toUTF16(spec, aRequestURL);
 
-  CopyUTF8toUTF16(spec, aRequestURL);
+  // Get the fragment from nsIURI.
+  aRv = uri->GetRef(aURLfragment);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
+  }
 }
-
 already_AddRefed<URL>
 ParseURLFromWorker(const GlobalObject& aGlobal, const nsAString& aInput,
                    ErrorResult& aRv)
 {
   workers::WorkerPrivate* worker = workers::GetCurrentThreadWorkerPrivate();
   MOZ_ASSERT(worker);
   worker->AssertIsOnWorkerThread();
 
   NS_ConvertUTF8toUTF16 baseURL(worker->GetLocationInfo().mHref);
   RefPtr<URL> url = URL::WorkerConstructor(aGlobal, aInput, baseURL, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     aRv.ThrowTypeError<MSG_INVALID_URL>(aInput);
   }
   return url.forget();
 }
-
 void
 GetRequestURLFromWorker(const GlobalObject& aGlobal, const nsAString& aInput,
-                        nsAString& aRequestURL, ErrorResult& aRv)
+                        nsAString& aRequestURL, nsACString& aURLfragment,
+                        ErrorResult& aRv)
 {
   RefPtr<URL> url = ParseURLFromWorker(aGlobal, aInput, aRv);
   if (aRv.Failed()) {
     return;
   }
-
   nsString username;
   url->GetUsername(username, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
   nsString password;
   url->GetPassword(password, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
-
   if (!username.IsEmpty() || !password.IsEmpty()) {
     aRv.ThrowTypeError<MSG_URL_HAS_CREDENTIALS>(aInput);
     return;
   }
+  // Get the fragment from URL.
+  nsAutoString fragment;
+  url->GetHash(fragment, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
+  }
+
+  // Note: URL::GetHash() includes the "#" and we want the fragment with out
+  // the hash symbol.
+  if (!fragment.IsEmpty()) {
+    CopyUTF16toUTF8(Substring(fragment, 1), aURLfragment);
+  }
 
   url->SetHash(EmptyString(), aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
-
   url->Stringify(aRequestURL, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 }
 
 class ReferrerSameOriginChecker final : public workers::WorkerMainThreadRunnable
 {
@@ -279,48 +290,43 @@ Request::Constructor(const GlobalObject&
       aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
       return nullptr;
     }
     if (body) {
       temporaryBody = body;
     }
 
     request = inputReq->GetInternalRequest();
-
   } else {
     // aInput is USVString.
     // We need to get url before we create a InternalRequest.
     nsAutoString input;
     input.Assign(aInput.GetAsUSVString());
-
     nsAutoString requestURL;
+    nsCString fragment;
     if (NS_IsMainThread()) {
       nsIDocument* doc = GetEntryDocument();
       if (doc) {
-        GetRequestURLFromDocument(doc, input, requestURL, aRv);
+        GetRequestURLFromDocument(doc, input, requestURL, fragment, aRv);
       } else {
         // If we don't have a document, we must assume that this is a full URL.
-        GetRequestURLFromChrome(input, requestURL, aRv);
+        GetRequestURLFromChrome(input, requestURL, fragment, aRv);
       }
     } else {
-      GetRequestURLFromWorker(aGlobal, input, requestURL, aRv);
+      GetRequestURLFromWorker(aGlobal, input, requestURL, fragment, aRv);
     }
-
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
-
-    request = new InternalRequest(NS_ConvertUTF16toUTF8(requestURL));
+    request = new InternalRequest(NS_ConvertUTF16toUTF8(requestURL), fragment);
   }
-
   request = request->GetRequestConstructorCopy(global, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
-
   RequestMode fallbackMode = RequestMode::EndGuard_;
   RequestCredentials fallbackCredentials = RequestCredentials::EndGuard_;
   RequestCache fallbackCache = RequestCache::EndGuard_;
   if (aInput.IsUSVString()) {
     fallbackMode = RequestMode::Cors;
     fallbackCredentials = RequestCredentials::Omit;
     fallbackCache = RequestCache::Default;
   }
--- a/dom/flyweb/HttpServer.cpp
+++ b/dom/flyweb/HttpServer.cpp
@@ -580,26 +580,23 @@ HttpServer::Connection::ConsumeLine(cons
     MOZ_ASSERT(!mPendingReq);
 
     // Process request line
     nsCWhitespaceTokenizer tokens(Substring(aBuffer, aLength));
 
     NS_ENSURE_TRUE(tokens.hasMoreTokens(), NS_ERROR_UNEXPECTED);
     nsDependentCSubstring method = tokens.nextToken();
     NS_ENSURE_TRUE(NS_IsValidHTTPToken(method), NS_ERROR_UNEXPECTED);
-
     NS_ENSURE_TRUE(tokens.hasMoreTokens(), NS_ERROR_UNEXPECTED);
     nsDependentCSubstring url = tokens.nextToken();
     // Seems like it's also allowed to pass full urls with scheme+host+port.
     // May need to support that.
     NS_ENSURE_TRUE(url.First() == '/', NS_ERROR_UNEXPECTED);
-
-    mPendingReq = new InternalRequest(url);
+    mPendingReq = new InternalRequest(url, /* aURLFragment */ EmptyCString());
     mPendingReq->SetMethod(method);
-
     NS_ENSURE_TRUE(tokens.hasMoreTokens(), NS_ERROR_UNEXPECTED);
     nsDependentCSubstring version = tokens.nextToken();
     NS_ENSURE_TRUE(StringBeginsWith(version, NS_LITERAL_CSTRING("HTTP/1.")),
                    NS_ERROR_UNEXPECTED);
     nsresult rv;
     // This integer parsing is likely not strict enough.
     nsCString reqVersion;
     reqVersion = Substring(version, MOZ_ARRAY_LENGTH("HTTP/1.") - 1);
--- a/dom/tests/mochitest/fetch/test_request.js
+++ b/dom/tests/mochitest/fetch/test_request.js
@@ -226,26 +226,24 @@ function testMethod() {
   }
 
   try {
     var r = new Request("", { method: "head", body: "hello" });
     ok(false, "HEAD/GET request cannot have a body");
   } catch(e) {
     is(e.name, "TypeError", "HEAD/GET request cannot have a body");
   }
-
   // Non HEAD/GET should not throw.
   var r = new Request("", { method: "patch", body: "hello" });
 }
-
 function testUrlFragment() {
   var req = new Request("./request#withfragment");
-  is(req.url, (new URL("./request", self.location.href)).href, "request.url should be serialized with exclude fragment flag set");
+  is(req.url, (new URL("./request#withfragment", self.location.href)).href,
+     "request.url should be serialized without exclude fragment flag set");
 }
-
 function testUrlMalformed() {
   try {
     var req = new Request("http:// example.com");
     ok(false, "Creating a Request with a malformed URL should throw a TypeError");
   } catch(e) {
     is(e.name, "TypeError", "Creating a Request with a malformed URL should throw a TypeError");
   }
 }
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -1243,16 +1243,17 @@ namespace {
 class FetchEventRunnable : public ExtendableFunctionalEventWorkerRunnable
                          , public nsIHttpHeaderVisitor {
   nsMainThreadPtrHandle<nsIInterceptedChannel> mInterceptedChannel;
   const nsCString mScriptSpec;
   nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mRegistration;
   nsTArray<nsCString> mHeaderNames;
   nsTArray<nsCString> mHeaderValues;
   nsCString mSpec;
+  nsCString mFragment;
   nsCString mMethod;
   nsString mClientId;
   bool mIsReload;
   RequestCache mCacheMode;
   RequestMode mRequestMode;
   RequestRedirect mRequestRedirect;
   RequestCredentials mRequestCredentials;
   nsContentPolicyType mContentPolicyType;
@@ -1314,28 +1315,27 @@ public:
 
     // Normally we rely on the Request constructor to strip the fragment, but
     // when creating the FetchEvent we bypass the constructor.  So strip the
     // fragment manually here instead.  We can't do it later when we create
     // the Request because that code executes off the main thread.
     nsCOMPtr<nsIURI> uriNoFragment;
     rv = uri->CloneIgnoringRef(getter_AddRefs(uriNoFragment));
     NS_ENSURE_SUCCESS(rv, rv);
-
     rv = uriNoFragment->GetSpec(mSpec);
     NS_ENSURE_SUCCESS(rv, rv);
+    rv = uri->GetRef(mFragment);
+    NS_ENSURE_SUCCESS(rv, rv);
 
     uint32_t loadFlags;
     rv = channel->GetLoadFlags(&loadFlags);
     NS_ENSURE_SUCCESS(rv, rv);
-
     nsCOMPtr<nsILoadInfo> loadInfo;
     rv = channel->GetLoadInfo(getter_AddRefs(loadInfo));
     NS_ENSURE_SUCCESS(rv, rv);
-
     mContentPolicyType = loadInfo->InternalContentPolicyType();
 
     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
     MOZ_ASSERT(httpChannel, "How come we don't have an HTTP channel?");
 
     nsAutoCString referrer;
     // Ignore the return value since the Referer header may not exist.
     httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("Referer"), referrer);
@@ -1470,18 +1470,18 @@ private:
     }
 
     ErrorResult result;
     internalHeaders->SetGuard(HeadersGuardEnum::Immutable, result);
     if (NS_WARN_IF(result.Failed())) {
       result.SuppressException();
       return false;
     }
-
     RefPtr<InternalRequest> internalReq = new InternalRequest(mSpec,
+                                                              mFragment,
                                                               mMethod,
                                                               internalHeaders.forget(),
                                                               mCacheMode,
                                                               mRequestMode,
                                                               mRequestRedirect,
                                                               mRequestCredentials,
                                                               NS_ConvertUTF8toUTF16(mReferrer),
                                                               mReferrerPolicy,
--- a/testing/web-platform/meta/fetch/api/request/request-init-003.sub.html.ini
+++ b/testing/web-platform/meta/fetch/api/request/request-init-003.sub.html.ini
@@ -1,8 +1,4 @@
 [request-init-003.sub.html]
   type: testharness
   [Check request values when initialized from url string]
     expected: FAIL
-
-  [Check request values when initialized from url and init values]
-    expected: FAIL
-
--- a/testing/web-platform/tests/service-workers/service-worker/fetch-event.https.html
+++ b/testing/web-platform/tests/service-workers/service-worker/fetch-event.https.html
@@ -414,24 +414,23 @@ async_test(function(t) {
     service_worker_unregister_and_register(t, worker, scope)
       .then(function(reg) {
           return wait_for_state(t, reg.installing, 'activated');
         })
       .then(function() { return with_iframe(scope + fragment); })
       .then(function(frame) {
           assert_equals(
             frame.contentDocument.body.textContent,
-            'Fragment Not Found',
-            'Service worker should not expose URL fragments.');
+            'Fragment Found :' + fragment,
+            'Service worker should expose URL fragments in request.');
           frame.remove();
           return service_worker_unregister_and_done(t, scope);
         })
       .catch(unreached_rejection(t));
   }, 'Service Worker must not expose FetchEvent URL fragments.');
-
 async_test(function(t) {
     var scope = 'resources/simple.html?cache';
     var frame;
     var cacheTypes = [
       undefined, 'default', 'no-store', 'reload', 'no-cache', 'force-cache', 'only-if-cached'
     ];
     service_worker_unregister_and_register(t, worker, scope)
       .then(function(reg) {
--- a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-test-worker.js
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-test-worker.js
@@ -75,31 +75,29 @@ function handleUsedCheck(event) {
         lastResponseForUsedCheck = response;
         return response;
       }));
   } else {
     event.respondWith(new Response(
         'bodyUsed: ' + lastResponseForUsedCheck.bodyUsed));
   }
 }
-
 function handleFragmentCheck(event) {
   var body;
   if (event.request.url.indexOf('#') === -1) {
     body = 'Fragment Not Found';
   } else {
-    body = 'Fragment Found';
+    body = 'Fragment Found :' +
+           event.request.url.substring(event.request.url.indexOf('#'));
   }
   event.respondWith(new Response(body));
 }
-
 function handleCache(event) {
   event.respondWith(new Response(event.request.cache));
 }
-
 function handleEventSource(event) {
   if (event.request.mode === 'navigate') {
     return;
   }
   var data = {
     mode: event.request.mode,
     cache: event.request.cache,
     credentials: event.request.credentials