From a6abc84e91e91b641ddbedd2d750997a482df01a Mon Sep 17 00:00:00 2001 From: Roy Tam Date: Tue, 18 Jun 2019 19:45:21 +0800 Subject: [PATCH] import change from rmottola/Arctic-Fox: - Bug 1141392 - Disallow a line break between ArrowParameters and the arrow. (386dac894) - Bug 1138651 - Enable CSS Scroll Snapping by Default on B2G. (2e416be93) - Bug 1140623 - Correct mochitest failures that occur when the layout.css.scroll-snap.enabled preference is enabled (V3 Patch). (cfb1ccd33) - Bug 1136467 - ServiceWorker: client.postMessage should land in navigator.serviceWorker.onmessage. (547bc9d03) - Bug 1137419 - Use correct principal for update. (9b409bd5c) - Bug 1137408 - Use inner window to create ServiceWorker for Fetch Event (11958cf2b) - Bug 1137250 - Fix incorrect initialization of Request object. (ec637ee57) - Bug 1139990 - Remove No activatingWorker warning. (49154e967) - Bug 1058311 - Update client interface. Implement matchAll WindowClient. (cde7ba62a) - Bug 1131353 - Make ServiceWorkerGlobalScope.close() throw InvalidAccessError; (0ec3f45d9) - Bug 1137816 - Add a test for interfaces exposed to service workers; (19b5f9504) - Bug 1137816 follow-up: Add WindowClient, added by bug 1058311 (ce6f0ba73) - Bug 1141274 - Allocate shared workers and service workers from separate namespaces; (776221d45) - Bug 1133763 - Part 1: Remember the security info associated with HTTP fetches and record it inside InternalResponse; (fec70ad02) - Bug 1133763 - Part 2: Transfer the security info associated with an InternalResponse across the IPC layer (e7192f129) - Bug 1133763 - Part 3: Wipe out the cache directory when detecting a change in the DB schema; (82d81819a) - Bug 1133763 - Part 4: Store the response's security info in the cache database; (426e94e4b) - Bug 1133763 - Part 5: Allow the security info on intercepted HTTP channels to be overridden (4d079bf92) - Bug 1133763 - Part 6: Set the correct security info on intercepted channels when using the respondWith API (83e2b2a7d) - Bug 1133763 - Part 7: Add automated tests for using FetchEvent.respondWith on an HTTPS request (e0ee10e17) - Bug 1133763 - Part 8: Ensure that FetchEvent.respondWith works correctly on HTTPS requests with a cloned response (0db13351e) - Bug 1139425 - Service Worker Client uuid tests. (480b262ed) - Bug 1139425 - Service Worker Client id should return a DOMString uuid. (8b9ea176d) - Bug 1142853 - Add caret resources to package-manifest.in. (7e1a59681) --- b2g/app/b2g.js | 3 + browser/installer/package-manifest.in | 12 + dom/base/nsDocument.cpp | 29 ++ dom/base/nsGlobalWindow.h | 15 + dom/base/nsIDocument.h | 5 + dom/bindings/BindingUtils.h | 12 +- dom/bindings/Bindings.conf | 5 + dom/cache/DBAction.cpp | 43 ++- dom/cache/DBAction.h | 2 + dom/cache/DBSchema.cpp | 24 +- dom/cache/DBSchema.h | 3 + dom/cache/FileUtils.cpp | 23 ++ dom/cache/FileUtils.h | 4 + dom/cache/PCacheTypes.ipdlh | 1 + dom/cache/TypeUtils.cpp | 3 + dom/fetch/FetchDriver.cpp | 6 + dom/fetch/InternalResponse.cpp | 21 ++ dom/fetch/InternalResponse.h | 13 + dom/webidl/Client.webidl | 18 +- dom/webidl/Clients.webidl | 5 +- dom/webidl/FetchEvent.webidl | 4 +- dom/webidl/ServiceWorkerContainer.webidl | 1 + dom/webidl/ServiceWorkerGlobalScope.webidl | 4 - dom/webidl/WorkerGlobalScope.webidl | 1 + dom/workers/RuntimeService.cpp | 26 +- dom/workers/ServiceWorkerClient.cpp | 59 +++- dom/workers/ServiceWorkerClient.h | 74 +++-- dom/workers/ServiceWorkerClients.cpp | 43 ++- dom/workers/ServiceWorkerClients.h | 6 + dom/workers/ServiceWorkerContainer.h | 1 + dom/workers/ServiceWorkerEvents.cpp | 45 ++- dom/workers/ServiceWorkerEvents.h | 6 +- dom/workers/ServiceWorkerManager.cpp | 48 ++- dom/workers/ServiceWorkerManager.h | 3 +- dom/workers/ServiceWorkerWindowClient.cpp | 34 +++ dom/workers/ServiceWorkerWindowClient.h | 65 ++++ dom/workers/WorkerScope.cpp | 8 +- dom/workers/WorkerScope.h | 8 +- dom/workers/moz.build | 2 + dom/workers/test/serviceworkers/close_test.js | 19 ++ .../fetch/https/clonedresponse/https_test.js | 15 + .../fetch/https/clonedresponse/index.html | 4 + .../fetch/https/clonedresponse/register.html | 26 ++ .../https/clonedresponse/unregister.html | 10 + .../serviceworkers/fetch/https/https_test.js | 11 + .../serviceworkers/fetch/https/index.html | 4 + .../serviceworkers/fetch/https/register.html | 26 ++ .../fetch/https/unregister.html | 10 + .../match_all_client/match_all_client_id.html | 31 ++ .../match_all_client_id_worker.js | 28 ++ .../match_all_controlled.html | 65 ++++ .../match_all_properties_worker.js | 20 ++ .../test/serviceworkers/message_receiver.html | 6 + dom/workers/test/serviceworkers/mochitest.ini | 24 ++ .../serviceworker_not_sharedworker.js | 21 ++ .../serviceworkers/serviceworker_wrapper.js | 131 ++++++++ .../sw_clients/service_worker_controlled.html | 2 +- .../test/serviceworkers/test_close.html | 69 +++++ .../test/serviceworkers/test_https_fetch.html | 56 ++++ .../test_https_fetch_cloned_response.html | 56 ++++ .../test_match_all_client_id.html | 86 ++++++ .../test_match_all_client_properties.html | 93 ++++++ .../test_serviceworker_interfaces.html | 113 +++++++ .../test_serviceworker_interfaces.js | 280 ++++++++++++++++++ .../test_serviceworker_not_sharedworker.html | 72 +++++ js/src/frontend/Parser.cpp | 13 +- .../Function/line-terminator-before-arrow.js | 9 + layout/style/generate-stylestructlist.py | 2 +- layout/style/nsCSSParser.cpp | 1 + layout/style/nsCSSPropList.h | 70 ++--- layout/style/nsComputedDOMStylePropertyList.h | 8 +- layout/style/nsRuleNode.cpp | 12 +- layout/style/test/property_database.js | 116 ++++---- netwerk/base/moz.build | 1 + .../base/nsINetworkInterceptController.idl | 7 +- netwerk/protocol/http/HttpBaseChannel.cpp | 13 + netwerk/protocol/http/HttpBaseChannel.h | 3 + netwerk/protocol/http/HttpChannelChild.h | 1 - netwerk/protocol/http/InterceptedChannel.cpp | 12 + netwerk/protocol/http/InterceptedChannel.h | 2 + netwerk/protocol/http/nsHttpChannel.h | 1 - 81 files changed, 2012 insertions(+), 222 deletions(-) create mode 100644 dom/workers/ServiceWorkerWindowClient.cpp create mode 100644 dom/workers/ServiceWorkerWindowClient.h create mode 100644 dom/workers/test/serviceworkers/close_test.js create mode 100644 dom/workers/test/serviceworkers/fetch/https/clonedresponse/https_test.js create mode 100644 dom/workers/test/serviceworkers/fetch/https/clonedresponse/index.html create mode 100644 dom/workers/test/serviceworkers/fetch/https/clonedresponse/register.html create mode 100644 dom/workers/test/serviceworkers/fetch/https/clonedresponse/unregister.html create mode 100644 dom/workers/test/serviceworkers/fetch/https/https_test.js create mode 100644 dom/workers/test/serviceworkers/fetch/https/index.html create mode 100644 dom/workers/test/serviceworkers/fetch/https/register.html create mode 100644 dom/workers/test/serviceworkers/fetch/https/unregister.html create mode 100644 dom/workers/test/serviceworkers/match_all_client/match_all_client_id.html create mode 100644 dom/workers/test/serviceworkers/match_all_client_id_worker.js create mode 100644 dom/workers/test/serviceworkers/match_all_clients/match_all_controlled.html create mode 100644 dom/workers/test/serviceworkers/match_all_properties_worker.js create mode 100644 dom/workers/test/serviceworkers/message_receiver.html create mode 100644 dom/workers/test/serviceworkers/serviceworker_not_sharedworker.js create mode 100644 dom/workers/test/serviceworkers/serviceworker_wrapper.js create mode 100644 dom/workers/test/serviceworkers/test_close.html create mode 100644 dom/workers/test/serviceworkers/test_https_fetch.html create mode 100644 dom/workers/test/serviceworkers/test_https_fetch_cloned_response.html create mode 100644 dom/workers/test/serviceworkers/test_match_all_client_id.html create mode 100644 dom/workers/test/serviceworkers/test_match_all_client_properties.html create mode 100644 dom/workers/test/serviceworkers/test_serviceworker_interfaces.html create mode 100644 dom/workers/test/serviceworkers/test_serviceworker_interfaces.js create mode 100644 dom/workers/test/serviceworkers/test_serviceworker_not_sharedworker.html create mode 100644 js/src/tests/ecma_6/Function/line-terminator-before-arrow.js diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js index 38359bcc14..9ebb317ac6 100644 --- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -625,6 +625,9 @@ pref("ui.scrollbarFadeDuration", 200); // Scrollbar position follows the document `dir` attribute pref("layout.scrollbar.side", 1); +// CSS Scroll Snapping +pref("layout.css.scroll-snap.enabled", true); + // Enable the ProcessPriorityManager, and give processes with no visible // documents a 1s grace period before they're eligible to be marked as // background. Background processes that are perceivable due to playing diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index 3fa53c71b4..bd850cb39a 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -742,6 +742,18 @@ @RESPATH@/res/table-remove-row-active.gif @RESPATH@/res/table-remove-row-hover.gif @RESPATH@/res/table-remove-row.gif +@RESPATH@/res/text_caret.png +@RESPATH@/res/text_caret@1.5x.png +@RESPATH@/res/text_caret@2.25x.png +@RESPATH@/res/text_caret@2x.png +@RESPATH@/res/text_caret_tilt_left.png +@RESPATH@/res/text_caret_tilt_left@1.5x.png +@RESPATH@/res/text_caret_tilt_left@2.25x.png +@RESPATH@/res/text_caret_tilt_left@2x.png +@RESPATH@/res/text_caret_tilt_right.png +@RESPATH@/res/text_caret_tilt_right@1.5x.png +@RESPATH@/res/text_caret_tilt_right@2.25x.png +@RESPATH@/res/text_caret_tilt_right@2x.png @RESPATH@/res/grabber.gif #ifdef XP_MACOSX @RESPATH@/res/cursors/* diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index 0f6b95c17a..7192fa35e0 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -12872,6 +12872,35 @@ nsIDocument::CreateHTMLElement(nsIAtom* aTag) return element.forget(); } +nsresult +nsIDocument::GetId(nsAString& aId) +{ + if (mId.IsEmpty()) { + nsresult rv; + nsCOMPtr uuidgen = do_GetService("@mozilla.org/uuid-generator;1", &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsID id; + rv = uuidgen->GenerateUUIDInPlace(&id); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Build a string in {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} format + char buffer[NSID_LENGTH]; + id.ToProvidedString(buffer); + NS_ConvertASCIItoUTF16 uuid(buffer); + + // Remove {} and the null terminator + mId.Assign(Substring(uuid, 1, NSID_LENGTH - 3)); + } + + aId = mId; + return NS_OK; +} + bool MarkDocumentTreeToBeInSyncOperation(nsIDocument* aDoc, void* aData) { diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index 9e3b910abb..575e038325 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -671,6 +671,21 @@ public: mCleanedUp); } + bool + HadOriginalOpener() const + { + MOZ_ASSERT(IsOuterWindow()); + return mHadOriginalOpener; + } + + bool + IsTopLevelWindow() + { + MOZ_ASSERT(IsOuterWindow()); + nsPIDOMWindow* parentWindow = GetScriptableTop(); + return parentWindow == static_cast(this); + } + virtual void FirePopupBlockedEvent(nsIDocument* aDoc, nsIURI* aPopupURI, diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h index c0140e8f42..3cecd2875d 100644 --- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -17,6 +17,8 @@ #include "nsINode.h" // for base class #include "nsIPrincipal.h" #include "nsIScriptGlobalObject.h" // for member (in nsCOMPtr) +#include "nsIServiceManager.h" +#include "nsIUUIDGenerator.h" #include "nsIURI.h" #include "nsPIDOMWindow.h" // for use in inline functions #include "nsPropertyTable.h" // for member @@ -770,6 +772,8 @@ public: return mAnonymousContents; } + nsresult GetId(nsAString& aId); + protected: virtual Element *GetRootElementInternal() const = 0; @@ -2852,6 +2856,7 @@ protected: nsCOMPtr mChannel; private: nsCString mContentType; + nsString mId; protected: // The document's security info diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index bfc40470c1..724773fcda 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -1935,15 +1935,15 @@ struct FakeString { return reinterpret_cast(this); } - nsAString* ToAStringPtr() { - return reinterpret_cast(this); - } - - operator const nsAString& () const { +operator const nsAString& () const { return *reinterpret_cast(this); } private: + nsAString* ToAStringPtr() { + return reinterpret_cast(this); + } + nsString::char_type* mData; nsString::size_type mLength; uint32_t mFlags; @@ -1959,6 +1959,8 @@ private: mData = const_cast(aData); } + friend class NonNull; + // A class to use for our static asserts to ensure our object layout // matches that of nsString. class StringAsserter; diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 75e332391a..845e1c8e67 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -1270,6 +1270,11 @@ DOMInterfaces = { 'nativeType': 'mozilla::dom::TextTrackRegion', }, +'WindowClient': { + 'nativeType': 'mozilla::dom::workers::ServiceWorkerWindowClient', + 'headerFile': 'mozilla/dom/workers/bindings/ServiceWorkerWindowClient.h', +}, + 'WebGLActiveInfo': { 'nativeType': 'mozilla::WebGLActiveInfo', 'headerFile': 'WebGLActiveInfo.h' diff --git a/dom/cache/DBAction.cpp b/dom/cache/DBAction.cpp index b8a2d136d9..ec463bf5c3 100644 --- a/dom/cache/DBAction.cpp +++ b/dom/cache/DBAction.cpp @@ -14,6 +14,8 @@ #include "nsIFile.h" #include "nsIURI.h" #include "nsNetUtil.h" +#include "DBSchema.h" +#include "FileUtils.h" namespace mozilla { namespace dom { @@ -75,6 +77,8 @@ DBAction::OpenConnection(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir, MOZ_ASSERT(aDBDir); MOZ_ASSERT(aConnOut); + nsCOMPtr conn; + bool exists; nsresult rv = aDBDir->Exists(&exists); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -123,21 +127,48 @@ DBAction::OpenConnection(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir, do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID); if (NS_WARN_IF(!ss)) { return NS_ERROR_UNEXPECTED; } - rv = ss->OpenDatabaseWithFileURL(dbFileUrl, aConnOut); + rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(conn)); if (rv == NS_ERROR_FILE_CORRUPTED) { NS_WARNING("Cache database corrupted. Recreating empty database."); + conn = nullptr; + // There is nothing else we can do to recover. Also, this data can // be deleted by QuotaManager at any time anyways. - rv = dbFile->Remove(false); + rv = WipeDatabase(dbFile, aDBDir); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - // TODO: clean up any orphaned body files (bug 1110446) - - rv = ss->OpenDatabaseWithFileURL(dbFileUrl, aConnOut); + rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(conn)); } if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - MOZ_ASSERT(*aConnOut); + + // Check the schema to make sure it is not too old. + int32_t schemaVersion = 0; + rv = conn->GetSchemaVersion(&schemaVersion); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + if (schemaVersion > 0 && schemaVersion < DBSchema::kMaxWipeSchemaVersion) { + conn = nullptr; + rv = WipeDatabase(dbFile, aDBDir); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(conn)); + } + + conn.forget(aConnOut); + + return rv; +} + +nsresult +DBAction::WipeDatabase(nsIFile* aDBFile, nsIFile* aDBDir) +{ + nsresult rv = aDBFile->Remove(false); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + // Delete the morgue as well. + rv = FileUtils::BodyDeleteDir(aDBDir); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + return rv; } diff --git a/dom/cache/DBAction.h b/dom/cache/DBAction.h index 497b7695a1..3238a0c0be 100644 --- a/dom/cache/DBAction.h +++ b/dom/cache/DBAction.h @@ -49,6 +49,8 @@ private: nsresult OpenConnection(const QuotaInfo& aQuotaInfo, nsIFile* aQuotaDir, mozIStorageConnection** aConnOut); + nsresult WipeDatabase(nsIFile* aDBFile, nsIFile* aDBDir); + const Mode mMode; }; diff --git a/dom/cache/DBSchema.cpp b/dom/cache/DBSchema.cpp index 03da25a384..5a156a80e0 100644 --- a/dom/cache/DBSchema.cpp +++ b/dom/cache/DBSchema.cpp @@ -20,7 +20,8 @@ namespace dom { namespace cache { -const int32_t DBSchema::kLatestSchemaVersion = 1; +const int32_t DBSchema::kMaxWipeSchemaVersion = 2; +const int32_t DBSchema::kLatestSchemaVersion = 2; const int32_t DBSchema::kMaxEntriesPerStatement = 255; using mozilla::void_t; @@ -96,6 +97,7 @@ DBSchema::CreateSchema(mozIStorageConnection* aConn) "response_status_text TEXT NOT NULL, " "response_headers_guard INTEGER NOT NULL, " "response_body_id TEXT NULL, " + "response_security_info BLOB NULL, " "cache_id INTEGER NOT NULL REFERENCES caches(id) ON DELETE CASCADE" ");" )); @@ -938,8 +940,9 @@ DBSchema::InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId, "response_status_text, " "response_headers_guard, " "response_body_id, " + "response_security_info, " "cache_id " - ") VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15)" + ") VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16)" ), getter_AddRefs(state)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -988,7 +991,12 @@ DBSchema::InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId, rv = BindId(state, 13, aResponseBodyId); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - rv = state->BindInt32Parameter(14, aCacheId); + rv = state->BindBlobParameter(14, reinterpret_cast + (aResponse.securityInfo().get()), + aResponse.securityInfo().Length()); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + rv = state->BindInt32Parameter(15, aCacheId); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } rv = state->Execute(); @@ -1075,7 +1083,8 @@ DBSchema::ReadResponse(mozIStorageConnection* aConn, EntryId aEntryId, "response_status, " "response_status_text, " "response_headers_guard, " - "response_body_id " + "response_body_id, " + "response_security_info " "FROM entries " "WHERE id=?1;" ), getter_AddRefs(state)); @@ -1120,6 +1129,13 @@ DBSchema::ReadResponse(mozIStorageConnection* aConn, EntryId aEntryId, if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } + uint8_t* data = nullptr; + uint32_t dataLength = 0; + rv = state->GetBlob(6, &dataLength, &data); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + aSavedResponseOut->mValue.securityInfo().Adopt( + reinterpret_cast(data), dataLength); + rv = aConn->CreateStatement(NS_LITERAL_CSTRING( "SELECT " "name, " diff --git a/dom/cache/DBSchema.h b/dom/cache/DBSchema.h index a592c96b9f..eeb78a9338 100644 --- a/dom/cache/DBSchema.h +++ b/dom/cache/DBSchema.h @@ -89,6 +89,9 @@ public: Namespace aNamespace, nsTArray& aKeysOut); + // We will wipe out databases with a schema versions less than this. + static const int32_t kMaxWipeSchemaVersion; + private: typedef int32_t EntryId; diff --git a/dom/cache/FileUtils.cpp b/dom/cache/FileUtils.cpp index 1bfda8b8f0..2cbfef755a 100644 --- a/dom/cache/FileUtils.cpp +++ b/dom/cache/FileUtils.cpp @@ -47,6 +47,29 @@ FileUtils::BodyCreateDir(nsIFile* aBaseDir) return rv; } +// static +nsresult +FileUtils::BodyDeleteDir(nsIFile* aBaseDir) +{ + MOZ_ASSERT(aBaseDir); + + nsCOMPtr aBodyDir; + nsresult rv = aBaseDir->Clone(getter_AddRefs(aBodyDir)); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + rv = aBodyDir->Append(NS_LITERAL_STRING("morgue")); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + rv = aBodyDir->Remove(/* recursive = */ true); + if (rv == NS_ERROR_FILE_NOT_FOUND || + rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) { + rv = NS_OK; + } + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + return rv; +} + // static nsresult FileUtils::BodyGetCacheDir(nsIFile* aBaseDir, const nsID& aId, diff --git a/dom/cache/FileUtils.h b/dom/cache/FileUtils.h index 8f9cf16fe1..76fd5ee90b 100644 --- a/dom/cache/FileUtils.h +++ b/dom/cache/FileUtils.h @@ -30,6 +30,10 @@ public: }; static nsresult BodyCreateDir(nsIFile* aBaseDir); + // Note that this function can only be used during the initialization of the + // database. We're unlikely to be able to delete the DB successfully past + // that point due to the file being in use. + static nsresult BodyDeleteDir(nsIFile* aBaseDir); static nsresult BodyGetCacheDir(nsIFile* aBaseDir, const nsID& aId, nsIFile** aCacheDirOut); diff --git a/dom/cache/PCacheTypes.ipdlh b/dom/cache/PCacheTypes.ipdlh index 03a6b6e633..ab6fee951a 100644 --- a/dom/cache/PCacheTypes.ipdlh +++ b/dom/cache/PCacheTypes.ipdlh @@ -70,6 +70,7 @@ struct PCacheResponse PHeadersEntry[] headers; HeadersGuardEnum headersGuard; PCacheReadStreamOrVoid body; + nsCString securityInfo; }; union PCacheResponseOrVoid diff --git a/dom/cache/TypeUtils.cpp b/dom/cache/TypeUtils.cpp index ff8d9ad34b..b643d69f60 100644 --- a/dom/cache/TypeUtils.cpp +++ b/dom/cache/TypeUtils.cpp @@ -233,6 +233,7 @@ TypeUtils::ToPCacheResponseWithoutBody(PCacheResponse& aOut, MOZ_ASSERT(headers); headers->GetPHeaders(aOut.headers()); aOut.headersGuard() = headers->Guard(); + aOut.securityInfo() = aIn.GetSecurityInfo(); } void @@ -319,6 +320,8 @@ TypeUtils::ToResponse(const PCacheResponse& aIn) ir->Headers()->Fill(*internalHeaders, result); MOZ_ASSERT(!result.Failed()); + ir->SetSecurityInfo(aIn.securityInfo()); + nsCOMPtr stream = ReadStream::Create(aIn.body()); ir->SetBody(stream); diff --git a/dom/fetch/FetchDriver.cpp b/dom/fetch/FetchDriver.cpp index cf0c9dfa1d..3fcd74daa2 100644 --- a/dom/fetch/FetchDriver.cpp +++ b/dom/fetch/FetchDriver.cpp @@ -665,6 +665,12 @@ FetchDriver::OnStartRequest(nsIRequest* aRequest, mResponse = BeginAndGetFilteredResponse(response); + nsCOMPtr securityInfo; + rv = channel->GetSecurityInfo(getter_AddRefs(securityInfo)); + if (securityInfo) { + mResponse->SetSecurityInfo(securityInfo); + } + // We open a pipe so that we can immediately set the pipe's read end as the // response's body. Setting the segment size to UINT32_MAX means that the // pipe has infinite space. The nsIChannel will continue to buffer data in diff --git a/dom/fetch/InternalResponse.cpp b/dom/fetch/InternalResponse.cpp index 144150172c..be78ddd5be 100644 --- a/dom/fetch/InternalResponse.cpp +++ b/dom/fetch/InternalResponse.cpp @@ -9,6 +9,7 @@ #include "mozilla/dom/InternalHeaders.h" #include "nsStreamUtils.h" +#include "nsSerializationHelper.h" namespace mozilla { namespace dom { @@ -32,6 +33,7 @@ InternalResponse::InternalResponse(const InternalResponse& aOther) , mStatus(aOther.mStatus) , mStatusText(aOther.mStatusText) , mContentType(aOther.mContentType) + , mSecurityInfo(aOther.mSecurityInfo) { } @@ -84,5 +86,24 @@ InternalResponse::CORSResponse(InternalResponse* aInner) return cors.forget(); } +void +InternalResponse::SetSecurityInfo(nsISupports* aSecurityInfo) +{ + MOZ_ASSERT(mSecurityInfo.IsEmpty(), "security info should only be set once"); + nsCOMPtr serializable = do_QueryInterface(aSecurityInfo); + if (!serializable) { + NS_WARNING("A non-serializable object was passed to InternalResponse::SetSecurityInfo"); + return; + } + NS_SerializeToString(serializable, mSecurityInfo); +} + +void +InternalResponse::SetSecurityInfo(const nsCString& aSecurityInfo) +{ + MOZ_ASSERT(mSecurityInfo.IsEmpty(), "security info should only be set once"); + mSecurityInfo = aSecurityInfo; +} + } // namespace dom } // namespace mozilla diff --git a/dom/fetch/InternalResponse.h b/dom/fetch/InternalResponse.h index de26a0eae4..97b5229629 100644 --- a/dom/fetch/InternalResponse.h +++ b/dom/fetch/InternalResponse.h @@ -123,6 +123,18 @@ public: mBody = aBody; } + const nsCString& + GetSecurityInfo() const + { + return mSecurityInfo; + } + + void + SetSecurityInfo(nsISupports* aSecurityInfo); + + void + SetSecurityInfo(const nsCString& aSecurityInfo); + private: ~InternalResponse() { } @@ -140,6 +152,7 @@ private: nsRefPtr mHeaders; nsCOMPtr mBody; nsCString mContentType; + nsCString mSecurityInfo; }; } // namespace dom diff --git a/dom/webidl/Client.webidl b/dom/webidl/Client.webidl index 570a9de527..b09d0b9b43 100644 --- a/dom/webidl/Client.webidl +++ b/dom/webidl/Client.webidl @@ -10,8 +10,24 @@ [Exposed=ServiceWorker] interface Client { - readonly attribute unsigned long id; + readonly attribute DOMString id; + readonly attribute USVString url; [Throws] void postMessage(any message, optional sequence transfer); }; + +[Exposed=ServiceWorker] +interface WindowClient : Client { + readonly attribute VisibilityState visibilityState; + readonly attribute boolean focused; + readonly attribute FrameType frameType; + Promise focus(); +}; + +enum FrameType { + "auxiliary", + "top-level", + "nested", + "none" +}; diff --git a/dom/webidl/Clients.webidl b/dom/webidl/Clients.webidl index d3a75cf4e5..1ae5dd6784 100644 --- a/dom/webidl/Clients.webidl +++ b/dom/webidl/Clients.webidl @@ -10,10 +10,11 @@ [Exposed=ServiceWorker] interface Clients { - // A list of client objects, identifiable by ID, that correspond to windows - // (or workers) that are "controlled" by this SW + // The objects returned will be new instances every time [Throws] Promise?> matchAll(optional ClientQueryOptions options); + Promise openWindow(USVString url); + Promise claim(); }; dictionary ClientQueryOptions { diff --git a/dom/webidl/FetchEvent.webidl b/dom/webidl/FetchEvent.webidl index b02bcd8dae..5f53dfb5f5 100644 --- a/dom/webidl/FetchEvent.webidl +++ b/dom/webidl/FetchEvent.webidl @@ -12,7 +12,9 @@ Exposed=(ServiceWorker)] interface FetchEvent : Event { readonly attribute Request request; - readonly attribute Client client; // The window issuing the request. + + // https://github.com/slightlyoff/ServiceWorker/issues/631 + readonly attribute Client? client; // The window issuing the request. readonly attribute boolean isReload; [Throws] void respondWith(Promise r); diff --git a/dom/webidl/ServiceWorkerContainer.webidl b/dom/webidl/ServiceWorkerContainer.webidl index c1e66df2b1..c06f29eb3c 100644 --- a/dom/webidl/ServiceWorkerContainer.webidl +++ b/dom/webidl/ServiceWorkerContainer.webidl @@ -32,6 +32,7 @@ interface ServiceWorkerContainer : EventTarget { attribute EventHandler oncontrollerchange; attribute EventHandler onreloadpage; attribute EventHandler onerror; + attribute EventHandler onmessage; }; // Testing only. diff --git a/dom/webidl/ServiceWorkerGlobalScope.webidl b/dom/webidl/ServiceWorkerGlobalScope.webidl index d9cd63801e..dd2763599a 100644 --- a/dom/webidl/ServiceWorkerGlobalScope.webidl +++ b/dom/webidl/ServiceWorkerGlobalScope.webidl @@ -36,10 +36,6 @@ interface ServiceWorkerGlobalScope : WorkerGlobalScope { // The event.source of these MessageEvents are instances of Client attribute EventHandler onmessage; - - // close() method inherited from WorkerGlobalScope is not exposed. - // FIXME(nsm): For now, overridden so it can be a no-op. - void close(); }; diff --git a/dom/webidl/WorkerGlobalScope.webidl b/dom/webidl/WorkerGlobalScope.webidl index 5334555d91..b7f397154f 100644 --- a/dom/webidl/WorkerGlobalScope.webidl +++ b/dom/webidl/WorkerGlobalScope.webidl @@ -22,6 +22,7 @@ interface WorkerGlobalScope : EventTarget { readonly attribute WorkerLocation location; + [Throws] void close(); attribute OnErrorEventHandler onerror; diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp index 0d7bc49d4e..6a6230e0c0 100644 --- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -267,14 +267,23 @@ GetWorkerPref(const nsACString& aPref, return result; } -// This function creates a key for a SharedWorker composed by "name|scriptSpec". +// This function creates a key for a SharedWorker composed by "shared|name|scriptSpec" +// and a key for a ServiceWorker composed by "service|scope|scriptSpec". // If the name contains a '|', this will be replaced by '||'. void GenerateSharedWorkerKey(const nsACString& aScriptSpec, const nsACString& aName, - nsCString& aKey) + WorkerType aWorkerType, nsCString& aKey) { aKey.Truncate(); - aKey.SetCapacity(aScriptSpec.Length() + aName.Length() + 1); + NS_NAMED_LITERAL_CSTRING(sharedPrefix, "shared|"); + NS_NAMED_LITERAL_CSTRING(servicePrefix, "service|"); + MOZ_ASSERT(servicePrefix.Length() > sharedPrefix.Length()); + MOZ_ASSERT(aWorkerType == WorkerTypeShared || + aWorkerType == WorkerTypeService); + aKey.SetCapacity(servicePrefix.Length() + aScriptSpec.Length() + + aName.Length() + 1); + + aKey.Append(aWorkerType == WorkerTypeService ? servicePrefix : sharedPrefix); nsACString::const_iterator start, end; aName.BeginReading(start); @@ -1457,7 +1466,8 @@ RuntimeService::RegisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate) const nsCString& sharedWorkerName = aWorkerPrivate->SharedWorkerName(); nsAutoCString key; - GenerateSharedWorkerKey(sharedWorkerScriptSpec, sharedWorkerName, key); + GenerateSharedWorkerKey(sharedWorkerScriptSpec, sharedWorkerName, + aWorkerPrivate->Type(), key); MOZ_ASSERT(!domainInfo->mSharedWorkerInfos.Get(key)); SharedWorkerInfo* sharedWorkerInfo = @@ -1562,7 +1572,8 @@ RuntimeService::UnregisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate) if (match.mSharedWorkerInfo) { nsAutoCString key; GenerateSharedWorkerKey(match.mSharedWorkerInfo->mScriptSpec, - match.mSharedWorkerInfo->mName, key); + match.mSharedWorkerInfo->mName, + aWorkerPrivate->Type(), key); domainInfo->mSharedWorkerInfos.Remove(key); } } @@ -2354,7 +2365,7 @@ RuntimeService::CreateSharedWorkerFromLoadInfo(JSContext* aCx, NS_ENSURE_SUCCESS(rv, rv); nsAutoCString key; - GenerateSharedWorkerKey(scriptSpec, aName, key); + GenerateSharedWorkerKey(scriptSpec, aName, aType, key); if (mDomainMap.Get(aLoadInfo->mDomain, &domainInfo) && domainInfo->mSharedWorkerInfos.Get(key, &sharedWorkerInfo)) { @@ -2429,7 +2440,8 @@ RuntimeService::ForgetSharedWorker(WorkerPrivate* aWorkerPrivate) if (match.mSharedWorkerInfo) { nsAutoCString key; GenerateSharedWorkerKey(match.mSharedWorkerInfo->mScriptSpec, - match.mSharedWorkerInfo->mName, key); + match.mSharedWorkerInfo->mName, + aWorkerPrivate->Type(), key); domainInfo->mSharedWorkerInfos.Remove(key); } } diff --git a/dom/workers/ServiceWorkerClient.cpp b/dom/workers/ServiceWorkerClient.cpp index 3472b65f68..09e98cb409 100644 --- a/dom/workers/ServiceWorkerClient.cpp +++ b/dom/workers/ServiceWorkerClient.cpp @@ -5,13 +5,13 @@ */ #include "ServiceWorkerClient.h" +#include "ServiceWorkerContainer.h" #include "mozilla/dom/MessageEvent.h" #include "nsGlobalWindow.h" +#include "nsIDocument.h" #include "WorkerPrivate.h" -#include "mozilla/dom/ClientBinding.h" - using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::dom::workers; @@ -26,6 +26,36 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerClient) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END +ServiceWorkerClientInfo::ServiceWorkerClientInfo(nsIDocument* aDoc) +{ + MOZ_ASSERT(aDoc); + MOZ_ASSERT(aDoc->GetWindow()); + + nsresult rv = aDoc->GetId(mClientId); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to get the UUID of the document."); + } + + nsRefPtr outerWindow = static_cast(aDoc->GetWindow()); + mWindowId = outerWindow->WindowID(); + aDoc->GetURL(mUrl); + mVisibilityState = aDoc->VisibilityState(); + + ErrorResult result; + mFocused = aDoc->HasFocus(result); + if (result.Failed()) { + NS_WARNING("Failed to get focus information."); + } + + if (!outerWindow->IsTopLevelWindow()) { + mFrameType = FrameType::Nested; + } else if (outerWindow->HadOriginalOpener()) { + mFrameType = FrameType::Auxiliary; + } else { + mFrameType = FrameType::Top_level; + } +} + JSObject* ServiceWorkerClient::WrapObject(JSContext* aCx) { @@ -36,15 +66,15 @@ namespace { class ServiceWorkerClientPostMessageRunnable final : public nsRunnable { - uint64_t mId; + uint64_t mWindowId; JSAutoStructuredCloneBuffer mBuffer; nsTArray> mClonedObjects; public: - ServiceWorkerClientPostMessageRunnable(uint64_t aId, + ServiceWorkerClientPostMessageRunnable(uint64_t aWindowId, JSAutoStructuredCloneBuffer&& aData, nsTArray>& aClonedObjects) - : mId(aId), + : mWindowId(aWindowId), mBuffer(Move(aData)) { mClonedObjects.SwapElements(aClonedObjects); @@ -54,21 +84,28 @@ public: Run() { AssertIsOnMainThread(); - nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mId); + nsGlobalWindow* window = nsGlobalWindow::GetOuterWindowWithId(mWindowId); if (!window) { return NS_ERROR_FAILURE; } + ErrorResult result; + dom::Navigator* navigator = window->GetNavigator(result); + if (NS_WARN_IF(result.Failed())) { + return result.ErrorCode(); + } + + nsRefPtr container = navigator->ServiceWorker(); AutoJSAPI jsapi; jsapi.Init(window); JSContext* cx = jsapi.cx(); - return DispatchDOMEvent(cx, window); + return DispatchDOMEvent(cx, container); } private: NS_IMETHOD - DispatchDOMEvent(JSContext* aCx, nsGlobalWindow* aTargetWindow) + DispatchDOMEvent(JSContext* aCx, ServiceWorkerContainer* aTargetContainer) { AssertIsOnMainThread(); @@ -84,7 +121,7 @@ private: return NS_ERROR_FAILURE; } - nsCOMPtr event = new MessageEvent(aTargetWindow, + nsCOMPtr event = new MessageEvent(aTargetContainer, nullptr, nullptr); nsresult rv = event->InitMessageEvent(NS_LITERAL_STRING("message"), @@ -101,7 +138,7 @@ private: event->SetTrusted(true); bool status = false; - aTargetWindow->DispatchEvent(event, &status); + aTargetContainer->DispatchEvent(event, &status); if (!status) { return NS_ERROR_FAILURE; @@ -150,7 +187,7 @@ ServiceWorkerClient::PostMessage(JSContext* aCx, JS::Handle aMessage, } nsRefPtr runnable = - new ServiceWorkerClientPostMessageRunnable(mId, Move(buffer), clonedObjects); + new ServiceWorkerClientPostMessageRunnable(mWindowId, Move(buffer), clonedObjects); nsresult rv = NS_DispatchToMainThread(runnable); if (NS_FAILED(rv)) { aRv.Throw(NS_ERROR_FAILURE); diff --git a/dom/workers/ServiceWorkerClient.h b/dom/workers/ServiceWorkerClient.h index 3532a17771..2ae4164957 100644 --- a/dom/workers/ServiceWorkerClient.h +++ b/dom/workers/ServiceWorkerClient.h @@ -11,47 +11,87 @@ #include "nsWrapperCache.h" #include "mozilla/ErrorResult.h" #include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/ClientBinding.h" namespace mozilla { namespace dom { namespace workers { -class ServiceWorkerClient final : public nsISupports, - public nsWrapperCache +class ServiceWorkerClient; +class ServiceWorkerWindowClient; + +// Used as a container object for information needed to create +// client objects. +class ServiceWorkerClientInfo MOZ_FINAL +{ + friend class ServiceWorkerClient; + friend class ServiceWorkerWindowClient; + +public: + explicit ServiceWorkerClientInfo(nsIDocument* aDoc); + +private: + nsString mClientId; + uint64_t mWindowId; + nsString mUrl; + + // Window Clients + VisibilityState mVisibilityState; + bool mFocused; + FrameType mFrameType; +}; + +class ServiceWorkerClient : public nsISupports, + public nsWrapperCache { public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ServiceWorkerClient) - ServiceWorkerClient(nsISupports* aOwner, uint64_t aId) + ServiceWorkerClient(nsISupports* aOwner, + const ServiceWorkerClientInfo& aClientInfo) : mOwner(aOwner), - mId(aId) + mId(aClientInfo.mClientId), + mWindowId(aClientInfo.mWindowId), + mUrl(aClientInfo.mUrl) { + MOZ_ASSERT(aOwner); } - uint32_t Id() const - { - return mId; - } - - nsISupports* GetParentObject() const + nsISupports* + GetParentObject() const { return mOwner; } - void PostMessage(JSContext* aCx, JS::Handle aMessage, - const Optional>& aTransferable, - ErrorResult& aRv); + void GetId(nsString& aRetval) const + { + aRetval = mId; + } + + void + GetUrl(nsAString& aUrl) const + { + aUrl.Assign(mUrl); + } + + void + PostMessage(JSContext* aCx, JS::Handle aMessage, + const Optional>& aTransferable, + ErrorResult& aRv); JSObject* WrapObject(JSContext* aCx) override; private: - ~ServiceWorkerClient() - { - } +protected: + virtual ~ServiceWorkerClient() + { } +private: nsCOMPtr mOwner; - uint64_t mId; + nsString mId; + uint64_t mWindowId; + nsString mUrl; }; } // namespace workers diff --git a/dom/workers/ServiceWorkerClients.cpp b/dom/workers/ServiceWorkerClients.cpp index d498736e05..adc5566aca 100644 --- a/dom/workers/ServiceWorkerClients.cpp +++ b/dom/workers/ServiceWorkerClients.cpp @@ -8,6 +8,7 @@ #include "ServiceWorkerClient.h" #include "ServiceWorkerClients.h" #include "ServiceWorkerManager.h" +#include "ServiceWorkerWindowClient.h" #include "WorkerPrivate.h" #include "WorkerRunnable.h" @@ -122,17 +123,17 @@ private: class ResolvePromiseWorkerRunnable final : public WorkerRunnable { nsRefPtr mPromiseHolder; - nsAutoPtr> mValue; + nsTArray mValue; public: ResolvePromiseWorkerRunnable(WorkerPrivate* aWorkerPrivate, PromiseHolder* aPromiseHolder, - nsAutoPtr>& aValue) + nsTArray& aValue) : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount), - mPromiseHolder(aPromiseHolder), - mValue(aValue) + mPromiseHolder(aPromiseHolder) { AssertIsOnMainThread(); + mValue.SwapElements(aValue); } bool @@ -145,10 +146,10 @@ public: MOZ_ASSERT(promise); nsTArray> ret; - for (size_t i = 0; i < mValue->Length(); i++) { + for (size_t i = 0; i < mValue.Length(); i++) { ret.AppendElement(nsRefPtr( - new ServiceWorkerClient(promise->GetParentObject(), - mValue->ElementAt(i)))); + new ServiceWorkerWindowClient(promise->GetParentObject(), + mValue.ElementAt(i)))); } promise->MaybeResolve(ret); @@ -216,7 +217,7 @@ public: } nsRefPtr swm = ServiceWorkerManager::GetInstance(); - nsAutoPtr> result(new nsTArray()); + nsTArray result; swm->GetAllClients(mScope, result); nsRefPtr r = @@ -282,3 +283,29 @@ ServiceWorkerClients::MatchAll(const ClientQueryOptions& aOptions, return promise.forget(); } + +already_AddRefed +ServiceWorkerClients::OpenWindow(const nsAString& aUrl) +{ + ErrorResult result; + nsRefPtr promise = Promise::Create(mWorkerScope, result); + if (NS_WARN_IF(result.Failed())) { + return nullptr; + } + + promise->MaybeReject(NS_ERROR_NOT_AVAILABLE); + return promise.forget(); +} + +already_AddRefed +ServiceWorkerClients::Claim() +{ + ErrorResult result; + nsRefPtr promise = Promise::Create(mWorkerScope, result); + if (NS_WARN_IF(result.Failed())) { + return nullptr; + } + + promise->MaybeReject(NS_ERROR_NOT_AVAILABLE); + return promise.forget(); +} diff --git a/dom/workers/ServiceWorkerClients.h b/dom/workers/ServiceWorkerClients.h index 1302ccc5b0..da603fa60f 100644 --- a/dom/workers/ServiceWorkerClients.h +++ b/dom/workers/ServiceWorkerClients.h @@ -31,6 +31,12 @@ public: already_AddRefed MatchAll(const ClientQueryOptions& aOptions, ErrorResult& aRv); + already_AddRefed + OpenWindow(const nsAString& aUrl); + + already_AddRefed + Claim(); + JSObject* WrapObject(JSContext* aCx) override; diff --git a/dom/workers/ServiceWorkerContainer.h b/dom/workers/ServiceWorkerContainer.h index 15343dd5cd..382a2462b4 100644 --- a/dom/workers/ServiceWorkerContainer.h +++ b/dom/workers/ServiceWorkerContainer.h @@ -31,6 +31,7 @@ public: IMPL_EVENT_HANDLER(controllerchange) IMPL_EVENT_HANDLER(reloadpage) IMPL_EVENT_HANDLER(error) + IMPL_EVENT_HANDLER(message) explicit ServiceWorkerContainer(nsPIDOMWindow* aWindow); diff --git a/dom/workers/ServiceWorkerEvents.cpp b/dom/workers/ServiceWorkerEvents.cpp index 59d2793912..e535f14b7a 100644 --- a/dom/workers/ServiceWorkerEvents.cpp +++ b/dom/workers/ServiceWorkerEvents.cpp @@ -14,6 +14,7 @@ #include "nsServiceManagerUtils.h" #include "nsStreamUtils.h" #include "nsNetCID.h" +#include "nsSerializationHelper.h" #include "mozilla/dom/FetchEventBinding.h" #include "mozilla/dom/PromiseNativeHandler.h" @@ -28,7 +29,6 @@ BEGIN_WORKERS_NAMESPACE FetchEvent::FetchEvent(EventTarget* aOwner) : Event(aOwner, nullptr, nullptr) -, mWindowId(0) , mIsReload(false) , mWaitToRespond(false) { @@ -41,11 +41,11 @@ FetchEvent::~FetchEvent() void FetchEvent::PostInit(nsMainThreadPtrHandle& aChannel, nsMainThreadPtrHandle& aServiceWorker, - uint64_t aWindowId) + nsAutoPtr& aClientInfo) { mChannel = aChannel; mServiceWorker = aServiceWorker; - mWindowId = aWindowId; + mClientInfo = aClientInfo; } /*static*/ already_AddRefed @@ -92,9 +92,12 @@ public: class FinishResponse final : public nsRunnable { nsMainThreadPtrHandle mChannel; + nsRefPtr mInternalResponse; public: - explicit FinishResponse(nsMainThreadPtrHandle& aChannel) + FinishResponse(nsMainThreadPtrHandle& aChannel, + InternalResponse* aInternalResponse) : mChannel(aChannel) + , mInternalResponse(aInternalResponse) { } @@ -102,7 +105,17 @@ public: Run() { AssertIsOnMainThread(); - nsresult rv = mChannel->FinishSynthesizedResponse(); + + nsCOMPtr infoObj; + nsresult rv = NS_DeserializeObject(mInternalResponse->GetSecurityInfo(), getter_AddRefs(infoObj)); + if (NS_SUCCEEDED(rv)) { + rv = mChannel->SetSecurityInfo(infoObj); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + rv = mChannel->FinishSynthesizedResponse(); NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to finish synthesized response"); return rv; } @@ -130,9 +143,12 @@ public: struct RespondWithClosure { nsMainThreadPtrHandle mInterceptedChannel; + nsRefPtr mInternalResponse; - explicit RespondWithClosure(nsMainThreadPtrHandle& aChannel) + RespondWithClosure(nsMainThreadPtrHandle& aChannel, + InternalResponse* aInternalResponse) : mInterceptedChannel(aChannel) + , mInternalResponse(aInternalResponse) { } }; @@ -142,7 +158,7 @@ void RespondWithCopyComplete(void* aClosure, nsresult aStatus) nsAutoPtr data(static_cast(aClosure)); nsCOMPtr event; if (NS_SUCCEEDED(aStatus)) { - event = new FinishResponse(data->mInterceptedChannel); + event = new FinishResponse(data->mInterceptedChannel, data->mInternalResponse); } else { event = new CancelChannelRunnable(data->mInterceptedChannel); } @@ -187,6 +203,11 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle aValu return; } + nsRefPtr ir = response->GetInternalResponse(); + if (NS_WARN_IF(!ir)) { + return; + } + nsCOMPtr body; response->GetBody(getter_AddRefs(body)); if (NS_WARN_IF(!body) || NS_WARN_IF(response->BodyUsed())) { @@ -200,7 +221,7 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle aValu return; } - nsAutoPtr closure(new RespondWithClosure(mInterceptedChannel)); + nsAutoPtr closure(new RespondWithClosure(mInterceptedChannel, ir)); nsCOMPtr stsThread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); if (NS_WARN_IF(!stsThread)) { @@ -244,10 +265,14 @@ FetchEvent::RespondWith(Promise& aPromise, ErrorResult& aRv) } already_AddRefed -FetchEvent::Client() +FetchEvent::GetClient() { if (!mClient) { - mClient = new ServiceWorkerClient(GetParentObject(), mWindowId); + if (!mClientInfo) { + return nullptr; + } + + mClient = new ServiceWorkerClient(GetParentObject(), *mClientInfo); } nsRefPtr client = mClient; return client.forget(); diff --git a/dom/workers/ServiceWorkerEvents.h b/dom/workers/ServiceWorkerEvents.h index 8def3f0adf..a107dfd758 100644 --- a/dom/workers/ServiceWorkerEvents.h +++ b/dom/workers/ServiceWorkerEvents.h @@ -32,7 +32,7 @@ class FetchEvent final : public Event nsMainThreadPtrHandle mServiceWorker; nsRefPtr mClient; nsRefPtr mRequest; - uint64_t mWindowId; + nsAutoPtr mClientInfo; bool mIsReload; bool mWaitToRespond; protected: @@ -51,7 +51,7 @@ public: void PostInit(nsMainThreadPtrHandle& aChannel, nsMainThreadPtrHandle& aServiceWorker, - uint64_t aWindowId); + nsAutoPtr& aClientInfo); static already_AddRefed Constructor(const GlobalObject& aGlobal, @@ -72,7 +72,7 @@ public: } already_AddRefed - Client(); + GetClient(); bool IsReload() const diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp index e3f8fc050b..d6c5574790 100644 --- a/dom/workers/ServiceWorkerManager.cpp +++ b/dom/workers/ServiceWorkerManager.cpp @@ -691,18 +691,10 @@ private: return Fail(rv); } - nsCOMPtr principal; - nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); - rv = ssm->GetNoAppCodebasePrincipal(uri, getter_AddRefs(principal)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return Fail(rv); - } - - nsCOMPtr channel; rv = NS_NewChannel(getter_AddRefs(channel), uri, - principal, + mPrincipal, nsILoadInfo::SEC_NORMAL, nsIContentPolicy::TYPE_SCRIPT); // FIXME(nsm): TYPE_SERVICEWORKER if (NS_WARN_IF(NS_FAILED(rv))) { @@ -1121,7 +1113,6 @@ ServiceWorkerRegistrationInfo::Activate() nsRefPtr swm = ServiceWorkerManager::GetInstance(); swm->InvalidateServiceWorkerRegistrationWorker(this, WhichServiceWorker::WAITING_WORKER | WhichServiceWorker::ACTIVE_WORKER); if (!activatingWorker) { - NS_WARNING("No activatingWorker!"); return; } @@ -2134,7 +2125,7 @@ class FetchEventRunnable : public WorkerRunnable nsMainThreadPtrHandle mServiceWorker; nsTArray mHeaderNames; nsTArray mHeaderValues; - uint64_t mWindowId; + nsAutoPtr mClientInfo; nsCString mSpec; nsCString mMethod; bool mIsReload; @@ -2142,11 +2133,11 @@ public: FetchEventRunnable(WorkerPrivate* aWorkerPrivate, nsMainThreadPtrHandle& aChannel, nsMainThreadPtrHandle& aServiceWorker, - uint64_t aWindowId) + nsAutoPtr& aClientInfo) : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount) , mInterceptedChannel(aChannel) , mServiceWorker(aServiceWorker) - , mWindowId(aWindowId) + , mClientInfo(aClientInfo) { MOZ_ASSERT(aWorkerPrivate); } @@ -2229,8 +2220,9 @@ private: MOZ_ASSERT(aWorkerPrivate->IsServiceWorker()); GlobalObject globalObj(aCx, aWorkerPrivate->GlobalScope()->GetWrapper()); + NS_ConvertUTF8toUTF16 local(mSpec); RequestOrUSVString requestInfo; - *requestInfo.SetAsUSVString().ToAStringPtr() = NS_ConvertUTF8toUTF16(mSpec); + requestInfo.SetAsUSVString().Rebind(local.Data(), local.Length()); RootedDictionary reqInit(aCx); reqInit.mMethod.Construct(mMethod); @@ -2270,7 +2262,7 @@ private: return false; } - event->PostInit(mInterceptedChannel, mServiceWorker, mWindowId); + event->PostInit(mInterceptedChannel, mServiceWorker, mClientInfo); event->SetTrusted(true); nsRefPtr target = do_QueryObject(aWorkerPrivate->GlobalScope()); @@ -2295,9 +2287,12 @@ ServiceWorkerManager::DispatchFetchEvent(nsIDocument* aDoc, nsIInterceptedChanne nsresult rv = aChannel->GetIsNavigation(&isNavigation); NS_ENSURE_SUCCESS(rv, rv); + nsAutoPtr clientInfo; + if (!isNavigation) { MOZ_ASSERT(aDoc); - rv = GetDocumentController(aDoc->GetWindow(), getter_AddRefs(serviceWorker)); + rv = GetDocumentController(aDoc->GetInnerWindow(), getter_AddRefs(serviceWorker)); + clientInfo = new ServiceWorkerClientInfo(aDoc); } else { nsCOMPtr internalChannel; rv = aChannel->GetChannel(getter_AddRefs(internalChannel)); @@ -2328,14 +2323,13 @@ ServiceWorkerManager::DispatchFetchEvent(nsIDocument* aDoc, nsIInterceptedChanne nsMainThreadPtrHandle handle( new nsMainThreadPtrHolder(aChannel, false)); - uint64_t windowId = aDoc ? aDoc->GetInnerWindow()->WindowID() : 0; - nsRefPtr sw = static_cast(serviceWorker.get()); nsMainThreadPtrHandle serviceWorkerHandle( new nsMainThreadPtrHolder(sw)); + // clientInfo is null if we don't have a controlled document nsRefPtr event = - new FetchEventRunnable(sw->GetWorkerPrivate(), handle, serviceWorkerHandle, windowId); + new FetchEventRunnable(sw->GetWorkerPrivate(), handle, serviceWorkerHandle, clientInfo); rv = event->Init(); NS_ENSURE_SUCCESS(rv, rv); @@ -2563,14 +2557,14 @@ namespace { class MOZ_STACK_CLASS FilterRegistrationData { public: - FilterRegistrationData(nsTArray* aDocuments, - ServiceWorkerRegistrationInfo* aRegistration) + FilterRegistrationData(nsTArray& aDocuments, + ServiceWorkerRegistrationInfo* aRegistration) : mDocuments(aDocuments), mRegistration(aRegistration) { } - nsTArray* mDocuments; + nsTArray& mDocuments; nsRefPtr mRegistration; }; @@ -2583,12 +2577,16 @@ EnumControlledDocuments(nsISupports* aKey, if (data->mRegistration != aRegistration) { return PL_DHASH_NEXT; } + nsCOMPtr document = do_QueryInterface(aKey); - if (!document || !document->GetInnerWindow()) { + + if (!document || !document->GetWindow()) { return PL_DHASH_NEXT; } - data->mDocuments->AppendElement(document->GetInnerWindow()->WindowID()); + ServiceWorkerClientInfo clientInfo(document); + data->mDocuments.AppendElement(clientInfo); + return PL_DHASH_NEXT; } @@ -2635,7 +2633,7 @@ FireControllerChangeOnMatchingDocument(nsISupports* aKey, void ServiceWorkerManager::GetAllClients(const nsCString& aScope, - nsTArray* aControlledDocuments) + nsTArray& aControlledDocuments) { nsRefPtr registration = GetRegistration(aScope); diff --git a/dom/workers/ServiceWorkerManager.h b/dom/workers/ServiceWorkerManager.h index 941cdfd997..88917bdf9f 100644 --- a/dom/workers/ServiceWorkerManager.h +++ b/dom/workers/ServiceWorkerManager.h @@ -43,6 +43,7 @@ class ServiceWorkerRegistration; namespace workers { class ServiceWorker; +class ServiceWorkerClientInfo; class ServiceWorkerInfo; class ServiceWorkerJobQueue; @@ -381,7 +382,7 @@ public: void GetAllClients(const nsCString& aScope, - nsTArray* aControlledDocuments); + nsTArray& aControlledDocuments); static already_AddRefed GetInstance(); diff --git a/dom/workers/ServiceWorkerWindowClient.cpp b/dom/workers/ServiceWorkerWindowClient.cpp new file mode 100644 index 0000000000..d6cddf21f5 --- /dev/null +++ b/dom/workers/ServiceWorkerWindowClient.cpp @@ -0,0 +1,34 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "ServiceWorkerWindowClient.h" + +#include "mozilla/dom/ClientBinding.h" + +using namespace mozilla::dom; +using namespace mozilla::dom::workers; + +JSObject* +ServiceWorkerWindowClient::WrapObject(JSContext* aCx) +{ + return WindowClientBinding::Wrap(aCx, this); +} + +already_AddRefed +ServiceWorkerWindowClient::Focus() const +{ + ErrorResult result; + nsCOMPtr global = do_QueryInterface(GetParentObject()); + MOZ_ASSERT(global); + + nsRefPtr promise = Promise::Create(global, result); + if (NS_WARN_IF(result.Failed())) { + return nullptr; + } + + promise->MaybeReject(NS_ERROR_NOT_AVAILABLE); + return promise.forget(); +} diff --git a/dom/workers/ServiceWorkerWindowClient.h b/dom/workers/ServiceWorkerWindowClient.h new file mode 100644 index 0000000000..8ea64e11cb --- /dev/null +++ b/dom/workers/ServiceWorkerWindowClient.h @@ -0,0 +1,65 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef mozilla_dom_workers_serviceworkerwindowclient_h +#define mozilla_dom_workers_serviceworkerwindowclient_h + +#include "ServiceWorkerClient.h" + +namespace mozilla { +namespace dom { +namespace workers { + +class ServiceWorkerWindowClient MOZ_FINAL : public ServiceWorkerClient +{ +public: + ServiceWorkerWindowClient(nsISupports* aOwner, + const ServiceWorkerClientInfo& aClientInfo) + : ServiceWorkerClient(aOwner, aClientInfo), + mVisibilityState(aClientInfo.mVisibilityState), + mFocused(aClientInfo.mFocused), + mFrameType(aClientInfo.mFrameType) + { + } + + virtual JSObject* + WrapObject(JSContext* aCx) MOZ_OVERRIDE; + + mozilla::dom::VisibilityState + VisibilityState() const + { + return mVisibilityState; + } + + bool + Focused() const + { + return mFocused; + } + + mozilla::dom::FrameType + FrameType() const + { + return mFrameType; + } + + already_AddRefed + Focus() const; + +private: + ~ServiceWorkerWindowClient() + { } + + mozilla::dom::VisibilityState mVisibilityState; + bool mFocused; + mozilla::dom::FrameType mFrameType; +}; + +} // namespace workers +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_workers_serviceworkerwindowclient_h diff --git a/dom/workers/WorkerScope.cpp b/dom/workers/WorkerScope.cpp index 40383f4dbb..d8694d5ed2 100644 --- a/dom/workers/WorkerScope.cpp +++ b/dom/workers/WorkerScope.cpp @@ -171,11 +171,15 @@ WorkerGlobalScope::GetExistingNavigator() const } void -WorkerGlobalScope::Close(JSContext* aCx) +WorkerGlobalScope::Close(JSContext* aCx, ErrorResult& aRv) { mWorkerPrivate->AssertIsOnWorkerThread(); - mWorkerPrivate->CloseInternal(aCx); + if (mWorkerPrivate->IsServiceWorker()) { + aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); + } else { + mWorkerPrivate->CloseInternal(aCx); + } } OnErrorEventHandlerNonNull* diff --git a/dom/workers/WorkerScope.h b/dom/workers/WorkerScope.h index 8d7594ad10..230203b82b 100644 --- a/dom/workers/WorkerScope.h +++ b/dom/workers/WorkerScope.h @@ -98,7 +98,7 @@ public: GetExistingNavigator() const; void - Close(JSContext* aCx); + Close(JSContext* aCx, ErrorResult& aRv); OnErrorEventHandlerNonNull* GetOnerror(); @@ -218,12 +218,6 @@ public: aScope = mScope; } - void - Close() const - { - // no-op close. - } - void Update(); diff --git a/dom/workers/moz.build b/dom/workers/moz.build index 28d2d89e41..e2e9d7938c 100644 --- a/dom/workers/moz.build +++ b/dom/workers/moz.build @@ -34,6 +34,7 @@ EXPORTS.mozilla.dom.workers.bindings += [ 'ServiceWorker.h', 'ServiceWorkerClient.h', 'ServiceWorkerClients.h', + 'ServiceWorkerWindowClient.h', 'SharedWorker.h', 'URL.h', 'WorkerFeature.h', @@ -69,6 +70,7 @@ UNIFIED_SOURCES += [ 'ServiceWorkerManager.cpp', 'ServiceWorkerRegistrar.cpp', 'ServiceWorkerRegistration.cpp', + 'ServiceWorkerWindowClient.cpp', 'SharedWorker.cpp', 'URL.cpp', 'WorkerDebuggerManager.cpp', diff --git a/dom/workers/test/serviceworkers/close_test.js b/dom/workers/test/serviceworkers/close_test.js new file mode 100644 index 0000000000..6138d6421e --- /dev/null +++ b/dom/workers/test/serviceworkers/close_test.js @@ -0,0 +1,19 @@ +function ok(v, msg) { + client.postMessage({status: "ok", result: !!v, message: msg}); +} + +var client; +onmessage = function(e) { + if (e.data.message == "start") { + self.clients.matchAll().then(function(clients) { + client = clients[0]; + try { + close(); + ok(false, "close() should throw"); + } catch (e) { + ok(e.name === "InvalidAccessError", "close() should throw InvalidAccessError"); + } + client.postMessage({status: "done"}); + }); + } +} diff --git a/dom/workers/test/serviceworkers/fetch/https/clonedresponse/https_test.js b/dom/workers/test/serviceworkers/fetch/https/clonedresponse/https_test.js new file mode 100644 index 0000000000..48f7b93073 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/https/clonedresponse/https_test.js @@ -0,0 +1,15 @@ +self.addEventListener("install", function(event) { + event.waitUntil(caches.open("cache").then(function(cache) { + return cache.add("index.html"); + })); +}); + +self.addEventListener("fetch", function(event) { + if (event.request.url.indexOf("index.html") >= 0) { + event.respondWith(new Promise(function(resolve, reject) { + caches.match(event.request).then(function(response) { + resolve(response.clone()); + }); + })); + } +}); diff --git a/dom/workers/test/serviceworkers/fetch/https/clonedresponse/index.html b/dom/workers/test/serviceworkers/fetch/https/clonedresponse/index.html new file mode 100644 index 0000000000..a435548443 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/https/clonedresponse/index.html @@ -0,0 +1,4 @@ + + diff --git a/dom/workers/test/serviceworkers/fetch/https/clonedresponse/register.html b/dom/workers/test/serviceworkers/fetch/https/clonedresponse/register.html new file mode 100644 index 0000000000..d2f168b8bd --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/https/clonedresponse/register.html @@ -0,0 +1,26 @@ + + diff --git a/dom/workers/test/serviceworkers/fetch/https/clonedresponse/unregister.html b/dom/workers/test/serviceworkers/fetch/https/clonedresponse/unregister.html new file mode 100644 index 0000000000..f811dd3c00 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/https/clonedresponse/unregister.html @@ -0,0 +1,10 @@ + + diff --git a/dom/workers/test/serviceworkers/fetch/https/https_test.js b/dom/workers/test/serviceworkers/fetch/https/https_test.js new file mode 100644 index 0000000000..01c1c72d86 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/https/https_test.js @@ -0,0 +1,11 @@ +self.addEventListener("install", function(event) { + event.waitUntil(caches.open("cache").then(function(cache) { + return cache.add("index.html"); + })); +}); + +self.addEventListener("fetch", function(event) { + if (event.request.url.indexOf("index.html") >= 0) { + event.respondWith(caches.match(event.request)); + } +}); diff --git a/dom/workers/test/serviceworkers/fetch/https/index.html b/dom/workers/test/serviceworkers/fetch/https/index.html new file mode 100644 index 0000000000..a435548443 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/https/index.html @@ -0,0 +1,4 @@ + + diff --git a/dom/workers/test/serviceworkers/fetch/https/register.html b/dom/workers/test/serviceworkers/fetch/https/register.html new file mode 100644 index 0000000000..d2f168b8bd --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/https/register.html @@ -0,0 +1,26 @@ + + diff --git a/dom/workers/test/serviceworkers/fetch/https/unregister.html b/dom/workers/test/serviceworkers/fetch/https/unregister.html new file mode 100644 index 0000000000..f811dd3c00 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/https/unregister.html @@ -0,0 +1,10 @@ + + diff --git a/dom/workers/test/serviceworkers/match_all_client/match_all_client_id.html b/dom/workers/test/serviceworkers/match_all_client/match_all_client_id.html new file mode 100644 index 0000000000..7ac6fc9d05 --- /dev/null +++ b/dom/workers/test/serviceworkers/match_all_client/match_all_client_id.html @@ -0,0 +1,31 @@ + + + + + Bug 1139425 - controlled page + + + + + + diff --git a/dom/workers/test/serviceworkers/match_all_client_id_worker.js b/dom/workers/test/serviceworkers/match_all_client_id_worker.js new file mode 100644 index 0000000000..a7d9ff594e --- /dev/null +++ b/dom/workers/test/serviceworkers/match_all_client_id_worker.js @@ -0,0 +1,28 @@ +onmessage = function(e) { + dump("MatchAllClientIdWorker:" + e.data + "\n"); + var id = []; + var iterations = 5; + var counter = 0; + + for (var i = 0; i < iterations; i++) { + self.clients.matchAll().then(function(res) { + if (!res.length) { + dump("ERROR: no clients are currently controlled.\n"); + } + + client = res[0]; + id[counter] = client.id; + counter++; + if (counter >= iterations) { + var response = true; + for (var index = 1; index < iterations; index++) { + if (id[0] != id[index]) { + response = false; + break; + } + } + client.postMessage(response); + } + }); + } +} diff --git a/dom/workers/test/serviceworkers/match_all_clients/match_all_controlled.html b/dom/workers/test/serviceworkers/match_all_clients/match_all_controlled.html new file mode 100644 index 0000000000..25317b9fc8 --- /dev/null +++ b/dom/workers/test/serviceworkers/match_all_clients/match_all_controlled.html @@ -0,0 +1,65 @@ + + + + + Bug 1058311 - controlled page + + + + + + diff --git a/dom/workers/test/serviceworkers/match_all_properties_worker.js b/dom/workers/test/serviceworkers/match_all_properties_worker.js new file mode 100644 index 0000000000..f007a5ce8e --- /dev/null +++ b/dom/workers/test/serviceworkers/match_all_properties_worker.js @@ -0,0 +1,20 @@ +onmessage = function(e) { + dump("MatchAllPropertiesWorker:" + e.data + "\n"); + self.clients.matchAll().then(function(res) { + if (!res.length) { + dump("ERROR: no clients are currently controlled.\n"); + } + + for (i = 0; i < res.length; i++) { + client = res[i]; + response = { + id: client.id, + url: client.url, + visibilityState: client.visibilityState, + focused: client.focused, + frameType: client.frameType + }; + client.postMessage(response); + } + }); +} diff --git a/dom/workers/test/serviceworkers/message_receiver.html b/dom/workers/test/serviceworkers/message_receiver.html new file mode 100644 index 0000000000..82cb587c72 --- /dev/null +++ b/dom/workers/test/serviceworkers/message_receiver.html @@ -0,0 +1,6 @@ + + diff --git a/dom/workers/test/serviceworkers/mochitest.ini b/dom/workers/test/serviceworkers/mochitest.ini index 00920aaa44..cb92dd14ae 100644 --- a/dom/workers/test/serviceworkers/mochitest.ini +++ b/dom/workers/test/serviceworkers/mochitest.ini @@ -24,11 +24,30 @@ support-files = fetch/index.html fetch/fetch_worker_script.js fetch/fetch_tests.js + fetch/https/index.html + fetch/https/register.html + fetch/https/unregister.html + fetch/https/https_test.js + fetch/https/clonedresponse/index.html + fetch/https/clonedresponse/register.html + fetch/https/clonedresponse/unregister.html + fetch/https/clonedresponse/https_test.js + match_all_properties_worker.js + match_all_clients/match_all_controlled.html + test_serviceworker_interfaces.js + serviceworker_wrapper.js + message_receiver.html + close_test.js + serviceworker_not_sharedworker.js + match_all_client/match_all_client_id.html + match_all_client_id_worker.js [test_unregister.html] skip-if = true # Bug 1133805 [test_installation_simple.html] [test_fetch_event.html] +[test_https_fetch.html] +[test_https_fetch_cloned_response.html] [test_match_all.html] [test_install_event.html] [test_navigator.html] @@ -40,3 +59,8 @@ skip-if = true # Bug 1133805 skip-if = true # Bug 1133805 [test_post_message.html] [test_post_message_advanced.html] +[test_match_all_client_properties.html] +[test_close.html] +[test_serviceworker_interfaces.html] +[test_serviceworker_not_sharedworker.html] +[test_match_all_client_id.html] diff --git a/dom/workers/test/serviceworkers/serviceworker_not_sharedworker.js b/dom/workers/test/serviceworkers/serviceworker_not_sharedworker.js new file mode 100644 index 0000000000..077da2366d --- /dev/null +++ b/dom/workers/test/serviceworkers/serviceworker_not_sharedworker.js @@ -0,0 +1,21 @@ +function OnMessage(e) +{ + if (e.data.msg == "whoareyou") { + if ("ServiceWorker" in self) { + self.clients.matchAll().then(function(clients) { + clients[0].postMessage({result: "serviceworker"}); + }); + } else { + port.postMessage({result: "sharedworker"}); + } + } +}; + +var port; +onconnect = function(e) { + port = e.ports[0]; + port.onmessage = OnMessage; + port.start(); +}; + +onmessage = OnMessage; diff --git a/dom/workers/test/serviceworkers/serviceworker_wrapper.js b/dom/workers/test/serviceworkers/serviceworker_wrapper.js new file mode 100644 index 0000000000..3184509489 --- /dev/null +++ b/dom/workers/test/serviceworkers/serviceworker_wrapper.js @@ -0,0 +1,131 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/publicdomain/zero/1.0/ +// +// ServiceWorker equivalent of worker_wrapper.js. + +var client; + +function ok(a, msg) { + dump("OK: " + !!a + " => " + a + ": " + msg + "\n"); + client.postMessage({type: 'status', status: !!a, msg: a + ": " + msg }); +} + +function is(a, b, msg) { + dump("IS: " + (a===b) + " => " + a + " | " + b + ": " + msg + "\n"); + client.postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg }); +} + +function workerTestArrayEquals(a, b) { + if (!Array.isArray(a) || !Array.isArray(b) || a.length != b.length) { + return false; + } + for (var i = 0, n = a.length; i < n; ++i) { + if (a[i] !== b[i]) { + return false; + } + } + return true; +} + +function workerTestDone() { + client.postMessage({ type: 'finish' }); +} + +function workerTestGetPrefs(prefs, cb) { + addEventListener('message', function workerTestGetPrefsCB(e) { + if (e.data.type != 'returnPrefs' || + !workerTestArrayEquals(prefs, e.data.prefs)) { + return; + } + removeEventListener('message', workerTestGetPrefsCB); + cb(e.data.result); + }); + client.postMessage({ + type: 'getPrefs', + prefs: prefs + }); +} + +function workerTestGetPermissions(permissions, cb) { + addEventListener('message', function workerTestGetPermissionsCB(e) { + if (e.data.type != 'returnPermissions' || + !workerTestArrayEquals(permissions, e.data.permissions)) { + return; + } + removeEventListener('message', workerTestGetPermissionsCB); + cb(e.data.result); + }); + client.postMessage({ + type: 'getPermissions', + permissions: permissions + }); +} + +function workerTestGetVersion(cb) { + addEventListener('message', function workerTestGetVersionCB(e) { + if (e.data.type !== 'returnVersion') { + return; + } + removeEventListener('message', workerTestGetVersionCB); + cb(e.data.result); + }); + client.postMessage({ + type: 'getVersion' + }); +} + +function workerTestGetUserAgent(cb) { + addEventListener('message', function workerTestGetUserAgentCB(e) { + if (e.data.type !== 'returnUserAgent') { + return; + } + removeEventListener('message', workerTestGetUserAgentCB); + cb(e.data.result); + }); + client.postMessage({ + type: 'getUserAgent' + }); +} + +function workerTestGetOSCPU(cb) { + addEventListener('message', function workerTestGetOSCPUCB(e) { + if (e.data.type !== 'returnOSCPU') { + return; + } + removeEventListener('message', workerTestGetOSCPUCB); + cb(e.data.result); + }); + client.postMessage({ + type: 'getOSCPU' + }); +} + +function workerTestGetIsB2G(cb) { + addEventListener('message', function workerTestGetIsB2GCB(e) { + if (e.data.type !== 'returnIsB2G') { + return; + } + removeEventListener('message', workerTestGetIsB2GCB); + cb(e.data.result); + }); + client.postMessage({ + type: 'getIsB2G' + }); +} + +addEventListener('message', function workerWrapperOnMessage(e) { + removeEventListener('message', workerWrapperOnMessage); + var data = e.data; + self.clients.matchAll().then(function(clients) { + client = clients[0]; + try { + importScripts(data.script); + } catch(e) { + client.postMessage({ + type: 'status', + status: false, + msg: 'worker failed to import ' + data.script + "; error: " + e.message + }); + } + }); +}); diff --git a/dom/workers/test/serviceworkers/sw_clients/service_worker_controlled.html b/dom/workers/test/serviceworkers/sw_clients/service_worker_controlled.html index 0a6c3bf51f..e0d7bce573 100644 --- a/dom/workers/test/serviceworkers/sw_clients/service_worker_controlled.html +++ b/dom/workers/test/serviceworkers/sw_clients/service_worker_controlled.html @@ -26,7 +26,7 @@ }); } - window.onmessage = function(msg) { + navigator.serviceWorker.onmessage = function(msg) { // forward message to the test page. parent.postMessage(msg.data, "*"); }; diff --git a/dom/workers/test/serviceworkers/test_close.html b/dom/workers/test/serviceworkers/test_close.html new file mode 100644 index 0000000000..04c9402d78 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_close.html @@ -0,0 +1,69 @@ + + + + + Bug 1131353 - test WorkerGlobalScope.close() on service workers + + + + +

