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 364644 7245abb243756fdc0b13a09417db08376904620e
parent 364643 74a75900ef5e03e86ccd5661002f4bc4392116cf
child 364645 3363a134f720f49c2f75cc953a297e5981cfea1f
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-beta@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbkelly
bugs1264178
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 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