+ +

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_https_fetch.html b/dom/workers/test/serviceworkers/test_https_fetch.html
new file mode 100644
index 0000000000..1c970346b6
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_https_fetch.html
@@ -0,0 +1,56 @@
+
+
+
+
+  Bug 1133763 - test fetch event in HTTPS origins
+  
+  
+
+
+

+ +

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_https_fetch_cloned_response.html b/dom/workers/test/serviceworkers/test_https_fetch_cloned_response.html
new file mode 100644
index 0000000000..c3438bda56
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_https_fetch_cloned_response.html
@@ -0,0 +1,56 @@
+
+
+
+
+  Bug 1133763 - test fetch event in HTTPS origins with a cloned response
+  
+  
+
+
+

+ +

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_match_all_client_id.html b/dom/workers/test/serviceworkers/test_match_all_client_id.html
new file mode 100644
index 0000000000..36b58dc835
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_match_all_client_id.html
@@ -0,0 +1,86 @@
+
+
+
+
+  Bug 1058311 - Test matchAll client id 
+  
+  
+
+
+

+ +

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_match_all_client_properties.html b/dom/workers/test/serviceworkers/test_match_all_client_properties.html
new file mode 100644
index 0000000000..394566ce5b
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_match_all_client_properties.html
@@ -0,0 +1,93 @@
+
+
+
+
+  Bug 1058311 - Test matchAll clients properties 
+  
+  
+
+
+

+ +

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.html b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.html
new file mode 100644
index 0000000000..27d053601e
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.html
@@ -0,0 +1,113 @@
+
+
+
+
+  Validate Interfaces Exposed to Service Workers
+  
+  
+  
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js
new file mode 100644
index 0000000000..818daf6eb7
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js
@@ -0,0 +1,280 @@
+// This is a list of all interfaces that are exposed to workers.
+// Please only add things to this list with great care and proper review
+// from the associated module peers.
+
+// This file lists global interfaces we want exposed and verifies they
+// are what we intend. Each entry in the arrays below can either be a
+// simple string with the interface name, or an object with a 'name'
+// property giving the interface name as a string, and additional
+// properties which qualify the exposure of that interface. For example:
+//
+// [
+//   "AGlobalInterface",
+//   {name: "ExperimentalThing", release: false},
+//   {name: "OptionalThing", pref: "some.thing.enabled"},
+// ];
+//
+// See createInterfaceMap() below for a complete list of properties.
+
+// IMPORTANT: Do not change this list without review from
+//            a JavaScript Engine peer!
+var ecmaGlobals =
+  [
+    "Array",
+    "ArrayBuffer",
+    "Boolean",
+    "DataView",
+    "Date",
+    "Error",
+    "EvalError",
+    "Float32Array",
+    "Float64Array",
+    "Function",
+    "Infinity",
+    "Int16Array",
+    "Int32Array",
+    "Int8Array",
+    "InternalError",
+    {name: "Intl", b2g: false, android: false},
+    "Iterator",
+    "JSON",
+    "Map",
+    "Math",
+    "NaN",
+    "Number",
+    "Object",
+    "Proxy",
+    "RangeError",
+    "ReferenceError",
+    "RegExp",
+    "Set",
+    {name: "SharedArrayBuffer", nightly: true},
+    {name: "SharedInt8Array", nightly: true},
+    {name: "SharedUint8Array", nightly: true},
+    {name: "SharedUint8ClampedArray", nightly: true},
+    {name: "SharedInt16Array", nightly: true},
+    {name: "SharedUint16Array", nightly: true},
+    {name: "SharedInt32Array", nightly: true},
+    {name: "SharedUint32Array", nightly: true},
+    {name: "SharedFloat32Array", nightly: true},
+    {name: "SharedFloat64Array", nightly: true},
+    {name: "SIMD", nightly: true},
+    {name: "Atomics", nightly: true},
+    "StopIteration",
+    "String",
+    "Symbol",
+    "SyntaxError",
+    {name: "TypedObject", nightly: true},
+    "TypeError",
+    "Uint16Array",
+    "Uint32Array",
+    "Uint8Array",
+    "Uint8ClampedArray",
+    "URIError",
+    "WeakMap",
+    "WeakSet",
+  ];
+// IMPORTANT: Do not change the list above without review from
+//            a JavaScript Engine peer!
+
+// IMPORTANT: Do not change the list below without review from a DOM peer!
+var interfaceNamesInGlobalScope =
+  [
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "Blob",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    { name: "BroadcastChannel", pref: "dom.broadcastChannel.enabled" },
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    { name: "Cache", pref: "dom.caches.enabled" },
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    { name: "CacheStorage", pref: "dom.caches.enabled" },
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "Client",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "Clients",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    { name: "DataStore", b2g: true },
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    { name: "DataStoreCursor", b2g: true },
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "DOMError",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "DOMException",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "DOMStringList",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "Event",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "EventTarget",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "ExtendableEvent",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "FetchEvent",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "File",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "FileReaderSync",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    { name: "Headers", pref: "dom.fetch.enabled" },
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBCursor",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBDatabase",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBFactory",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBIndex",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBKeyRange",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBObjectStore",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBOpenDBRequest",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBRequest",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBTransaction",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBVersionChangeEvent",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "ImageData",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "InstallEvent",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "MessageEvent",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "MessagePort",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "Performance",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "Promise",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "Request",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "Response",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "ServiceWorker",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "ServiceWorkerGlobalScope",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "TextDecoder",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "TextEncoder",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "XMLHttpRequest",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "XMLHttpRequestEventTarget",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "XMLHttpRequestUpload",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "URL",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "URLSearchParams",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+   { name: "WebSocket", pref: "dom.workers.websocket.enabled" },
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "WindowClient",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "WorkerGlobalScope",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "WorkerLocation",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "WorkerNavigator",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+  ];
+// IMPORTANT: Do not change the list above without review from a DOM peer!
+
+function createInterfaceMap(prefMap, permissionMap, version, userAgent, isB2G) {
+  var isNightly = version.endsWith("a1");
+  var isRelease = !version.contains("a");
+  var isDesktop = !/Mobile|Tablet/.test(userAgent);
+  var isAndroid = !!navigator.userAgent.contains("Android");
+
+  var interfaceMap = {};
+
+  function addInterfaces(interfaces)
+  {
+    for (var entry of interfaces) {
+      if (typeof(entry) === "string") {
+        interfaceMap[entry] = true;
+      } else if ((entry.nightly === !isNightly) ||
+                 (entry.desktop === !isDesktop) ||
+                 (entry.android === !isAndroid) ||
+                 (entry.b2g === !isB2G) ||
+                 (entry.release === !isRelease) ||
+                 (entry.pref && !prefMap[entry.pref])  ||
+                 (entry.permission && !permissionMap[entry.permission])) {
+        interfaceMap[entry.name] = false;
+      } else {
+        interfaceMap[entry.name] = true;
+      }
+    }
+  }
+
+  addInterfaces(ecmaGlobals);
+  addInterfaces(interfaceNamesInGlobalScope);
+
+  return interfaceMap;
+}
+
+function runTest(prefMap, permissionMap, version, userAgent, isB2G) {
+  var interfaceMap = createInterfaceMap(prefMap, permissionMap, version, userAgent, isB2G);
+  for (var name of Object.getOwnPropertyNames(self)) {
+    // An interface name should start with an upper case character.
+    if (!/^[A-Z]/.test(name)) {
+      continue;
+    }
+    ok(interfaceMap[name],
+       "If this is failing: DANGER, are you sure you want to expose the new interface " + name +
+       " to all webpages as a property on the service worker? Do not make a change to this file without a " +
+       " review from a DOM peer for that specific change!!! (or a JS peer for changes to ecmaGlobals)");
+    delete interfaceMap[name];
+  }
+  for (var name of Object.keys(interfaceMap)) {
+    ok(name in self === interfaceMap[name],
+       name + " should " + (interfaceMap[name] ? "" : " NOT") + " be defined on the global scope");
+    if (!interfaceMap[name]) {
+      delete interfaceMap[name];
+    }
+  }
+  is(Object.keys(interfaceMap).length, 0,
+     "The following interface(s) are not enumerated: " + Object.keys(interfaceMap).join(", "));
+}
+
+function appendPrefs(prefs, interfaces) {
+  for (var entry of interfaces) {
+    if (entry.pref !== undefined && prefs.indexOf(entry.pref) === -1) {
+      prefs.push(entry.pref);
+    }
+  }
+}
+
+var prefs = [];
+appendPrefs(prefs, ecmaGlobals);
+appendPrefs(prefs, interfaceNamesInGlobalScope);
+
+function appendPermissions(permissions, interfaces) {
+  for (var entry of interfaces) {
+    if (entry.permission !== undefined &&
+        permissions.indexOf(entry.permission) === -1) {
+      permissions.push(entry.permission);
+    }
+  }
+}
+
+var permissions = [];
+appendPermissions(permissions, ecmaGlobals);
+appendPermissions(permissions, interfaceNamesInGlobalScope);
+
+workerTestGetPrefs(prefs, function(prefMap) {
+  workerTestGetPermissions(permissions, function(permissionMap) {
+    workerTestGetVersion(function(version) {
+      workerTestGetUserAgent(function(userAgent) {
+        workerTestGetIsB2G(function(isB2G) {
+          runTest(prefMap, permissionMap, version, userAgent, isB2G);
+          workerTestDone();
+	});
+      });
+    });
+  });
+});
diff --git a/dom/workers/test/serviceworkers/test_serviceworker_not_sharedworker.html b/dom/workers/test/serviceworkers/test_serviceworker_not_sharedworker.html
new file mode 100644
index 0000000000..5fc433bcbe
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_serviceworker_not_sharedworker.html
@@ -0,0 +1,72 @@
+
+
+
+
+  Bug 1141274 - test that service workers and shared workers are separate
+  
+  
+
+
+

+ +

+
+
+
+
diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp
index c978849e02..99a14b8040 100644
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -2513,7 +2513,7 @@ Parser::functionArgsAndBodyGeneric(Node pn, HandleFunction fun, Fu
     if (!body)
         return false;
 
-    if (kind != Method && kind != Lazy && 
+    if (kind != Method && kind != Lazy &&
         fun->name() && !checkStrictBinding(fun->name(), pn))
     {
         return false;
@@ -6232,6 +6232,15 @@ Parser::assignExpr(InvokedPrediction invoked)
       case TOK_POWASSIGN:    kind = PNK_POWASSIGN;    op = JSOP_POW;    break;
 
       case TOK_ARROW: {
+        // A line terminator between ArrowParameters and the => should trigger a SyntaxError.
+        tokenStream.ungetToken();
+        TokenKind next;
+        if (!tokenStream.peekTokenSameLine(&next) || next != TOK_ARROW) {
+            report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
+                   "expression", TokenKindToDesc(TOK_ARROW));
+            return null();
+        }
+
         tokenStream.seek(start);
         if (!abortIfSyntaxParser())
             return null();
@@ -8248,7 +8257,7 @@ Parser::primaryExpr(TokenKind tt, InvokedPrediction invoked)
             return null();
         }
 
-        if (!tokenStream.peekToken(&next))
+        if (!tokenStream.peekTokenSameLine(&next))
             return null();
         if (next != TOK_ARROW) {
             report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
diff --git a/js/src/tests/ecma_6/Function/line-terminator-before-arrow.js b/js/src/tests/ecma_6/Function/line-terminator-before-arrow.js
new file mode 100644
index 0000000000..52bdce7f3b
--- /dev/null
+++ b/js/src/tests/ecma_6/Function/line-terminator-before-arrow.js
@@ -0,0 +1,9 @@
+assertThrowsInstanceOf(() => eval("() \n => {}"), SyntaxError);
+assertThrowsInstanceOf(() => eval("a \n => {}"), SyntaxError);
+assertThrowsInstanceOf(() => eval("(a) /*\n*/ => {}"), SyntaxError);
+assertThrowsInstanceOf(() => eval("(a, b) \n => {}"), SyntaxError);
+assertThrowsInstanceOf(() => eval("(a, b = 1) \n => {}"), SyntaxError);
+assertThrowsInstanceOf(() => eval("(a, ...b) \n => {}"), SyntaxError);
+assertThrowsInstanceOf(() => eval("(a, b = 1, ...c) \n => {}"), SyntaxError);
+
+reportCompare(0, 0, "ok");
diff --git a/layout/style/generate-stylestructlist.py b/layout/style/generate-stylestructlist.py
index 3d4d271c08..33239a3ad0 100755
--- a/layout/style/generate-stylestructlist.py
+++ b/layout/style/generate-stylestructlist.py
@@ -64,7 +64,7 @@ STYLE_STRUCTS = [("INHERITED",) + x for x in [
     ("Background",     "nullptr",   NORMAL_DEP + LENGTH_DEP + COLOR_DEP),
     ("Position",       "nullptr",   NORMAL_DEP + LENGTH_DEP),
     ("TextReset",      "nullptr",   NORMAL_DEP + LENGTH_DEP + COLOR_DEP),
-    ("Display",        "nullptr",   NORMAL_DEP),
+    ("Display",        "nullptr",   NORMAL_DEP + LENGTH_DEP),
     ("Content",        "nullptr",   NORMAL_DEP + LENGTH_DEP),
     ("UIReset",        "nullptr",   NORMAL_DEP),
     ("Table",          "nullptr",   NORMAL_DEP),
diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp
index b27e408366..553bb5c067 100644
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -14961,6 +14961,7 @@ CSSParserImpl::ParseScrollSnapPoints(nsCSSValue& aValue, nsCSSProperty aPropID)
     functionArray->Item(1) = lengthValue;
     return true;
   }
+  UngetToken();
   return false;
 }
 
diff --git a/layout/style/nsCSSPropList.h b/layout/style/nsCSSPropList.h
index 6a9df0c68b..aacddf16a2 100644
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -3071,31 +3071,30 @@ CSS_PROP_DISPLAY(
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
 CSS_PROP_DISPLAY(
-    scroll-snap-type-x,
-    scroll_snap_type_x,
-    ScrollSnapTypeX,
-    CSS_PROPERTY_PARSE_VALUE,
+    scroll-snap-coordinate,
+    scroll_snap_coordinate,
+    ScrollSnapCoordinate,
+    CSS_PROPERTY_PARSE_VALUE |
+        CSS_PROPERTY_VALUE_PARSER_FUNCTION |
+        CSS_PROPERTY_VALUE_LIST_USES_COMMAS |
+        CSS_PROPERTY_STORES_CALC,
     "layout.css.scroll-snap.enabled",
-    VARIANT_HK,
-    kScrollSnapTypeKTable,
+    0,
+    kBackgroundPositionKTable,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
 CSS_PROP_DISPLAY(
-    scroll-snap-type-y,
-    scroll_snap_type_y,
-    ScrollSnapTypeY,
-    CSS_PROPERTY_PARSE_VALUE,
+    scroll-snap-destination,
+    scroll_snap_destination,
+    ScrollSnapDestination,
+    CSS_PROPERTY_PARSE_VALUE |
+        CSS_PROPERTY_VALUE_PARSER_FUNCTION |
+        CSS_PROPERTY_STORES_CALC,
     "layout.css.scroll-snap.enabled",
-    VARIANT_HK,
-    kScrollSnapTypeKTable,
+    0,
+    kBackgroundPositionKTable,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
-CSS_PROP_SHORTHAND(
-    scroll-snap-type,
-    scroll_snap_type,
-    ScrollSnapType,
-    CSS_PROPERTY_PARSE_FUNCTION,
-    "layout.css.scroll-snap.enabled")
 CSS_PROP_DISPLAY(
     scroll-snap-points-x,
     scroll_snap_points_x,
@@ -3120,29 +3119,30 @@ CSS_PROP_DISPLAY(
     nullptr,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
+CSS_PROP_SHORTHAND(
+    scroll-snap-type,
+    scroll_snap_type,
+    ScrollSnapType,
+    CSS_PROPERTY_PARSE_FUNCTION,
+    "layout.css.scroll-snap.enabled")
 CSS_PROP_DISPLAY(
-    scroll-snap-destination,
-    scroll_snap_destination,
-    ScrollSnapDestination,
-    CSS_PROPERTY_PARSE_VALUE |
-        CSS_PROPERTY_VALUE_PARSER_FUNCTION |
-        CSS_PROPERTY_STORES_CALC,
+    scroll-snap-type-x,
+    scroll_snap_type_x,
+    ScrollSnapTypeX,
+    CSS_PROPERTY_PARSE_VALUE,
     "layout.css.scroll-snap.enabled",
-    0,
-    kBackgroundPositionKTable,
+    VARIANT_HK,
+    kScrollSnapTypeKTable,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
 CSS_PROP_DISPLAY(
-    scroll-snap-coordinate,
-    scroll_snap_coordinate,
-    ScrollSnapCoordinate,
-    CSS_PROPERTY_PARSE_VALUE |
-        CSS_PROPERTY_VALUE_PARSER_FUNCTION |
-        CSS_PROPERTY_VALUE_LIST_USES_COMMAS |
-        CSS_PROPERTY_STORES_CALC,
+    scroll-snap-type-y,
+    scroll_snap_type_y,
+    ScrollSnapTypeY,
+    CSS_PROPERTY_PARSE_VALUE,
     "layout.css.scroll-snap.enabled",
-    0,
-    kBackgroundPositionKTable,
+    VARIANT_HK,
+    kScrollSnapTypeKTable,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
 CSS_PROP_BACKENDONLY(
diff --git a/layout/style/nsComputedDOMStylePropertyList.h b/layout/style/nsComputedDOMStylePropertyList.h
index baf4c55fe3..5f78b5117d 100644
--- a/layout/style/nsComputedDOMStylePropertyList.h
+++ b/layout/style/nsComputedDOMStylePropertyList.h
@@ -209,12 +209,12 @@ COMPUTED_STYLE_PROP(right,                         Right)
 COMPUTED_STYLE_PROP(ruby_align,                    RubyAlign)
 COMPUTED_STYLE_PROP(ruby_position,                 RubyPosition)
 COMPUTED_STYLE_PROP(scroll_behavior,               ScrollBehavior)
-COMPUTED_STYLE_PROP(scroll_snap_type_x,            ScrollSnapTypeX)
-COMPUTED_STYLE_PROP(scroll_snap_type_y,            ScrollSnapTypeY)
+COMPUTED_STYLE_PROP(scroll_snap_coordinate,        ScrollSnapCoordinate)
+COMPUTED_STYLE_PROP(scroll_snap_destination,       ScrollSnapDestination)
 COMPUTED_STYLE_PROP(scroll_snap_points_x,          ScrollSnapPointsX)
 COMPUTED_STYLE_PROP(scroll_snap_points_y,          ScrollSnapPointsY)
-COMPUTED_STYLE_PROP(scroll_snap_destination,       ScrollSnapDestination)
-COMPUTED_STYLE_PROP(scroll_snap_coordinate,        ScrollSnapCoordinate)
+COMPUTED_STYLE_PROP(scroll_snap_type_x,            ScrollSnapTypeX)
+COMPUTED_STYLE_PROP(scroll_snap_type_y,            ScrollSnapTypeY)
 //// COMPUTED_STYLE_PROP(size,                     Size)
 COMPUTED_STYLE_PROP(table_layout,                  TableLayout)
 COMPUTED_STYLE_PROP(text_align,                    TextAlign)
diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp
index bb74403053..964205deba 100644
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -5260,10 +5260,11 @@ nsRuleNode::ComputeDisplayData(void* aStartStruct,
   // scroll-snap-points-x: none, inherit, initial
   const nsCSSValue& scrollSnapPointsX = *aRuleData->ValueForScrollSnapPointsX();
   switch (scrollSnapPointsX.GetUnit()) {
+    case eCSSUnit_Null:
+      break;
     case eCSSUnit_Initial:
     case eCSSUnit_Unset:
     case eCSSUnit_None:
-    case eCSSUnit_Null:
       display->mScrollSnapPointsX.SetNoneValue();
       break;
     case eCSSUnit_Inherit:
@@ -5294,10 +5295,11 @@ nsRuleNode::ComputeDisplayData(void* aStartStruct,
   // scroll-snap-points-y: none, inherit, initial
   const nsCSSValue& scrollSnapPointsY = *aRuleData->ValueForScrollSnapPointsY();
   switch (scrollSnapPointsY.GetUnit()) {
+    case eCSSUnit_Null:
+      break;
     case eCSSUnit_Initial:
     case eCSSUnit_Unset:
     case eCSSUnit_None:
-    case eCSSUnit_Null:
       display->mScrollSnapPointsY.SetNoneValue();
       break;
     case eCSSUnit_Inherit:
@@ -5328,9 +5330,10 @@ nsRuleNode::ComputeDisplayData(void* aStartStruct,
   // scroll-snap-destination: inherit, initial
   const nsCSSValue& snapDestination = *aRuleData->ValueForScrollSnapDestination();
   switch (snapDestination.GetUnit()) {
+    case eCSSUnit_Null:
+      break;
     case eCSSUnit_Initial:
     case eCSSUnit_Unset:
-    case eCSSUnit_Null:
       display->mScrollSnapDestination.SetInitialZeroValues();
       break;
     case eCSSUnit_Inherit:
@@ -5348,10 +5351,11 @@ nsRuleNode::ComputeDisplayData(void* aStartStruct,
 
   const nsCSSValue& snapCoordinate = *aRuleData->ValueForScrollSnapCoordinate();
   switch (snapCoordinate.GetUnit()) {
+    case eCSSUnit_Null:
+      break;
     case eCSSUnit_Initial:
     case eCSSUnit_Unset:
     case eCSSUnit_None:
-    case eCSSUnit_Null:
       // Unset and Initial is none, indicated by an empty array
       display->mScrollSnapCoordinate.Clear();
       break;
diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js
index eed2b2a8ad..8cd956aeea 100644
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -6436,64 +6436,6 @@ if (SpecialPowers.getBoolPref("layout.css.scroll-behavior.property-enabled")) {
 }
 
 if (SpecialPowers.getBoolPref("layout.css.scroll-snap.enabled")) {
-  gCSSProperties["scroll-snap-type-x"] = {
-    domProp: "scrollSnapTypeX",
-    inherited: false,
-    type: CSS_TYPE_LONGHAND,
-    initial_values: [ "none" ],
-    other_values: ["mandatory", "proximity"],
-    invalid_values: [ "auto",  "1px" ]
-  };
-  gCSSProperties["scroll-snap-type-y"] = {
-    domProp: "scrollSnapTypeY",
-    inherited: false,
-    type: CSS_TYPE_LONGHAND,
-    initial_values: [ "none" ],
-    other_values: ["mandatory", "proximity"],
-    invalid_values: [ "auto",  "1px" ]
-  };
-  gCSSProperties["scroll-snap-type"] = {
-    domProp: "scrollSnapType",
-    inherited: false,
-    type: CSS_TYPE_TRUE_SHORTHAND,
-    subproperties: [ "scroll-snap-type-x", "scroll-snap-type-y" ],
-    initial_values: [ "none" ],
-    other_values: [ "mandatory", "proximity" ],
-    invalid_values: [ "auto",  "1px" ]
-  };
-  gCSSProperties["scroll-snap-points-x"] = {
-    domProp: "scrollSnapPointsX",
-    inherited: false,
-    type: CSS_TYPE_LONGHAND,
-    initial_values: [ "none" ],
-    other_values: [ "repeat(100%)", "repeat(120px)", "repeat(calc(3*25px))" ],
-    invalid_values: [ "auto", "1px", "left", "rgb(1,2,3)" ]
-  }
-  gCSSProperties["scroll-snap-points-y"] = {
-    domProp: "scrollSnapPointsY",
-    inherited: false,
-    type: CSS_TYPE_LONGHAND,
-    initial_values: [ "none" ],
-    other_values: [ "repeat(100%)", "repeat(120px)", "repeat(calc(3*25px))" ],
-    invalid_values: [ "auto", "1px", "top", "rgb(1,2,3)" ]
-  }
-  gCSSProperties["scroll-snap-destination"] = {
-    domProp: "scrollSnapDestination",
-    inherited: false,
-    type: CSS_TYPE_LONGHAND,
-    initial_values: [ "0px 0px" ],
-    other_values: [ "25% 25%", "6px 5px", "20% 3em", "0 0", "0in 1in",
-                    "top", "right", "top left", "top right", "center",
-                    "calc(2px)",
-                    "calc(50%)",
-                    "calc(3*25px)",
-                    "calc(3*25px) 5px",
-                    "5px calc(3*25px)",
-                    "calc(20%) calc(3*25px)",
-                    "calc(25px*3)",
-                    "calc(3*25px + 50%)"],
-    invalid_values: [ "auto", "none", "default" ]
-  }
   gCSSProperties["scroll-snap-coordinate"] = {
     domProp: "scrollSnapCoordinate",
     inherited: false,
@@ -6512,6 +6454,64 @@ if (SpecialPowers.getBoolPref("layout.css.scroll-snap.enabled")) {
                     "calc(20%) calc(3*25px), center"],
     invalid_values: [ "auto", "default" ]
   }
+  gCSSProperties["scroll-snap-destination"] = {
+    domProp: "scrollSnapDestination",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "0px 0px" ],
+    other_values: [ "25% 25%", "6px 5px", "20% 3em", "0in 1in",
+                    "top", "right", "top left", "top right", "center",
+                    "calc(2px)",
+                    "calc(50%)",
+                    "calc(3*25px)",
+                    "calc(3*25px) 5px",
+                    "5px calc(3*25px)",
+                    "calc(20%) calc(3*25px)",
+                    "calc(25px*3)",
+                    "calc(3*25px + 50%)"],
+    invalid_values: [ "auto", "none", "default" ]
+  }
+  gCSSProperties["scroll-snap-points-x"] = {
+    domProp: "scrollSnapPointsX",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: [ "repeat(100%)", "repeat(120px)", "repeat(calc(3*25px))" ],
+    invalid_values: [ "auto", "1px", "left", "rgb(1,2,3)" ]
+  }
+  gCSSProperties["scroll-snap-points-y"] = {
+    domProp: "scrollSnapPointsY",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: [ "repeat(100%)", "repeat(120px)", "repeat(calc(3*25px))" ],
+    invalid_values: [ "auto", "1px", "top", "rgb(1,2,3)" ]
+  }
+  gCSSProperties["scroll-snap-type"] = {
+    domProp: "scrollSnapType",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    subproperties: [ "scroll-snap-type-x", "scroll-snap-type-y" ],
+    initial_values: [ "none" ],
+    other_values: [ "mandatory", "proximity" ],
+    invalid_values: [ "auto",  "1px" ]
+  };
+  gCSSProperties["scroll-snap-type-x"] = {
+    domProp: "scrollSnapTypeX",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: ["mandatory", "proximity"],
+    invalid_values: [ "auto",  "1px" ]
+  };
+  gCSSProperties["scroll-snap-type-y"] = {
+    domProp: "scrollSnapTypeY",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: ["mandatory", "proximity"],
+    invalid_values: [ "auto",  "1px" ]
+  };
 }
 
 if (SpecialPowers.getBoolPref("layout.css.unset-value.enabled")) {
diff --git a/netwerk/base/moz.build b/netwerk/base/moz.build
index deedf768cc..54aecf7c95 100644
--- a/netwerk/base/moz.build
+++ b/netwerk/base/moz.build
@@ -147,6 +147,7 @@ EXPORTS += [
     'nsMIMEInputStream.h',
     'nsNetUtil.h',
     'nsReadLine.h',
+    'nsSerializationHelper.h',
     'nsStreamListenerWrapper.h',
     'nsTemporaryFileInputStream.h',
     'nsURIHashKey.h',
diff --git a/netwerk/base/nsINetworkInterceptController.idl b/netwerk/base/nsINetworkInterceptController.idl
index 576082c245..c87a1cedc0 100644
--- a/netwerk/base/nsINetworkInterceptController.idl
+++ b/netwerk/base/nsINetworkInterceptController.idl
@@ -16,7 +16,7 @@ interface nsIURI;
  * which do not implement nsIChannel.
  */
 
-[scriptable, uuid(9d127b63-dfad-484d-a0e1-cb82697a095b)]
+[scriptable, uuid(155378c1-ebb2-4492-917f-85483430d5f5)]
 interface nsIInterceptedChannel : nsISupports
 {
     /**
@@ -59,6 +59,11 @@ interface nsIInterceptedChannel : nsISupports
      * True if the underlying request was caused by a navigation attempt.
      */
     readonly attribute bool isNavigation;
+
+    /**
+     * This method allows to override the security info for the channel.
+     */
+    void setSecurityInfo(in nsISupports securityInfo);
 };
 
 /**
diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp
index 3c7c882145..67e5c0debf 100644
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -1419,6 +1419,19 @@ HttpBaseChannel::SetRedirectionLimit(uint32_t value)
   return NS_OK;
 }
 
+nsresult
+HttpBaseChannel::OverrideSecurityInfo(nsISupports* aSecurityInfo)
+{
+  MOZ_RELEASE_ASSERT(!mSecurityInfo,
+                     "This can only be called when we don't have a security info object already");
+  MOZ_RELEASE_ASSERT(aSecurityInfo,
+                     "This can only be called with a valid security info object");
+  MOZ_RELEASE_ASSERT(ShouldIntercept(),
+                     "This can only be called on channels that can be intercepted");
+  mSecurityInfo = aSecurityInfo;
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 HttpBaseChannel::IsNoStoreResponse(bool *value)
 {
diff --git a/netwerk/protocol/http/HttpBaseChannel.h b/netwerk/protocol/http/HttpBaseChannel.h
index 1d94c4d371..70b210b728 100644
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -239,6 +239,8 @@ public:
     const NetAddr& GetSelfAddr() { return mSelfAddr; }
     const NetAddr& GetPeerAddr() { return mPeerAddr; }
 
+    nsresult OverrideSecurityInfo(nsISupports* aSecurityInfo);
+
 public: /* Necko internal use only... */
     bool IsNavigation();
 
@@ -316,6 +318,7 @@ protected:
   nsAutoPtr     mResponseHead;
   nsRefPtr    mConnectionInfo;
   nsCOMPtr            mProxyInfo;
+  nsCOMPtr             mSecurityInfo;
 
   nsCString                         mSpec; // ASCII encoded URL spec
   nsCString                         mContentTypeHint;
diff --git a/netwerk/protocol/http/HttpChannelChild.h b/netwerk/protocol/http/HttpChannelChild.h
index e9a84c4f51..95553d1de9 100644
--- a/netwerk/protocol/http/HttpChannelChild.h
+++ b/netwerk/protocol/http/HttpChannelChild.h
@@ -163,7 +163,6 @@ private:
 
   RequestHeaderTuples mClientSetRequestHeaders;
   nsCOMPtr mRedirectChannelChild;
-  nsCOMPtr mSecurityInfo;
   nsRefPtr mInterceptListener;
   nsRefPtr mSynthesizedResponsePump;
 
diff --git a/netwerk/protocol/http/InterceptedChannel.cpp b/netwerk/protocol/http/InterceptedChannel.cpp
index 9cc4ead8b4..12ed6a27f0 100644
--- a/netwerk/protocol/http/InterceptedChannel.cpp
+++ b/netwerk/protocol/http/InterceptedChannel.cpp
@@ -194,6 +194,12 @@ InterceptedChannelChrome::Cancel()
   return NS_OK;
 }
 
+NS_IMETHODIMP
+InterceptedChannelChrome::SetSecurityInfo(nsISupports* aSecurityInfo)
+{
+  return mChannel->OverrideSecurityInfo(aSecurityInfo);
+}
+
 InterceptedChannelContent::InterceptedChannelContent(HttpChannelChild* aChannel,
                                                      nsINetworkInterceptController* aController,
                                                      nsIStreamListener* aListener)
@@ -290,5 +296,11 @@ InterceptedChannelContent::Cancel()
   return NS_OK;
 }
 
+NS_IMETHODIMP
+InterceptedChannelContent::SetSecurityInfo(nsISupports* aSecurityInfo)
+{
+  return mChannel->OverrideSecurityInfo(aSecurityInfo);
+}
+
 } // namespace net
 } // namespace mozilla
diff --git a/netwerk/protocol/http/InterceptedChannel.h b/netwerk/protocol/http/InterceptedChannel.h
index cf3eb70143..6cc4aaa8c8 100644
--- a/netwerk/protocol/http/InterceptedChannel.h
+++ b/netwerk/protocol/http/InterceptedChannel.h
@@ -75,6 +75,7 @@ public:
   NS_IMETHOD GetChannel(nsIChannel** aChannel) override;
   NS_IMETHOD SynthesizeHeader(const nsACString& aName, const nsACString& aValue) override;
   NS_IMETHOD Cancel() override;
+  NS_IMETHOD SetSecurityInfo(nsISupports* aSecurityInfo) override;
 
   virtual void NotifyController() override;
 };
@@ -103,6 +104,7 @@ public:
   NS_IMETHOD GetChannel(nsIChannel** aChannel) override;
   NS_IMETHOD SynthesizeHeader(const nsACString& aName, const nsACString& aValue) override;
   NS_IMETHOD Cancel() override;
+  NS_IMETHOD SetSecurityInfo(nsISupports* aSecurityInfo) override;
 
   virtual void NotifyController() override;
 };
diff --git a/netwerk/protocol/http/nsHttpChannel.h b/netwerk/protocol/http/nsHttpChannel.h
index ad07732925..f7a249d3c0 100644
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -376,7 +376,6 @@ private:
     void SetPushedStream(Http2PushedStream *stream);
 
 private:
-    nsCOMPtr             mSecurityInfo;
     nsCOMPtr           mProxyRequest;
 
     nsRefPtr       mTransactionPump;