From 790be48aa60feb3bac680d7311e733b5d5031f74 Mon Sep 17 00:00:00 2001 From: Bevis Tseng Date: Wed, 2 Nov 2016 23:12:31 +0100 Subject: [PATCH 01/96] Bug 1309527 - Fix the race condition to prevent the access of metadata in both PBackground thread and the Connection thread. r=janv --- dom/indexedDB/ActorsParent.cpp | 40 +++++++++---------- .../meta/IndexedDB/idbindex-rename.html.ini | 11 ----- .../idbobjectstore-rename-store.html.ini | 22 ---------- 3 files changed, 18 insertions(+), 55 deletions(-) delete mode 100644 testing/web-platform/meta/IndexedDB/idbindex-rename.html.ini delete mode 100644 testing/web-platform/meta/IndexedDB/idbobjectstore-rename-store.html.ini diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp index a037b3b43d13..b2d66ec6ecdb 100644 --- a/dom/indexedDB/ActorsParent.cpp +++ b/dom/indexedDB/ActorsParent.cpp @@ -7915,16 +7915,18 @@ class RenameObjectStoreOp final { friend class VersionChangeTransaction; - const RefPtr mMetadata; + const int64_t mId; + const nsString mNewName; private: // Only created by VersionChangeTransaction. RenameObjectStoreOp(VersionChangeTransaction* aTransaction, FullObjectStoreMetadata* const aMetadata) : VersionChangeTransactionOp(aTransaction) - , mMetadata(aMetadata) + , mId(aMetadata->mCommonMetadata.id()) + , mNewName(aMetadata->mCommonMetadata.name()) { - MOZ_ASSERT(aMetadata->mCommonMetadata.id()); + MOZ_ASSERT(mId); } ~RenameObjectStoreOp() @@ -8109,8 +8111,9 @@ class RenameIndexOp final { friend class VersionChangeTransaction; - const RefPtr mMetadata; const int64_t mObjectStoreId; + const int64_t mIndexId; + const nsString mNewName; private: // Only created by VersionChangeTransaction. @@ -8118,10 +8121,11 @@ private: FullIndexMetadata* const aMetadata, int64_t aObjectStoreId) : VersionChangeTransactionOp(aTransaction) - , mMetadata(aMetadata) , mObjectStoreId(aObjectStoreId) + , mIndexId(aMetadata->mCommonMetadata.id()) + , mNewName(aMetadata->mCommonMetadata.name()) { - MOZ_ASSERT(aMetadata->mCommonMetadata.id()); + MOZ_ASSERT(mIndexId); } ~RenameIndexOp() @@ -24396,12 +24400,10 @@ RenameObjectStoreOp::DoDatabaseWork(DatabaseConnection* aConnection) &stmt)); MOZ_ALWAYS_SUCCEEDS( - stmt->BindStringByName(NS_LITERAL_CSTRING("name"), - mMetadata->mCommonMetadata.name())); + stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mNewName)); MOZ_ALWAYS_SUCCEEDS( - stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), - mMetadata->mCommonMetadata.id())); + stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mId)); bool hasResult; MOZ_ALWAYS_SUCCEEDS(stmt->ExecuteStep(&hasResult)); @@ -24425,15 +24427,13 @@ RenameObjectStoreOp::DoDatabaseWork(DatabaseConnection* aConnection) return rv; } - rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), - mMetadata->mCommonMetadata.name()); + rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mNewName); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), - mMetadata->mCommonMetadata.id()); + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mId); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; @@ -25506,12 +25506,10 @@ RenameIndexOp::DoDatabaseWork(DatabaseConnection* aConnection) mObjectStoreId)); MOZ_ALWAYS_SUCCEEDS( - stmt->BindStringByName(NS_LITERAL_CSTRING("name"), - mMetadata->mCommonMetadata.name())); + stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mNewName)); MOZ_ALWAYS_SUCCEEDS( - stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), - mMetadata->mCommonMetadata.id())); + stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mIndexId)); bool hasResult; MOZ_ALWAYS_SUCCEEDS(stmt->ExecuteStep(&hasResult)); @@ -25537,15 +25535,13 @@ RenameIndexOp::DoDatabaseWork(DatabaseConnection* aConnection) return rv; } - rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), - mMetadata->mCommonMetadata.name()); + rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mNewName); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), - mMetadata->mCommonMetadata.id()); + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mIndexId); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; diff --git a/testing/web-platform/meta/IndexedDB/idbindex-rename.html.ini b/testing/web-platform/meta/IndexedDB/idbindex-rename.html.ini deleted file mode 100644 index 40f63c2125cf..000000000000 --- a/testing/web-platform/meta/IndexedDB/idbindex-rename.html.ini +++ /dev/null @@ -1,11 +0,0 @@ -[idbindex-rename.html] - type: testharness - disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1309527 - expected: - if debug and not e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): CRASH - if debug and not e10s and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): CRASH - if debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): CRASH - if debug and e10s and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): CRASH - if debug and e10s and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): CRASH - if debug and not e10s and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): CRASH - if debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): CRASH diff --git a/testing/web-platform/meta/IndexedDB/idbobjectstore-rename-store.html.ini b/testing/web-platform/meta/IndexedDB/idbobjectstore-rename-store.html.ini deleted file mode 100644 index 4fd17af4e0e0..000000000000 --- a/testing/web-platform/meta/IndexedDB/idbobjectstore-rename-store.html.ini +++ /dev/null @@ -1,22 +0,0 @@ -[idbobjectstore-rename-store.html] - type: testharness - disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1309527 - expected: - if debug and not e10s and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): CRASH - if debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): CRASH - if debug and e10s and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86") and (bits == 32): CRASH - if debug and e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): CRASH - if not debug and e10s and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): TIMEOUT - if debug and e10s and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): CRASH - if debug and e10s and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): CRASH - if not debug and not e10s and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): TIMEOUT - if debug and not e10s and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): CRASH - [IndexedDB object store can be renamed to "\\u0000"] - expected: - if not debug and not e10s and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): TIMEOUT - - [IndexedDB object store can be renamed to "\\uDC00\\uD800"] - expected: - if not debug and e10s and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): TIMEOUT - if not debug and not e10s and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): NOTRUN - From ec7cac2aa12f41e10b213b856af7d3fa800804be Mon Sep 17 00:00:00 2001 From: David Anderson Date: Wed, 2 Nov 2016 16:45:38 -0700 Subject: [PATCH 02/96] Fix shutdown crash in VsyncBridgeChild when the GPU process is enabled. (bug 1314816, r=mattwoodrow) --- gfx/ipc/GPUProcessManager.cpp | 5 ++++- gfx/ipc/VsyncBridgeChild.cpp | 8 +++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/gfx/ipc/GPUProcessManager.cpp b/gfx/ipc/GPUProcessManager.cpp index d5075272f009..a95d091da223 100644 --- a/gfx/ipc/GPUProcessManager.cpp +++ b/gfx/ipc/GPUProcessManager.cpp @@ -397,7 +397,10 @@ GPUProcessManager::DestroyProcess() mProcessToken = 0; mProcess = nullptr; mGPUChild = nullptr; - mVsyncBridge = nullptr; + if (mVsyncBridge) { + mVsyncBridge->Close(); + mVsyncBridge = nullptr; + } } RefPtr diff --git a/gfx/ipc/VsyncBridgeChild.cpp b/gfx/ipc/VsyncBridgeChild.cpp index cb43acd0185c..f4239d8632b7 100644 --- a/gfx/ipc/VsyncBridgeChild.cpp +++ b/gfx/ipc/VsyncBridgeChild.cpp @@ -114,8 +114,14 @@ VsyncBridgeChild::Close() if (!mProcessToken) { return; } - PVsyncBridgeChild::Close(); + + // Clear the process token so we don't notify the GPUProcessManager. It already + // knows we're closed since it manually called Close, and in fact the GPM could + // have already been destroyed during shutdown. mProcessToken = 0; + + // Close the underlying IPC channel. + PVsyncBridgeChild::Close(); } void From 0eb23cdd2cc86291ce00f2c313a3033299514110 Mon Sep 17 00:00:00 2001 From: Cameron McCormack Date: Thu, 3 Nov 2016 09:40:53 +0800 Subject: [PATCH 03/96] Bug 1310463 - Part 1: Make list-style-image use nsStyleImageRequest for storage. r=xidorn MozReview-Commit-ID: ENTo2HNbBpN * * * [mq]: x MozReview-Commit-ID: 2SNJ4bXYpTH --- layout/style/nsRuleNode.cpp | 15 +++++++++------ layout/style/nsStyleStruct.cpp | 15 +++++++++++++-- layout/style/nsStyleStruct.h | 18 ++++++------------ 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index 4aeb887075ed..645992fff0d8 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -145,7 +145,10 @@ SetStyleImageRequest(function aCallback, RefPtr request; if (aProxy) { css::ImageValue* imageValue = aValue.GetImageStructValue(); - ImageTracker* imageTracker = aPresContext->Document()->ImageTracker(); + ImageTracker* imageTracker = + (aModeFlags & nsStyleImageRequest::Mode::Track) + ? aPresContext->Document()->ImageTracker() + : nullptr; request = new nsStyleImageRequest(aModeFlags, aProxy, imageValue, imageTracker); } @@ -7964,18 +7967,18 @@ nsRuleNode::ComputeListData(void* aStartStruct, // list-style-image: url, none, inherit const nsCSSValue* imageValue = aRuleData->ValueForListStyleImage(); if (eCSSUnit_Image == imageValue->GetUnit()) { - SetImageRequest([&](imgRequestProxy* req) { - list->SetListStyleImage(req); - }, mPresContext, *imageValue); + SetStyleImageRequest([&](nsStyleImageRequest* req) { + list->mListStyleImage = req; + }, mPresContext, *imageValue, nsStyleImageRequest::Mode(0)); } else if (eCSSUnit_None == imageValue->GetUnit() || eCSSUnit_Initial == imageValue->GetUnit()) { - list->SetListStyleImage(nullptr); + list->mListStyleImage = nullptr; } else if (eCSSUnit_Inherit == imageValue->GetUnit() || eCSSUnit_Unset == imageValue->GetUnit()) { conditions.SetUncacheable(); - list->SetListStyleImage(parentList->GetListStyleImage()); + list->mListStyleImage = parentList->mListStyleImage; } // list-style-position: enum, inherit, initial diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index c240f44a4b78..4f71b158d934 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -664,14 +664,25 @@ nsStyleList::~nsStyleList() nsStyleList::nsStyleList(const nsStyleList& aSource) : mListStylePosition(aSource.mListStylePosition) + , mListStyleImage(aSource.mListStyleImage) , mCounterStyle(aSource.mCounterStyle) , mQuotes(aSource.mQuotes) , mImageRegion(aSource.mImageRegion) { - SetListStyleImage(aSource.GetListStyleImage()); MOZ_COUNT_CTOR(nsStyleList); } +void +nsStyleList::FinishStyle(nsPresContext* aPresContext) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aPresContext->StyleSet()->IsServo()); + + if (mListStyleImage && !mListStyleImage->IsResolved()) { + mListStyleImage->Resolve(aPresContext); + } +} + void nsStyleList::SetQuotesInherit(const nsStyleList* aOther) { @@ -736,7 +747,7 @@ nsStyleList::CalcDifference(const nsStyleList& aNewData) const if (mListStylePosition != aNewData.mListStylePosition) { return nsChangeHint_ReconstructFrame; } - if (EqualImages(mListStyleImage, aNewData.mListStyleImage) && + if (DefinitelyEqualImages(mListStyleImage, aNewData.mListStyleImage) && mCounterStyle == aNewData.mCounterStyle) { if (mImageRegion.IsEqualInterior(aNewData.mImageRegion)) { return nsChangeHint(0); diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index 37e58ca2fea3..5b9bc8c86fb7 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -1523,7 +1523,8 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleList explicit nsStyleList(StyleStructContext aContext); nsStyleList(const nsStyleList& aStyleList); ~nsStyleList(); - void FinishStyle(nsPresContext* aPresContext) {} + + void FinishStyle(nsPresContext* aPresContext); void* operator new(size_t sz, nsStyleList* aSelf) { return aSelf; } void* operator new(size_t sz, nsPresContext* aContext) { @@ -1554,16 +1555,9 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleList sNoneQuotes = nullptr; } - imgRequestProxy* GetListStyleImage() const { return mListStyleImage; } - void SetListStyleImage(imgRequestProxy* aReq) + imgRequestProxy* GetListStyleImage() const { - if (mListStyleImage) { - mListStyleImage->UnlockImage(); - } - mListStyleImage = aReq; - if (mListStyleImage) { - mListStyleImage->LockImage(); - } + return mListStyleImage ? mListStyleImage->get() : nullptr; } void GetListStyleType(nsSubstring& aType) const { mCounterStyle->GetStyleName(aType); } @@ -1591,10 +1585,10 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleList void SetQuotesNone(); void SetQuotes(nsStyleQuoteValues::QuotePairArray&& aValues); - uint8_t mListStylePosition; // [inherited] + uint8_t mListStylePosition; // [inherited] + RefPtr mListStyleImage; // [inherited] private: RefPtr mCounterStyle; // [inherited] - RefPtr mListStyleImage; // [inherited] RefPtr mQuotes; // [inherited] nsStyleList& operator=(const nsStyleList& aOther) = delete; public: From c0a347f7d04c445f1f2e7b99a2c4f26890d8cb57 Mon Sep 17 00:00:00 2001 From: Cameron McCormack Date: Thu, 3 Nov 2016 09:40:53 +0800 Subject: [PATCH 04/96] Bug 1310463 - Part 2: Simplify nsComputedDOMStyle::DoGetListStyleImage a little. r=xidorn MozReview-Commit-ID: Kxbxu8mZcQs --- layout/style/nsComputedDOMStyle.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index deaf24ed4e07..2337e30b9f03 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -2086,7 +2086,7 @@ nsComputedDOMStyle::SetValueToStyleImage(const nsStyleImage& aStyleImage, if (!req) { // XXXheycam If we had some problem resolving the imgRequestProxy, // maybe we should just use the URL stored in the nsStyleImage's - // mImageValue? + // mImageValue? (Similarly in DoGetListStyleImage.) aValue->SetIdent(eCSSKeyword_none); break; } @@ -3504,13 +3504,16 @@ nsComputedDOMStyle::DoGetListStyleImage() const nsStyleList* list = StyleList(); - if (!list->GetListStyleImage()) { + // XXXheycam As in SetValueToStyleImage, we might want to use the + // URL stored in the nsStyleImageRequest's mImageValue if we + // failed to resolve the imgRequestProxy. + + imgRequestProxy* image = list->GetListStyleImage(); + if (!image) { val->SetIdent(eCSSKeyword_none); } else { nsCOMPtr uri; - if (list->GetListStyleImage()) { - list->GetListStyleImage()->GetURI(getter_AddRefs(uri)); - } + image->GetURI(getter_AddRefs(uri)); val->SetURI(uri); } From e627525329b5e445abf430da9940f33ef6dcb3c0 Mon Sep 17 00:00:00 2001 From: Cameron McCormack Date: Thu, 3 Nov 2016 09:40:53 +0800 Subject: [PATCH 05/96] Bug 1310463 - Part 3: Add FFI functions for setting list-style-image. r=xidorn MozReview-Commit-ID: LNK4UbfMfRk --- layout/style/ServoBindings.cpp | 59 +++++++++++++++++++++++++++------- layout/style/ServoBindings.h | 9 ++++++ 2 files changed, 56 insertions(+), 12 deletions(-) diff --git a/layout/style/ServoBindings.cpp b/layout/style/ServoBindings.cpp index 6076cfc32242..b6dcf7a1a715 100644 --- a/layout/style/ServoBindings.cpp +++ b/layout/style/ServoBindings.cpp @@ -734,14 +734,13 @@ Gecko_SetGradientImageValue(nsStyleImage* aImage, nsStyleGradient* aGradient) aImage->SetGradientData(aGradient); } -void -Gecko_SetUrlImageValue(nsStyleImage* aImage, - const uint8_t* aURLString, uint32_t aURLStringLength, - ThreadSafeURIHolder* aBaseURI, - ThreadSafeURIHolder* aReferrer, - ThreadSafePrincipalHolder* aPrincipal) +static already_AddRefed +CreateStyleImageRequest(nsStyleImageRequest::Mode aModeFlags, + const uint8_t* aURLString, uint32_t aURLStringLength, + ThreadSafeURIHolder* aBaseURI, + ThreadSafeURIHolder* aReferrer, + ThreadSafePrincipalHolder* aPrincipal) { - MOZ_ASSERT(aImage); MOZ_ASSERT(aURLString); MOZ_ASSERT(aBaseURI); MOZ_ASSERT(aReferrer); @@ -754,11 +753,22 @@ Gecko_SetUrlImageValue(nsStyleImage* aImage, RefPtr urlBuffer = nsCSSValue::BufferFromString(url); RefPtr req = - new nsStyleImageRequest(nsStyleImageRequest::Mode::Track, - urlBuffer, - do_AddRef(aBaseURI), - do_AddRef(aReferrer), - do_AddRef(aPrincipal)); + new nsStyleImageRequest(aModeFlags, urlBuffer, do_AddRef(aBaseURI), + do_AddRef(aReferrer), do_AddRef(aPrincipal)); + return req.forget(); +} + +void +Gecko_SetUrlImageValue(nsStyleImage* aImage, + const uint8_t* aURLString, uint32_t aURLStringLength, + ThreadSafeURIHolder* aBaseURI, + ThreadSafeURIHolder* aReferrer, + ThreadSafePrincipalHolder* aPrincipal) +{ + RefPtr req = + CreateStyleImageRequest(nsStyleImageRequest::Mode::Track, + aURLString, aURLStringLength, + aBaseURI, aReferrer, aPrincipal); aImage->SetImageRequest(req.forget()); } @@ -803,6 +813,31 @@ Gecko_CreateGradient(uint8_t aShape, return result; } +void +Gecko_SetListStyleImageNone(nsStyleList* aList) +{ + aList->mListStyleImage = nullptr; +} + +void +Gecko_SetListStyleImage(nsStyleList* aList, + const uint8_t* aURLString, uint32_t aURLStringLength, + ThreadSafeURIHolder* aBaseURI, + ThreadSafeURIHolder* aReferrer, + ThreadSafePrincipalHolder* aPrincipal) +{ + aList->mListStyleImage = + CreateStyleImageRequest(nsStyleImageRequest::Mode(0), + aURLString, aURLStringLength, + aBaseURI, aReferrer, aPrincipal); +} + +void +Gecko_CopyListStyleImageFrom(nsStyleList* aList, const nsStyleList* aSource) +{ + aList->mListStyleImage = aSource->mListStyleImage; +} + void Gecko_EnsureTArrayCapacity(void* aArray, size_t aCapacity, size_t aElemSize) { diff --git a/layout/style/ServoBindings.h b/layout/style/ServoBindings.h index a9758090d428..6a62c53e6d9d 100644 --- a/layout/style/ServoBindings.h +++ b/layout/style/ServoBindings.h @@ -177,6 +177,15 @@ nsStyleGradient* Gecko_CreateGradient(uint8_t shape, bool legacy_syntax, uint32_t stops); +// list-style-image style. +void Gecko_SetListStyleImageNone(nsStyleList* style_struct); +void Gecko_SetListStyleImage(nsStyleList* style_struct, + const uint8_t* string_bytes, uint32_t string_length, + ThreadSafeURIHolder* base_uri, + ThreadSafeURIHolder* referrer, + ThreadSafePrincipalHolder* principal); +void Gecko_CopyListStyleImageFrom(nsStyleList* dest, const nsStyleList* src); + // Display style. void Gecko_SetMozBinding(nsStyleDisplay* style_struct, const uint8_t* string_bytes, uint32_t string_length, From 1261559426170f74e6cb29a82eda96e7a44c0848 Mon Sep 17 00:00:00 2001 From: Phil Ringnalda Date: Wed, 2 Nov 2016 19:22:18 -0700 Subject: [PATCH 06/96] Backed out changeset 2e8791563f4d (bug 1309527) for Win8 timeouts in idbobjectstore-rename-store.html --- dom/indexedDB/ActorsParent.cpp | 40 ++++++++++--------- .../meta/IndexedDB/idbindex-rename.html.ini | 11 +++++ .../idbobjectstore-rename-store.html.ini | 22 ++++++++++ 3 files changed, 55 insertions(+), 18 deletions(-) create mode 100644 testing/web-platform/meta/IndexedDB/idbindex-rename.html.ini create mode 100644 testing/web-platform/meta/IndexedDB/idbobjectstore-rename-store.html.ini diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp index b2d66ec6ecdb..a037b3b43d13 100644 --- a/dom/indexedDB/ActorsParent.cpp +++ b/dom/indexedDB/ActorsParent.cpp @@ -7915,18 +7915,16 @@ class RenameObjectStoreOp final { friend class VersionChangeTransaction; - const int64_t mId; - const nsString mNewName; + const RefPtr mMetadata; private: // Only created by VersionChangeTransaction. RenameObjectStoreOp(VersionChangeTransaction* aTransaction, FullObjectStoreMetadata* const aMetadata) : VersionChangeTransactionOp(aTransaction) - , mId(aMetadata->mCommonMetadata.id()) - , mNewName(aMetadata->mCommonMetadata.name()) + , mMetadata(aMetadata) { - MOZ_ASSERT(mId); + MOZ_ASSERT(aMetadata->mCommonMetadata.id()); } ~RenameObjectStoreOp() @@ -8111,9 +8109,8 @@ class RenameIndexOp final { friend class VersionChangeTransaction; + const RefPtr mMetadata; const int64_t mObjectStoreId; - const int64_t mIndexId; - const nsString mNewName; private: // Only created by VersionChangeTransaction. @@ -8121,11 +8118,10 @@ private: FullIndexMetadata* const aMetadata, int64_t aObjectStoreId) : VersionChangeTransactionOp(aTransaction) + , mMetadata(aMetadata) , mObjectStoreId(aObjectStoreId) - , mIndexId(aMetadata->mCommonMetadata.id()) - , mNewName(aMetadata->mCommonMetadata.name()) { - MOZ_ASSERT(mIndexId); + MOZ_ASSERT(aMetadata->mCommonMetadata.id()); } ~RenameIndexOp() @@ -24400,10 +24396,12 @@ RenameObjectStoreOp::DoDatabaseWork(DatabaseConnection* aConnection) &stmt)); MOZ_ALWAYS_SUCCEEDS( - stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mNewName)); + stmt->BindStringByName(NS_LITERAL_CSTRING("name"), + mMetadata->mCommonMetadata.name())); MOZ_ALWAYS_SUCCEEDS( - stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mId)); + stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), + mMetadata->mCommonMetadata.id())); bool hasResult; MOZ_ALWAYS_SUCCEEDS(stmt->ExecuteStep(&hasResult)); @@ -24427,13 +24425,15 @@ RenameObjectStoreOp::DoDatabaseWork(DatabaseConnection* aConnection) return rv; } - rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mNewName); + rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), + mMetadata->mCommonMetadata.name()); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mId); + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), + mMetadata->mCommonMetadata.id()); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; @@ -25506,10 +25506,12 @@ RenameIndexOp::DoDatabaseWork(DatabaseConnection* aConnection) mObjectStoreId)); MOZ_ALWAYS_SUCCEEDS( - stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mNewName)); + stmt->BindStringByName(NS_LITERAL_CSTRING("name"), + mMetadata->mCommonMetadata.name())); MOZ_ALWAYS_SUCCEEDS( - stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mIndexId)); + stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), + mMetadata->mCommonMetadata.id())); bool hasResult; MOZ_ALWAYS_SUCCEEDS(stmt->ExecuteStep(&hasResult)); @@ -25535,13 +25537,15 @@ RenameIndexOp::DoDatabaseWork(DatabaseConnection* aConnection) return rv; } - rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mNewName); + rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), + mMetadata->mCommonMetadata.name()); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mIndexId); + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), + mMetadata->mCommonMetadata.id()); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; diff --git a/testing/web-platform/meta/IndexedDB/idbindex-rename.html.ini b/testing/web-platform/meta/IndexedDB/idbindex-rename.html.ini new file mode 100644 index 000000000000..40f63c2125cf --- /dev/null +++ b/testing/web-platform/meta/IndexedDB/idbindex-rename.html.ini @@ -0,0 +1,11 @@ +[idbindex-rename.html] + type: testharness + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1309527 + expected: + if debug and not e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): CRASH + if debug and not e10s and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): CRASH + if debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): CRASH + if debug and e10s and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): CRASH + if debug and e10s and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): CRASH + if debug and not e10s and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): CRASH + if debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): CRASH diff --git a/testing/web-platform/meta/IndexedDB/idbobjectstore-rename-store.html.ini b/testing/web-platform/meta/IndexedDB/idbobjectstore-rename-store.html.ini new file mode 100644 index 000000000000..4fd17af4e0e0 --- /dev/null +++ b/testing/web-platform/meta/IndexedDB/idbobjectstore-rename-store.html.ini @@ -0,0 +1,22 @@ +[idbobjectstore-rename-store.html] + type: testharness + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1309527 + expected: + if debug and not e10s and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): CRASH + if debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): CRASH + if debug and e10s and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86") and (bits == 32): CRASH + if debug and e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): CRASH + if not debug and e10s and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): TIMEOUT + if debug and e10s and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): CRASH + if debug and e10s and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): CRASH + if not debug and not e10s and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): TIMEOUT + if debug and not e10s and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): CRASH + [IndexedDB object store can be renamed to "\\u0000"] + expected: + if not debug and not e10s and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): TIMEOUT + + [IndexedDB object store can be renamed to "\\uDC00\\uD800"] + expected: + if not debug and e10s and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): TIMEOUT + if not debug and not e10s and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): NOTRUN + From 2af130581e2b9f887d5a6c82608777c12889ee58 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 3 Nov 2016 13:39:26 +1100 Subject: [PATCH 07/96] Bug 1314556 - Convert TestAudioEventTimeline.cpp to a gtest. r=padenot. This required moving template function ValidateEvent into the .h file to avoid linking problems in xul-gtest. --HG-- rename : dom/media/webaudio/compiledtest/TestAudioEventTimeline.cpp => dom/media/webaudio/gtest/TestAudioEventTimeline.cpp rename : dom/media/webaudio/compiledtest/moz.build => dom/media/webaudio/gtest/moz.build extra : rebase_source : 1a8917fc0e75e9c264bc88aec5565d9fff085c12 --- dom/media/webaudio/AudioEventTimeline.cpp | 92 ------------------- dom/media/webaudio/AudioEventTimeline.h | 88 +++++++++++++++++- .../TestAudioEventTimeline.cpp | 84 ++++++----------- .../{compiledtest => gtest}/moz.build | 8 +- dom/media/webaudio/moz.build | 2 +- testing/cppunittest.ini | 1 - 6 files changed, 118 insertions(+), 157 deletions(-) rename dom/media/webaudio/{compiledtest => gtest}/TestAudioEventTimeline.cpp (89%) rename dom/media/webaudio/{compiledtest => gtest}/moz.build (78%) diff --git a/dom/media/webaudio/AudioEventTimeline.cpp b/dom/media/webaudio/AudioEventTimeline.cpp index 66cb362882ec..4b5ea7d81b5d 100644 --- a/dom/media/webaudio/AudioEventTimeline.cpp +++ b/dom/media/webaudio/AudioEventTimeline.cpp @@ -51,98 +51,6 @@ static float ExtractValueFromCurve(double startTime, float* aCurve, uint32_t aCu namespace mozilla { namespace dom { -template bool -AudioEventTimeline::ValidateEvent(AudioTimelineEvent& aEvent, - ErrorResult& aRv) -{ - MOZ_ASSERT(NS_IsMainThread()); - - auto TimeOf = [](const AudioTimelineEvent& aEvent) -> double { - return aEvent.template Time(); - }; - - // Validate the event itself - if (!WebAudioUtils::IsTimeValid(TimeOf(aEvent)) || - !WebAudioUtils::IsTimeValid(aEvent.mTimeConstant)) { - aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); - return false; - } - - if (aEvent.mType == AudioTimelineEvent::SetValueCurve) { - if (!aEvent.mCurve || !aEvent.mCurveLength) { - aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); - return false; - } - for (uint32_t i = 0; i < aEvent.mCurveLength; ++i) { - if (!IsValid(aEvent.mCurve[i])) { - aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); - return false; - } - } - } - - bool timeAndValueValid = IsValid(aEvent.mValue) && - IsValid(aEvent.mDuration); - if (!timeAndValueValid) { - aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); - return false; - } - - // Make sure that non-curve events don't fall within the duration of a - // curve event. - for (unsigned i = 0; i < mEvents.Length(); ++i) { - if (mEvents[i].mType == AudioTimelineEvent::SetValueCurve && - !(aEvent.mType == AudioTimelineEvent::SetValueCurve && - TimeOf(aEvent) == TimeOf(mEvents[i])) && - TimeOf(mEvents[i]) <= TimeOf(aEvent) && - TimeOf(mEvents[i]) + mEvents[i].mDuration >= TimeOf(aEvent)) { - aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); - return false; - } - } - - // Make sure that curve events don't fall in a range which includes other - // events. - if (aEvent.mType == AudioTimelineEvent::SetValueCurve) { - for (unsigned i = 0; i < mEvents.Length(); ++i) { - // In case we have two curve at the same time - if (mEvents[i].mType == AudioTimelineEvent::SetValueCurve && - TimeOf(mEvents[i]) == TimeOf(aEvent)) { - continue; - } - if (TimeOf(mEvents[i]) > TimeOf(aEvent) && - TimeOf(mEvents[i]) < TimeOf(aEvent) + aEvent.mDuration) { - aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); - return false; - } - } - } - - // Make sure that invalid values are not used for exponential curves - if (aEvent.mType == AudioTimelineEvent::ExponentialRamp) { - if (aEvent.mValue <= 0.f) { - aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); - return false; - } - const AudioTimelineEvent* previousEvent = GetPreviousEvent(TimeOf(aEvent)); - if (previousEvent) { - if (previousEvent->mValue <= 0.f) { - aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); - return false; - } - } else { - if (mValue <= 0.f) { - aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); - return false; - } - } - } - return true; -} -template bool -AudioEventTimeline::ValidateEvent(AudioTimelineEvent& aEvent, - ErrorResult& aRv); - // This method computes the AudioParam value at a given time based on the event timeline template void AudioEventTimeline::GetValuesAtTimeHelper(TimeType aTime, float* aBuffer, diff --git a/dom/media/webaudio/AudioEventTimeline.h b/dom/media/webaudio/AudioEventTimeline.h index 283b4309710e..ae06ad4db9b5 100644 --- a/dom/media/webaudio/AudioEventTimeline.h +++ b/dom/media/webaudio/AudioEventTimeline.h @@ -171,8 +171,92 @@ public: { } template - bool ValidateEvent(AudioTimelineEvent& aEvent, - ErrorResult& aRv); + bool ValidateEvent(AudioTimelineEvent& aEvent, ErrorResult& aRv) + { + MOZ_ASSERT(NS_IsMainThread()); + + auto TimeOf = [](const AudioTimelineEvent& aEvent) -> double { + return aEvent.template Time(); + }; + + // Validate the event itself + if (!WebAudioUtils::IsTimeValid(TimeOf(aEvent)) || + !WebAudioUtils::IsTimeValid(aEvent.mTimeConstant)) { + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + return false; + } + + if (aEvent.mType == AudioTimelineEvent::SetValueCurve) { + if (!aEvent.mCurve || !aEvent.mCurveLength) { + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + return false; + } + for (uint32_t i = 0; i < aEvent.mCurveLength; ++i) { + if (!IsValid(aEvent.mCurve[i])) { + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + return false; + } + } + } + + bool timeAndValueValid = IsValid(aEvent.mValue) && + IsValid(aEvent.mDuration); + if (!timeAndValueValid) { + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + return false; + } + + // Make sure that non-curve events don't fall within the duration of a + // curve event. + for (unsigned i = 0; i < mEvents.Length(); ++i) { + if (mEvents[i].mType == AudioTimelineEvent::SetValueCurve && + !(aEvent.mType == AudioTimelineEvent::SetValueCurve && + TimeOf(aEvent) == TimeOf(mEvents[i])) && + TimeOf(mEvents[i]) <= TimeOf(aEvent) && + TimeOf(mEvents[i]) + mEvents[i].mDuration >= TimeOf(aEvent)) { + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + return false; + } + } + + // Make sure that curve events don't fall in a range which includes other + // events. + if (aEvent.mType == AudioTimelineEvent::SetValueCurve) { + for (unsigned i = 0; i < mEvents.Length(); ++i) { + // In case we have two curve at the same time + if (mEvents[i].mType == AudioTimelineEvent::SetValueCurve && + TimeOf(mEvents[i]) == TimeOf(aEvent)) { + continue; + } + if (TimeOf(mEvents[i]) > TimeOf(aEvent) && + TimeOf(mEvents[i]) < TimeOf(aEvent) + aEvent.mDuration) { + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + return false; + } + } + } + + // Make sure that invalid values are not used for exponential curves + if (aEvent.mType == AudioTimelineEvent::ExponentialRamp) { + if (aEvent.mValue <= 0.f) { + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + return false; + } + const AudioTimelineEvent* previousEvent = GetPreviousEvent(TimeOf(aEvent)); + if (previousEvent) { + if (previousEvent->mValue <= 0.f) { + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + return false; + } + } else { + if (mValue <= 0.f) { + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + return false; + } + } + } + return true; + } template void InsertEvent(const AudioTimelineEvent& aEvent) diff --git a/dom/media/webaudio/compiledtest/TestAudioEventTimeline.cpp b/dom/media/webaudio/gtest/TestAudioEventTimeline.cpp similarity index 89% rename from dom/media/webaudio/compiledtest/TestAudioEventTimeline.cpp rename to dom/media/webaudio/gtest/TestAudioEventTimeline.cpp index 2f347f5f96c1..0b2af73d3813 100644 --- a/dom/media/webaudio/compiledtest/TestAudioEventTimeline.cpp +++ b/dom/media/webaudio/gtest/TestAudioEventTimeline.cpp @@ -4,10 +4,10 @@ * 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 "AudioEventTimeline.cpp" -#include "TestHarness.h" +#include "AudioEventTimeline.h" #include #include +#include "gtest/gtest.h" // Mock the MediaStream class namespace mozilla { @@ -27,11 +27,10 @@ using std::numeric_limits; // Some simple testing primitives void ok(bool val, const char* msg) { - if (val) { - passed(msg); - } else { - fail(msg); + if (!val) { + fprintf(stderr, "failure: %s", msg); } + ASSERT_TRUE(val); } namespace std { @@ -50,7 +49,7 @@ template void is(const T& a, const U& b, const char* msg) { std::stringstream ss; - ss << msg << ", Got: " << a << ", expected: " << b; + ss << msg << ", Got: " << a << ", expected: " << b << std::endl; ok(a == b, ss.str().c_str()); } @@ -61,7 +60,7 @@ void is(const float& a, const float& b, const char* msg) const float kEpsilon = 0.00001f; std::stringstream ss; - ss << msg << ", Got: " << a << ", expected: " << b; + ss << msg << ", Got: " << a << ", expected: " << b << std::endl; ok(fabsf(a - b) < kEpsilon, ss.str().c_str()); } @@ -94,7 +93,7 @@ private: typedef AudioEventTimeline Timeline; -void TestSpecExample() +TEST(AudioEventTimeline, SpecExample) { // First, run the basic tests Timeline timeline(10.0f); @@ -150,7 +149,7 @@ void TestSpecExample() is(timeline.GetValueAtTime(1.0), 1.0f, "Correct value"); } -void TestInvalidEvents() +TEST(AudioEventTimeline, InvalidEvents) { static_assert(numeric_limits::has_quiet_NaN, "Platform must have a quiet NaN"); const float NaN = numeric_limits::quiet_NaN(); @@ -218,7 +217,7 @@ void TestInvalidEvents() is(rv, NS_ERROR_DOM_SYNTAX_ERR, "Correct error code returned"); } -void TestEventReplacement() +TEST(AudioEventTimeline, EventReplacement) { Timeline timeline(10.0f); @@ -237,7 +236,7 @@ void TestEventReplacement() is(timeline.GetValueAtTime(0.1), 30.0f, "The first event should be overwritten"); } -void TestEventRemoval() +TEST(AudioEventTimeline, EventRemoval) { Timeline timeline(10.0f); @@ -258,7 +257,7 @@ void TestEventRemoval() ok(timeline.HasSimpleValue(), "No event should remain scheduled"); } -void TestBeforeFirstEventSetValue() +TEST(AudioEventTimeline, BeforeFirstEventSetValue) { Timeline timeline(10.0f); @@ -268,7 +267,7 @@ void TestBeforeFirstEventSetValue() is(timeline.GetValueAtTime(0.5), 10.0f, "Retrun the default value before the first event"); } -void TestBeforeFirstEventSetTarget() +TEST(AudioEventTimeline, BeforeFirstEventSetTarget) { Timeline timeline(10.0f); @@ -278,7 +277,7 @@ void TestBeforeFirstEventSetTarget() is(timeline.GetValueAtTime(0.5), 10.0f, "Retrun the default value before the first event"); } -void TestBeforeFirstEventLinearRamp() +TEST(AudioEventTimeline, BeforeFirstEventLinearRamp) { Timeline timeline(10.0f); @@ -288,7 +287,7 @@ void TestBeforeFirstEventLinearRamp() is(timeline.GetValueAtTime(0.5), 10.0f, "Retrun the default value before the first event"); } -void TestBeforeFirstEventExponentialRamp() +TEST(AudioEventTimeline, BeforeFirstEventExponentialRamp) { Timeline timeline(10.0f); @@ -298,7 +297,7 @@ void TestBeforeFirstEventExponentialRamp() is(timeline.GetValueAtTime(0.5), 10.0f, "Retrun the default value before the first event"); } -void TestAfterLastValueEvent() +TEST(AudioEventTimeline, AfterLastValueEvent) { Timeline timeline(10.0f); @@ -308,7 +307,7 @@ void TestAfterLastValueEvent() is(timeline.GetValueAtTime(1.5), 20.0f, "Return the last value after the last SetValue event"); } -void TestAfterLastTargetValueEvent() +TEST(AudioEventTimeline, AfterLastTargetValueEvent) { Timeline timeline(10.0f); @@ -318,7 +317,7 @@ void TestAfterLastTargetValueEvent() is(timeline.GetValueAtTime(10.), (20.f + (10.f - 20.f) * expf(-9.0f / 5.0f)), "Return the value after the last SetTarget event based on the curve"); } -void TestAfterLastTargetValueEventWithValueSet() +TEST(AudioEventTimeline, AfterLastTargetValueEventWithValueSet) { Timeline timeline(10.0f); @@ -338,7 +337,7 @@ void TestAfterLastTargetValueEventWithValueSet() is(timeline.GetValueAtTime(10.), (20.f + (50.f - 20.f) * expf(-9.0f / 5.0f)), "Return the value after SetValue and the last SetTarget event based on the curve"); } -void TestValue() +TEST(AudioEventTimeline, Value) { Timeline timeline(10.0f); @@ -354,7 +353,7 @@ void TestValue() is(timeline.Value(), 20.0f, "Should not be able to set the value"); } -void TestLinearRampAtZero() +TEST(AudioEventTimeline, LinearRampAtZero) { Timeline timeline(10.0f); @@ -364,7 +363,7 @@ void TestLinearRampAtZero() is(timeline.GetValueAtTime(0.0), 20.0f, "Should get the correct value when t0 == t1 == 0"); } -void TestExponentialRampAtZero() +TEST(AudioEventTimeline, ExponentialRampAtZero) { Timeline timeline(10.0f); @@ -374,7 +373,7 @@ void TestExponentialRampAtZero() is(timeline.GetValueAtTime(0.0), 20.0f, "Should get the correct value when t0 == t1 == 0"); } -void TestLinearRampAtSameTime() +TEST(AudioEventTimeline, LinearRampAtSameTime) { Timeline timeline(10.0f); @@ -385,7 +384,7 @@ void TestLinearRampAtSameTime() is(timeline.GetValueAtTime(1.0), 20.0f, "Should get the correct value when t0 == t1"); } -void TestExponentialRampAtSameTime() +TEST(AudioEventTimeline, ExponentialRampAtSameTime) { Timeline timeline(10.0f); @@ -396,7 +395,7 @@ void TestExponentialRampAtSameTime() is(timeline.GetValueAtTime(1.0), 20.0f, "Should get the correct value when t0 == t1"); } -void TestSetTargetZeroTimeConstant() +TEST(AudioEventTimeline, SetTargetZeroTimeConstant) { Timeline timeline(10.0f); @@ -406,7 +405,7 @@ void TestSetTargetZeroTimeConstant() is(timeline.GetValueAtTime(1.0), 20.0f, "Should get the correct value when t0 == t1"); } -void TestExponentialInvalidPreviousZeroValue() +TEST(AudioEventTimeline, ExponentialInvalidPreviousZeroValue) { Timeline timeline(0.f); @@ -432,8 +431,7 @@ void TestExponentialInvalidPreviousZeroValue() is(rv, NS_OK, "Should succeed this time"); } -void -TestSettingValueCurveTwice() +TEST(AudioEventTimeline, SettingValueCurveTwice) { Timeline timeline(0.f); float curve[] = { -1.0f, 0.0f, 1.0f }; @@ -445,33 +443,3 @@ TestSettingValueCurveTwice() is(rv, NS_OK, "SetValueCurveAtTime succeeded"); } -int main() -{ - ScopedXPCOM xpcom("TestAudioEventTimeline"); - if (xpcom.failed()) { - return 1; - } - - TestSpecExample(); - TestInvalidEvents(); - TestEventReplacement(); - TestEventRemoval(); - TestBeforeFirstEventSetValue(); - TestBeforeFirstEventSetTarget(); - TestBeforeFirstEventLinearRamp(); - TestBeforeFirstEventExponentialRamp(); - TestAfterLastValueEvent(); - TestAfterLastTargetValueEvent(); - TestAfterLastTargetValueEventWithValueSet(); - TestValue(); - TestLinearRampAtZero(); - TestExponentialRampAtZero(); - TestLinearRampAtSameTime(); - TestExponentialRampAtSameTime(); - TestSetTargetZeroTimeConstant(); - TestExponentialInvalidPreviousZeroValue(); - TestSettingValueCurveTwice(); - - return gFailCount > 0; -} - diff --git a/dom/media/webaudio/compiledtest/moz.build b/dom/media/webaudio/gtest/moz.build similarity index 78% rename from dom/media/webaudio/compiledtest/moz.build rename to dom/media/webaudio/gtest/moz.build index b7c5059e9c20..2cc13b038dc2 100644 --- a/dom/media/webaudio/compiledtest/moz.build +++ b/dom/media/webaudio/gtest/moz.build @@ -4,10 +4,12 @@ # 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/. -GeckoCppUnitTests([ - 'TestAudioEventTimeline', -]) +UNIFIED_SOURCES += [ + 'TestAudioEventTimeline.cpp', +] LOCAL_INCLUDES += [ '..', ] + +FINAL_LIBRARY = 'xul-gtest' diff --git a/dom/media/webaudio/moz.build b/dom/media/webaudio/moz.build index 94042f8704c0..d1a9f568095d 100644 --- a/dom/media/webaudio/moz.build +++ b/dom/media/webaudio/moz.build @@ -9,7 +9,7 @@ with Files('*'): DIRS += ['blink'] -TEST_DIRS += ['compiledtest'] +TEST_DIRS += ['gtest'] MOCHITEST_MANIFESTS += [ 'test/blink/mochitest.ini', diff --git a/testing/cppunittest.ini b/testing/cppunittest.ini index 17b8eedcdb6b..f7b8e497cae1 100644 --- a/testing/cppunittest.ini +++ b/testing/cppunittest.ini @@ -6,7 +6,6 @@ [TestAtomics] [TestAudioBuffers] skip-if = os == 'b2g' # Bug 1062937 -[TestAudioEventTimeline] [TestAudioMixer] [TestAutoPtr] [TestAutoRef] From 99ef07a8a29dc8b4b974f284e682ba487b19299a Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Sun, 30 Oct 2016 19:54:23 -0700 Subject: [PATCH 08/96] Bug 1312690: Cleanup. r=aswan MozReview-Commit-ID: IorfzVPKyIE --HG-- extra : rebase_source : 2255208c0e8bad655afd307b1241e784c355c83c --- toolkit/components/extensions/Extension.jsm | 13 ------------- toolkit/components/extensions/ExtensionUtils.jsm | 14 ++++++-------- 2 files changed, 6 insertions(+), 21 deletions(-) diff --git a/toolkit/components/extensions/Extension.jsm b/toolkit/components/extensions/Extension.jsm index 2d8bf31b4c47..2ab51b13e972 100644 --- a/toolkit/components/extensions/Extension.jsm +++ b/toolkit/components/extensions/Extension.jsm @@ -380,19 +380,6 @@ class ExtensionChildProxyContext extends ProxyContext { function findPathInObject(obj, path, printErrors = true) { for (let elt of path.split(".")) { - // If we get a null object before reaching the requested path - // (e.g. the API object is returned only on particular kind of contexts instead - // of based on WebExtensions permissions, like it happens for the devtools APIs), - // stop searching and return undefined. - // TODO(robwu): This should never be reached. If an API is not available for - // a context, it should be declared as such in the schema and enforced by - // `shouldInject`, for instance using the same logic that is used to opt-in - // to APIs in content scripts. - // If this check is kept, then there is a discrepancy between APIs depending - // on whether it is generated locally or remotely: Non-existing local APIs - // are excluded in `shouldInject` by this check, but remote APIs do not have - // this information and will therefore cause the schema API generator to - // create an API that proxies to a non-existing API implementation. if (!obj || !(elt in obj)) { if (printErrors) { Cu.reportError(`WebExtension API ${path} not found (it may be unimplemented by Firefox).`); diff --git a/toolkit/components/extensions/ExtensionUtils.jsm b/toolkit/components/extensions/ExtensionUtils.jsm index 1977d6cf41ee..0999ece660c7 100644 --- a/toolkit/components/extensions/ExtensionUtils.jsm +++ b/toolkit/components/extensions/ExtensionUtils.jsm @@ -2042,16 +2042,14 @@ class SchemaAPIManager extends EventEmitter { wantXrays: false, sandboxName: `Namespace of ext-*.js scripts for ${this.processType}`, }); - Object.defineProperty(global, "console", {get() { return console; }}); - global.extensions = this; - global.global = global; - global.Cc = Cc; - global.Ci = Ci; - global.Cu = Cu; - global.Cr = Cr; + + Object.assign(global, {global, Cc, Ci, Cu, Cr, XPCOMUtils, extensions: this}); + + XPCOMUtils.defineLazyGetter(global, "console", getConsole); + XPCOMUtils.defineLazyModuleGetter(global, "require", "resource://devtools/shared/Loader.jsm"); - global.XPCOMUtils = XPCOMUtils; + return global; } From 2bc5203e8bd6954a53b714ff89454c41014d389d Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Wed, 2 Nov 2016 19:09:04 -0700 Subject: [PATCH 09/96] Bug 1312690: Lazily initialize schema bindings. r=aswan MozReview-Commit-ID: GoVUlANCgHg --HG-- extra : rebase_source : 63ad0522291f1526b476d709cb1a201e52dc09d3 --- .../components/extensions/ExtensionUtils.jsm | 1 + toolkit/components/extensions/Schemas.jsm | 310 ++++++++++++------ .../test/mochitest/test_ext_all_apis.js | 2 +- .../test/mochitest/test_ext_schema.html | 4 +- 4 files changed, 205 insertions(+), 112 deletions(-) diff --git a/toolkit/components/extensions/ExtensionUtils.jsm b/toolkit/components/extensions/ExtensionUtils.jsm index 0999ece660c7..d722e481805b 100644 --- a/toolkit/components/extensions/ExtensionUtils.jsm +++ b/toolkit/components/extensions/ExtensionUtils.jsm @@ -2190,6 +2190,7 @@ this.ExtensionUtils = { runSafeWithoutClone, stylesheetMap, BaseContext, + DefaultMap, DefaultWeakMap, EventEmitter, EventManager, diff --git a/toolkit/components/extensions/Schemas.jsm b/toolkit/components/extensions/Schemas.jsm index b38455a00083..0fc0628bacbc 100644 --- a/toolkit/components/extensions/Schemas.jsm +++ b/toolkit/components/extensions/Schemas.jsm @@ -19,9 +19,20 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/ExtensionUtils.jsm"); var { + DefaultMap, instanceOf, } = ExtensionUtils; +class DeepMap extends DefaultMap { + constructor() { + super(() => new DeepMap()); + } + + getPath(...keys) { + return keys.reduce((map, prop) => map.get(prop), this); + } +} + XPCOMUtils.defineLazyServiceGetter(this, "contentPolicyService", "@mozilla.org/addons/content-policy;1", "nsIAddonContentPolicy"); @@ -57,6 +68,58 @@ function readJSON(url) { }); } +/** + * Defines a lazy getter for the given property on the given object. Any + * security wrappers are waived on the object before the property is + * defined, and the getter and setter methods are wrapped for the target + * scope. + * + * The given getter function is guaranteed to be called only once, even + * if the target scope retrieves the wrapped getter from the property + * descriptor and calls it directly. + * + * @param {object} object + * The object on which to define the getter. + * @param {string|Symbol} prop + * The property name for which to define the getter. + * @param {function} getter + * The function to call in order to generate the final property + * value. + */ +function exportLazyGetter(object, prop, getter) { + object = Cu.waiveXrays(object); + + let redefine = value => { + if (value === undefined) { + delete object[prop]; + } else { + Object.defineProperty(object, prop, { + enumerable: true, + configurable: true, + writable: true, + value, + }); + } + + getter = null; + + return value; + }; + + Object.defineProperty(object, prop, { + enumerable: true, + configurable: true, + + get: Cu.exportFunction(function() { + return redefine(getter.call(this)); + }, object), + + set: Cu.exportFunction(value => { + redefine(value); + }, object), + }); +} + // Parses a regular expression, with support for the Python extended // syntax that allows setting flags by including the string (?im) function parsePattern(pattern) { @@ -874,11 +937,13 @@ class StringType extends Type { inject(apiImpl, path, name, dest, context) { if (this.enumeration) { - let obj = Cu.createObjectIn(dest, {defineAs: name}); - for (let e of this.enumeration) { - let key = e.toUpperCase(); - obj[key] = e; - } + exportLazyGetter(dest, name, () => { + let obj = Cu.createObjectIn(dest); + for (let e of this.enumeration) { + obj[e.toUpperCase()] = e; + } + return obj; + }); } } } @@ -1423,31 +1488,35 @@ class SubModuleProperty extends Entry { } inject(apiImpl, path, name, dest, context) { - let obj = Cu.createObjectIn(dest, {defineAs: name}); + exportLazyGetter(dest, name, () => { + let obj = Cu.createObjectIn(dest); - let ns = Schemas.namespaces.get(this.namespaceName); - let type = ns.get(this.reference); - if (!type && this.reference.includes(".")) { - let [namespaceName, ref] = this.reference.split("."); - ns = Schemas.namespaces.get(namespaceName); - type = ns.get(ref); - } - if (!type || !(type instanceof SubModuleType)) { - throw new Error(`Internal error: ${this.namespaceName}.${this.reference} is not a sub-module`); - } - - let functions = type.functions; - for (let fun of functions) { - let subpath = path.concat(name); - let namespace = subpath.join("."); - let allowedContexts = fun.allowedContexts.length ? fun.allowedContexts : ns.defaultContexts; - if (context.shouldInject(namespace, fun.name, allowedContexts)) { - let apiImpl = context.getImplementation(namespace, fun.name); - fun.inject(apiImpl, subpath, fun.name, obj, context); + let ns = Schemas.namespaces.get(this.namespaceName); + let type = ns.get(this.reference); + if (!type && this.reference.includes(".")) { + let [namespaceName, ref] = this.reference.split("."); + ns = Schemas.namespaces.get(namespaceName); + type = ns.get(ref); + } + if (!type || !(type instanceof SubModuleType)) { + throw new Error(`Internal error: ${this.namespaceName}.${this.reference} is not a sub-module`); } - } - // TODO: Inject this.properties. + let functions = type.functions; + for (let fun of functions) { + let subpath = path.concat(name); + let namespace = subpath.join("."); + let allowedContexts = fun.allowedContexts.length ? fun.allowedContexts : ns.defaultContexts; + if (context.shouldInject(namespace, fun.name, allowedContexts)) { + let apiImpl = context.getImplementation(namespace, fun.name); + fun.inject(apiImpl, subpath, fun.name, obj, context); + } + } + + // TODO: Inject this.properties. + + return obj; + }); } } @@ -1562,37 +1631,39 @@ class FunctionEntry extends CallEntry { return; } - let stub; - if (this.isAsync) { - stub = (...args) => { - this.checkDeprecated(context); - let actuals = this.checkParameters(args, context); - let callback = null; - if (this.hasAsyncCallback) { - callback = actuals.pop(); - } - if (callback === null && context.isChromeCompat) { - // We pass an empty stub function as a default callback for - // the `chrome` API, so promise objects are not returned, - // and lastError values are reported immediately. - callback = () => {}; - } - return apiImpl.callAsyncFunction(actuals, callback); - }; - } else if (!this.returns) { - stub = (...args) => { - this.checkDeprecated(context); - let actuals = this.checkParameters(args, context); - return apiImpl.callFunctionNoReturn(actuals); - }; - } else { - stub = (...args) => { - this.checkDeprecated(context); - let actuals = this.checkParameters(args, context); - return apiImpl.callFunction(actuals); - }; - } - Cu.exportFunction(stub, dest, {defineAs: name}); + exportLazyGetter(dest, name, () => { + let stub; + if (this.isAsync) { + stub = (...args) => { + this.checkDeprecated(context); + let actuals = this.checkParameters(args, context); + let callback = null; + if (this.hasAsyncCallback) { + callback = actuals.pop(); + } + if (callback === null && context.isChromeCompat) { + // We pass an empty stub function as a default callback for + // the `chrome` API, so promise objects are not returned, + // and lastError values are reported immediately. + callback = () => {}; + } + return apiImpl.callAsyncFunction(actuals, callback); + }; + } else if (!this.returns) { + stub = (...args) => { + this.checkDeprecated(context); + let actuals = this.checkParameters(args, context); + return apiImpl.callFunctionNoReturn(actuals); + }; + } else { + stub = (...args) => { + this.checkDeprecated(context); + let actuals = this.checkParameters(args, context); + return apiImpl.callFunction(actuals); + }; + } + return Cu.exportFunction(stub, dest); + }); } } @@ -1622,26 +1693,31 @@ class Event extends CallEntry { return; } - let addStub = (listener, ...args) => { - listener = this.checkListener(listener, context); - let actuals = this.checkParameters(args, context); - apiImpl.addListener(listener, actuals); - }; + exportLazyGetter(dest, name, () => { + let addStub = (listener, ...args) => { + listener = this.checkListener(listener, context); + let actuals = this.checkParameters(args, context); + apiImpl.addListener(listener, actuals); + }; - let removeStub = (listener) => { - listener = this.checkListener(listener, context); - apiImpl.removeListener(listener); - }; + let removeStub = (listener) => { + listener = this.checkListener(listener, context); + apiImpl.removeListener(listener); + }; - let hasStub = (listener) => { - listener = this.checkListener(listener, context); - return apiImpl.hasListener(listener); - }; + let hasStub = (listener) => { + listener = this.checkListener(listener, context); + return apiImpl.hasListener(listener); + }; - let obj = Cu.createObjectIn(dest, {defineAs: name}); - Cu.exportFunction(addStub, obj, {defineAs: "addListener"}); - Cu.exportFunction(removeStub, obj, {defineAs: "removeListener"}); - Cu.exportFunction(hasStub, obj, {defineAs: "hasListener"}); + let obj = Cu.createObjectIn(dest); + + Cu.exportFunction(addStub, obj, {defineAs: "addListener"}); + Cu.exportFunction(removeStub, obj, {defineAs: "removeListener"}); + Cu.exportFunction(hasStub, obj, {defineAs: "hasListener"}); + + return obj; + }); } } @@ -1671,6 +1747,7 @@ this.Schemas = { let ns = this.namespaces.get(namespaceName); if (!ns) { ns = new Map(); + ns.name = namespaceName; ns.permissions = null; ns.allowedContexts = []; ns.defaultContexts = []; @@ -1921,6 +1998,8 @@ this.Schemas = { return true; }, + exportLazyGetter, + /** * Inject registered extension APIs into `dest`. * @@ -1932,53 +2011,66 @@ this.Schemas = { inject(dest, wrapperFuncs) { let context = new InjectionContext(wrapperFuncs); - for (let [namespace, ns] of this.namespaces) { - if (ns.permissions && !ns.permissions.some(perm => context.hasPermission(perm))) { - continue; - } + let createNamespace = ns => { + let obj = Cu.createObjectIn(dest); - if (!wrapperFuncs.shouldInject(namespace, null, ns.allowedContexts)) { - continue; - } - - let obj = Cu.createObjectIn(dest, {defineAs: namespace}); for (let [name, entry] of ns) { - let allowedContexts = entry.allowedContexts.length ? entry.allowedContexts : ns.defaultContexts; - if (context.shouldInject(namespace, name, allowedContexts)) { - let apiImpl = context.getImplementation(namespace, name); - entry.inject(apiImpl, [namespace], name, obj, context); + let allowedContexts = entry.allowedContexts; + if (!allowedContexts.length) { + allowedContexts = ns.defaultContexts; + } + + if (context.shouldInject(ns.name, name, allowedContexts)) { + let apiImpl = context.getImplementation(ns.name, name); + entry.inject(apiImpl, [ns.name], name, obj, context); } } // Remove the namespace object if it is empty - if (!Object.keys(obj).length) { - delete dest[namespace]; - // process the next namespace. + if (Object.keys(obj).length) { + return obj; + } + }; + + let createNestedNamespaces = (parent, namespaces) => { + for (let [prop, namespace] of namespaces) { + if (namespace instanceof DeepMap) { + exportLazyGetter(parent, prop, () => { + let obj = Cu.createObjectIn(parent); + createNestedNamespaces(obj, namespace); + return obj; + }); + } else { + exportLazyGetter(parent, prop, + () => createNamespace(namespace)); + } + } + }; + + let nestedNamespaces = new DeepMap(); + for (let ns of this.namespaces.values()) { + if (ns.permissions && !ns.permissions.some(perm => context.hasPermission(perm))) { continue; } - // If the nested namespaced API object (e.g devtools.inspectedWindow) is not empty, - // then turn `dest["nested.namespace"]` into `dest["nested"]["namespace"]`. - if (namespace.includes(".")) { - let apiObj = dest[namespace]; - delete dest[namespace]; + if (!wrapperFuncs.shouldInject(ns.name, null, ns.allowedContexts)) { + continue; + } - let nsLevels = namespace.split("."); - let currentObj = dest; - for (let nsLevel of nsLevels.slice(0, -1)) { - if (!currentObj[nsLevel]) { - // Create the namespace level if it doesn't exist yet. - currentObj = Cu.createObjectIn(currentObj, {defineAs: nsLevel}); - } else { - // Move currentObj to the nested object if it already exists. - currentObj = currentObj[nsLevel]; - } - } + if (ns.name.includes(".")) { + let path = ns.name.split("."); + let leafName = path.pop(); - // Copy the apiObj as the final nested level. - currentObj[nsLevels.pop()] = apiObj; + let parent = nestedNamespaces.getPath(...path); + + parent.set(leafName, ns); + } else { + exportLazyGetter(dest, ns.name, + () => createNamespace(ns)); } } + + createNestedNamespaces(dest, nestedNamespaces); }, /** diff --git a/toolkit/components/extensions/test/mochitest/test_ext_all_apis.js b/toolkit/components/extensions/test/mochitest/test_ext_all_apis.js index 7857afae00d5..c46e41550b83 100644 --- a/toolkit/components/extensions/test/mochitest/test_ext_all_apis.js +++ b/toolkit/components/extensions/test/mochitest/test_ext_all_apis.js @@ -106,7 +106,7 @@ function sendAllApis() { let val = obj[key]; if (typeof val == "object" && val !== null && mayRecurse(key, val)) { diveDeeper(`${path}.${key}`, val); - } else { + } else if (val !== undefined) { results.push(`${path}.${key}`); } } diff --git a/toolkit/components/extensions/test/mochitest/test_ext_schema.html b/toolkit/components/extensions/test/mochitest/test_ext_schema.html index c68a2491ce68..8a0e11c56c08 100644 --- a/toolkit/components/extensions/test/mochitest/test_ext_schema.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_schema.html @@ -16,9 +16,9 @@ add_task(function* testEmptySchema() { function background() { - browser.test.assertTrue(!("manifest" in browser), "browser.manifest is not defined"); + browser.test.assertEq(undefined, browser.manifest, "browser.manifest is not defined"); browser.test.assertTrue("storage" in browser, "browser.storage should be defined"); - browser.test.assertTrue(!("contextMenus" in browser), "browser.contextMenus should not be defined"); + browser.test.assertEq(undefined, browser.contextMenus, "browser.contextMenus should not be defined"); browser.test.notifyPass("schema"); } From e9c3a589bba2f94fb498d2f841fe2f746d15c304 Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Wed, 2 Nov 2016 19:21:04 -0700 Subject: [PATCH 10/96] Bug 1312690: Lazily initialize extension APIs. r=aswan MozReview-Commit-ID: 2ofzT6wPvus --HG-- extra : rebase_source : 462e3eca2a9750fb07ad753322e86bc1494a8b4e --- browser/components/extensions/ext-utils.js | 17 +++-- .../browser/browser_ext_popup_shutdown.js | 2 - toolkit/components/extensions/Extension.jsm | 23 +++--- .../components/extensions/ExtensionChild.jsm | 71 ++++++++++++------- .../extensions/ExtensionContent.jsm | 68 +++++++++++------- .../components/extensions/ExtensionUtils.jsm | 46 +++++++++++- .../test/xpcshell/test_ext_api_permissions.js | 15 +++- 7 files changed, 167 insertions(+), 75 deletions(-) diff --git a/browser/components/extensions/ext-utils.js b/browser/components/extensions/ext-utils.js index 00fb499cb199..5ed57f08eac0 100644 --- a/browser/components/extensions/ext-utils.js +++ b/browser/components/extensions/ext-utils.js @@ -78,15 +78,6 @@ XPCOMUtils.defineLazyGetter(this, "standaloneStylesheets", () => { return stylesheets; }); -/* eslint-disable mozilla/balanced-listeners */ -extensions.on("page-shutdown", (type, context) => { - if (context.viewType == "popup" && context.active) { - // TODO(robwu): This is not webext-oop compatible. - context.xulBrowser.contentWindow.close(); - } -}); -/* eslint-enable mozilla/balanced-listeners */ - class BasePopup { constructor(extension, viewNode, popupURL, browserStyle, fixedWidth = false) { this.extension = extension; @@ -97,6 +88,8 @@ class BasePopup { this.destroyed = false; this.fixedWidth = fixedWidth; + extension.callOnClose(this); + this.contentReady = new Promise(resolve => { this._resolveContentReady = resolve; }); @@ -120,7 +113,13 @@ class BasePopup { return BasePopup.instances.get(window).get(extension); } + close() { + this.closePopup(); + } + destroy() { + this.extension.forgetOnClose(this); + this.destroyed = true; this.browserLoadedDeferred.reject(new Error("Popup destroyed")); return this.browserReady.then(() => { diff --git a/browser/components/extensions/test/browser/browser_ext_popup_shutdown.js b/browser/components/extensions/test/browser/browser_ext_popup_shutdown.js index 4f317ca88fed..22ee2b10043a 100644 --- a/browser/components/extensions/test/browser/browser_ext_popup_shutdown.js +++ b/browser/components/extensions/test/browser/browser_ext_popup_shutdown.js @@ -74,7 +74,5 @@ add_task(function* testPageAction() { yield extension.unload(); - yield new Promise(resolve => setTimeout(resolve, 0)); - is(panel.parentNode, null, "Panel should be removed from the document"); }); diff --git a/toolkit/components/extensions/Extension.jsm b/toolkit/components/extensions/Extension.jsm index 2ab51b13e972..7ad9a376d171 100644 --- a/toolkit/components/extensions/Extension.jsm +++ b/toolkit/components/extensions/Extension.jsm @@ -83,6 +83,7 @@ if (!AppConstants.RELEASE_OR_BETA) { Cu.import("resource://gre/modules/ExtensionUtils.jsm"); var { BaseContext, + defineLazyGetter, EventEmitter, SchemaAPIManager, LocaleData, @@ -289,22 +290,16 @@ class ProxyContext extends BaseContext { this.currentMessageManager = xulBrowser.messageManager; this._docShellTracker = new BrowserDocshellFollower(xulBrowser, this.onBrowserChange.bind(this)); - this.principal_ = principal; - this.apiObj = {}; - GlobalManager.injectInObject(this, false, this.apiObj); + Object.defineProperty(this, "principal", { + value: principal, enumerable: true, configurable: true, + }); this.listenerProxies = new Map(); - this.sandbox = Cu.Sandbox(principal, {}); - Management.emit("proxy-context-load", this); } - get principal() { - return this.principal_; - } - get cloneScope() { return this.sandbox; } @@ -334,6 +329,16 @@ class ProxyContext extends BaseContext { } } +defineLazyGetter(ProxyContext.prototype, "apiObj", function() { + let obj = {}; + GlobalManager.injectInObject(this, false, obj); + return obj; +}); + +defineLazyGetter(ProxyContext.prototype, "sandbox", function() { + return Cu.Sandbox(this.principal); +}); + // The parent ProxyContext of an ExtensionContext in ExtensionChild.jsm. class ExtensionChildProxyContext extends ProxyContext { constructor(envType, extension, params, xulBrowser) { diff --git a/toolkit/components/extensions/ExtensionChild.jsm b/toolkit/components/extensions/ExtensionChild.jsm index be0f2cbba0a1..0d501282df4e 100644 --- a/toolkit/components/extensions/ExtensionChild.jsm +++ b/toolkit/components/extensions/ExtensionChild.jsm @@ -34,6 +34,7 @@ var { getInnerWindowID, BaseContext, ChildAPIManager, + defineLazyGetter, LocalAPIImplementation, Messenger, SchemaAPIManager, @@ -169,33 +170,22 @@ class ExtensionContext extends BaseContext { if (uri) { sender.url = uri.spec; } + this.sender = sender; - let filter = {extensionId: extension.id}; - let optionalFilter = {}; - // Addon-generated messages (not necessarily from the same process as the - // addon itself) are sent to the main process, which forwards them via the - // parent process message manager. Specific replies can be sent to the frame - // message manager. - this.messenger = new Messenger(this, [Services.cpmm, this.messageManager], sender, filter, optionalFilter); - - let localApis = {}; - apiManager.generateAPIs(this, localApis); - this.childManager = new WannabeChildAPIManager(this, this.messageManager, localApis, { - envType: "addon_parent", - viewType, - url: uri.spec, + Schemas.exportLazyGetter(contentWindow, "browser", () => { + let browserObj = Cu.createObjectIn(contentWindow); + Schemas.inject(browserObj, this.childManager); + return browserObj; }); - let chromeApiWrapper = Object.create(this.childManager); - chromeApiWrapper.isChromeCompat = true; - let browserObj = Cu.createObjectIn(contentWindow, {defineAs: "browser"}); - let chromeObj = Cu.createObjectIn(contentWindow, {defineAs: "chrome"}); - Schemas.inject(browserObj, this.childManager); - Schemas.inject(chromeObj, chromeApiWrapper); + Schemas.exportLazyGetter(contentWindow, "chrome", () => { + let chromeApiWrapper = Object.create(this.childManager); + chromeApiWrapper.isChromeCompat = true; - if (viewType == "background") { - apiManager.global.initializeBackgroundPage(contentWindow); - } + let chromeObj = Cu.createObjectIn(contentWindow); + Schemas.inject(chromeObj, chromeApiWrapper); + return chromeObj; + }); this.extension.views.add(this); } @@ -230,12 +220,45 @@ class ExtensionContext extends BaseContext { return; } + if (this.contentWindow) { + this.contentWindow.close(); + } + super.unload(); - this.childManager.close(); this.extension.views.delete(this); } } +defineLazyGetter(ExtensionContext.prototype, "messenger", function() { + let filter = {extensionId: this.extension.id}; + let optionalFilter = {}; + // Addon-generated messages (not necessarily from the same process as the + // addon itself) are sent to the main process, which forwards them via the + // parent process message manager. Specific replies can be sent to the frame + // message manager. + return new Messenger(this, [Services.cpmm, this.messageManager], this.sender, + filter, optionalFilter); +}); + +defineLazyGetter(ExtensionContext.prototype, "childManager", function() { + let localApis = {}; + apiManager.generateAPIs(this, localApis); + + if (this.viewType == "background") { + apiManager.global.initializeBackgroundPage(this.contentWindow); + } + + let childManager = new WannabeChildAPIManager(this, this.messageManager, localApis, { + envType: "addon_parent", + viewType: this.viewType, + url: this.uri.spec, + }); + + this.callOnClose(childManager); + + return childManager; +}); + // All subframes in a tab, background page, popup, etc. have the same view type. // This class keeps track of such global state. // Note that this is created even for non-extension tabs because at present we diff --git a/toolkit/components/extensions/ExtensionContent.jsm b/toolkit/components/extensions/ExtensionContent.jsm index 8222d346b3ce..a6f08c4773fc 100644 --- a/toolkit/components/extensions/ExtensionContent.jsm +++ b/toolkit/components/extensions/ExtensionContent.jsm @@ -49,6 +49,7 @@ Cu.import("resource://gre/modules/ExtensionChild.jsm"); Cu.import("resource://gre/modules/ExtensionUtils.jsm"); var { runSafeSyncWithoutClone, + defineLazyGetter, BaseContext, LocaleData, Messenger, @@ -274,7 +275,6 @@ class ExtensionContext extends BaseContext { this.scripts = []; - let prin; let contentPrincipal = contentWindow.document.nodePrincipal; let ssm = Services.scriptSecurityManager; @@ -284,12 +284,13 @@ class ExtensionContext extends BaseContext { attrs.addonId = this.extension.id; let extensionPrincipal = ssm.createCodebasePrincipal(this.extension.baseURI, attrs); + let principal; if (ssm.isSystemPrincipal(contentPrincipal)) { // Make sure we don't hand out the system principal by accident. // also make sure that the null principal has the right origin attributes - prin = ssm.createNullPrincipal(attrs); + principal = ssm.createNullPrincipal(attrs); } else { - prin = [contentPrincipal, extensionPrincipal]; + principal = [contentPrincipal, extensionPrincipal]; } if (isExtensionPage) { @@ -314,7 +315,7 @@ class ExtensionContext extends BaseContext { addonId: attrs.addonId, }; - this.sandbox = Cu.Sandbox(prin, { + this.sandbox = Cu.Sandbox(principal, { metadata, sandboxPrototype: contentWindow, wantXrays: true, @@ -337,32 +338,24 @@ class ExtensionContext extends BaseContext { configurable: true, }); - let url = contentWindow.location.href; - // The |sender| parameter is passed directly to the extension. - let sender = {id: this.extension.uuid, frameId, url}; - let filter = {extensionId: this.extension.id}; - let optionalFilter = {frameId}; - this.messenger = new Messenger(this, [this.messageManager], sender, filter, optionalFilter); + this.url = contentWindow.location.href; - this.chromeObj = Cu.createObjectIn(this.sandbox, {defineAs: "browser"}); + defineLazyGetter(this, "chromeObj", () => { + let chromeObj = Cu.createObjectIn(this.sandbox); - // Sandboxes don't get Xrays for some weird compatibility - // reason. However, we waive here anyway in case that changes. - Cu.waiveXrays(this.sandbox).chrome = this.chromeObj; - - let localApis = {}; - apiManager.generateAPIs(this, localApis); - this.childManager = new ChildAPIManager(this, this.messageManager, localApis, { - envType: "content_parent", - url, + Schemas.inject(chromeObj, this.childManager); + return chromeObj; }); - Schemas.inject(this.chromeObj, this.childManager); + Schemas.exportLazyGetter(this.sandbox, "browser", () => this.chromeObj); + Schemas.exportLazyGetter(this.sandbox, "chrome", () => this.chromeObj); - // This is an iframe with content script API enabled. (See Bug 1214658 for rationale) + // This is an iframe with content script API enabled (bug 1214658) if (isExtensionPage) { - Cu.waiveXrays(this.contentWindow).chrome = this.chromeObj; - Cu.waiveXrays(this.contentWindow).browser = this.chromeObj; + Schemas.exportLazyGetter(this.contentWindow, + "browser", () => this.chromeObj); + Schemas.exportLazyGetter(this.contentWindow, + "chrome", () => this.chromeObj); } } @@ -398,8 +391,6 @@ class ExtensionContext extends BaseContext { close() { super.unload(); - this.childManager.close(); - if (this.contentWindow) { for (let script of this.scripts) { if (script.requiresCleanup) { @@ -419,6 +410,29 @@ class ExtensionContext extends BaseContext { } } +defineLazyGetter(ExtensionContext.prototype, "messenger", function() { + // The |sender| parameter is passed directly to the extension. + let sender = {id: this.extension.uuid, frameId: this.frameId, url: this.url}; + let filter = {extensionId: this.extension.id}; + let optionalFilter = {frameId: this.frameId}; + + return new Messenger(this, [this.messageManager], sender, filter, optionalFilter); +}); + +defineLazyGetter(ExtensionContext.prototype, "childManager", function() { + let localApis = {}; + apiManager.generateAPIs(this, localApis); + + let childManager = new ChildAPIManager(this, this.messageManager, localApis, { + envType: "content_parent", + url: this.url, + }); + + this.callOnClose(childManager); + + return childManager; +}); + // Responsible for creating ExtensionContexts and injecting content // scripts into them when new documents are created. DocumentManager = { @@ -708,7 +722,7 @@ DocumentManager = { } else { let contexts = this.contentScriptWindows.get(getInnerWindowID(window)) || new Map(); for (let context of contexts.values()) { - context.triggerScripts(this.getWindowState(window)); + context.triggerScripts(when); } } }, diff --git a/toolkit/components/extensions/ExtensionUtils.jsm b/toolkit/components/extensions/ExtensionUtils.jsm index d722e481805b..ff377cb16435 100644 --- a/toolkit/components/extensions/ExtensionUtils.jsm +++ b/toolkit/components/extensions/ExtensionUtils.jsm @@ -204,6 +204,8 @@ class BaseContext { this.messageManager = docShell.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIContentFrameMessageManager); + MessageChannel.setupMessageManagers([this.messageManager]); + let onPageShow = event => { if (!event || event.target === document) { this.docShell = docShell; @@ -1439,8 +1441,6 @@ function Messenger(context, messageManagers, sender, filter, optionalFilter) { this.sender = sender; this.filter = filter; this.optionalFilter = optionalFilter; - - MessageChannel.setupMessageManagers(messageManagers); } Messenger.prototype = { @@ -2170,7 +2170,49 @@ const stylesheetMap = new DefaultMap(url => { return styleSheetService.preloadSheet(uri, styleSheetService.AGENT_SHEET); }); +/** + * Defines a lazy getter for the given property on the given object. The + * first time the property is accessed, the return value of the getter + * is defined on the current `this` object with the given property name. + * Importantly, this means that a lazy getter defined on an object + * prototype will be invoked separately for each object instance that + * it's accessed on. + * + * @param {object} object + * The prototype object on which to define the getter. + * @param {string|Symbol} prop + * The property name for which to define the getter. + * @param {function} getter + * The function to call in order to generate the final property + * value. + */ +function defineLazyGetter(object, prop, getter) { + let redefine = (obj, value) => { + Object.defineProperty(obj, prop, { + enumerable: true, + configurable: true, + writable: true, + value, + }); + return value; + }; + + Object.defineProperty(object, prop, { + enumerable: true, + configurable: true, + + get() { + return redefine(this, getter.call(this)); + }, + + set(value) { + redefine(this, value); + }, + }); +} + this.ExtensionUtils = { + defineLazyGetter, detectLanguage, extend, flushJarCache, diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_api_permissions.js b/toolkit/components/extensions/test/xpcshell/test_ext_api_permissions.js index 333cdd145290..85ac798e2848 100644 --- a/toolkit/components/extensions/test/xpcshell/test_ext_api_permissions.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_api_permissions.js @@ -14,7 +14,10 @@ function getNextContext() { add_task(function* test_storage_api_without_permissions() { let extension = ExtensionTestUtils.loadExtension({ - background() {}, + background() { + // Force API initialization. + void browser.storage; + }, manifest: { permissions: [], @@ -26,6 +29,9 @@ add_task(function* test_storage_api_without_permissions() { let context = yield contextPromise; + // Force API initialization. + void context.apiObj; + ok(!("storage" in context._unwrappedAPIs), "The storage API should not be initialized"); @@ -34,7 +40,9 @@ add_task(function* test_storage_api_without_permissions() { add_task(function* test_storage_api_with_permissions() { let extension = ExtensionTestUtils.loadExtension({ - background() {}, + background() { + void browser.storage; + }, manifest: { permissions: ["storage"], @@ -46,6 +54,9 @@ add_task(function* test_storage_api_with_permissions() { let context = yield contextPromise; + // Force API initialization. + void context.apiObj; + equal(typeof context._unwrappedAPIs.storage, "object", "The storage API should be initialized"); From fbd0e78bd4c5e979b9c23924f255dcd5a3677fdd Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Sun, 30 Oct 2016 20:24:12 -0700 Subject: [PATCH 11/96] Bug 1312690: Lazily initialize binding implementation objects. r=aswan MozReview-Commit-ID: E8K6In6JtWO --HG-- extra : rebase_source : 285a97db6ed7febec4188b2842730fe690a7e5b1 --- toolkit/components/extensions/Schemas.jsm | 27 ++++++++++--------- .../test_ext_schemas_allowed_contexts.js | 15 ++++++++--- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/toolkit/components/extensions/Schemas.jsm b/toolkit/components/extensions/Schemas.jsm index 0fc0628bacbc..284ab518f9c4 100644 --- a/toolkit/components/extensions/Schemas.jsm +++ b/toolkit/components/extensions/Schemas.jsm @@ -605,13 +605,12 @@ class Entry { * `context` is used to call the actual implementation * of a given function or event. * - * @param {SchemaAPIInterface} apiImpl The implementation of the API. * @param {Array} path The API path, e.g. `["storage", "local"]`. * @param {string} name The method name, e.g. "get". * @param {object} dest The object where `path`.`name` should be stored. * @param {InjectionContext} context */ - inject(apiImpl, path, name, dest, context) { + inject(path, name, dest, context) { } } @@ -935,7 +934,7 @@ class StringType extends Type { return baseType == "string"; } - inject(apiImpl, path, name, dest, context) { + inject(path, name, dest, context) { if (this.enumeration) { exportLazyGetter(dest, name, () => { let obj = Cu.createObjectIn(dest); @@ -1414,7 +1413,7 @@ class ValueProperty extends Entry { this.value = value; } - inject(apiImpl, path, name, dest, context) { + inject(path, name, dest, context) { dest[name] = this.value; } } @@ -1434,11 +1433,13 @@ class TypeProperty extends Entry { throw context.makeError(`${msg} for ${this.namespaceName}.${this.name}.`); } - inject(apiImpl, path, name, dest, context) { + inject(path, name, dest, context) { if (this.unsupported) { return; } + let apiImpl = context.getImplementation(path.join("."), name); + let getStub = () => { this.checkDeprecated(context); return apiImpl.getProperty(); @@ -1487,7 +1488,7 @@ class SubModuleProperty extends Entry { this.properties = properties; } - inject(apiImpl, path, name, dest, context) { + inject(path, name, dest, context) { exportLazyGetter(dest, name, () => { let obj = Cu.createObjectIn(dest); @@ -1508,8 +1509,7 @@ class SubModuleProperty extends Entry { let namespace = subpath.join("."); let allowedContexts = fun.allowedContexts.length ? fun.allowedContexts : ns.defaultContexts; if (context.shouldInject(namespace, fun.name, allowedContexts)) { - let apiImpl = context.getImplementation(namespace, fun.name); - fun.inject(apiImpl, subpath, fun.name, obj, context); + fun.inject(subpath, fun.name, obj, context); } } @@ -1622,7 +1622,7 @@ class FunctionEntry extends CallEntry { this.hasAsyncCallback = type.hasAsyncCallback; } - inject(apiImpl, path, name, dest, context) { + inject(path, name, dest, context) { if (this.unsupported) { return; } @@ -1632,6 +1632,8 @@ class FunctionEntry extends CallEntry { } exportLazyGetter(dest, name, () => { + let apiImpl = context.getImplementation(path.join("."), name); + let stub; if (this.isAsync) { stub = (...args) => { @@ -1684,7 +1686,7 @@ class Event extends CallEntry { return r.value; } - inject(apiImpl, path, name, dest, context) { + inject(path, name, dest, context) { if (this.unsupported) { return; } @@ -1694,6 +1696,8 @@ class Event extends CallEntry { } exportLazyGetter(dest, name, () => { + let apiImpl = context.getImplementation(path.join("."), name); + let addStub = (listener, ...args) => { listener = this.checkListener(listener, context); let actuals = this.checkParameters(args, context); @@ -2021,8 +2025,7 @@ this.Schemas = { } if (context.shouldInject(ns.name, name, allowedContexts)) { - let apiImpl = context.getImplementation(ns.name, name); - entry.inject(apiImpl, [ns.name], name, obj, context); + entry.inject([ns.name], name, obj, context); } } diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_schemas_allowed_contexts.js b/toolkit/components/extensions/test/xpcshell/test_ext_schemas_allowed_contexts.js index 657cc91cbed5..606459764ce8 100644 --- a/toolkit/components/extensions/test/xpcshell/test_ext_schemas_allowed_contexts.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_schemas_allowed_contexts.js @@ -73,7 +73,7 @@ add_task(function* testRestrictions() { let localWrapper = { shouldInject(ns, name, allowedContexts) { name = name === null ? ns : ns + "." + name; - results[name] = allowedContexts.join(); + results[name] = allowedContexts.join(","); return true; }, getImplementation() { @@ -88,8 +88,17 @@ add_task(function* testRestrictions() { Schemas.inject(root, localWrapper); function verify(path, expected) { + let obj = root; + for (let thing of path.split(".")) { + try { + obj = obj[thing]; + } catch (e) { + // Blech. + } + } + let result = results[path]; - do_check_eq(result, expected); + equal(result, expected); } verify("noAllowedContexts", ""); @@ -129,7 +138,7 @@ add_task(function* testRestrictions() { // This is a constant, so it does not matter that getImplementation does not // return an implementation since the API injector should take care of it. - do_check_eq(root.noAllowedContexts.prop3, 1); + equal(root.noAllowedContexts.prop3, 1); Assert.throws(() => root.noAllowedContexts.prop1, /undefined/, From 74012f4fcdfb1a414edb0d8330ab8095cb1a334f Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Wed, 2 Nov 2016 13:57:19 -0700 Subject: [PATCH 12/96] Bug 1312690: Load content scripts asynchronously when possible. r=aswan MozReview-Commit-ID: BzpZA4stbCI --HG-- extra : rebase_source : 1b93f4ee7add989b3716b75c26ee9835e086d29c --- js/xpconnect/idl/mozIJSSubScriptLoader.idl | 3 ++ .../extensions/ExtensionContent.jsm | 43 +++++++++++++++---- .../mochitest/test_ext_contentscript.html | 15 ++++--- .../test_ext_contentscript_context.html | 1 + 4 files changed, 47 insertions(+), 15 deletions(-) diff --git a/js/xpconnect/idl/mozIJSSubScriptLoader.idl b/js/xpconnect/idl/mozIJSSubScriptLoader.idl index bbd9169f99f7..1b5ed901b229 100644 --- a/js/xpconnect/idl/mozIJSSubScriptLoader.idl +++ b/js/xpconnect/idl/mozIJSSubScriptLoader.idl @@ -39,6 +39,9 @@ interface mozIJSSubScriptLoader : nsISupports * - charset: specifying the character encoding of the file (default: ASCII) * - target: an object to evaluate onto (default: global object of the caller) * - ignoreCache: if set to true, will bypass the cache for reading the file. + * - async: if set to true, the script will be loaded + * asynchronously, and a Promise is returned which + * resolves to its result when execution is complete. * @retval rv the value returned by the sub-script */ [implicit_jscontext] diff --git a/toolkit/components/extensions/ExtensionContent.jsm b/toolkit/components/extensions/ExtensionContent.jsm index a6f08c4773fc..f4667759f4b2 100644 --- a/toolkit/components/extensions/ExtensionContent.jsm +++ b/toolkit/components/extensions/ExtensionContent.jsm @@ -188,7 +188,32 @@ Script.prototype = { } }, - tryInject(window, sandbox, shouldRun) { + /** + * Tries to inject this script into the given window and sandbox, if + * there are pending operations for the window's current load state. + * + * @param {Window} window + * The DOM Window to inject the scripts and CSS into. + * @param {Sandbox} sandbox + * A Sandbox inheriting from `window` in which to evaluate the + * injected scripts. + * @param {function} shouldRun + * A function which, when passed the document load state that a + * script is expected to run at, returns `true` if we should + * currently be injecting scripts for that load state. + * + * For initial injection of a script, this function should + * return true if the document is currently in or has already + * passed through the given state. For injections triggered by + * document state changes, it should only return true if the + * given state exactly matches the state that triggered the + * change. + * @param {string} when + * The document's current load state, or if triggered by a + * document state change, the new document state that triggered + * the injection. + */ + tryInject(window, sandbox, shouldRun, when) { if (!this.matches(window)) { this.deferred.reject({message: "No matching window"}); return; @@ -218,7 +243,9 @@ Script.prototype = { let options = { target: sandbox, charset: "UTF-8", - async: false, + // Inject asynchronously unless we're expected to inject before any + // page scripts have run, and we haven't already missed that boat. + async: this.run_at !== "document_start" || when !== "document_start", }; try { result = Services.scriptloader.loadSubScriptWithOptions(url, options); @@ -363,13 +390,13 @@ class ExtensionContext extends BaseContext { return this.sandbox; } - execute(script, shouldRun) { - script.tryInject(this.contentWindow, this.sandbox, shouldRun); + execute(script, shouldRun, when) { + script.tryInject(this.contentWindow, this.sandbox, shouldRun, when); } - addScript(script) { + addScript(script, when) { let state = DocumentManager.getWindowState(this.contentWindow); - this.execute(script, scheduled => isWhenBeforeOrSame(scheduled, state)); + this.execute(script, scheduled => isWhenBeforeOrSame(scheduled, state), when); // Save the script in case it has pending operations in later load // states, but only if we're before document_idle, or require cleanup. @@ -380,7 +407,7 @@ class ExtensionContext extends BaseContext { triggerScripts(documentState) { for (let script of this.scripts) { - this.execute(script, scheduled => scheduled == documentState); + this.execute(script, scheduled => scheduled == documentState, documentState); } if (documentState == "document_idle") { // Don't bother saving scripts after document_idle. @@ -715,7 +742,7 @@ DocumentManager = { for (let script of extension.scripts) { if (script.matches(window)) { let context = this.getContentScriptContext(extension, window); - context.addScript(script); + context.addScript(script, when); } } } diff --git a/toolkit/components/extensions/test/mochitest/test_ext_contentscript.html b/toolkit/components/extensions/test/mochitest/test_ext_contentscript.html index 5f71c631dc2d..39f1bfabd0ae 100644 --- a/toolkit/components/extensions/test/mochitest/test_ext_contentscript.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_contentscript.html @@ -15,26 +15,27 @@ add_task(function* test_contentscript() { function background() { - browser.runtime.onMessage.addListener(([msg, expectedState, readyState], sender) => { + browser.runtime.onMessage.addListener(([msg, expectedStates, readyState], sender) => { if (msg == "chrome-namespace-ok") { browser.test.sendMessage(msg); return; } - browser.test.assertEq(msg, "script-run", "message type is correct"); - browser.test.assertEq(readyState, expectedState, "readyState is correct"); - browser.test.sendMessage("script-run-" + expectedState); + browser.test.assertEq("script-run", msg, "message type is correct"); + browser.test.assertTrue(expectedStates.includes(readyState), + `readyState "${readyState}" is one of [${expectedStates}]`); + browser.test.sendMessage("script-run-" + expectedStates[0]); }); } function contentScriptStart() { - browser.runtime.sendMessage(["script-run", "loading", document.readyState]); + browser.runtime.sendMessage(["script-run", ["loading"], document.readyState]); } function contentScriptEnd() { - browser.runtime.sendMessage(["script-run", "interactive", document.readyState]); + browser.runtime.sendMessage(["script-run", ["interactive", "complete"], document.readyState]); } function contentScriptIdle() { - browser.runtime.sendMessage(["script-run", "complete", document.readyState]); + browser.runtime.sendMessage(["script-run", ["complete"], document.readyState]); } function contentScript() { diff --git a/toolkit/components/extensions/test/mochitest/test_ext_contentscript_context.html b/toolkit/components/extensions/test/mochitest/test_ext_contentscript_context.html index f8e9a6be5817..ff64c25f5ca8 100644 --- a/toolkit/components/extensions/test/mochitest/test_ext_contentscript_context.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_contentscript_context.html @@ -32,6 +32,7 @@ add_task(function* test_contentscript_context() { content_scripts: [{ "matches": ["http://example.com/"], "js": ["content_script.js"], + "run_at": "document_start", }], }, From f163c8b4cb266a942ba2b4d615f1892e3580d915 Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Mon, 31 Oct 2016 12:46:39 -0700 Subject: [PATCH 13/96] Bug 1312690: Create content script sandboxes in same zone as content window. r=aswan MozReview-Commit-ID: K44jMuVfhz4 --HG-- extra : rebase_source : c37bafe019c53b3e3528ab750bf17cad6507e457 --- toolkit/components/extensions/ExtensionContent.jsm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/toolkit/components/extensions/ExtensionContent.jsm b/toolkit/components/extensions/ExtensionContent.jsm index f4667759f4b2..4715ab328a5e 100644 --- a/toolkit/components/extensions/ExtensionContent.jsm +++ b/toolkit/components/extensions/ExtensionContent.jsm @@ -330,6 +330,7 @@ class ExtensionContext extends BaseContext { // into the iframe's window, see Bug 1214658 for rationale) this.sandbox = Cu.Sandbox(contentWindow, { sandboxPrototype: contentWindow, + sameZoneAs: contentWindow, wantXrays: false, isWebExtensionContentScript: true, }); @@ -345,6 +346,7 @@ class ExtensionContext extends BaseContext { this.sandbox = Cu.Sandbox(principal, { metadata, sandboxPrototype: contentWindow, + sameZoneAs: contentWindow, wantXrays: true, isWebExtensionContentScript: true, wantExportHelpers: true, From d374c039ef0b0440fffb111e4f4d7e6e91bbd6f6 Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Wed, 2 Nov 2016 13:45:01 -0700 Subject: [PATCH 14/96] Bug 1312690: Speed up MatchPattern matching. r=aswan MozReview-Commit-ID: 2QmKnG0aBQw --HG-- extra : rebase_source : 2480036cad45922e56e9db6dfd63b336b578e5ce --- toolkit/modules/addons/MatchPattern.jsm | 59 ++++++++++--------------- 1 file changed, 24 insertions(+), 35 deletions(-) diff --git a/toolkit/modules/addons/MatchPattern.jsm b/toolkit/modules/addons/MatchPattern.jsm index a15227670788..f47074840df9 100644 --- a/toolkit/modules/addons/MatchPattern.jsm +++ b/toolkit/modules/addons/MatchPattern.jsm @@ -41,8 +41,8 @@ function globToRegexp(pat, allowQuestion) { function SingleMatchPattern(pat) { if (pat == "") { this.schemes = PERMITTED_SCHEMES; - this.host = "*"; - this.path = new RegExp(".*"); + this.hostMatch = () => true; + this.pathMatch = () => true; } else if (!pat) { this.schemes = []; } else { @@ -59,42 +59,41 @@ function SingleMatchPattern(pat) { } else { this.schemes = [match[1]]; } - this.host = match[2]; - this.path = globToRegexp(match[3], false); // We allow the host to be empty for file URLs. - if (this.host == "" && this.schemes[0] != "file") { + if (match[2] == "" && this.schemes[0] != "file") { Cu.reportError(`Invalid match pattern: '${pat}'`); this.schemes = []; return; } + + this.host = match[2]; + this.hostMatch = this.getHostMatcher(match[2]); + + let pathMatch = globToRegexp(match[3], false); + this.pathMatch = pathMatch.test.bind(pathMatch); } } SingleMatchPattern.prototype = { - matches(uri, ignorePath = false) { - if (!this.schemes.includes(uri.scheme)) { - return false; - } - + getHostMatcher(host) { // This code ignores the port, as Chrome does. - if (this.host == "*") { - // Don't check anything. - } else if (this.host[0] == "*") { - // It must be *.foo. We also need to allow foo by itself. - let suffix = this.host.substr(2); - if (uri.host != suffix && !uri.host.endsWith("." + suffix)) { - return false; - } - } else if (this.host != uri.host) { - return false; + if (host == "*") { + return () => true; } + if (host.startsWith("*.")) { + let suffix = host.substr(2); + let dotSuffix = "." + suffix; - if (!ignorePath && !this.path.test(uri.path)) { - return false; + return ({host}) => host === suffix || host.endsWith(dotSuffix); } + return uri => uri.host === host; + }, - return true; + matches(uri, ignorePath = false) { + return (this.schemes.includes(uri.scheme) && + this.hostMatch(uri) && + (ignorePath || this.pathMatch(uri.path))); }, }; @@ -112,21 +111,11 @@ this.MatchPattern = function(pat) { MatchPattern.prototype = { // |uri| should be an nsIURI. matches(uri) { - for (let matcher of this.matchers) { - if (matcher.matches(uri)) { - return true; - } - } - return false; + return this.matchers.some(matcher => matcher.matches(uri)); }, matchesIgnoringPath(uri) { - for (let matcher of this.matchers) { - if (matcher.matches(uri, true)) { - return true; - } - } - return false; + return this.matchers.some(matcher => matcher.matches(uri, true)); }, // Checks that this match pattern grants access to read the given From 46cb76edfadde53f919c7dbab881afeb2fa45d45 Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Tue, 1 Nov 2016 16:53:55 -0700 Subject: [PATCH 15/96] Bug 1312690: Remove now-useless .matches() call from tryInject. r=aswan MozReview-Commit-ID: 9EVWJFSe1tU --HG-- extra : rebase_source : 5fb8ba9579fbc3e58ed214bad18fffd055bf61ac --- toolkit/components/extensions/ExtensionContent.jsm | 5 ----- 1 file changed, 5 deletions(-) diff --git a/toolkit/components/extensions/ExtensionContent.jsm b/toolkit/components/extensions/ExtensionContent.jsm index 4715ab328a5e..026b099c84a5 100644 --- a/toolkit/components/extensions/ExtensionContent.jsm +++ b/toolkit/components/extensions/ExtensionContent.jsm @@ -214,11 +214,6 @@ Script.prototype = { * the injection. */ tryInject(window, sandbox, shouldRun, when) { - if (!this.matches(window)) { - this.deferred.reject({message: "No matching window"}); - return; - } - if (shouldRun("document_start")) { let winUtils = window.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindowUtils); From 92f05e0b165f7283943137d282d0fc2122a644ef Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Tue, 1 Nov 2016 16:56:21 -0700 Subject: [PATCH 16/96] Bug 1312690: Add some stupid microoptimizations. r=aswan MozReview-Commit-ID: 4qoNuG2k0KS --HG-- extra : rebase_source : fff22127b80bb8edc8578c2035d4689452ed4f6e --- toolkit/components/extensions/ExtensionContent.jsm | 6 +++--- toolkit/components/extensions/ExtensionManagement.jsm | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/toolkit/components/extensions/ExtensionContent.jsm b/toolkit/components/extensions/ExtensionContent.jsm index 026b099c84a5..d16ce6166dc3 100644 --- a/toolkit/components/extensions/ExtensionContent.jsm +++ b/toolkit/components/extensions/ExtensionContent.jsm @@ -215,11 +215,11 @@ Script.prototype = { */ tryInject(window, sandbox, shouldRun, when) { if (shouldRun("document_start")) { - let winUtils = window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - let {cssURLs} = this; if (cssURLs.length > 0) { + let winUtils = window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + let method = this.remove_css ? winUtils.removeSheetUsingURIString : winUtils.loadSheetUsingURIString; for (let url of cssURLs) { runSafeSyncWithoutClone(method, url, winUtils.AUTHOR_SHEET); diff --git a/toolkit/components/extensions/ExtensionManagement.jsm b/toolkit/components/extensions/ExtensionManagement.jsm index dca162f4e355..324c5b71b933 100644 --- a/toolkit/components/extensions/ExtensionManagement.jsm +++ b/toolkit/components/extensions/ExtensionManagement.jsm @@ -239,8 +239,7 @@ var Service = { // extensionURIToAddonID, which ensures that we don't inject our // API into webAccessibleResources or remote web pages. function getAddonIdForWindow(window) { - let principal = window.document.nodePrincipal; - return principal.originAttributes.addonId; + return Cu.getObjectPrincipal(window).originAttributes.addonId; } const API_LEVELS = Object.freeze({ From e6b9c2b457e8598740120543ddf5ac8e4ad6cfd1 Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Tue, 1 Nov 2016 09:41:30 -0700 Subject: [PATCH 17/96] Bug 1307551: Don't attempt to pre-load popup for disabled browserAction. r=bsilverberg MozReview-Commit-ID: 8imoqD1Xoja --HG-- extra : rebase_source : 8f0fa63b813f77bc8cde28aa1878c880ac96a3df extra : amend_source : dee66aef3c55b737fb8f946d9184fc82794f0ff5 --- .../extensions/ext-browserAction.js | 3 +- .../browser_ext_browserAction_popup.js | 73 ++++++++++++++++++- 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/browser/components/extensions/ext-browserAction.js b/browser/components/extensions/ext-browserAction.js index 8226927ed037..2d1760dab5f7 100644 --- a/browser/components/extensions/ext-browserAction.js +++ b/browser/components/extensions/ext-browserAction.js @@ -193,8 +193,9 @@ BrowserAction.prototype = { // be ready by the time we get a complete click. let tab = window.gBrowser.selectedTab; let popupURL = this.getProperty(tab, "popup"); + let enabled = this.getProperty(tab, "enabled"); - if (popupURL) { + if (popupURL && enabled) { this.pendingPopup = this.getPopup(window, popupURL); window.addEventListener("mouseup", this, true); } else { diff --git a/browser/components/extensions/test/browser/browser_ext_browserAction_popup.js b/browser/components/extensions/test/browser/browser_ext_browserAction_popup.js index 04c72e4fe2f2..d993fbea2e67 100644 --- a/browser/components/extensions/test/browser/browser_ext_browserAction_popup.js +++ b/browser/components/extensions/test/browser/browser_ext_browserAction_popup.js @@ -273,7 +273,7 @@ add_task(function* testBrowserActionClickCanceled() { // We need to do these tests during the mouseup event cycle, since the click // and command events will be dispatched immediately after mouseup, and void // the results. - let mouseUpPromise = BrowserTestUtils.waitForEvent(widget.node, "mouseup", event => { + let mouseUpPromise = BrowserTestUtils.waitForEvent(widget.node, "mouseup", false, event => { isnot(browserAction.pendingPopup, null, "Pending popup was not cleared"); isnot(browserAction.pendingPopupTimeout, null, "Have a pending popup timeout"); return true; @@ -291,3 +291,74 @@ add_task(function* testBrowserActionClickCanceled() { yield extension.unload(); }); + +add_task(function* testBrowserActionDisabled() { + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + "browser_action": { + "default_popup": "popup.html", + "browser_style": true, + }, + }, + + background() { + browser.browserAction.disable(); + }, + + files: { + "popup.html": ``, + "popup.js"() { + browser.test.fail("Should not get here"); + }, + }, + }); + + yield extension.startup(); + + const {GlobalManager, Management: {global: {browserActionFor}}} = Cu.import("resource://gre/modules/Extension.jsm", {}); + + let ext = GlobalManager.extensionMap.get(extension.id); + let browserAction = browserActionFor(ext); + + let widget = getBrowserActionWidget(extension).forWindow(window); + + // Test canceled click. + EventUtils.synthesizeMouseAtCenter(widget.node, {type: "mousedown", button: 0}, window); + + is(browserAction.pendingPopup, null, "Have no pending popup"); + is(browserAction.pendingPopupTimeout, null, "Have no pending popup timeout"); + + EventUtils.synthesizeMouseAtCenter(document.documentElement, {type: "mouseup", button: 0}, window); + + is(browserAction.pendingPopup, null, "Have no pending popup"); + is(browserAction.pendingPopupTimeout, null, "Have no pending popup timeout"); + + + // Test completed click. + EventUtils.synthesizeMouseAtCenter(widget.node, {type: "mousedown", button: 0}, window); + + is(browserAction.pendingPopup, null, "Have no pending popup"); + is(browserAction.pendingPopupTimeout, null, "Have no pending popup timeout"); + + // We need to do these tests during the mouseup event cycle, since the click + // and command events will be dispatched immediately after mouseup, and void + // the results. + let mouseUpPromise = BrowserTestUtils.waitForEvent(widget.node, "mouseup", false, event => { + is(browserAction.pendingPopup, null, "Have no pending popup"); + is(browserAction.pendingPopupTimeout, null, "Have no pending popup timeout"); + return true; + }); + + EventUtils.synthesizeMouseAtCenter(widget.node, {type: "mouseup", button: 0}, window); + + yield mouseUpPromise; + + is(browserAction.pendingPopup, null, "Have no pending popup"); + is(browserAction.pendingPopupTimeout, null, "Have no pending popup timeout"); + + // Give the popup a chance to load and trigger a failure, if it was + // erroneously opened. + yield new Promise(resolve => setTimeout(resolve, 250)); + + yield extension.unload(); +}); From 9ec833d34e70e72b5ef1a288a54e127895571ed4 Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Tue, 11 Oct 2016 21:08:33 +0100 Subject: [PATCH 18/96] Bug 1308421: Handle MessageChannel responses correctly after docshells have been swapped. r=aswan MozReview-Commit-ID: HDTrBfdclXd --HG-- extra : rebase_source : ec09a6772df798982fc2d52153186fd901a3984c extra : amend_source : da03f0645ee6561497936f7c42d46f941ad2dd77 extra : histedit_source : 0937500d487c46a738c0a2783b04054ec238a940 --- .../extensions/test/browser/browser.ini | 1 + .../browser/browser_ext_popup_sendMessage.js | 94 +++++++++++++ .../components/extensions/MessageChannel.jsm | 128 ++++++++++++++++-- 3 files changed, 214 insertions(+), 9 deletions(-) create mode 100644 browser/components/extensions/test/browser/browser_ext_popup_sendMessage.js diff --git a/browser/components/extensions/test/browser/browser.ini b/browser/components/extensions/test/browser/browser.ini index 0349d45ed66f..5c3dccd3a7c9 100644 --- a/browser/components/extensions/test/browser/browser.ini +++ b/browser/components/extensions/test/browser/browser.ini @@ -53,6 +53,7 @@ tags = webextensions [browser_ext_popup_api_injection.js] [browser_ext_popup_background.js] [browser_ext_popup_corners.js] +[browser_ext_popup_sendMessage.js] [browser_ext_popup_shutdown.js] [browser_ext_runtime_openOptionsPage.js] [browser_ext_runtime_openOptionsPage_uninstall.js] diff --git a/browser/components/extensions/test/browser/browser_ext_popup_sendMessage.js b/browser/components/extensions/test/browser/browser_ext_popup_sendMessage.js new file mode 100644 index 000000000000..715c01ff8554 --- /dev/null +++ b/browser/components/extensions/test/browser/browser_ext_popup_sendMessage.js @@ -0,0 +1,94 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +"use strict"; + +add_task(function* test_popup_sendMessage_reply() { + let scriptPage = url => `${url}`; + + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + "browser_action": { + "default_popup": "popup.html", + "browser_style": true, + }, + + "page_action": { + "default_popup": "popup.html", + "browser_style": true, + }, + }, + + files: { + "popup.html": scriptPage("popup.js"), + "popup.js": function() { + browser.runtime.onMessage.addListener(msg => { + if (msg == "popup-ping") { + return Promise.resolve("popup-pong"); + } + }); + + browser.runtime.sendMessage("background-ping").then(response => { + browser.test.sendMessage("background-ping-response", response); + }); + }, + }, + + background() { + browser.tabs.query({active: true, currentWindow: true}).then(([tab]) => { + return browser.pageAction.show(tab.id); + }).then(() => { + browser.test.sendMessage("page-action-ready"); + }); + + browser.runtime.onMessage.addListener(msg => { + if (msg == "background-ping") { + browser.runtime.sendMessage("popup-ping").then(response => { + browser.test.sendMessage("popup-ping-response", response); + }); + + return new Promise(resolve => { + // Wait long enough that we're relatively sure the docShells have + // been swapped. Note that this value is fairly arbitrary. The load + // event that triggers the swap should happen almost immediately + // after the message is sent. The extra quarter of a second gives us + // enough leeway that we can expect to respond after the swap in the + // vast majority of cases. + setTimeout(resolve, 250); + }).then(() => { + return "background-pong"; + }); + } + }); + }, + }); + + yield extension.startup(); + + { + clickBrowserAction(extension); + + let pong = yield extension.awaitMessage("background-ping-response"); + is(pong, "background-pong", "Got pong"); + + pong = yield extension.awaitMessage("popup-ping-response"); + is(pong, "popup-pong", "Got pong"); + + yield closeBrowserAction(extension); + } + + yield extension.awaitMessage("page-action-ready"); + + { + clickPageAction(extension); + + let pong = yield extension.awaitMessage("background-ping-response"); + is(pong, "background-pong", "Got pong"); + + pong = yield extension.awaitMessage("popup-ping-response"); + is(pong, "popup-pong", "Got pong"); + + yield closePageAction(extension); + } + + yield extension.unload(); +}); diff --git a/toolkit/components/extensions/MessageChannel.jsm b/toolkit/components/extensions/MessageChannel.jsm index 6241d06b8831..3bcd8ffab942 100644 --- a/toolkit/components/extensions/MessageChannel.jsm +++ b/toolkit/components/extensions/MessageChannel.jsm @@ -112,7 +112,119 @@ XPCOMUtils.defineLazyModuleGetter(this, "PromiseUtils", "resource://gre/modules/PromiseUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm"); +/** + * Acts as a proxy for a message manager or message manager owner, and + * tracks docShell swaps so that messages are always sent to the same + * receiver, even if it is moved to a different . + * + * Currently only proxies message sending functions, and does not handle + * transfering listeners in any way. + * + * @param {nsIMessageSender|Element} target + * The target message manager on which to send messages, or the + * element which owns it. + */ +class MessageManagerProxy { + constructor(target) { + if (target instanceof Ci.nsIMessageSender) { + Object.defineProperty(this, "messageManager", { + value: target, + configurable: true, + writable: true, + }); + } else { + this.addListeners(target); + } + } + /** + * Disposes of the proxy object, removes event listeners, and drops + * all references to the underlying message manager. + * + * Must be called before the last reference to the proxy is dropped, + * unless the underlying message manager or is also being + * destroyed. + */ + dispose() { + if (this.eventTarget) { + this.removeListeners(this.eventTarget); + this.eventTarget = null; + } else { + this.messageManager = null; + } + } + + /** + * Returns true if the given target is the same as, or owns, the given + * message manager. + * + * @param {nsIMessageSender|MessageManagerProxy|Element} target + * The message manager, MessageManagerProxy, or + * element agaisnt which to match. + * @param {nsIMessageSender} messageManager + * The message manager against which to match `target`. + * + * @returns {boolean} + * True if `messageManager` is the same object as `target`, or + * `target` is a MessageManagerProxy or element that + * is tied to it. + */ + static matches(target, messageManager) { + return target === messageManager || target.messageManager === messageManager; + } + + /** + * @property {nsIMessageSender|null} messageManager + * The message manager that is currently being proxied. This + * may change during the life of the proxy object, so should + * not be stored elsewhere. + */ + get messageManager() { + return this.eventTarget && this.eventTarget.messageManager; + } + + /** + * Sends a message on the proxied message manager. + * + * @param {array} args + * Arguments to be passed verbatim to the underlying + * sendAsyncMessage method. + * @returns {undefined} + */ + sendAsyncMessage(...args) { + return this.messageManager.sendAsyncMessage(...args); + } + + /** + * @private + * Adds docShell swap listeners to the message manager owner. + * + * @param {Element} target + * The target element. + */ + addListeners(target) { + target.addEventListener("SwapDocShells", this); + this.eventTarget = target; + } + + /** + * @private + * Removes docShell swap listeners to the message manager owner. + * + * @param {Element} target + * The target element. + */ + removeListeners(target) { + target.removeEventListener("SwapDocShells", this); + } + + handleEvent(event) { + if (event.type == "SwapDocShells") { + this.removeListeners(this.eventTarget); + this.addListeners(event.detail); + } + } +} /** * Handles the mapping and dispatching of messages to their registered @@ -610,14 +722,6 @@ this.MessageChannel = { * @param {nsIMessageSender|{messageManager:nsIMessageSender}} data.target */ _handleMessage(handlers, data) { - // The target passed to `receiveMessage` is sometimes a message manager - // owner instead of a message manager, so make sure to convert it to a - // message manager first if necessary. - let {target} = data; - if (!(target instanceof Ci.nsIMessageSender)) { - target = target.messageManager; - } - if (data.responseType == this.RESPONSE_NONE) { handlers.forEach(handler => { // The sender expects no reply, so dump any errors to the console. @@ -631,6 +735,8 @@ this.MessageChannel = { return; } + let target = new MessageManagerProxy(data.target); + let deferred = { sender: data.sender, messageManager: target, @@ -673,6 +779,10 @@ this.MessageChannel = { } target.sendAsyncMessage(MESSAGE_RESPONSE, response); + }).catch(e => { + Cu.reportError(e); + }).then(() => { + target.dispose(); }); this._addPendingResponse(deferred); @@ -771,7 +881,7 @@ this.MessageChannel = { */ abortMessageManager(target, reason) { for (let response of this.pendingResponses) { - if (response.messageManager === target) { + if (MessageManagerProxy.matches(response.messageManager, target)) { response.reject(reason); } } From 108b8f0823934440514edf50d82d35f8c325ca77 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 3 Nov 2016 13:46:12 +1100 Subject: [PATCH 19/96] Bug 1314497 (part 1) - Convert intl/lwbrk/tests/TestLineBreak.cpp to a gtest. r=emk. This commit does the following. - Converts the test to a gtest, including renaming the directory. - Factors out the duplicated checking code into a separate Check() function. - Avoids printing any output unless a failure occurs. (The gtest harness prints out minimal output, like the name of the current test.) - Removes SampleWordBreakUsage(), which prints some output but doesn't actually test anything, and so isn't useful. --HG-- rename : intl/lwbrk/tests/TestLineBreak.cpp => intl/lwbrk/gtest/TestLineBreak.cpp rename : intl/lwbrk/tests/moz.build => intl/lwbrk/gtest/moz.build extra : rebase_source : e673299e237a1a8a3b0a33cfc0a0024ab7dee271 --- intl/lwbrk/{tests => gtest}/TestLineBreak.cpp | 314 +++++------------- intl/lwbrk/{tests => gtest}/moz.build | 9 +- intl/lwbrk/moz.build | 2 +- testing/cppunittest.ini | 1 - 4 files changed, 85 insertions(+), 241 deletions(-) rename intl/lwbrk/{tests => gtest}/TestLineBreak.cpp (56%) rename intl/lwbrk/{tests => gtest}/moz.build (78%) diff --git a/intl/lwbrk/tests/TestLineBreak.cpp b/intl/lwbrk/gtest/TestLineBreak.cpp similarity index 56% rename from intl/lwbrk/tests/TestLineBreak.cpp rename to intl/lwbrk/gtest/TestLineBreak.cpp index 2272c0b8407b..c320cb202c36 100644 --- a/intl/lwbrk/tests/TestLineBreak.cpp +++ b/intl/lwbrk/gtest/TestLineBreak.cpp @@ -10,11 +10,8 @@ #include "nsILineBreaker.h" #include "nsIWordBreaker.h" #include "nsLWBrkCIID.h" -#include "nsStringAPI.h" -#include "nsEmbedString.h" -#include "TestHarness.h" - -#define WORK_AROUND_SERVICE_MANAGER_ASSERT +#include "nsString.h" +#include "gtest/gtest.h" NS_DEFINE_CID(kLBrkCID, NS_LBRK_CID); NS_DEFINE_CID(kWBrkCID, NS_WBRK_CID); @@ -25,7 +22,7 @@ static char teng1[] = //01234567890123456789012345678901234567890123456789012345678901234567890123456789 "This is a test to test(reasonable) line break. This 0.01123 = 45 x 48."; -static uint32_t exp1[] = { +static uint32_t lexp1[] = { 4,7,9,14,17,34,39,40,41,42,49,54,62,64,67,69,73 }; @@ -50,7 +47,7 @@ static uint32_t wexp2[] = { //01234567890123456789012345678901234567890123456789012345678901234567890123456789 static char teng3[] = "It's a test to test(ronae ) line break...."; -static uint32_t exp3[] = { +static uint32_t lexp3[] = { 4,6,11,14,25,27,32,42 }; static uint32_t wexp3[] = { @@ -62,16 +59,59 @@ static char ruler1[] = static char ruler2[] = "0123456789012345678901234567890123456789012345678901234567890123456789012"; +bool +Check(const char* in, const uint32_t* out, uint32_t outlen, uint32_t i, + uint32_t res[256]) +{ + bool ok = true; + + if (i != outlen) { + ok = false; + printf("WARNING!!! return size wrong, expect %d but got %d \n", + outlen, i); + } + + for (uint32_t j = 0; j < i; j++) { + if (j < outlen) { + if (res[j] != out[j]) { + ok = false; + printf("[%d] expect %d but got %d\n", j, out[j], res[j]); + } + } else { + ok = false; + printf("[%d] additional %d\n", j, res[j]); + } + } + + if (!ok) { + printf("string = \n%s\n", in); + printf("%s\n", ruler1); + printf("%s\n", ruler2); + + printf("Expect = \n"); + for (uint32_t j = 0; j < outlen; j++) { + printf("%d,", out[j]); + } + + printf("\nResult = \n"); + for (uint32_t j = 0; j < i; j++) { + printf("%d,", res[j]); + } + printf("\n"); + } + + return ok; +} bool TestASCIILB(nsILineBreaker *lb, - const char* in, const uint32_t len, + const char* in, const uint32_t* out, uint32_t outlen) { NS_ConvertASCIItoUTF16 eng1(in); - uint32_t i,j; + uint32_t i; uint32_t res[256]; - bool ok = true; int32_t curr; + for(i = 0, curr = 0; (curr != NS_LINEBREAKER_NEED_MORE_TEXT) && (i < 256); i++) { @@ -79,52 +119,18 @@ bool TestASCIILB(nsILineBreaker *lb, res [i] = curr != NS_LINEBREAKER_NEED_MORE_TEXT ? curr : eng1.Length(); } - if (i != outlen) - { - ok = false; - printf("WARNING!!! return size wrong, expect %d but got %d \n", - outlen, i); - } - printf("string = \n%s\n", in); - printf("%s\n", ruler1); - printf("%s\n", ruler2); - printf("Expect = \n"); - for(j=0;jNextWord(eng1.get(), eng1.Length(), curr); @@ -133,174 +139,47 @@ bool TestASCIIWB(nsIWordBreaker *lb, { res [i] = curr != NS_WORDBREAKER_NEED_MORE_TEXT ? curr : eng1.Length(); } - if (i != outlen) - { - ok = false; - printf("WARNING!!! return size wrong, expect %d but got %d\n", - outlen, i); - } - printf("string = \n%s\n", in); - printf("%s\n", ruler1); - printf("%s\n", ruler2); - printf("Expect = \n"); - for(j=0;j Date: Thu, 3 Nov 2016 13:46:14 +1100 Subject: [PATCH 20/96] Bug 1314497 (part 2) - Reformat intl/lwbrk/tests/TestLineBreak.cpp. This patch makes no functional changes. --HG-- extra : rebase_source : 9b0c1576d3b938037962a2c817845f680d73873d --- intl/lwbrk/gtest/TestLineBreak.cpp | 352 ++++++++++++++--------------- 1 file changed, 170 insertions(+), 182 deletions(-) diff --git a/intl/lwbrk/gtest/TestLineBreak.cpp b/intl/lwbrk/gtest/TestLineBreak.cpp index c320cb202c36..ca9f0bde399d 100644 --- a/intl/lwbrk/gtest/TestLineBreak.cpp +++ b/intl/lwbrk/gtest/TestLineBreak.cpp @@ -1,7 +1,9 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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 #include "nsXPCOM.h" #include "nsIComponentManager.h" @@ -16,8 +18,7 @@ NS_DEFINE_CID(kLBrkCID, NS_LBRK_CID); NS_DEFINE_CID(kWBrkCID, NS_WBRK_CID); - -static char teng1[] = +static char teng1[] = // 1 2 3 4 5 6 7 //01234567890123456789012345678901234567890123456789012345678901234567890123456789 "This is a test to test(reasonable) line break. This 0.01123 = 45 x 48."; @@ -27,29 +28,32 @@ static uint32_t lexp1[] = { }; static uint32_t wexp1[] = { - 4,5,7,8,9,10,14,15,17,18,22,23,33,34,35,39,43,48,49,50,54,55,56,57,62,63, 64,65,67,68,69,70,72 }; + +static char teng2[] = // 1 2 3 4 5 6 7 //01234567890123456789012345678901234567890123456789012345678901234567890123456789 -static char teng2[] = "()((reasonab(l)e) line break. .01123=45x48."; static uint32_t lexp2[] = { 17,22,23,30,44 }; + static uint32_t wexp2[] = { 4,12,13,14,15,16,17,18,22,24,29,30,31,32,37,38,43 }; +static char teng3[] = // 1 2 3 4 5 6 7 //01234567890123456789012345678901234567890123456789012345678901234567890123456789 -static char teng3[] = "It's a test to test(ronae ) line break...."; + static uint32_t lexp3[] = { 4,6,11,14,25,27,32,42 }; + static uint32_t wexp3[] = { 2,3,4,5,6,7,11,12,14,15,19,20,25,26,27,28,32,33,38 }; @@ -103,234 +107,218 @@ Check(const char* in, const uint32_t* out, uint32_t outlen, uint32_t i, return ok; } -bool TestASCIILB(nsILineBreaker *lb, - const char* in, - const uint32_t* out, uint32_t outlen) +bool +TestASCIILB(nsILineBreaker *lb, + const char* in, + const uint32_t* out, uint32_t outlen) { - NS_ConvertASCIItoUTF16 eng1(in); - uint32_t i; - uint32_t res[256]; - int32_t curr; + NS_ConvertASCIItoUTF16 eng1(in); + uint32_t i; + uint32_t res[256]; + int32_t curr; - for(i = 0, curr = 0; (curr != NS_LINEBREAKER_NEED_MORE_TEXT) && - (i < 256); i++) - { - curr = lb->Next(eng1.get(), eng1.Length(), curr); - res [i] = curr != NS_LINEBREAKER_NEED_MORE_TEXT ? curr : eng1.Length(); - - } + for (i = 0, curr = 0; + curr != NS_LINEBREAKER_NEED_MORE_TEXT && i < 256; + i++) { + curr = lb->Next(eng1.get(), eng1.Length(), curr); + res[i] = curr != NS_LINEBREAKER_NEED_MORE_TEXT ? curr : eng1.Length(); + } - return Check(in, out, outlen, i, res); + return Check(in, out, outlen, i, res); } -bool TestASCIIWB(nsIWordBreaker *lb, - const char* in, - const uint32_t* out, uint32_t outlen) +bool +TestASCIIWB(nsIWordBreaker *lb, + const char* in, + const uint32_t* out, uint32_t outlen) { - NS_ConvertASCIItoUTF16 eng1(in); + NS_ConvertASCIItoUTF16 eng1(in); - uint32_t i; - uint32_t res[256]; - int32_t curr = 0; + uint32_t i; + uint32_t res[256]; + int32_t curr = 0; - for(i = 0, curr = lb->NextWord(eng1.get(), eng1.Length(), curr); - (curr != NS_WORDBREAKER_NEED_MORE_TEXT) && (i < 256); - curr = lb->NextWord(eng1.get(), eng1.Length(), curr), i++) - { - res [i] = curr != NS_WORDBREAKER_NEED_MORE_TEXT ? curr : eng1.Length(); - } + for (i = 0, curr = lb->NextWord(eng1.get(), eng1.Length(), curr); + curr != NS_WORDBREAKER_NEED_MORE_TEXT && i < 256; + curr = lb->NextWord(eng1.get(), eng1.Length(), curr), i++) { + res [i] = curr != NS_WORDBREAKER_NEED_MORE_TEXT ? curr : eng1.Length(); + } - return Check(in, out, outlen, i, res); + return Check(in, out, outlen, i, res); } - - + TEST(LineBreak, LineBreaker) { - nsILineBreaker *t = nullptr; - nsresult res; + nsILineBreaker *t = nullptr; + nsresult res = CallGetService(kLBrkCID, &t); + ASSERT_TRUE(NS_SUCCEEDED(res) && t); + NS_IF_RELEASE(t); - res = CallGetService(kLBrkCID, &t); - ASSERT_TRUE(NS_SUCCEEDED(res) && t); - NS_IF_RELEASE(t); + res = CallGetService(kLBrkCID, &t); + ASSERT_TRUE(NS_SUCCEEDED(res) && t); - res = CallGetService(kLBrkCID, &t); - ASSERT_TRUE(NS_SUCCEEDED(res) && t); + ASSERT_TRUE(TestASCIILB(t, teng1, lexp1, sizeof(lexp1) / sizeof(uint32_t))); + ASSERT_TRUE(TestASCIILB(t, teng2, lexp2, sizeof(lexp2) / sizeof(uint32_t))); + ASSERT_TRUE(TestASCIILB(t, teng3, lexp3, sizeof(lexp3) / sizeof(uint32_t))); - ASSERT_TRUE(TestASCIILB(t, teng1, lexp1, sizeof(lexp1) / sizeof(uint32_t))); - ASSERT_TRUE(TestASCIILB(t, teng2, lexp2, sizeof(lexp2) / sizeof(uint32_t))); - ASSERT_TRUE(TestASCIILB(t, teng3, lexp3, sizeof(lexp3) / sizeof(uint32_t))); - - NS_RELEASE(t); + NS_RELEASE(t); } TEST(LineBreak, WordBreaker) { - nsIWordBreaker *t = nullptr; - nsresult res; + nsIWordBreaker *t = nullptr; + nsresult res = CallGetService(kWBrkCID, &t); + ASSERT_TRUE(NS_SUCCEEDED(res) && t); + NS_IF_RELEASE(t); - res = CallGetService(kWBrkCID, &t); - ASSERT_TRUE(NS_SUCCEEDED(res) && t); - NS_IF_RELEASE(t); + res = CallGetService(kWBrkCID, &t); + ASSERT_TRUE(NS_SUCCEEDED(res) && t); - res = CallGetService(kWBrkCID, &t); - ASSERT_TRUE(NS_SUCCEEDED(res) && t); + ASSERT_TRUE(TestASCIIWB(t, teng1, wexp1, sizeof(wexp1) / sizeof(uint32_t))); + ASSERT_TRUE(TestASCIIWB(t, teng2, wexp2, sizeof(wexp2) / sizeof(uint32_t))); + ASSERT_TRUE(TestASCIIWB(t, teng3, wexp3, sizeof(wexp3) / sizeof(uint32_t))); - ASSERT_TRUE(TestASCIIWB(t, teng1, wexp1, sizeof(wexp1) / sizeof(uint32_t))); - ASSERT_TRUE(TestASCIIWB(t, teng2, wexp2, sizeof(wexp2) / sizeof(uint32_t))); - ASSERT_TRUE(TestASCIIWB(t, teng3, wexp3, sizeof(wexp3) / sizeof(uint32_t))); - - NS_RELEASE(t); + NS_RELEASE(t); } -void SamplePrintWordWithBreak(); -void SampleFindWordBreakFromPosition(uint32_t fragN, uint32_t offset); -// Sample Code +// 012345678901234 +static const char wb0[] = "T"; +static const char wb1[] = "h"; +static const char wb2[] = "is is a int"; +static const char wb3[] = "ernationali"; +static const char wb4[] = "zation work."; -// 012345678901234 -static const char wb0[] = "T"; -static const char wb1[] = "h"; -static const char wb2[] = "is is a int"; -static const char wb3[] = "ernationali"; -static const char wb4[] = "zation work."; +static const char* wb[] = { wb0, wb1, wb2, wb3, wb4 }; -static const char* wb[] = {wb0,wb1,wb2,wb3,wb4}; -void SampleWordBreakUsage() +void +SamplePrintWordWithBreak() { - SamplePrintWordWithBreak(); - SampleFindWordBreakFromPosition(0,0); // This - SampleFindWordBreakFromPosition(1,0); // This - SampleFindWordBreakFromPosition(2,0); // This - SampleFindWordBreakFromPosition(2,1); // This - SampleFindWordBreakFromPosition(2,9); // [space] - SampleFindWordBreakFromPosition(2,10); // internationalization - SampleFindWordBreakFromPosition(3,4); // internationalization - SampleFindWordBreakFromPosition(3,8); // internationalization - SampleFindWordBreakFromPosition(4,6); // [space] - SampleFindWordBreakFromPosition(4,7); // work -} - + uint32_t numOfFragment = sizeof(wb) / sizeof(char*); + nsIWordBreaker* wbk = nullptr; -void SamplePrintWordWithBreak() -{ - uint32_t numOfFragment = sizeof(wb) / sizeof(char*); - nsIWordBreaker *wbk = nullptr; + CallGetService(kWBrkCID, &wbk); - CallGetService(kWBrkCID, &wbk); + nsAutoString result; - nsAutoString result; + for (uint32_t i = 0; i < numOfFragment; i++) { + NS_ConvertASCIItoUTF16 fragText(wb[i]); - for(uint32_t i = 0; i < numOfFragment; i++) - { - NS_ConvertASCIItoUTF16 fragText(wb[i]); - - int32_t cur = 0; + int32_t cur = 0; + cur = wbk->NextWord(fragText.get(), fragText.Length(), cur); + uint32_t start = 0; + for (uint32_t j = 0; cur != NS_WORDBREAKER_NEED_MORE_TEXT; j++) { + result.Append(Substring(fragText, start, cur - start)); + result.Append('^'); + start = (cur >= 0 ? cur : cur - start); cur = wbk->NextWord(fragText.get(), fragText.Length(), cur); - uint32_t start = 0; - for(uint32_t j = 0; cur != NS_WORDBREAKER_NEED_MORE_TEXT ; j++) - { - result.Append(Substring(fragText, start, cur - start)); - result.Append('^'); - start = (cur >= 0 ? cur : cur - start); - cur = wbk->NextWord(fragText.get(), fragText.Length(), cur); + } + + result.Append(Substring(fragText, fragText.Length() - start)); + + if (i != numOfFragment - 1) { + NS_ConvertASCIItoUTF16 nextFragText(wb[i+1]); + + bool canBreak = true; + canBreak = wbk->BreakInBetween(fragText.get(), + fragText.Length(), + nextFragText.get(), + nextFragText.Length()); + if (canBreak) { + result.Append('^'); } + fragText.Assign(nextFragText); + } + } + printf("Output From SamplePrintWordWithBreak() \n\n"); + printf("[%s]\n", NS_ConvertUTF16toUTF8(result).get()); - result.Append(Substring(fragText, fragText.Length() - start)); - - if( i != (numOfFragment -1 )) - { - NS_ConvertASCIItoUTF16 nextFragText(wb[i+1]); - - bool canBreak = true; - canBreak = wbk->BreakInBetween( fragText.get(), - fragText.Length(), - nextFragText.get(), - nextFragText.Length()); - if(canBreak) - result.Append('^'); - - fragText.Assign(nextFragText); - } - } - printf("Output From SamplePrintWordWithBreak() \n\n"); - printf("[%s]\n", NS_ConvertUTF16toUTF8(result).get()); - - NS_IF_RELEASE(wbk); + NS_IF_RELEASE(wbk); } -void SampleFindWordBreakFromPosition(uint32_t fragN, uint32_t offset) +void +SampleFindWordBreakFromPosition(uint32_t fragN, uint32_t offset) { - uint32_t numOfFragment = sizeof(wb) / sizeof(char*); - nsIWordBreaker *wbk = nullptr; + uint32_t numOfFragment = sizeof(wb) / sizeof(char*); + nsIWordBreaker* wbk = nullptr; - CallGetService(kWBrkCID, &wbk); + CallGetService(kWBrkCID, &wbk); - NS_ConvertASCIItoUTF16 fragText(wb[fragN]); - - nsWordRange res = wbk->FindWord(fragText.get(), fragText.Length(), offset); + NS_ConvertASCIItoUTF16 fragText(wb[fragN]); - bool canBreak; - nsAutoString result(Substring(fragText, res.mBegin, res.mEnd-res.mBegin)); + nsWordRange res = wbk->FindWord(fragText.get(), fragText.Length(), offset); - if((uint32_t)fragText.Length() == res.mEnd) // if we hit the end of the fragment - { - nsAutoString curFragText = fragText; - for(uint32_t p = fragN +1; p < numOfFragment ;p++) - { - NS_ConvertASCIItoUTF16 nextFragText(wb[p]); - canBreak = wbk->BreakInBetween(curFragText.get(), - curFragText.Length(), - nextFragText.get(), - nextFragText.Length()); - if(canBreak) - break; - - nsWordRange r = wbk->FindWord(nextFragText.get(), nextFragText.Length(), - 0); + bool canBreak; + nsAutoString result(Substring(fragText, res.mBegin, res.mEnd-res.mBegin)); - result.Append(Substring(nextFragText, r.mBegin, r.mEnd - r.mBegin)); + if ((uint32_t)fragText.Length() == res.mEnd) { + // if we hit the end of the fragment + nsAutoString curFragText = fragText; + for(uint32_t p = fragN +1; p < numOfFragment ;p++) + { + NS_ConvertASCIItoUTF16 nextFragText(wb[p]); + canBreak = wbk->BreakInBetween(curFragText.get(), + curFragText.Length(), + nextFragText.get(), + nextFragText.Length()); + if (canBreak) { + break; + } + nsWordRange r = wbk->FindWord(nextFragText.get(), nextFragText.Length(), + 0); - if((uint32_t)nextFragText.Length() != r.mEnd) - break; + result.Append(Substring(nextFragText, r.mBegin, r.mEnd - r.mBegin)); - nextFragText.Assign(curFragText); - } - } - - if(0 == res.mBegin) // if we hit the beginning of the fragment - { - nsAutoString curFragText = fragText; - for(uint32_t p = fragN ; p > 0 ;p--) - { - NS_ConvertASCIItoUTF16 prevFragText(wb[p-1]); - canBreak = wbk->BreakInBetween(prevFragText.get(), - prevFragText.Length(), - curFragText.get(), - curFragText.Length()); - if(canBreak) - break; - - nsWordRange r = wbk->FindWord(prevFragText.get(), prevFragText.Length(), - prevFragText.Length()); + if ((uint32_t)nextFragText.Length() != r.mEnd) { + break; + } + nextFragText.Assign(curFragText); + } + } - result.Insert(Substring(prevFragText, r.mBegin, r.mEnd - r.mBegin), 0); + if (0 == res.mBegin) { + // if we hit the beginning of the fragment + nsAutoString curFragText = fragText; + for (uint32_t p = fragN; p > 0; p--) { + NS_ConvertASCIItoUTF16 prevFragText(wb[p-1]); + canBreak = wbk->BreakInBetween(prevFragText.get(), + prevFragText.Length(), + curFragText.get(), + curFragText.Length()); + if (canBreak) { + break; + } + nsWordRange r = wbk->FindWord(prevFragText.get(), prevFragText.Length(), + prevFragText.Length()); - if(0 != r.mBegin) - break; + result.Insert(Substring(prevFragText, r.mBegin, r.mEnd - r.mBegin), 0); - prevFragText.Assign(curFragText); - } - } - - printf("Output From SamplePrintWordWithBreak() \n\n"); - printf("[%s]\n", NS_ConvertUTF16toUTF8(result).get()); + if (0 != r.mBegin) { + break; + } + prevFragText.Assign(curFragText); + } + } - NS_IF_RELEASE(wbk); + printf("Output From SamplePrintWordWithBreak() \n\n"); + printf("[%s]\n", NS_ConvertUTF16toUTF8(result).get()); + + NS_IF_RELEASE(wbk); } // XXX: this prints output but doesn't actually test anything and so cannot -// fail. It should be converted to a real test. +// fail. Bug 1314497 is open to convert it to a real test. TEST(LineBreak, SampleWordBreakUsage) { - SampleWordBreakUsage(); + SamplePrintWordWithBreak(); + SampleFindWordBreakFromPosition(0,0); // This + SampleFindWordBreakFromPosition(1,0); // This + SampleFindWordBreakFromPosition(2,0); // This + SampleFindWordBreakFromPosition(2,1); // This + SampleFindWordBreakFromPosition(2,9); // [space] + SampleFindWordBreakFromPosition(2,10); // internationalization + SampleFindWordBreakFromPosition(3,4); // internationalization + SampleFindWordBreakFromPosition(3,8); // internationalization + SampleFindWordBreakFromPosition(4,6); // [space] + SampleFindWordBreakFromPosition(4,7); // work } From df85c99c08bc97569d85f3851f14796ba48978c6 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Thu, 3 Nov 2016 14:41:02 +1100 Subject: [PATCH 21/96] Bug 1294299 part 1 - Make nsDOMCSSDeclaration use DeclarationBlock. r=heycam MozReview-Commit-ID: B2eHrpxOMJ8 --HG-- extra : source : 1fb048c40e848189bc31f5309d4f85b6101975c2 --- layout/style/StyleRule.cpp | 11 +++++----- layout/style/nsCSSRules.cpp | 14 ++++++------ layout/style/nsCSSRules.h | 8 +++---- layout/style/nsComputedDOMStyle.cpp | 4 ++-- layout/style/nsComputedDOMStyle.h | 4 ++-- layout/style/nsDOMCSSAttrDeclaration.cpp | 20 +++++++++--------- layout/style/nsDOMCSSAttrDeclaration.h | 4 ++-- layout/style/nsDOMCSSDeclaration.cpp | 27 +++++++++++++----------- layout/style/nsDOMCSSDeclaration.h | 6 +++--- 9 files changed, 51 insertions(+), 47 deletions(-) diff --git a/layout/style/StyleRule.cpp b/layout/style/StyleRule.cpp index 1ed2d2d385b0..6aade8897984 100644 --- a/layout/style/StyleRule.cpp +++ b/layout/style/StyleRule.cpp @@ -11,6 +11,7 @@ #include "mozilla/css/StyleRule.h" +#include "mozilla/DeclarationBlockInlines.h" #include "mozilla/StyleSheetInlines.h" #include "mozilla/MemoryReporting.h" #include "mozilla/css/GroupRule.h" @@ -1062,8 +1063,8 @@ public: NS_IMETHOD GetParentRule(nsIDOMCSSRule **aParent) override; void DropReference(void); - virtual css::Declaration* GetCSSDeclaration(Operation aOperation) override; - virtual nsresult SetCSSDeclaration(css::Declaration* aDecl) override; + virtual DeclarationBlock* GetCSSDeclaration(Operation aOperation) override; + virtual nsresult SetCSSDeclaration(DeclarationBlock* aDecl) override; virtual void GetCSSParsingEnvironment(CSSParsingEnvironment& aCSSParseEnv) override; virtual nsIDocument* DocToUpdate() override; @@ -1166,7 +1167,7 @@ DOMCSSDeclarationImpl::DropReference(void) mRule = nullptr; } -css::Declaration* +DeclarationBlock* DOMCSSDeclarationImpl::GetCSSDeclaration(Operation aOperation) { if (mRule) { @@ -1203,7 +1204,7 @@ DOMCSSDeclarationImpl::GetParentRule(nsIDOMCSSRule **aParent) } nsresult -DOMCSSDeclarationImpl::SetCSSDeclaration(css::Declaration* aDecl) +DOMCSSDeclarationImpl::SetCSSDeclaration(DeclarationBlock* aDecl) { NS_PRECONDITION(mRule, "can only be called when |GetCSSDeclaration| returned a declaration"); @@ -1216,7 +1217,7 @@ DOMCSSDeclarationImpl::SetCSSDeclaration(css::Declaration* aDecl) mozAutoDocUpdate updateBatch(owningDoc, UPDATE_STYLE, true); - mRule->SetDeclaration(aDecl); + mRule->SetDeclaration(aDecl->AsGecko()); if (sheet) { sheet->DidDirty(); diff --git a/layout/style/nsCSSRules.cpp b/layout/style/nsCSSRules.cpp index 21e3ec2ac1cd..25fb4c6ff60c 100644 --- a/layout/style/nsCSSRules.cpp +++ b/layout/style/nsCSSRules.cpp @@ -29,7 +29,7 @@ #include "nsContentUtils.h" #include "nsError.h" #include "nsStyleUtil.h" -#include "mozilla/css/Declaration.h" +#include "mozilla/DeclarationBlockInlines.h" #include "nsCSSParser.h" #include "nsDOMClassInfoID.h" #include "mozilla/dom/CSSStyleDeclarationBinding.h" @@ -1923,7 +1923,7 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsCSSKeyframeStyleDeclaration) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_INTERFACE_MAP_END_INHERITING(nsDOMCSSDeclaration) -css::Declaration* +DeclarationBlock* nsCSSKeyframeStyleDeclaration::GetCSSDeclaration(Operation aOperation) { if (mRule) { @@ -1949,10 +1949,10 @@ nsCSSKeyframeStyleDeclaration::GetParentRule(nsIDOMCSSRule **aParent) } nsresult -nsCSSKeyframeStyleDeclaration::SetCSSDeclaration(css::Declaration* aDecl) +nsCSSKeyframeStyleDeclaration::SetCSSDeclaration(DeclarationBlock* aDecl) { MOZ_ASSERT(aDecl, "must be non-null"); - mRule->ChangeDeclaration(aDecl); + mRule->ChangeDeclaration(aDecl->AsGecko()); return NS_OK; } @@ -2470,7 +2470,7 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsCSSPageStyleDeclaration) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_INTERFACE_MAP_END_INHERITING(nsDOMCSSDeclaration) -css::Declaration* +DeclarationBlock* nsCSSPageStyleDeclaration::GetCSSDeclaration(Operation aOperation) { if (mRule) { @@ -2496,10 +2496,10 @@ nsCSSPageStyleDeclaration::GetParentRule(nsIDOMCSSRule** aParent) } nsresult -nsCSSPageStyleDeclaration::SetCSSDeclaration(css::Declaration* aDecl) +nsCSSPageStyleDeclaration::SetCSSDeclaration(DeclarationBlock* aDecl) { MOZ_ASSERT(aDecl, "must be non-null"); - mRule->ChangeDeclaration(aDecl); + mRule->ChangeDeclaration(aDecl->AsGecko()); return NS_OK; } diff --git a/layout/style/nsCSSRules.h b/layout/style/nsCSSRules.h index 921570c7e3cd..27621073c6d9 100644 --- a/layout/style/nsCSSRules.h +++ b/layout/style/nsCSSRules.h @@ -355,8 +355,8 @@ public: NS_IMETHOD GetParentRule(nsIDOMCSSRule **aParent) override; void DropReference() { mRule = nullptr; } - virtual mozilla::css::Declaration* GetCSSDeclaration(Operation aOperation) override; - virtual nsresult SetCSSDeclaration(mozilla::css::Declaration* aDecl) override; + virtual mozilla::DeclarationBlock* GetCSSDeclaration(Operation aOperation) override; + virtual nsresult SetCSSDeclaration(mozilla::DeclarationBlock* aDecl) override; virtual void GetCSSParsingEnvironment(CSSParsingEnvironment& aCSSParseEnv) override; virtual nsIDocument* DocToUpdate() override; @@ -485,8 +485,8 @@ public: NS_IMETHOD GetParentRule(nsIDOMCSSRule **aParent) override; void DropReference() { mRule = nullptr; } - virtual mozilla::css::Declaration* GetCSSDeclaration(Operation aOperation) override; - virtual nsresult SetCSSDeclaration(mozilla::css::Declaration* aDecl) override; + virtual mozilla::DeclarationBlock* GetCSSDeclaration(Operation aOperation) override; + virtual nsresult SetCSSDeclaration(mozilla::DeclarationBlock* aDecl) override; virtual void GetCSSParsingEnvironment(CSSParsingEnvironment& aCSSParseEnv) override; virtual nsIDocument* DocToUpdate() override; diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index 2337e30b9f03..7191977adb7e 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -559,7 +559,7 @@ nsComputedDOMStyle::GetPresShellForContent(nsIContent* aContent) // nsDOMCSSDeclaration abstract methods which should never be called // on a nsComputedDOMStyle object, but must be defined to avoid // compile errors. -css::Declaration* +DeclarationBlock* nsComputedDOMStyle::GetCSSDeclaration(Operation) { NS_RUNTIMEABORT("called nsComputedDOMStyle::GetCSSDeclaration"); @@ -567,7 +567,7 @@ nsComputedDOMStyle::GetCSSDeclaration(Operation) } nsresult -nsComputedDOMStyle::SetCSSDeclaration(css::Declaration*) +nsComputedDOMStyle::SetCSSDeclaration(DeclarationBlock*) { NS_RUNTIMEABORT("called nsComputedDOMStyle::SetCSSDeclaration"); return NS_ERROR_FAILURE; diff --git a/layout/style/nsComputedDOMStyle.h b/layout/style/nsComputedDOMStyle.h index 6767d04808fd..4b29297f8138 100644 --- a/layout/style/nsComputedDOMStyle.h +++ b/layout/style/nsComputedDOMStyle.h @@ -105,8 +105,8 @@ public: // nsDOMCSSDeclaration abstract methods which should never be called // on a nsComputedDOMStyle object, but must be defined to avoid // compile errors. - virtual mozilla::css::Declaration* GetCSSDeclaration(Operation) override; - virtual nsresult SetCSSDeclaration(mozilla::css::Declaration*) override; + virtual mozilla::DeclarationBlock* GetCSSDeclaration(Operation) override; + virtual nsresult SetCSSDeclaration(mozilla::DeclarationBlock*) override; virtual nsIDocument* DocToUpdate() override; virtual void GetCSSParsingEnvironment(CSSParsingEnvironment& aCSSParseEnv) override; diff --git a/layout/style/nsDOMCSSAttrDeclaration.cpp b/layout/style/nsDOMCSSAttrDeclaration.cpp index b593ec2a1075..819a79ab51a3 100644 --- a/layout/style/nsDOMCSSAttrDeclaration.cpp +++ b/layout/style/nsDOMCSSAttrDeclaration.cpp @@ -69,12 +69,12 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMCSSAttributeDeclaration) NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMCSSAttributeDeclaration) nsresult -nsDOMCSSAttributeDeclaration::SetCSSDeclaration(css::Declaration* aDecl) +nsDOMCSSAttributeDeclaration::SetCSSDeclaration(DeclarationBlock* aDecl) { NS_ASSERTION(mElement, "Must have Element to set the declaration!"); - return - mIsSMILOverride ? mElement->SetSMILOverrideStyleDeclaration(aDecl, true) : - mElement->SetInlineStyleDeclaration(aDecl, nullptr, true); + return mIsSMILOverride + ? mElement->SetSMILOverrideStyleDeclaration(aDecl->AsGecko(), true) + : mElement->SetInlineStyleDeclaration(aDecl, nullptr, true); } nsIDocument* @@ -85,18 +85,17 @@ nsDOMCSSAttributeDeclaration::DocToUpdate() return mElement->OwnerDoc(); } -css::Declaration* +DeclarationBlock* nsDOMCSSAttributeDeclaration::GetCSSDeclaration(Operation aOperation) { if (!mElement) return nullptr; - css::Declaration* declaration; + DeclarationBlock* declaration; if (mIsSMILOverride) { declaration = mElement->GetSMILOverrideStyleDeclaration(); } else { - DeclarationBlock* decl = mElement->GetInlineStyleDeclaration(); - declaration = decl && decl->IsGecko() ? decl->AsGecko() : nullptr; + declaration = mElement->GetInlineStyleDeclaration(); } // Notify observers that our style="" attribute is going to change @@ -134,10 +133,11 @@ nsDOMCSSAttributeDeclaration::GetCSSDeclaration(Operation aOperation) // this *can* fail (inside SetAttrAndNotify, at least). nsresult rv; - if (mIsSMILOverride) + if (mIsSMILOverride) { rv = mElement->SetSMILOverrideStyleDeclaration(decl, false); - else + } else { rv = mElement->SetInlineStyleDeclaration(decl, nullptr, false); + } if (NS_FAILED(rv)) { return nullptr; // the decl will be destroyed along with the style rule diff --git a/layout/style/nsDOMCSSAttrDeclaration.h b/layout/style/nsDOMCSSAttrDeclaration.h index 9865aaf5290d..7c0fbacc0558 100644 --- a/layout/style/nsDOMCSSAttrDeclaration.h +++ b/layout/style/nsDOMCSSAttrDeclaration.h @@ -30,7 +30,7 @@ public: // If GetCSSDeclaration returns non-null, then the decl it returns // is owned by our current style rule. - virtual mozilla::css::Declaration* GetCSSDeclaration(Operation aOperation) override; + virtual mozilla::DeclarationBlock* GetCSSDeclaration(Operation aOperation) override; virtual void GetCSSParsingEnvironment(CSSParsingEnvironment& aCSSParseEnv) override; NS_IMETHOD GetParentRule(nsIDOMCSSRule **aParent) override; @@ -42,7 +42,7 @@ public: protected: ~nsDOMCSSAttributeDeclaration(); - virtual nsresult SetCSSDeclaration(mozilla::css::Declaration* aDecl) override; + virtual nsresult SetCSSDeclaration(mozilla::DeclarationBlock* aDecl) override; virtual nsIDocument* DocToUpdate() override; RefPtr mElement; diff --git a/layout/style/nsDOMCSSDeclaration.cpp b/layout/style/nsDOMCSSDeclaration.cpp index b7d8636b5463..21c63fc35424 100644 --- a/layout/style/nsDOMCSSDeclaration.cpp +++ b/layout/style/nsDOMCSSDeclaration.cpp @@ -8,6 +8,7 @@ #include "nsDOMCSSDeclaration.h" #include "nsCSSParser.h" +#include "mozilla/DeclarationBlockInlines.h" #include "mozilla/StyleSheetInlines.h" #include "mozilla/css/Rule.h" #include "mozilla/DeclarationBlockInlines.h" @@ -47,7 +48,7 @@ nsDOMCSSDeclaration::GetPropertyValue(const nsCSSPropertyID aPropID, NS_PRECONDITION(aPropID != eCSSProperty_UNKNOWN, "Should never pass eCSSProperty_UNKNOWN around"); - css::Declaration* decl = GetCSSDeclaration(eOperation_Read); + css::Declaration* decl = GetCSSDeclaration(eOperation_Read)->AsGecko(); aValue.Truncate(); if (decl) { @@ -63,7 +64,7 @@ nsDOMCSSDeclaration::GetCustomPropertyValue(const nsAString& aPropertyName, MOZ_ASSERT(Substring(aPropertyName, 0, CSS_CUSTOM_NAME_PREFIX_LENGTH).EqualsLiteral("--")); - css::Declaration* decl = GetCSSDeclaration(eOperation_Read); + css::Declaration* decl = GetCSSDeclaration(eOperation_Read)->AsGecko(); if (!decl) { aValue.Truncate(); return; @@ -115,7 +116,7 @@ nsDOMCSSDeclaration::SetPropertyValue(const nsCSSPropertyID aPropID, NS_IMETHODIMP nsDOMCSSDeclaration::GetCssText(nsAString& aCssText) { - css::Declaration* decl = GetCSSDeclaration(eOperation_Read); + css::Declaration* decl = GetCSSDeclaration(eOperation_Read)->AsGecko(); aCssText.Truncate(); if (decl) { @@ -130,7 +131,7 @@ nsDOMCSSDeclaration::SetCssText(const nsAString& aCssText) { // We don't need to *do* anything with the old declaration, but we need // to ensure that it exists, or else SetCSSDeclaration may crash. - css::Declaration* olddecl = GetCSSDeclaration(eOperation_Modify); + css::Declaration* olddecl = GetCSSDeclaration(eOperation_Modify)->AsGecko(); if (!olddecl) { return NS_ERROR_NOT_AVAILABLE; } @@ -165,7 +166,7 @@ nsDOMCSSDeclaration::SetCssText(const nsAString& aCssText) NS_IMETHODIMP nsDOMCSSDeclaration::GetLength(uint32_t* aLength) { - css::Declaration* decl = GetCSSDeclaration(eOperation_Read); + css::Declaration* decl = GetCSSDeclaration(eOperation_Read)->AsGecko(); if (decl) { *aLength = decl->Count(); @@ -187,7 +188,7 @@ nsDOMCSSDeclaration::GetPropertyCSSValue(const nsAString& aPropertyName, ErrorRe void nsDOMCSSDeclaration::IndexedGetter(uint32_t aIndex, bool& aFound, nsAString& aPropName) { - css::Declaration* decl = GetCSSDeclaration(eOperation_Read); + css::Declaration* decl = GetCSSDeclaration(eOperation_Read)->AsGecko(); aFound = decl && decl->GetNthProperty(aIndex, aPropName); } @@ -226,7 +227,7 @@ nsDOMCSSDeclaration::GetAuthoredPropertyValue(const nsAString& aPropertyName, return NS_OK; } - css::Declaration* decl = GetCSSDeclaration(eOperation_Read); + css::Declaration* decl = GetCSSDeclaration(eOperation_Read)->AsGecko(); if (!decl) { return NS_ERROR_FAILURE; } @@ -239,7 +240,7 @@ NS_IMETHODIMP nsDOMCSSDeclaration::GetPropertyPriority(const nsAString& aPropertyName, nsAString& aReturn) { - css::Declaration* decl = GetCSSDeclaration(eOperation_Read); + css::Declaration* decl = GetCSSDeclaration(eOperation_Read)->AsGecko(); aReturn.Truncate(); if (decl && decl->GetValueIsImportant(aPropertyName)) { @@ -331,7 +332,7 @@ nsDOMCSSDeclaration::ParsePropertyValue(const nsCSSPropertyID aPropID, const nsAString& aPropValue, bool aIsImportant) { - css::Declaration* olddecl = GetCSSDeclaration(eOperation_Modify); + css::Declaration* olddecl = GetCSSDeclaration(eOperation_Modify)->AsGecko(); if (!olddecl) { return NS_ERROR_NOT_AVAILABLE; } @@ -369,7 +370,7 @@ nsDOMCSSDeclaration::ParseCustomPropertyValue(const nsAString& aPropertyName, { MOZ_ASSERT(nsCSSProps::IsCustomPropertyName(aPropertyName)); - css::Declaration* olddecl = GetCSSDeclaration(eOperation_Modify); + css::Declaration* olddecl = GetCSSDeclaration(eOperation_Modify)->AsGecko(); if (!olddecl) { return NS_ERROR_NOT_AVAILABLE; } @@ -406,7 +407,8 @@ nsDOMCSSDeclaration::ParseCustomPropertyValue(const nsAString& aPropertyName, nsresult nsDOMCSSDeclaration::RemoveProperty(const nsCSSPropertyID aPropID) { - css::Declaration* olddecl = GetCSSDeclaration(eOperation_RemoveProperty); + css::Declaration* olddecl = + GetCSSDeclaration(eOperation_RemoveProperty)->AsGecko(); if (!olddecl) { return NS_OK; // no decl, so nothing to remove } @@ -429,7 +431,8 @@ nsDOMCSSDeclaration::RemoveCustomProperty(const nsAString& aPropertyName) MOZ_ASSERT(Substring(aPropertyName, 0, CSS_CUSTOM_NAME_PREFIX_LENGTH).EqualsLiteral("--")); - css::Declaration* olddecl = GetCSSDeclaration(eOperation_RemoveProperty); + css::Declaration* olddecl = + GetCSSDeclaration(eOperation_RemoveProperty)->AsGecko(); if (!olddecl) { return NS_OK; // no decl, so nothing to remove } diff --git a/layout/style/nsDOMCSSDeclaration.h b/layout/style/nsDOMCSSDeclaration.h index b563f8c7cef5..626d0171f097 100644 --- a/layout/style/nsDOMCSSDeclaration.h +++ b/layout/style/nsDOMCSSDeclaration.h @@ -20,8 +20,8 @@ struct JSContext; class JSObject; namespace mozilla { +class DeclarationBlock; namespace css { -class Declaration; class Loader; class Rule; } // namespace css @@ -119,8 +119,8 @@ protected: // AttributeWillChange. eOperation_RemoveProperty }; - virtual mozilla::css::Declaration* GetCSSDeclaration(Operation aOperation) = 0; - virtual nsresult SetCSSDeclaration(mozilla::css::Declaration* aDecl) = 0; + virtual mozilla::DeclarationBlock* GetCSSDeclaration(Operation aOperation) = 0; + virtual nsresult SetCSSDeclaration(mozilla::DeclarationBlock* aDecl) = 0; // Document that we must call BeginUpdate/EndUpdate on around the // calls to SetCSSDeclaration and the style rule mutation that leads // to it. From 60ba3c835d884388e5b9e124c1509e68e0cfb644 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Thu, 3 Nov 2016 14:41:02 +1100 Subject: [PATCH 22/96] Bug 1294299 part 2 - Use DeclarationBlock for SMIL override style. r=heycam MozReview-Commit-ID: 573o5vmC81z --HG-- extra : source : c284764640e86b9bf63f976b79aa66252f8b635d --- dom/base/Element.cpp | 5 +++-- dom/base/Element.h | 6 +++--- dom/base/FragmentOrElement.cpp | 1 + dom/base/FragmentOrElement.h | 6 ++---- layout/style/nsDOMCSSAttrDeclaration.cpp | 2 +- layout/style/nsHTMLCSSStyleSheet.cpp | 7 ++++--- 6 files changed, 14 insertions(+), 13 deletions(-) diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index b13bcdc9f717..c8d0c9fee4fb 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -57,6 +57,7 @@ #include "mozilla/AnimationComparator.h" #include "mozilla/AsyncEventDispatcher.h" #include "mozilla/ContentEvents.h" +#include "mozilla/DeclarationBlockInlines.h" #include "mozilla/EffectSet.h" #include "mozilla/EventDispatcher.h" #include "mozilla/EventListenerManager.h" @@ -1967,7 +1968,7 @@ Element::GetSMILOverrideStyle() return slots->mSMILOverrideStyle; } -css::Declaration* +DeclarationBlock* Element::GetSMILOverrideStyleDeclaration() { Element::nsDOMSlots *slots = GetExistingDOMSlots(); @@ -1975,7 +1976,7 @@ Element::GetSMILOverrideStyleDeclaration() } nsresult -Element::SetSMILOverrideStyleDeclaration(css::Declaration* aDeclaration, +Element::SetSMILOverrideStyleDeclaration(DeclarationBlock* aDeclaration, bool aNotify) { Element::nsDOMSlots *slots = DOMSlots(); diff --git a/dom/base/Element.h b/dom/base/Element.h index 6a043a4dd8bf..5d878df60668 100644 --- a/dom/base/Element.h +++ b/dom/base/Element.h @@ -271,15 +271,15 @@ public: * Get the SMIL override style declaration for this element. If the * rule hasn't been created, this method simply returns null. */ - virtual css::Declaration* GetSMILOverrideStyleDeclaration(); + virtual DeclarationBlock* GetSMILOverrideStyleDeclaration(); /** * Set the SMIL override style declaration for this element. If * aNotify is true, this method will notify the document's pres * context, so that the style changes will be noticed. */ - virtual nsresult SetSMILOverrideStyleDeclaration(css::Declaration* aDeclaration, - bool aNotify); + virtual nsresult SetSMILOverrideStyleDeclaration( + DeclarationBlock* aDeclaration, bool aNotify); /** * Returns a new nsISMILAttr that allows the caller to animate the given diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp index b0918a105d11..293177ce7ca9 100644 --- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -18,6 +18,7 @@ #include "mozilla/dom/FragmentOrElement.h" #include "mozilla/AsyncEventDispatcher.h" +#include "mozilla/DeclarationBlockInlines.h" #include "mozilla/EffectSet.h" #include "mozilla/EventDispatcher.h" #include "mozilla/EventListenerManager.h" diff --git a/dom/base/FragmentOrElement.h b/dom/base/FragmentOrElement.h index 5c0bdf070d58..3cb5575fe063 100644 --- a/dom/base/FragmentOrElement.h +++ b/dom/base/FragmentOrElement.h @@ -33,9 +33,7 @@ class nsDOMStringMap; class nsIURI; namespace mozilla { -namespace css { -class Declaration; -} // namespace css +class DeclarationBlock; namespace dom { class DOMIntersectionObserver; class Element; @@ -284,7 +282,7 @@ public: /** * Holds any SMIL override style declaration for this element. */ - RefPtr mSMILOverrideStyleDeclaration; + RefPtr mSMILOverrideStyleDeclaration; /** * An object implementing nsIDOMMozNamedAttrMap for this content (attributes) diff --git a/layout/style/nsDOMCSSAttrDeclaration.cpp b/layout/style/nsDOMCSSAttrDeclaration.cpp index 819a79ab51a3..269f7a44509e 100644 --- a/layout/style/nsDOMCSSAttrDeclaration.cpp +++ b/layout/style/nsDOMCSSAttrDeclaration.cpp @@ -73,7 +73,7 @@ nsDOMCSSAttributeDeclaration::SetCSSDeclaration(DeclarationBlock* aDecl) { NS_ASSERTION(mElement, "Must have Element to set the declaration!"); return mIsSMILOverride - ? mElement->SetSMILOverrideStyleDeclaration(aDecl->AsGecko(), true) + ? mElement->SetSMILOverrideStyleDeclaration(aDecl, true) : mElement->SetInlineStyleDeclaration(aDecl, nullptr, true); } diff --git a/layout/style/nsHTMLCSSStyleSheet.cpp b/layout/style/nsHTMLCSSStyleSheet.cpp index de901e98c4ff..f5e8cde0bee7 100644 --- a/layout/style/nsHTMLCSSStyleSheet.cpp +++ b/layout/style/nsHTMLCSSStyleSheet.cpp @@ -66,12 +66,13 @@ nsHTMLCSSStyleSheet::ElementRulesMatching(nsPresContext* aPresContext, nsRuleWalker* aRuleWalker) { // just get the one and only style rule from the content's STYLE attribute - if (DeclarationBlock* declaration = aElement->GetInlineStyleDeclaration()) { + DeclarationBlock* declaration = aElement->GetInlineStyleDeclaration(); + if (declaration) { declaration->SetImmutable(); aRuleWalker->Forward(declaration->AsGecko()); } - css::Declaration* declaration = aElement->GetSMILOverrideStyleDeclaration(); + declaration = aElement->GetSMILOverrideStyleDeclaration(); if (declaration) { MOZ_ASSERT(aPresContext->RestyleManager()->IsGecko(), "stylo: ElementRulesMatching must not be called when we have " @@ -81,7 +82,7 @@ nsHTMLCSSStyleSheet::ElementRulesMatching(nsPresContext* aPresContext, // Animation restyle (or non-restyle traversal of rules) // Now we can walk SMIL overrride style, without triggering transitions. declaration->SetImmutable(); - aRuleWalker->Forward(declaration); + aRuleWalker->Forward(declaration->AsGecko()); } } } From 92119e2034b75de8a480b7227436beccf93946eb Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Thu, 3 Nov 2016 14:41:02 +1100 Subject: [PATCH 23/96] Bug 1294299 part 3 - Make it possible to create empty ServoDeclarationBlock. r=heycam MozReview-Commit-ID: FSdSXDFoxM --HG-- extra : source : 47bdaffadab2dca8be8b6e49c7decf8ef02ea1e5 --- layout/style/ServoBindingList.h | 2 ++ layout/style/ServoDeclarationBlock.h | 3 +++ layout/style/nsDOMCSSAttrDeclaration.cpp | 9 +++++++-- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/layout/style/ServoBindingList.h b/layout/style/ServoBindingList.h index dc3d6986b89c..2c73bd2bab0c 100644 --- a/layout/style/ServoBindingList.h +++ b/layout/style/ServoBindingList.h @@ -62,6 +62,8 @@ SERVO_BINDING_FUNC(Servo_RestyleWithAddedDeclaration, // Style attribute SERVO_BINDING_FUNC(Servo_ParseStyleAttribute, RawServoDeclarationBlockStrong, const nsACString* data) +SERVO_BINDING_FUNC(Servo_DeclarationBlock_CreateEmpty, + RawServoDeclarationBlockStrong) SERVO_BINDING_FUNC(Servo_DeclarationBlock_AddRef, void, RawServoDeclarationBlockBorrowed declarations) SERVO_BINDING_FUNC(Servo_DeclarationBlock_Release, void, diff --git a/layout/style/ServoDeclarationBlock.h b/layout/style/ServoDeclarationBlock.h index 0456ea4ca55b..12110dfda8a6 100644 --- a/layout/style/ServoDeclarationBlock.h +++ b/layout/style/ServoDeclarationBlock.h @@ -14,6 +14,9 @@ namespace mozilla { class ServoDeclarationBlock final : public DeclarationBlock { public: + ServoDeclarationBlock() + : ServoDeclarationBlock(Servo_DeclarationBlock_CreateEmpty().Consume()) {} + NS_INLINE_DECL_REFCOUNTING(ServoDeclarationBlock) static already_AddRefed diff --git a/layout/style/nsDOMCSSAttrDeclaration.cpp b/layout/style/nsDOMCSSAttrDeclaration.cpp index 269f7a44509e..ce638a9c2ece 100644 --- a/layout/style/nsDOMCSSAttrDeclaration.cpp +++ b/layout/style/nsDOMCSSAttrDeclaration.cpp @@ -128,8 +128,13 @@ nsDOMCSSAttributeDeclaration::GetCSSDeclaration(Operation aOperation) } // cannot fail - RefPtr decl = new css::Declaration(); - decl->InitializeEmpty(); + RefPtr decl; + if (mElement->IsStyledByServo()) { + decl = new ServoDeclarationBlock(); + } else { + decl = new css::Declaration(); + decl->AsGecko()->InitializeEmpty(); + } // this *can* fail (inside SetAttrAndNotify, at least). nsresult rv; From 20238e1d4c0610e2074a7f2f2679dce03e512aea Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Thu, 3 Nov 2016 14:41:02 +1100 Subject: [PATCH 24/96] Bug 1294299 part 4 - Implement length and item getter. r=SimonSapin,heycam MozReview-Commit-ID: IQs8Wjdsi1r --HG-- extra : source : f1550376915d299d3aef5025107785d458788e9d --- layout/style/DeclarationBlock.h | 3 +++ layout/style/DeclarationBlockInlines.h | 12 ++++++++++++ layout/style/ServoBindingList.h | 5 +++++ layout/style/ServoDeclarationBlock.h | 8 ++++++++ layout/style/nsDOMCSSDeclaration.cpp | 4 ++-- 5 files changed, 30 insertions(+), 2 deletions(-) diff --git a/layout/style/DeclarationBlock.h b/layout/style/DeclarationBlock.h index 94776becff30..e9ffd18b4d89 100644 --- a/layout/style/DeclarationBlock.h +++ b/layout/style/DeclarationBlock.h @@ -92,6 +92,9 @@ public: return c.mHTMLCSSStyleSheet; } + inline uint32_t Count() const; + inline bool GetNthProperty(uint32_t aIndex, nsAString& aReturn) const; + private: union { // We only ever have one of these since we have an diff --git a/layout/style/DeclarationBlockInlines.h b/layout/style/DeclarationBlockInlines.h index 7f839be0dd2d..763f94620e6c 100644 --- a/layout/style/DeclarationBlockInlines.h +++ b/layout/style/DeclarationBlockInlines.h @@ -26,6 +26,18 @@ DeclarationBlock::Release() MOZ_STYLO_FORWARD(Release, ()) } +uint32_t +DeclarationBlock::Count() const +{ + MOZ_STYLO_FORWARD(Count, ()) +} + +bool +DeclarationBlock::GetNthProperty(uint32_t aIndex, nsAString& aReturn) const +{ + MOZ_STYLO_FORWARD(GetNthProperty, (aIndex, aReturn)) +} + } // namespace mozilla #endif // mozilla_DeclarationBlockInlines_h diff --git a/layout/style/ServoBindingList.h b/layout/style/ServoBindingList.h index 2c73bd2bab0c..d1122596190e 100644 --- a/layout/style/ServoBindingList.h +++ b/layout/style/ServoBindingList.h @@ -74,6 +74,11 @@ SERVO_BINDING_FUNC(Servo_DeclarationBlock_Equals, bool, SERVO_BINDING_FUNC(Servo_DeclarationBlock_SerializeOneValue, void, RawServoDeclarationBlockBorrowed declarations, nsString* buffer) +SERVO_BINDING_FUNC(Servo_DeclarationBlock_Count, uint32_t, + RawServoDeclarationBlockBorrowed declarations) +SERVO_BINDING_FUNC(Servo_DeclarationBlock_GetNthProperty, bool, + RawServoDeclarationBlockBorrowed declarations, + uint32_t index, nsAString* result) // CSS supports() SERVO_BINDING_FUNC(Servo_CSSSupports, bool, diff --git a/layout/style/ServoDeclarationBlock.h b/layout/style/ServoDeclarationBlock.h index 12110dfda8a6..3671f27e9080 100644 --- a/layout/style/ServoDeclarationBlock.h +++ b/layout/style/ServoDeclarationBlock.h @@ -29,6 +29,14 @@ public: return reinterpret_cast(&mRaw); } + uint32_t Count() const { + return Servo_DeclarationBlock_Count(mRaw); + } + bool GetNthProperty(uint32_t aIndex, nsAString& aReturn) const { + aReturn.Truncate(); + return Servo_DeclarationBlock_GetNthProperty(mRaw, aIndex, &aReturn); + } + protected: explicit ServoDeclarationBlock( already_AddRefed aRaw) diff --git a/layout/style/nsDOMCSSDeclaration.cpp b/layout/style/nsDOMCSSDeclaration.cpp index 21c63fc35424..dab5c3154d37 100644 --- a/layout/style/nsDOMCSSDeclaration.cpp +++ b/layout/style/nsDOMCSSDeclaration.cpp @@ -166,7 +166,7 @@ nsDOMCSSDeclaration::SetCssText(const nsAString& aCssText) NS_IMETHODIMP nsDOMCSSDeclaration::GetLength(uint32_t* aLength) { - css::Declaration* decl = GetCSSDeclaration(eOperation_Read)->AsGecko(); + DeclarationBlock* decl = GetCSSDeclaration(eOperation_Read); if (decl) { *aLength = decl->Count(); @@ -188,7 +188,7 @@ nsDOMCSSDeclaration::GetPropertyCSSValue(const nsAString& aPropertyName, ErrorRe void nsDOMCSSDeclaration::IndexedGetter(uint32_t aIndex, bool& aFound, nsAString& aPropName) { - css::Declaration* decl = GetCSSDeclaration(eOperation_Read)->AsGecko(); + DeclarationBlock* decl = GetCSSDeclaration(eOperation_Read); aFound = decl && decl->GetNthProperty(aIndex, aPropName); } From d1d1109e6f77f5de944daa605f33ca032b1eb7c3 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Thu, 3 Nov 2016 14:41:02 +1100 Subject: [PATCH 25/96] Bug 1294299 part 5 - Implement getter and setter of cssText. r=heycam MozReview-Commit-ID: Bho5PKtrT1I --HG-- extra : source : 6db28f1d25c87fbc26125ab0a7d88d71c1a2a8ba --- dom/base/nsAttrValue.cpp | 2 +- layout/style/DeclarationBlock.h | 2 ++ layout/style/DeclarationBlockInlines.h | 6 ++++++ layout/style/ServoBindingList.h | 3 +++ layout/style/ServoDeclarationBlock.cpp | 4 ++-- layout/style/ServoDeclarationBlock.h | 6 +++++- layout/style/nsDOMCSSDeclaration.cpp | 30 +++++++++++++++----------- 7 files changed, 37 insertions(+), 16 deletions(-) diff --git a/dom/base/nsAttrValue.cpp b/dom/base/nsAttrValue.cpp index 34f350d03c43..da3e96865abd 100644 --- a/dom/base/nsAttrValue.cpp +++ b/dom/base/nsAttrValue.cpp @@ -1720,7 +1720,7 @@ nsAttrValue::ParseStyleAttribute(const nsAString& aString, RefPtr decl; if (ownerDoc->GetStyleBackendType() == StyleBackendType::Servo) { - decl = ServoDeclarationBlock::FromStyleAttribute(aString); + decl = ServoDeclarationBlock::FromCssText(aString); } else { css::Loader* cssLoader = ownerDoc->CSSLoader(); nsCSSParser cssParser(cssLoader); diff --git a/layout/style/DeclarationBlock.h b/layout/style/DeclarationBlock.h index e9ffd18b4d89..060f45cab7ac 100644 --- a/layout/style/DeclarationBlock.h +++ b/layout/style/DeclarationBlock.h @@ -92,6 +92,8 @@ public: return c.mHTMLCSSStyleSheet; } + inline void ToString(nsAString& aString) const; + inline uint32_t Count() const; inline bool GetNthProperty(uint32_t aIndex, nsAString& aReturn) const; diff --git a/layout/style/DeclarationBlockInlines.h b/layout/style/DeclarationBlockInlines.h index 763f94620e6c..a3323d0b70a3 100644 --- a/layout/style/DeclarationBlockInlines.h +++ b/layout/style/DeclarationBlockInlines.h @@ -26,6 +26,12 @@ DeclarationBlock::Release() MOZ_STYLO_FORWARD(Release, ()) } +void +DeclarationBlock::ToString(nsAString& aString) const +{ + MOZ_STYLO_FORWARD(ToString, (aString)) +} + uint32_t DeclarationBlock::Count() const { diff --git a/layout/style/ServoBindingList.h b/layout/style/ServoBindingList.h index d1122596190e..682d47f97587 100644 --- a/layout/style/ServoBindingList.h +++ b/layout/style/ServoBindingList.h @@ -71,6 +71,9 @@ SERVO_BINDING_FUNC(Servo_DeclarationBlock_Release, void, SERVO_BINDING_FUNC(Servo_DeclarationBlock_Equals, bool, RawServoDeclarationBlockBorrowed a, RawServoDeclarationBlockBorrowed b) +SERVO_BINDING_FUNC(Servo_DeclarationBlock_GetCssText, void, + RawServoDeclarationBlockBorrowed declarations, + nsAString* result) SERVO_BINDING_FUNC(Servo_DeclarationBlock_SerializeOneValue, void, RawServoDeclarationBlockBorrowed declarations, nsString* buffer) diff --git a/layout/style/ServoDeclarationBlock.cpp b/layout/style/ServoDeclarationBlock.cpp index 5aa17a73cad1..d2c29c96dd88 100644 --- a/layout/style/ServoDeclarationBlock.cpp +++ b/layout/style/ServoDeclarationBlock.cpp @@ -10,9 +10,9 @@ namespace mozilla { /* static */ already_AddRefed -ServoDeclarationBlock::FromStyleAttribute(const nsAString& aString) +ServoDeclarationBlock::FromCssText(const nsAString& aCssText) { - NS_ConvertUTF16toUTF8 value(aString); + NS_ConvertUTF16toUTF8 value(aCssText); RefPtr raw = Servo_ParseStyleAttribute(&value).Consume(); RefPtr decl = new ServoDeclarationBlock(raw.forget()); diff --git a/layout/style/ServoDeclarationBlock.h b/layout/style/ServoDeclarationBlock.h index 3671f27e9080..eca8e8944113 100644 --- a/layout/style/ServoDeclarationBlock.h +++ b/layout/style/ServoDeclarationBlock.h @@ -20,7 +20,7 @@ public: NS_INLINE_DECL_REFCOUNTING(ServoDeclarationBlock) static already_AddRefed - FromStyleAttribute(const nsAString& aString); + FromCssText(const nsAString& aCssText); RawServoDeclarationBlock* const* RefRaw() const { static_assert(sizeof(RefPtr) == @@ -29,6 +29,10 @@ public: return reinterpret_cast(&mRaw); } + void ToString(nsAString& aResult) const { + Servo_DeclarationBlock_GetCssText(mRaw, &aResult); + } + uint32_t Count() const { return Servo_DeclarationBlock_Count(mRaw); } diff --git a/layout/style/nsDOMCSSDeclaration.cpp b/layout/style/nsDOMCSSDeclaration.cpp index dab5c3154d37..4dd6b6f20413 100644 --- a/layout/style/nsDOMCSSDeclaration.cpp +++ b/layout/style/nsDOMCSSDeclaration.cpp @@ -116,7 +116,7 @@ nsDOMCSSDeclaration::SetPropertyValue(const nsCSSPropertyID aPropID, NS_IMETHODIMP nsDOMCSSDeclaration::GetCssText(nsAString& aCssText) { - css::Declaration* decl = GetCSSDeclaration(eOperation_Read)->AsGecko(); + DeclarationBlock* decl = GetCSSDeclaration(eOperation_Read); aCssText.Truncate(); if (decl) { @@ -131,7 +131,7 @@ nsDOMCSSDeclaration::SetCssText(const nsAString& aCssText) { // We don't need to *do* anything with the old declaration, but we need // to ensure that it exists, or else SetCSSDeclaration may crash. - css::Declaration* olddecl = GetCSSDeclaration(eOperation_Modify)->AsGecko(); + DeclarationBlock* olddecl = GetCSSDeclaration(eOperation_Modify); if (!olddecl) { return NS_ERROR_NOT_AVAILABLE; } @@ -149,18 +149,24 @@ nsDOMCSSDeclaration::SetCssText(const nsAString& aCssText) // rule (see stack in bug 209575). mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true); - RefPtr decl(new css::Declaration()); - decl->InitializeEmpty(); - nsCSSParser cssParser(env.mCSSLoader); - bool changed; - nsresult result = cssParser.ParseDeclarations(aCssText, env.mSheetURI, - env.mBaseURI, - env.mPrincipal, decl, &changed); - if (NS_FAILED(result) || !changed) { - return result; + RefPtr newdecl; + if (olddecl->IsServo()) { + newdecl = ServoDeclarationBlock::FromCssText(aCssText); + } else { + RefPtr decl(new css::Declaration()); + decl->InitializeEmpty(); + nsCSSParser cssParser(env.mCSSLoader); + bool changed; + nsresult result = cssParser.ParseDeclarations(aCssText, env.mSheetURI, + env.mBaseURI, env.mPrincipal, + decl, &changed); + if (NS_FAILED(result) || !changed) { + return result; + } + newdecl = decl.forget(); } - return SetCSSDeclaration(decl); + return SetCSSDeclaration(newdecl); } NS_IMETHODIMP From ec88449c05281c6c458b5f4373ab27b4db749b93 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Thu, 3 Nov 2016 14:41:02 +1100 Subject: [PATCH 26/96] Bug 1294299 part 6 - Change ident of float property to float_. r=heycam MozReview-Commit-ID: 9w57lJhNQKy --HG-- extra : source : 14393c1e79f4ffefdd1a723639ea062322c3aa8c --- layout/style/nsCSSPropList.h | 2 +- layout/style/nsComputedDOMStylePropertyList.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/layout/style/nsCSSPropList.h b/layout/style/nsCSSPropList.h index 3150d7f8b43b..fb393109fa50 100644 --- a/layout/style/nsCSSPropList.h +++ b/layout/style/nsCSSPropList.h @@ -1793,7 +1793,7 @@ CSS_PROP_POSITION( eStyleAnimType_Discrete) CSS_PROP_DISPLAY( float, - float, + float_, CSS_PROP_PUBLIC_OR_PRIVATE(CssFloat, Float), CSS_PROPERTY_PARSE_VALUE | CSS_PROPERTY_APPLIES_TO_FIRST_LETTER, diff --git a/layout/style/nsComputedDOMStylePropertyList.h b/layout/style/nsComputedDOMStylePropertyList.h index badefac5dc11..7c0457e34284 100644 --- a/layout/style/nsComputedDOMStylePropertyList.h +++ b/layout/style/nsComputedDOMStylePropertyList.h @@ -126,7 +126,7 @@ COMPUTED_STYLE_PROP(flex_direction, FlexDirection) COMPUTED_STYLE_PROP(flex_grow, FlexGrow) COMPUTED_STYLE_PROP(flex_shrink, FlexShrink) COMPUTED_STYLE_PROP(flex_wrap, FlexWrap) -COMPUTED_STYLE_PROP(float, Float) +COMPUTED_STYLE_PROP(float_, Float) //// COMPUTED_STYLE_PROP(font, Font) COMPUTED_STYLE_PROP(font_family, FontFamily) COMPUTED_STYLE_PROP(font_feature_settings, FontFeatureSettings) From fd4ecd1066706d065ef363b47693c75fefd642f4 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Thu, 3 Nov 2016 14:41:02 +1100 Subject: [PATCH 27/96] Bug 1294299 part 7 - Generate static atoms for CSS properties. r=emilio,heycam MozReview-Commit-ID: FCjbgKagQO1 --HG-- extra : source : aaa46ff4380bb5aecc9c2a7509323b395f4f4935 --- layout/build/nsLayoutStatics.cpp | 1 + layout/style/nsCSSProps.cpp | 44 ++++++++++++++++++++++++++++++++ layout/style/nsCSSProps.h | 24 +++++++++++++++++ 3 files changed, 69 insertions(+) diff --git a/layout/build/nsLayoutStatics.cpp b/layout/build/nsLayoutStatics.cpp index 0432e1528253..522312c50dfe 100644 --- a/layout/build/nsLayoutStatics.cpp +++ b/layout/build/nsLayoutStatics.cpp @@ -156,6 +156,7 @@ nsLayoutStatics::Initialize() nsCSSPseudoClasses::AddRefAtoms(); nsCSSPseudoElements::AddRefAtoms(); nsCSSKeywords::AddRefTable(); + nsCSSProps::AddRefAtoms(); nsCSSProps::AddRefTable(); nsColorNames::AddRefTable(); nsGkAtoms::AddRefAtoms(); diff --git a/layout/style/nsCSSProps.cpp b/layout/style/nsCSSProps.cpp index c52ae358658d..799cb1468b91 100644 --- a/layout/style/nsCSSProps.cpp +++ b/layout/style/nsCSSProps.cpp @@ -21,6 +21,7 @@ #include "mozilla/LookAndFeel.h" // for system colors #include "nsString.h" +#include "nsStaticAtom.h" #include "nsStaticNameTable.h" #include "mozilla/Preferences.h" @@ -713,6 +714,49 @@ nsCSSProps::GetStringValue(nsCSSCounterDesc aCounterDesc) } } +#define CSS_PROP(name_, id_, ...) nsICSSProperty* nsCSSProps::id_; +#define CSS_PROP_SHORTHAND(name_, id_, ...) CSS_PROP(name_, id_, ...) +#define CSS_PROP_LIST_INCLUDE_LOGICAL +#include "nsCSSPropList.h" +#undef CSS_PROP_LIST_INCLUDE_LOGICAL +#undef CSS_PROP_SHORTHAND +#undef CSS_PROP + +#define CSS_PROP(name_, id_, ...) NS_STATIC_ATOM_BUFFER(id_##_buffer, #name_) +#define CSS_PROP_SHORTHAND(name_, id_, ...) CSS_PROP(name_, id_, ...) +#define CSS_PROP_LIST_INCLUDE_LOGICAL +#include "nsCSSPropList.h" +#undef CSS_PROP_LIST_INCLUDE_LOGICAL +#undef CSS_PROP_SHORTHAND +#undef CSS_PROP + +static const nsStaticAtom CSSProps_info[] = { +#define CSS_PROP(name_, id_, ...) \ + NS_STATIC_ATOM(id_##_buffer, (nsIAtom**)&nsCSSProps::id_), +#define CSS_PROP_SHORTHAND(name_, id_, ...) CSS_PROP(name_, id_, __VA_ARGS__) +#define CSS_PROP_LIST_INCLUDE_LOGICAL +#include "nsCSSPropList.h" +#undef CSS_PROP_LIST_INCLUDE_LOGICAL +#undef CSS_PROP_SHORTHAND +#undef CSS_PROP +}; + +nsICSSProperty* nsCSSProps::gPropertyAtomTable[eCSSProperty_COUNT]; + +/* static */ void +nsCSSProps::AddRefAtoms() +{ + NS_RegisterStaticAtoms(CSSProps_info); +#define CSS_PROP(name_, id_, ...) \ + gPropertyAtomTable[eCSSProperty_##id_] = nsCSSProps::id_; +#define CSS_PROP_SHORTHAND(name_, id_, ...) CSS_PROP(name_, id_, ...) +#define CSS_PROP_LIST_INCLUDE_LOGICAL +#include "nsCSSPropList.h" +#undef CSS_PROP_LIST_INCLUDE_LOGICAL +#undef CSS_PROP_SHORTHAND +#undef CSS_PROP +} + /***************************************************************************/ const KTableEntry nsCSSProps::kAnimationDirectionKTable[] = { diff --git a/layout/style/nsCSSProps.h b/layout/style/nsCSSProps.h index 8d0d5c5f6937..00f7c139425d 100644 --- a/layout/style/nsCSSProps.h +++ b/layout/style/nsCSSProps.h @@ -13,6 +13,7 @@ #include #include +#include "nsIAtom.h" #include "nsString.h" #include "nsCSSPropertyID.h" #include "nsStyleStructFwd.h" @@ -331,6 +332,10 @@ enum nsStyleAnimType { eStyleAnimType_None }; +// Empty class derived from nsIAtom so that function signatures can +// require an atom from the atom list. +class nsICSSProperty : public nsIAtom {}; + class nsCSSProps { public: typedef mozilla::CSSEnabledState EnabledState; @@ -668,6 +673,25 @@ public: return false; } +public: + static void AddRefAtoms(); + static nsICSSProperty* AtomForProperty(nsCSSPropertyID aProperty) + { + MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT); + return gPropertyAtomTable[aProperty]; + } + +#define CSS_PROP(name_, id_, ...) static nsICSSProperty* id_; +#define CSS_PROP_SHORTHAND(name_, id_, ...) CSS_PROP(name_, id_, ...) +#define CSS_PROP_LIST_INCLUDE_LOGICAL +#include "nsCSSPropList.h" +#undef CSS_PROP_LIST_INCLUDE_LOGICAL +#undef CSS_PROP_SHORTHAND +#undef CSS_PROP + +private: + static nsICSSProperty* gPropertyAtomTable[eCSSProperty_COUNT]; + public: // Storing the enabledstate_ value in an nsCSSPropertyID variable is a small hack From 5e29805363aa8c8b6aba36996384a89f087ba844 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Thu, 3 Nov 2016 14:41:02 +1100 Subject: [PATCH 28/96] Bug 1294299 part 8 - Refactor interface provided by css::Declaration. r=heycam The main targets of this refactor are: 1. Move most of the logic of distinguishing properties and custom properties from nsDOMCSSDeclaration into css::Declaration, which gives ServoDeclarationBlock more flexibility to implement. 2. Rename those methods of css::Declaration to provide a clear interface which makes sense for implementing in ServoDeclarationBlock, and also avoid method overload, which can impede the forward macro, on them. MozReview-Commit-ID: 2cCqF855TVK --HG-- extra : source : 3837e1e558caac4f2901e838371e97c17821530e --- dom/base/nsTreeSanitizer.cpp | 2 +- dom/canvas/CanvasRenderingContext2D.cpp | 2 +- editor/libeditor/CSSEditUtils.cpp | 2 +- layout/style/Declaration.cpp | 132 +++++++++++++++--------- layout/style/Declaration.h | 35 ++++--- layout/style/nsCSSParser.cpp | 8 +- layout/style/nsDOMCSSDeclaration.cpp | 109 +++++-------------- layout/style/nsDOMCSSDeclaration.h | 9 +- 8 files changed, 135 insertions(+), 164 deletions(-) diff --git a/dom/base/nsTreeSanitizer.cpp b/dom/base/nsTreeSanitizer.cpp index d611fb24c672..dc53ea5fad1d 100644 --- a/dom/base/nsTreeSanitizer.cpp +++ b/dom/base/nsTreeSanitizer.cpp @@ -1068,7 +1068,7 @@ nsTreeSanitizer::SanitizeStyleDeclaration(mozilla::css::Declaration* aDeclaratio nsAutoString& aRuleText) { bool didSanitize = aDeclaration->HasProperty(eCSSProperty_binding); - aDeclaration->RemoveProperty(eCSSProperty_binding); + aDeclaration->RemovePropertyByID(eCSSProperty_binding); aDeclaration->ToString(aRuleText); return didSanitize; } diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index c778523e0fa3..8cf999e61f17 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -2686,7 +2686,7 @@ GetFontStyleContext(Element* aElement, const nsAString& aFont, // parsed (including having line-height removed). (Older drafts of // the spec required font sizes be converted to pixels, but that no // longer seems to be required.) - decl->GetValue(eCSSProperty_font, aOutUsedFont); + decl->GetPropertyValueByID(eCSSProperty_font, aOutUsedFont); return sc.forget(); } diff --git a/editor/libeditor/CSSEditUtils.cpp b/editor/libeditor/CSSEditUtils.cpp index 03616863e456..5199838c01e7 100644 --- a/editor/libeditor/CSSEditUtils.cpp +++ b/editor/libeditor/CSSEditUtils.cpp @@ -556,7 +556,7 @@ CSSEditUtils::GetCSSInlinePropertyBase(nsINode* aNode, nsCSSProps::LookupProperty(nsDependentAtomString(aProperty), CSSEnabledState::eForAllContent); MOZ_ASSERT(prop != eCSSProperty_UNKNOWN); - decl->AsGecko()->GetValue(prop, aValue); + decl->AsGecko()->GetPropertyValueByID(prop, aValue); return NS_OK; } diff --git a/layout/style/Declaration.cpp b/layout/style/Declaration.cpp index 09c917b06236..314edc7fcad8 100644 --- a/layout/style/Declaration.cpp +++ b/layout/style/Declaration.cpp @@ -116,7 +116,7 @@ Declaration::MightMapInheritedStyleData() Declaration::GetDiscretelyAnimatedCSSValue(nsCSSPropertyID aProperty, nsCSSValue* aValue) { - nsCSSCompressedDataBlock* data = GetValueIsImportant(aProperty) + nsCSSCompressedDataBlock* data = GetPropertyIsImportantByID(aProperty) ? mImportantData : mData; const nsCSSValue* value = data->ValueFor(aProperty); if (!value) { @@ -151,8 +151,69 @@ Declaration::ValueAppended(nsCSSPropertyID aProperty) mOrder.AppendElement(static_cast(aProperty)); } +template +inline void +DispatchPropertyOperation(const nsAString& aProperty, + PropFunc aPropFunc, CustomPropFunc aCustomPropFunc) +{ + nsCSSPropertyID propID = + nsCSSProps::LookupProperty(aProperty, CSSEnabledState::eForAllContent); + if (propID != eCSSProperty_UNKNOWN) { + if (propID != eCSSPropertyExtra_variable) { + aPropFunc(propID); + } else { + aCustomPropFunc(Substring(aProperty, CSS_CUSTOM_NAME_PREFIX_LENGTH)); + } + } +} + void -Declaration::RemoveProperty(nsCSSPropertyID aProperty) +Declaration::GetPropertyValue(const nsAString& aProperty, + nsAString& aValue) const +{ + DispatchPropertyOperation(aProperty, + [&](nsCSSPropertyID propID) { GetPropertyValueByID(propID, aValue); }, + [&](const nsAString& name) { GetVariableValue(name, aValue); }); +} + +void +Declaration::GetPropertyValueByID(nsCSSPropertyID aPropID, + nsAString& aValue) const +{ + GetPropertyValueInternal(aPropID, aValue, nsCSSValue::eNormalized); +} + +void +Declaration::GetAuthoredPropertyValue(const nsAString& aProperty, + nsAString& aValue) const +{ + DispatchPropertyOperation(aProperty, + [&](nsCSSPropertyID propID) { + GetPropertyValueInternal(propID, aValue, nsCSSValue::eAuthorSpecified); + }, + [&](const nsAString& name) { GetVariableValue(name, aValue); }); +} + +bool +Declaration::GetPropertyIsImportant(const nsAString& aProperty) const +{ + bool r = false; + DispatchPropertyOperation(aProperty, + [&](nsCSSPropertyID propID) { r = GetPropertyIsImportantByID(propID); }, + [&](const nsAString& name) { r = GetVariableIsImportant(name); }); + return r; +} + +void +Declaration::RemoveProperty(const nsAString& aProperty) +{ + DispatchPropertyOperation(aProperty, + [&](nsCSSPropertyID propID) { RemovePropertyByID(propID); }, + [&](const nsAString& name) { RemoveVariable(name); }); +} + +void +Declaration::RemovePropertyByID(nsCSSPropertyID aProperty) { MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT); @@ -180,7 +241,7 @@ Declaration::HasProperty(nsCSSPropertyID aProperty) const MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT_no_shorthands, "property ID out of range"); - nsCSSCompressedDataBlock *data = GetValueIsImportant(aProperty) + nsCSSCompressedDataBlock *data = GetPropertyIsImportantByID(aProperty) ? mImportantData : mData; const nsCSSValue *val = data->ValueFor(aProperty); return !!val; @@ -194,7 +255,7 @@ Declaration::AppendValueToString(nsCSSPropertyID aProperty, MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT_no_shorthands, "property ID out of range"); - nsCSSCompressedDataBlock *data = GetValueIsImportant(aProperty) + nsCSSCompressedDataBlock *data = GetPropertyIsImportantByID(aProperty) ? mImportantData : mData; const nsCSSValue *val = data->ValueFor(aProperty); if (!val) { @@ -205,18 +266,6 @@ Declaration::AppendValueToString(nsCSSPropertyID aProperty, return true; } -void -Declaration::GetValue(nsCSSPropertyID aProperty, nsAString& aValue) const -{ - GetValue(aProperty, aValue, nsCSSValue::eNormalized); -} - -void -Declaration::GetAuthoredValue(nsCSSPropertyID aProperty, nsAString& aValue) const -{ - GetValue(aProperty, aValue, nsCSSValue::eAuthorSpecified); -} - static void AppendSingleImageLayerPositionValue(const nsCSSValue& aPositionX, const nsCSSValue& aPositionY, @@ -489,8 +538,9 @@ Declaration::GetImageLayerPositionValue( } void -Declaration::GetValue(nsCSSPropertyID aProperty, nsAString& aValue, - nsCSSValue::Serialization aSerialization) const +Declaration::GetPropertyValueInternal( + nsCSSPropertyID aProperty, nsAString& aValue, + nsCSSValue::Serialization aSerialization) const { aValue.Truncate(0); @@ -1500,23 +1550,7 @@ Declaration::GetValue(nsCSSPropertyID aProperty, nsAString& aValue, } bool -Declaration::GetValueIsImportant(const nsAString& aProperty) const -{ - nsCSSPropertyID propID = nsCSSProps:: - LookupProperty(aProperty, CSSEnabledState::eIgnoreEnabledState); - if (propID == eCSSProperty_UNKNOWN) { - return false; - } - if (propID == eCSSPropertyExtra_variable) { - const nsSubstring& variableName = - Substring(aProperty, CSS_CUSTOM_NAME_PREFIX_LENGTH); - return GetVariableValueIsImportant(variableName); - } - return GetValueIsImportant(propID); -} - -bool -Declaration::GetValueIsImportant(nsCSSPropertyID aProperty) const +Declaration::GetPropertyIsImportantByID(nsCSSPropertyID aProperty) const { if (!mImportantData) return false; @@ -1555,7 +1589,7 @@ Declaration::AppendPropertyAndValueToString(nsCSSPropertyID aProperty, AppendValueToString(aProperty, aResult, nsCSSValue::eNormalized); else aResult.Append(aValue); - if (GetValueIsImportant(aProperty)) { + if (GetPropertyIsImportantByID(aProperty)) { aResult.AppendLiteral(" ! important"); } aResult.AppendLiteral("; "); @@ -1622,7 +1656,8 @@ Declaration::ToString(nsAString& aString) const SetImmutable(); nsCSSCompressedDataBlock *systemFontData = - GetValueIsImportant(eCSSProperty__x_system_font) ? mImportantData : mData; + GetPropertyIsImportantByID(eCSSProperty__x_system_font) ? mImportantData + : mData; const nsCSSValue *systemFont = systemFontData->ValueFor(eCSSProperty__x_system_font); const bool haveSystemFont = systemFont && @@ -1671,7 +1706,7 @@ Declaration::ToString(nsAString& aString) const // least, which is exactly the order we want to test them. nsCSSPropertyID shorthand = *shorthands; - GetValue(shorthand, value); + GetPropertyValueByID(shorthand, value); // in the system font case, skip over font-variant shorthand, since all // subproperties are already dealt with via the font shorthand @@ -1680,8 +1715,8 @@ Declaration::ToString(nsAString& aString) const continue; } - // If GetValue gives us a non-empty string back, we can use that - // value; otherwise it's not possible to use this shorthand. + // If GetPropertyValueByID gives us a non-empty string back, we can + // use that value; otherwise it's not possible to use this shorthand. if (!value.IsEmpty()) { AppendPropertyAndValueToString(shorthand, value, aString); shorthandsUsed.AppendElement(shorthand); @@ -1808,8 +1843,7 @@ Declaration::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const } void -Declaration::GetVariableDeclaration(const nsAString& aName, - nsAString& aValue) const +Declaration::GetVariableValue(const nsAString& aName, nsAString& aValue) const { aValue.Truncate(); @@ -1842,11 +1876,11 @@ Declaration::GetVariableDeclaration(const nsAString& aName, } void -Declaration::AddVariableDeclaration(const nsAString& aName, - CSSVariableDeclarations::Type aType, - const nsString& aValue, - bool aIsImportant, - bool aOverrideImportant) +Declaration::AddVariable(const nsAString& aName, + CSSVariableDeclarations::Type aType, + const nsString& aValue, + bool aIsImportant, + bool aOverrideImportant) { MOZ_ASSERT(IsMutable()); @@ -1910,7 +1944,7 @@ Declaration::AddVariableDeclaration(const nsAString& aName, } void -Declaration::RemoveVariableDeclaration(const nsAString& aName) +Declaration::RemoveVariable(const nsAString& aName) { if (mVariables) { mVariables->Remove(aName); @@ -1925,7 +1959,7 @@ Declaration::RemoveVariableDeclaration(const nsAString& aName) } bool -Declaration::GetVariableValueIsImportant(const nsAString& aName) const +Declaration::GetVariableIsImportant(const nsAString& aName) const { return mImportantVariables && mImportantVariables->Has(aName); } diff --git a/layout/style/Declaration.h b/layout/style/Declaration.h index c77c995f87c3..ac0c25894761 100644 --- a/layout/style/Declaration.h +++ b/layout/style/Declaration.h @@ -121,18 +121,19 @@ public: */ void ValueAppended(nsCSSPropertyID aProperty); - void RemoveProperty(nsCSSPropertyID aProperty); + void GetPropertyValue(const nsAString& aProperty, nsAString& aValue) const; + void GetPropertyValueByID(nsCSSPropertyID aPropID, nsAString& aValue) const; + void GetAuthoredPropertyValue(const nsAString& aProperty, + nsAString& aValue) const; + bool GetPropertyIsImportant(const nsAString& aProperty) const; + void RemoveProperty(const nsAString& aProperty); + void RemovePropertyByID(nsCSSPropertyID aProperty); bool HasProperty(nsCSSPropertyID aProperty) const; - void GetValue(nsCSSPropertyID aProperty, nsAString& aValue) const; - void GetAuthoredValue(nsCSSPropertyID aProperty, nsAString& aValue) const; - bool HasImportantData() const { return mImportantData || mImportantVariables; } - bool GetValueIsImportant(nsCSSPropertyID aProperty) const; - bool GetValueIsImportant(const nsAString& aProperty) const; /** * Adds a custom property declaration to this object. @@ -145,18 +146,18 @@ public: * @param aOverrideImportant When aIsImportant is false, whether an * existing !important declaration will be overridden. */ - void AddVariableDeclaration(const nsAString& aName, - CSSVariableDeclarations::Type aType, - const nsString& aValue, - bool aIsImportant, - bool aOverrideImportant); + void AddVariable(const nsAString& aName, + CSSVariableDeclarations::Type aType, + const nsString& aValue, + bool aIsImportant, + bool aOverrideImportant); /** * Removes a custom property declaration from this object. * * @param aName The variable name (i.e., without the "--" prefix). */ - void RemoveVariableDeclaration(const nsAString& aName); + void RemoveVariable(const nsAString& aName); /** * Gets the string value for a custom property declaration of a variable @@ -167,13 +168,13 @@ public: * stored. If the value is 'initial' or 'inherit', that exact string * will be stored in aValue. */ - void GetVariableDeclaration(const nsAString& aName, nsAString& aValue) const; + void GetVariableValue(const nsAString& aName, nsAString& aValue) const; /** * Returns whether the custom property declaration for a variable with * the given name was !important. */ - bool GetVariableValueIsImportant(const nsAString& aName) const; + bool GetVariableIsImportant(const nsAString& aName) const; uint32_t Count() const { return mOrder.Length(); @@ -309,8 +310,10 @@ private: Declaration& operator=(const Declaration& aCopy) = delete; bool operator==(const Declaration& aCopy) const = delete; - void GetValue(nsCSSPropertyID aProperty, nsAString& aValue, - nsCSSValue::Serialization aValueSerialization) const; + void GetPropertyValueInternal(nsCSSPropertyID aProperty, nsAString& aValue, + nsCSSValue::Serialization aValueSerialization) + const; + bool GetPropertyIsImportantByID(nsCSSPropertyID aProperty) const; static void AppendImportanceToString(bool aIsImportant, nsAString& aString); // return whether there was a value in |aValue| (i.e., it had a non-null unit) diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index 01161a35d8eb..3ca55c95a29b 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -2108,8 +2108,8 @@ CSSParserImpl::ParseVariable(const nsAString& aVariableName, OUTPUT_ERROR(); } else { CLEAR_ERROR(); - aDeclaration->AddVariableDeclaration(aVariableName, variableType, - variableValue, aIsImportant, true); + aDeclaration->AddVariable(aVariableName, variableType, + variableValue, aIsImportant, true); *aChanged = true; } @@ -7492,8 +7492,8 @@ CSSParserImpl::ParseDeclaration(css::Declaration* aDeclaration, CSS_CUSTOM_NAME_PREFIX_LENGTH).EqualsLiteral("--")); // remove '--' nsDependentString varName(propertyName, CSS_CUSTOM_NAME_PREFIX_LENGTH); - aDeclaration->AddVariableDeclaration(varName, variableType, variableValue, - status == ePriority_Important, false); + aDeclaration->AddVariable(varName, variableType, variableValue, + status == ePriority_Important, false); } else { *aChanged |= mData.TransferFromBlock(mTempData, propID, EnabledState(), status == ePriority_Important, diff --git a/layout/style/nsDOMCSSDeclaration.cpp b/layout/style/nsDOMCSSDeclaration.cpp index 4dd6b6f20413..0f2eac42f161 100644 --- a/layout/style/nsDOMCSSDeclaration.cpp +++ b/layout/style/nsDOMCSSDeclaration.cpp @@ -48,33 +48,13 @@ nsDOMCSSDeclaration::GetPropertyValue(const nsCSSPropertyID aPropID, NS_PRECONDITION(aPropID != eCSSProperty_UNKNOWN, "Should never pass eCSSProperty_UNKNOWN around"); - css::Declaration* decl = GetCSSDeclaration(eOperation_Read)->AsGecko(); - aValue.Truncate(); - if (decl) { - decl->GetValue(aPropID, aValue); + if (css::Declaration* decl = GetCSSDeclaration(eOperation_Read)->AsGecko()) { + decl->GetPropertyValueByID(aPropID, aValue); } return NS_OK; } -void -nsDOMCSSDeclaration::GetCustomPropertyValue(const nsAString& aPropertyName, - nsAString& aValue) -{ - MOZ_ASSERT(Substring(aPropertyName, 0, - CSS_CUSTOM_NAME_PREFIX_LENGTH).EqualsLiteral("--")); - - css::Declaration* decl = GetCSSDeclaration(eOperation_Read)->AsGecko(); - if (!decl) { - aValue.Truncate(); - return; - } - - decl->GetVariableDeclaration(Substring(aPropertyName, - CSS_CUSTOM_NAME_PREFIX_LENGTH), - aValue); -} - NS_IMETHODIMP nsDOMCSSDeclaration::SetPropertyValue(const nsCSSPropertyID aPropID, const nsAString& aValue) @@ -106,7 +86,7 @@ nsDOMCSSDeclaration::SetPropertyValue(const nsCSSPropertyID aPropID, if (aValue.IsEmpty()) { // If the new value of the property is an empty string we remove the // property. - return RemoveProperty(aPropID); + return RemovePropertyInternal(aPropID); } return ParsePropertyValue(aPropID, aValue, false); @@ -202,43 +182,20 @@ NS_IMETHODIMP nsDOMCSSDeclaration::GetPropertyValue(const nsAString& aPropertyName, nsAString& aReturn) { - const nsCSSPropertyID propID = - nsCSSProps::LookupProperty(aPropertyName, CSSEnabledState::eForAllContent); - if (propID == eCSSProperty_UNKNOWN) { - aReturn.Truncate(); - return NS_OK; + aReturn.Truncate(); + if (css::Declaration* decl = GetCSSDeclaration(eOperation_Read)->AsGecko()) { + decl->GetPropertyValue(aPropertyName, aReturn); } - - if (propID == eCSSPropertyExtra_variable) { - GetCustomPropertyValue(aPropertyName, aReturn); - return NS_OK; - } - - return GetPropertyValue(propID, aReturn); + return NS_OK; } NS_IMETHODIMP nsDOMCSSDeclaration::GetAuthoredPropertyValue(const nsAString& aPropertyName, nsAString& aReturn) { - const nsCSSPropertyID propID = - nsCSSProps::LookupProperty(aPropertyName, CSSEnabledState::eForAllContent); - if (propID == eCSSProperty_UNKNOWN) { - aReturn.Truncate(); - return NS_OK; + if (css::Declaration* decl = GetCSSDeclaration(eOperation_Read)->AsGecko()) { + decl->GetAuthoredPropertyValue(aPropertyName, aReturn); } - - if (propID == eCSSPropertyExtra_variable) { - GetCustomPropertyValue(aPropertyName, aReturn); - return NS_OK; - } - - css::Declaration* decl = GetCSSDeclaration(eOperation_Read)->AsGecko(); - if (!decl) { - return NS_ERROR_FAILURE; - } - - decl->GetAuthoredValue(propID, aReturn); return NS_OK; } @@ -249,7 +206,7 @@ nsDOMCSSDeclaration::GetPropertyPriority(const nsAString& aPropertyName, css::Declaration* decl = GetCSSDeclaration(eOperation_Read)->AsGecko(); aReturn.Truncate(); - if (decl && decl->GetValueIsImportant(aPropertyName)) { + if (decl && decl->GetPropertyIsImportant(aPropertyName)) { aReturn.AssignLiteral("important"); } @@ -261,6 +218,13 @@ nsDOMCSSDeclaration::SetProperty(const nsAString& aPropertyName, const nsAString& aValue, const nsAString& aPriority) { + if (aValue.IsEmpty()) { + // If the new value of the property is an empty string we remove the + // property. + // XXX this ignores the priority string, should it? + return RemovePropertyInternal(aPropertyName); + } + // In the common (and fast) cases we can use the property id nsCSSPropertyID propID = nsCSSProps::LookupProperty(aPropertyName, CSSEnabledState::eForAllContent); @@ -268,16 +232,6 @@ nsDOMCSSDeclaration::SetProperty(const nsAString& aPropertyName, return NS_OK; } - if (aValue.IsEmpty()) { - // If the new value of the property is an empty string we remove the - // property. - // XXX this ignores the priority string, should it? - if (propID == eCSSPropertyExtra_variable) { - return RemoveCustomProperty(aPropertyName); - } - return RemoveProperty(propID); - } - bool important; if (aPriority.IsEmpty()) { important = false; @@ -298,22 +252,9 @@ NS_IMETHODIMP nsDOMCSSDeclaration::RemoveProperty(const nsAString& aPropertyName, nsAString& aReturn) { - const nsCSSPropertyID propID = - nsCSSProps::LookupProperty(aPropertyName, CSSEnabledState::eForAllContent); - if (propID == eCSSProperty_UNKNOWN) { - aReturn.Truncate(); - return NS_OK; - } - - if (propID == eCSSPropertyExtra_variable) { - RemoveCustomProperty(aPropertyName); - return NS_OK; - } - - nsresult rv = GetPropertyValue(propID, aReturn); + nsresult rv = GetPropertyValue(aPropertyName, aReturn); NS_ENSURE_SUCCESS(rv, rv); - - return RemoveProperty(propID); + return RemovePropertyInternal(aPropertyName); } /* static */ void @@ -411,7 +352,7 @@ nsDOMCSSDeclaration::ParseCustomPropertyValue(const nsAString& aPropertyName, } nsresult -nsDOMCSSDeclaration::RemoveProperty(const nsCSSPropertyID aPropID) +nsDOMCSSDeclaration::RemovePropertyInternal(nsCSSPropertyID aPropID) { css::Declaration* olddecl = GetCSSDeclaration(eOperation_RemoveProperty)->AsGecko(); @@ -427,16 +368,13 @@ nsDOMCSSDeclaration::RemoveProperty(const nsCSSPropertyID aPropID) mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true); RefPtr decl = olddecl->EnsureMutable(); - decl->RemoveProperty(aPropID); + decl->RemovePropertyByID(aPropID); return SetCSSDeclaration(decl); } nsresult -nsDOMCSSDeclaration::RemoveCustomProperty(const nsAString& aPropertyName) +nsDOMCSSDeclaration::RemovePropertyInternal(const nsAString& aPropertyName) { - MOZ_ASSERT(Substring(aPropertyName, 0, - CSS_CUSTOM_NAME_PREFIX_LENGTH).EqualsLiteral("--")); - css::Declaration* olddecl = GetCSSDeclaration(eOperation_RemoveProperty)->AsGecko(); if (!olddecl) { @@ -451,7 +389,6 @@ nsDOMCSSDeclaration::RemoveCustomProperty(const nsAString& aPropertyName) mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true); RefPtr decl = olddecl->EnsureMutable(); - decl->RemoveVariableDeclaration(Substring(aPropertyName, - CSS_CUSTOM_NAME_PREFIX_LENGTH)); + decl->RemoveProperty(aPropertyName); return SetCSSDeclaration(decl); } diff --git a/layout/style/nsDOMCSSDeclaration.h b/layout/style/nsDOMCSSDeclaration.h index 626d0171f097..5b2308e4af7d 100644 --- a/layout/style/nsDOMCSSDeclaration.h +++ b/layout/style/nsDOMCSSDeclaration.h @@ -160,16 +160,13 @@ protected: const nsAString& aPropValue, bool aIsImportant); - // Prop-id based version of RemoveProperty. Note that this does not - // return the old value; it just does a straight removal. - nsresult RemoveProperty(const nsCSSPropertyID aPropID); - - void GetCustomPropertyValue(const nsAString& aPropertyName, nsAString& aValue); - nsresult RemoveCustomProperty(const nsAString& aPropertyName); nsresult ParseCustomPropertyValue(const nsAString& aPropertyName, const nsAString& aPropValue, bool aIsImportant); + nsresult RemovePropertyInternal(nsCSSPropertyID aPropID); + nsresult RemovePropertyInternal(const nsAString& aProperty); + protected: virtual ~nsDOMCSSDeclaration(); }; From 3196565e73f77f30f95a666bbfca615c4d71ff56 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Thu, 3 Nov 2016 14:41:02 +1100 Subject: [PATCH 29/96] Bug 1294299 part 9 - Implement Clone for ServoDeclarationBlock. r=heycam MozReview-Commit-ID: 5y2h26j87Sz --HG-- extra : source : 2e09860b35c9ab89fe959f9c6895a793858c4fee --- dom/html/nsGenericHTMLElement.cpp | 8 +------- layout/style/DeclarationBlock.h | 2 ++ layout/style/DeclarationBlockInlines.h | 12 ++++++++++++ layout/style/ServoBindingList.h | 2 ++ layout/style/ServoDeclarationBlock.h | 4 ++++ 5 files changed, 21 insertions(+), 7 deletions(-) diff --git a/dom/html/nsGenericHTMLElement.cpp b/dom/html/nsGenericHTMLElement.cpp index 62ca0b29f8a9..d75001a83a45 100644 --- a/dom/html/nsGenericHTMLElement.cpp +++ b/dom/html/nsGenericHTMLElement.cpp @@ -190,16 +190,10 @@ nsGenericHTMLElement::CopyInnerTo(Element* aDst) if (name->Equals(nsGkAtoms::style, kNameSpaceID_None) && value->Type() == nsAttrValue::eCSSDeclaration) { DeclarationBlock* decl = value->GetCSSDeclarationValue(); - if (decl->IsServo()) { - MOZ_CRASH("stylo: clone not implemented"); - continue; - } - // We can't just set this as a string, because that will fail // to reparse the string into style data until the node is // inserted into the document. Clone the Rule instead. - RefPtr - declClone = new css::Declaration(*decl->AsGecko()); + RefPtr declClone = decl->Clone(); rv = aDst->SetInlineStyleDeclaration(declClone, &valStr, false); NS_ENSURE_SUCCESS(rv, rv); diff --git a/layout/style/DeclarationBlock.h b/layout/style/DeclarationBlock.h index 060f45cab7ac..b93d7f598fe5 100644 --- a/layout/style/DeclarationBlock.h +++ b/layout/style/DeclarationBlock.h @@ -41,6 +41,8 @@ public: inline MozExternalRefCountType AddRef(); inline MozExternalRefCountType Release(); + inline already_AddRefed Clone() const; + /** * Return whether |this| may be modified. */ diff --git a/layout/style/DeclarationBlockInlines.h b/layout/style/DeclarationBlockInlines.h index a3323d0b70a3..d29a96e5b7a5 100644 --- a/layout/style/DeclarationBlockInlines.h +++ b/layout/style/DeclarationBlockInlines.h @@ -26,6 +26,18 @@ DeclarationBlock::Release() MOZ_STYLO_FORWARD(Release, ()) } +already_AddRefed +DeclarationBlock::Clone() const +{ + RefPtr result; + if (IsGecko()) { + result = new css::Declaration(*AsGecko()); + } else { + result = new ServoDeclarationBlock(*AsServo()); + } + return result.forget(); +} + void DeclarationBlock::ToString(nsAString& aString) const { diff --git a/layout/style/ServoBindingList.h b/layout/style/ServoBindingList.h index 682d47f97587..daaf766921e6 100644 --- a/layout/style/ServoBindingList.h +++ b/layout/style/ServoBindingList.h @@ -64,6 +64,8 @@ SERVO_BINDING_FUNC(Servo_ParseStyleAttribute, RawServoDeclarationBlockStrong, const nsACString* data) SERVO_BINDING_FUNC(Servo_DeclarationBlock_CreateEmpty, RawServoDeclarationBlockStrong) +SERVO_BINDING_FUNC(Servo_DeclarationBlock_Clone, RawServoDeclarationBlockStrong, + RawServoDeclarationBlockBorrowed declarations) SERVO_BINDING_FUNC(Servo_DeclarationBlock_AddRef, void, RawServoDeclarationBlockBorrowed declarations) SERVO_BINDING_FUNC(Servo_DeclarationBlock_Release, void, diff --git a/layout/style/ServoDeclarationBlock.h b/layout/style/ServoDeclarationBlock.h index eca8e8944113..515eca8c006a 100644 --- a/layout/style/ServoDeclarationBlock.h +++ b/layout/style/ServoDeclarationBlock.h @@ -17,6 +17,10 @@ public: ServoDeclarationBlock() : ServoDeclarationBlock(Servo_DeclarationBlock_CreateEmpty().Consume()) {} + ServoDeclarationBlock(const ServoDeclarationBlock& aCopy) + : DeclarationBlock(aCopy) + , mRaw(Servo_DeclarationBlock_Clone(aCopy.mRaw).Consume()) {} + NS_INLINE_DECL_REFCOUNTING(ServoDeclarationBlock) static already_AddRefed From c5a5be01aafa39663241ee65255d7b41ab0c7863 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Thu, 3 Nov 2016 14:41:02 +1100 Subject: [PATCH 30/96] Bug 1294299 part 10 - Implement DeclarationBlock.EnsureMutable. r=heycam MozReview-Commit-ID: KpaypyY5moC --HG-- extra : source : abf407828f1272475d7765064dd9f6c654f16079 --- layout/style/Declaration.cpp | 13 ------------ layout/style/Declaration.h | 13 ++++++------ layout/style/DeclarationBlock.h | 5 +++++ layout/style/DeclarationBlockInlines.h | 14 +++++++++++++ layout/style/nsDOMCSSDeclaration.cpp | 29 +++++++++++++------------- 5 files changed, 39 insertions(+), 35 deletions(-) diff --git a/layout/style/Declaration.cpp b/layout/style/Declaration.cpp index 314edc7fcad8..474ff0db4625 100644 --- a/layout/style/Declaration.cpp +++ b/layout/style/Declaration.cpp @@ -1813,19 +1813,6 @@ Declaration::InitializeEmpty() mData = nsCSSCompressedDataBlock::CreateEmptyBlock(); } -already_AddRefed -Declaration::EnsureMutable() -{ - MOZ_ASSERT(mData, "should only be called when not expanded"); - RefPtr result; - if (!IsMutable()) { - result = new Declaration(*this); - } else { - result = this; - } - return result.forget(); -} - size_t Declaration::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const { diff --git a/layout/style/Declaration.h b/layout/style/Declaration.h index ac0c25894761..a18c38b5dd16 100644 --- a/layout/style/Declaration.h +++ b/layout/style/Declaration.h @@ -188,6 +188,10 @@ public: nsCSSCompressedDataBlock* GetNormalBlock() const { return mData; } nsCSSCompressedDataBlock* GetImportantBlock() const { return mImportantData; } + void AssertNotExpanded() const { + MOZ_ASSERT(mData, "should only be called when not expanded"); + } + /** * Initialize this declaration as holding no data. Cannot fail. */ @@ -223,7 +227,7 @@ public: } void MapImportantRuleInfoInto(nsRuleData *aRuleData) const { - MOZ_ASSERT(mData, "called while expanded"); + AssertNotExpanded(); MOZ_ASSERT(mImportantData || mImportantVariables, "must have important data or variables"); if (mImportantData) { @@ -251,7 +255,7 @@ public: bool* aChanged) { AssertMutable(); - MOZ_ASSERT(mData, "called while expanded"); + AssertNotExpanded(); if (nsCSSProps::IsShorthand(aProperty)) { *aChanged = false; @@ -280,11 +284,6 @@ public: return !!mData->ValueFor(aProperty); } - /** - * Copy |this|, if necessary to ensure that it can be modified. - */ - already_AddRefed EnsureMutable(); - /** * Clear the data, in preparation for its replacement with entirely * new data by a call to |CompressFrom|. diff --git a/layout/style/DeclarationBlock.h b/layout/style/DeclarationBlock.h index b93d7f598fe5..f42590d9bd18 100644 --- a/layout/style/DeclarationBlock.h +++ b/layout/style/DeclarationBlock.h @@ -63,6 +63,11 @@ public: */ void SetImmutable() const { mImmutable = true; } + /** + * Copy |this|, if necessary to ensure that it can be modified. + */ + inline already_AddRefed EnsureMutable(); + void SetOwningRule(css::Rule* aRule) { MOZ_ASSERT(!mContainer.mOwningRule || !aRule, "should never overwrite one rule with another"); diff --git a/layout/style/DeclarationBlockInlines.h b/layout/style/DeclarationBlockInlines.h index d29a96e5b7a5..ed81d372d979 100644 --- a/layout/style/DeclarationBlockInlines.h +++ b/layout/style/DeclarationBlockInlines.h @@ -38,6 +38,20 @@ DeclarationBlock::Clone() const return result.forget(); } +already_AddRefed +DeclarationBlock::EnsureMutable() +{ +#ifdef DEBUG + if (IsGecko()) { + AsGecko()->AssertNotExpanded(); + } +#endif + if (!IsMutable()) { + return Clone(); + } + return do_AddRef(this); +} + void DeclarationBlock::ToString(nsAString& aString) const { diff --git a/layout/style/nsDOMCSSDeclaration.cpp b/layout/style/nsDOMCSSDeclaration.cpp index 0f2eac42f161..b56a97b636ba 100644 --- a/layout/style/nsDOMCSSDeclaration.cpp +++ b/layout/style/nsDOMCSSDeclaration.cpp @@ -279,7 +279,7 @@ nsDOMCSSDeclaration::ParsePropertyValue(const nsCSSPropertyID aPropID, const nsAString& aPropValue, bool aIsImportant) { - css::Declaration* olddecl = GetCSSDeclaration(eOperation_Modify)->AsGecko(); + DeclarationBlock* olddecl = GetCSSDeclaration(eOperation_Modify); if (!olddecl) { return NS_ERROR_NOT_AVAILABLE; } @@ -296,12 +296,13 @@ nsDOMCSSDeclaration::ParsePropertyValue(const nsCSSPropertyID aPropID, // between when we mutate the declaration and when we set the new // rule (see stack in bug 209575). mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true); - RefPtr decl = olddecl->EnsureMutable(); + RefPtr decl = olddecl->EnsureMutable(); nsCSSParser cssParser(env.mCSSLoader); bool changed; - cssParser.ParseProperty(aPropID, aPropValue, env.mSheetURI, env.mBaseURI, - env.mPrincipal, decl, &changed, aIsImportant); + cssParser.ParseProperty(aPropID, aPropValue, + env.mSheetURI, env.mBaseURI, env.mPrincipal, + decl->AsGecko(), &changed, aIsImportant); if (!changed) { // Parsing failed -- but we don't throw an exception for that. return NS_OK; @@ -317,7 +318,7 @@ nsDOMCSSDeclaration::ParseCustomPropertyValue(const nsAString& aPropertyName, { MOZ_ASSERT(nsCSSProps::IsCustomPropertyName(aPropertyName)); - css::Declaration* olddecl = GetCSSDeclaration(eOperation_Modify)->AsGecko(); + DeclarationBlock* olddecl = GetCSSDeclaration(eOperation_Modify); if (!olddecl) { return NS_ERROR_NOT_AVAILABLE; } @@ -334,14 +335,14 @@ nsDOMCSSDeclaration::ParseCustomPropertyValue(const nsAString& aPropertyName, // between when we mutate the declaration and when we set the new // rule (see stack in bug 209575). mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true); - RefPtr decl = olddecl->EnsureMutable(); + RefPtr decl = olddecl->EnsureMutable(); nsCSSParser cssParser(env.mCSSLoader); bool changed; cssParser.ParseVariable(Substring(aPropertyName, CSS_CUSTOM_NAME_PREFIX_LENGTH), aPropValue, env.mSheetURI, - env.mBaseURI, env.mPrincipal, decl, + env.mBaseURI, env.mPrincipal, decl->AsGecko(), &changed, aIsImportant); if (!changed) { // Parsing failed -- but we don't throw an exception for that. @@ -354,8 +355,7 @@ nsDOMCSSDeclaration::ParseCustomPropertyValue(const nsAString& aPropertyName, nsresult nsDOMCSSDeclaration::RemovePropertyInternal(nsCSSPropertyID aPropID) { - css::Declaration* olddecl = - GetCSSDeclaration(eOperation_RemoveProperty)->AsGecko(); + DeclarationBlock* olddecl = GetCSSDeclaration(eOperation_RemoveProperty); if (!olddecl) { return NS_OK; // no decl, so nothing to remove } @@ -367,16 +367,15 @@ nsDOMCSSDeclaration::RemovePropertyInternal(nsCSSPropertyID aPropID) // rule (see stack in bug 209575). mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true); - RefPtr decl = olddecl->EnsureMutable(); - decl->RemovePropertyByID(aPropID); + RefPtr decl = olddecl->EnsureMutable(); + decl->AsGecko()->RemovePropertyByID(aPropID); return SetCSSDeclaration(decl); } nsresult nsDOMCSSDeclaration::RemovePropertyInternal(const nsAString& aPropertyName) { - css::Declaration* olddecl = - GetCSSDeclaration(eOperation_RemoveProperty)->AsGecko(); + DeclarationBlock* olddecl = GetCSSDeclaration(eOperation_RemoveProperty); if (!olddecl) { return NS_OK; // no decl, so nothing to remove } @@ -388,7 +387,7 @@ nsDOMCSSDeclaration::RemovePropertyInternal(const nsAString& aPropertyName) // rule (see stack in bug 209575). mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true); - RefPtr decl = olddecl->EnsureMutable(); - decl->RemoveProperty(aPropertyName); + RefPtr decl = olddecl->EnsureMutable(); + decl->AsGecko()->RemoveProperty(aPropertyName); return SetCSSDeclaration(decl); } From 73635714edf8f65df330d3c166462a62ad636b10 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Thu, 3 Nov 2016 14:41:02 +1100 Subject: [PATCH 31/96] Bug 1294299 part 11 - Implement getting and removing property. r=SimonSapin,heycam MozReview-Commit-ID: 4xvfXR8mkfN --HG-- extra : source : 97da71dbf3666dd5eaf4f568c1e2a13b603fd6fa --- layout/style/DeclarationBlock.h | 12 ++++ layout/style/DeclarationBlockInlines.h | 39 ++++++++++++ layout/style/ServoBindingList.h | 9 +++ layout/style/ServoDeclarationBlock.cpp | 88 ++++++++++++++++++++++++++ layout/style/ServoDeclarationBlock.h | 10 +++ layout/style/nsDOMCSSDeclaration.cpp | 12 ++-- 6 files changed, 164 insertions(+), 6 deletions(-) diff --git a/layout/style/DeclarationBlock.h b/layout/style/DeclarationBlock.h index f42590d9bd18..c3ed663b440c 100644 --- a/layout/style/DeclarationBlock.h +++ b/layout/style/DeclarationBlock.h @@ -15,6 +15,8 @@ #include "mozilla/ServoUtils.h" #include "mozilla/StyleBackendType.h" +#include "nsCSSPropertyID.h" + class nsHTMLCSSStyleSheet; namespace mozilla { @@ -104,6 +106,16 @@ public: inline uint32_t Count() const; inline bool GetNthProperty(uint32_t aIndex, nsAString& aReturn) const; + inline void GetPropertyValue(const nsAString& aProperty, + nsAString& aValue) const; + inline void GetPropertyValueByID(nsCSSPropertyID aPropID, + nsAString& aValue) const; + inline void GetAuthoredPropertyValue(const nsAString& aProperty, + nsAString& aValue) const; + inline bool GetPropertyIsImportant(const nsAString& aProperty) const; + inline void RemoveProperty(const nsAString& aProperty); + inline void RemovePropertyByID(nsCSSPropertyID aProperty); + private: union { // We only ever have one of these since we have an diff --git a/layout/style/DeclarationBlockInlines.h b/layout/style/DeclarationBlockInlines.h index ed81d372d979..791d24498aca 100644 --- a/layout/style/DeclarationBlockInlines.h +++ b/layout/style/DeclarationBlockInlines.h @@ -70,6 +70,45 @@ DeclarationBlock::GetNthProperty(uint32_t aIndex, nsAString& aReturn) const MOZ_STYLO_FORWARD(GetNthProperty, (aIndex, aReturn)) } +void +DeclarationBlock::GetPropertyValue(const nsAString& aProperty, + nsAString& aValue) const +{ + MOZ_STYLO_FORWARD(GetPropertyValue, (aProperty, aValue)) +} + +void +DeclarationBlock::GetPropertyValueByID(nsCSSPropertyID aPropID, + nsAString& aValue) const +{ + MOZ_STYLO_FORWARD(GetPropertyValueByID, (aPropID, aValue)) +} + +void +DeclarationBlock::GetAuthoredPropertyValue(const nsAString& aProperty, + nsAString& aValue) const +{ + MOZ_STYLO_FORWARD(GetAuthoredPropertyValue, (aProperty, aValue)) +} + +bool +DeclarationBlock::GetPropertyIsImportant(const nsAString& aProperty) const +{ + MOZ_STYLO_FORWARD(GetPropertyIsImportant, (aProperty)) +} + +void +DeclarationBlock::RemoveProperty(const nsAString& aProperty) +{ + MOZ_STYLO_FORWARD(RemoveProperty, (aProperty)) +} + +void +DeclarationBlock::RemovePropertyByID(nsCSSPropertyID aProperty) +{ + MOZ_STYLO_FORWARD(RemovePropertyByID, (aProperty)) +} + } // namespace mozilla #endif // mozilla_DeclarationBlockInlines_h diff --git a/layout/style/ServoBindingList.h b/layout/style/ServoBindingList.h index daaf766921e6..982bc8173b5e 100644 --- a/layout/style/ServoBindingList.h +++ b/layout/style/ServoBindingList.h @@ -84,6 +84,15 @@ SERVO_BINDING_FUNC(Servo_DeclarationBlock_Count, uint32_t, SERVO_BINDING_FUNC(Servo_DeclarationBlock_GetNthProperty, bool, RawServoDeclarationBlockBorrowed declarations, uint32_t index, nsAString* result) +SERVO_BINDING_FUNC(Servo_DeclarationBlock_GetPropertyValue, void, + RawServoDeclarationBlockBorrowed declarations, + nsIAtom* property, bool is_custom, nsAString* value) +SERVO_BINDING_FUNC(Servo_DeclarationBlock_GetPropertyIsImportant, bool, + RawServoDeclarationBlockBorrowed declarations, + nsIAtom* property, bool is_custom) +SERVO_BINDING_FUNC(Servo_DeclarationBlock_RemoveProperty, void, + RawServoDeclarationBlockBorrowed declarations, + nsIAtom* property, bool is_custom) // CSS supports() SERVO_BINDING_FUNC(Servo_CSSSupports, bool, diff --git a/layout/style/ServoDeclarationBlock.cpp b/layout/style/ServoDeclarationBlock.cpp index d2c29c96dd88..1dc691bfb1dc 100644 --- a/layout/style/ServoDeclarationBlock.cpp +++ b/layout/style/ServoDeclarationBlock.cpp @@ -7,6 +7,8 @@ #include "mozilla/ServoBindings.h" +#include "nsCSSProps.h" + namespace mozilla { /* static */ already_AddRefed @@ -19,4 +21,90 @@ ServoDeclarationBlock::FromCssText(const nsAString& aCssText) return decl.forget(); } +/** + * An RAII class holding an atom for the given property. + */ +class MOZ_STACK_CLASS PropertyAtomHolder +{ +public: + explicit PropertyAtomHolder(const nsAString& aProperty) + { + nsCSSPropertyID propID = + nsCSSProps::LookupProperty(aProperty, CSSEnabledState::eForAllContent); + if (propID == eCSSPropertyExtra_variable) { + mIsCustomProperty = true; + mAtom = NS_Atomize( + Substring(aProperty, CSS_CUSTOM_NAME_PREFIX_LENGTH)).take(); + } else { + mIsCustomProperty = false; + if (propID != eCSSProperty_UNKNOWN) { + mAtom = nsCSSProps::AtomForProperty(propID); + } else { + mAtom = nullptr; + } + } + } + + ~PropertyAtomHolder() + { + if (mIsCustomProperty) { + NS_RELEASE(mAtom); + } + } + + explicit operator bool() const { return !!mAtom; } + nsIAtom* Atom() const { MOZ_ASSERT(mAtom); return mAtom; } + bool IsCustomProperty() const { return mIsCustomProperty; } + +private: + nsIAtom* mAtom; + bool mIsCustomProperty; +}; + +void +ServoDeclarationBlock::GetPropertyValue(const nsAString& aProperty, + nsAString& aValue) const +{ + if (PropertyAtomHolder holder{aProperty}) { + Servo_DeclarationBlock_GetPropertyValue( + mRaw, holder.Atom(), holder.IsCustomProperty(), &aValue); + } +} + +void +ServoDeclarationBlock::GetPropertyValueByID(nsCSSPropertyID aPropID, + nsAString& aValue) const +{ + nsIAtom* atom = nsCSSProps::AtomForProperty(aPropID); + Servo_DeclarationBlock_GetPropertyValue(mRaw, atom, false, &aValue); +} + +bool +ServoDeclarationBlock::GetPropertyIsImportant(const nsAString& aProperty) const +{ + if (PropertyAtomHolder holder{aProperty}) { + return Servo_DeclarationBlock_GetPropertyIsImportant( + mRaw, holder.Atom(), holder.IsCustomProperty()); + } + return false; +} + +void +ServoDeclarationBlock::RemoveProperty(const nsAString& aProperty) +{ + AssertMutable(); + if (PropertyAtomHolder holder{aProperty}) { + Servo_DeclarationBlock_RemoveProperty(mRaw, holder.Atom(), + holder.IsCustomProperty()); + } +} + +void +ServoDeclarationBlock::RemovePropertyByID(nsCSSPropertyID aPropID) +{ + AssertMutable(); + nsIAtom* atom = nsCSSProps::AtomForProperty(aPropID); + Servo_DeclarationBlock_RemoveProperty(mRaw, atom, false); +} + } // namespace mozilla diff --git a/layout/style/ServoDeclarationBlock.h b/layout/style/ServoDeclarationBlock.h index 515eca8c006a..79120a26f98a 100644 --- a/layout/style/ServoDeclarationBlock.h +++ b/layout/style/ServoDeclarationBlock.h @@ -45,6 +45,16 @@ public: return Servo_DeclarationBlock_GetNthProperty(mRaw, aIndex, &aReturn); } + void GetPropertyValue(const nsAString& aProperty, nsAString& aValue) const; + void GetPropertyValueByID(nsCSSPropertyID aPropID, nsAString& aValue) const; + void GetAuthoredPropertyValue(const nsAString& aProperty, + nsAString& aValue) const { + GetPropertyValue(aProperty, aValue); + } + bool GetPropertyIsImportant(const nsAString& aProperty) const; + void RemoveProperty(const nsAString& aProperty); + void RemovePropertyByID(nsCSSPropertyID aPropID); + protected: explicit ServoDeclarationBlock( already_AddRefed aRaw) diff --git a/layout/style/nsDOMCSSDeclaration.cpp b/layout/style/nsDOMCSSDeclaration.cpp index b56a97b636ba..f4c04a2dc641 100644 --- a/layout/style/nsDOMCSSDeclaration.cpp +++ b/layout/style/nsDOMCSSDeclaration.cpp @@ -49,7 +49,7 @@ nsDOMCSSDeclaration::GetPropertyValue(const nsCSSPropertyID aPropID, "Should never pass eCSSProperty_UNKNOWN around"); aValue.Truncate(); - if (css::Declaration* decl = GetCSSDeclaration(eOperation_Read)->AsGecko()) { + if (DeclarationBlock* decl = GetCSSDeclaration(eOperation_Read)) { decl->GetPropertyValueByID(aPropID, aValue); } return NS_OK; @@ -183,7 +183,7 @@ nsDOMCSSDeclaration::GetPropertyValue(const nsAString& aPropertyName, nsAString& aReturn) { aReturn.Truncate(); - if (css::Declaration* decl = GetCSSDeclaration(eOperation_Read)->AsGecko()) { + if (DeclarationBlock* decl = GetCSSDeclaration(eOperation_Read)) { decl->GetPropertyValue(aPropertyName, aReturn); } return NS_OK; @@ -193,7 +193,7 @@ NS_IMETHODIMP nsDOMCSSDeclaration::GetAuthoredPropertyValue(const nsAString& aPropertyName, nsAString& aReturn) { - if (css::Declaration* decl = GetCSSDeclaration(eOperation_Read)->AsGecko()) { + if (DeclarationBlock* decl = GetCSSDeclaration(eOperation_Read)) { decl->GetAuthoredPropertyValue(aPropertyName, aReturn); } return NS_OK; @@ -203,7 +203,7 @@ NS_IMETHODIMP nsDOMCSSDeclaration::GetPropertyPriority(const nsAString& aPropertyName, nsAString& aReturn) { - css::Declaration* decl = GetCSSDeclaration(eOperation_Read)->AsGecko(); + DeclarationBlock* decl = GetCSSDeclaration(eOperation_Read); aReturn.Truncate(); if (decl && decl->GetPropertyIsImportant(aPropertyName)) { @@ -368,7 +368,7 @@ nsDOMCSSDeclaration::RemovePropertyInternal(nsCSSPropertyID aPropID) mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true); RefPtr decl = olddecl->EnsureMutable(); - decl->AsGecko()->RemovePropertyByID(aPropID); + decl->RemovePropertyByID(aPropID); return SetCSSDeclaration(decl); } @@ -388,6 +388,6 @@ nsDOMCSSDeclaration::RemovePropertyInternal(const nsAString& aPropertyName) mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true); RefPtr decl = olddecl->EnsureMutable(); - decl->AsGecko()->RemoveProperty(aPropertyName); + decl->RemoveProperty(aPropertyName); return SetCSSDeclaration(decl); } From f9aa28d18ddf1fb085f970137218df173974e8e6 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Thu, 3 Nov 2016 14:41:02 +1100 Subject: [PATCH 32/96] Bug 1294299 part 12 - Implemnet setter of properties. r=SimonSapin,heycam MozReview-Commit-ID: 4tGUowsn6AK --HG-- extra : source : 5d3d948883bac4624a62382d04e2cc036acb3525 --- layout/style/ServoBindingList.h | 4 ++++ layout/style/ServoDeclarationBlock.h | 1 + layout/style/nsDOMCSSDeclaration.cpp | 33 +++++++++++++++++++--------- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/layout/style/ServoBindingList.h b/layout/style/ServoBindingList.h index 982bc8173b5e..1baf09d273ba 100644 --- a/layout/style/ServoBindingList.h +++ b/layout/style/ServoBindingList.h @@ -90,6 +90,10 @@ SERVO_BINDING_FUNC(Servo_DeclarationBlock_GetPropertyValue, void, SERVO_BINDING_FUNC(Servo_DeclarationBlock_GetPropertyIsImportant, bool, RawServoDeclarationBlockBorrowed declarations, nsIAtom* property, bool is_custom) +SERVO_BINDING_FUNC(Servo_DeclarationBlock_SetProperty, bool, + RawServoDeclarationBlockBorrowed declarations, + nsIAtom* property, bool is_custom, + nsACString* value, bool is_important) SERVO_BINDING_FUNC(Servo_DeclarationBlock_RemoveProperty, void, RawServoDeclarationBlockBorrowed declarations, nsIAtom* property, bool is_custom) diff --git a/layout/style/ServoDeclarationBlock.h b/layout/style/ServoDeclarationBlock.h index 79120a26f98a..2cf7b1619e36 100644 --- a/layout/style/ServoDeclarationBlock.h +++ b/layout/style/ServoDeclarationBlock.h @@ -26,6 +26,7 @@ public: static already_AddRefed FromCssText(const nsAString& aCssText); + RawServoDeclarationBlock* Raw() const { return mRaw; } RawServoDeclarationBlock* const* RefRaw() const { static_assert(sizeof(RefPtr) == sizeof(RawServoDeclarationBlock*), diff --git a/layout/style/nsDOMCSSDeclaration.cpp b/layout/style/nsDOMCSSDeclaration.cpp index f4c04a2dc641..bd6c6069d0a8 100644 --- a/layout/style/nsDOMCSSDeclaration.cpp +++ b/layout/style/nsDOMCSSDeclaration.cpp @@ -298,11 +298,18 @@ nsDOMCSSDeclaration::ParsePropertyValue(const nsCSSPropertyID aPropID, mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true); RefPtr decl = olddecl->EnsureMutable(); - nsCSSParser cssParser(env.mCSSLoader); bool changed; - cssParser.ParseProperty(aPropID, aPropValue, - env.mSheetURI, env.mBaseURI, env.mPrincipal, - decl->AsGecko(), &changed, aIsImportant); + if (decl->IsGecko()) { + nsCSSParser cssParser(env.mCSSLoader); + cssParser.ParseProperty(aPropID, aPropValue, + env.mSheetURI, env.mBaseURI, env.mPrincipal, + decl->AsGecko(), &changed, aIsImportant); + } else { + nsIAtom* atom = nsCSSProps::AtomForProperty(aPropID); + NS_ConvertUTF16toUTF8 value(aPropValue); + changed = Servo_DeclarationBlock_SetProperty( + decl->AsServo()->Raw(), atom, false, &value, aIsImportant); + } if (!changed) { // Parsing failed -- but we don't throw an exception for that. return NS_OK; @@ -337,13 +344,19 @@ nsDOMCSSDeclaration::ParseCustomPropertyValue(const nsAString& aPropertyName, mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true); RefPtr decl = olddecl->EnsureMutable(); - nsCSSParser cssParser(env.mCSSLoader); bool changed; - cssParser.ParseVariable(Substring(aPropertyName, - CSS_CUSTOM_NAME_PREFIX_LENGTH), - aPropValue, env.mSheetURI, - env.mBaseURI, env.mPrincipal, decl->AsGecko(), - &changed, aIsImportant); + auto propName = Substring(aPropertyName, CSS_CUSTOM_NAME_PREFIX_LENGTH); + if (decl->IsGecko()) { + nsCSSParser cssParser(env.mCSSLoader); + cssParser.ParseVariable(propName, aPropValue, env.mSheetURI, + env.mBaseURI, env.mPrincipal, decl->AsGecko(), + &changed, aIsImportant); + } else { + RefPtr atom = NS_Atomize(propName); + NS_ConvertUTF16toUTF8 value(aPropValue); + changed = Servo_DeclarationBlock_SetProperty( + decl->AsServo()->Raw(), atom, true, &value, aIsImportant); + } if (!changed) { // Parsing failed -- but we don't throw an exception for that. return NS_OK; From 67de44415ae3e98a0bfb278cbdbed0a248741b68 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Thu, 3 Nov 2016 14:41:02 +1100 Subject: [PATCH 33/96] Bug 1294299 part 13 - Post restyle event with style attribute hint for style change. r=heycam MozReview-Commit-ID: 8quLmF0xaxv --HG-- extra : source : 756be1f6f24db58250575910149e3c63f2320615 --- layout/base/ServoRestyleManager.cpp | 17 +++++++++++++++++ layout/base/ServoRestyleManager.h | 5 +---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/layout/base/ServoRestyleManager.cpp b/layout/base/ServoRestyleManager.cpp index 2c3a175d497e..07516d1f4d9a 100644 --- a/layout/base/ServoRestyleManager.cpp +++ b/layout/base/ServoRestyleManager.cpp @@ -35,6 +35,12 @@ ServoRestyleManager::PostRestyleEvent(Element* aElement, return; // Nothing to do. } + // XXX This is a temporary hack to make style attribute change works. + // In the future, we should be able to use this hint directly. + if (aRestyleHint & eRestyle_StyleAttribute) { + aRestyleHint |= eRestyle_Subtree; + } + // Note that unlike in Servo, we don't mark elements as dirty until we process // the restyle hints in ProcessPendingRestyles. if (aRestyleHint || aMinChangeHint) { @@ -507,6 +513,17 @@ ServoRestyleManager::AttributeWillChange(Element* aElement, snapshot->AddAttrs(aElement); } +void +ServoRestyleManager::AttributeChanged(Element* aElement, int32_t aNameSpaceID, + nsIAtom* aAttribute, int32_t aModType, + const nsAttrValue* aOldValue) +{ + MOZ_ASSERT(SnapshotForElement(aElement)->HasAttrs()); + if (aAttribute == nsGkAtoms::style) { + PostRestyleEvent(aElement, eRestyle_StyleAttribute, nsChangeHint(0)); + } +} + nsresult ServoRestyleManager::ReparentStyleContext(nsIFrame* aFrame) { diff --git a/layout/base/ServoRestyleManager.h b/layout/base/ServoRestyleManager.h index 5c2a7eee71d2..6856171c188c 100644 --- a/layout/base/ServoRestyleManager.h +++ b/layout/base/ServoRestyleManager.h @@ -73,10 +73,7 @@ public: void AttributeChanged(dom::Element* aElement, int32_t aNameSpaceID, nsIAtom* aAttribute, int32_t aModType, - const nsAttrValue* aOldValue) - { - MOZ_ASSERT(SnapshotForElement(aElement)->HasAttrs()); - } + const nsAttrValue* aOldValue); nsresult ReparentStyleContext(nsIFrame* aFrame); From 469de34a316978e4b63ee5c18c294f78d99113c9 Mon Sep 17 00:00:00 2001 From: Chris Peterson Date: Wed, 2 Nov 2016 21:18:36 -0700 Subject: [PATCH 34/96] Bug 1313903 - Fix -Wmismatched-parameter-types warning in widget/cocoa/nsAppShell.mm. r=spohl widget/cocoa/nsAppShell.mm:113:47 [-Wmismatched-parameter-types] conflicting parameter types in implementation of 'nextEventMatchingMask:untilDate:inMode:dequeue:': 'NSEventMask' (aka 'unsigned long long') vs 'NSUInteger' (aka 'unsigned long') NSWindow's nextEventMatchingMask method expects a parameter of type NSEventMask, not NSUInteger, when building x86_64 with SDK 10.12: https://developer.apple.com/reference/appkit/nswindow/1419304-nexteventmatchingmask --- widget/cocoa/nsAppShell.mm | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/widget/cocoa/nsAppShell.mm b/widget/cocoa/nsAppShell.mm index b57904389e49..33ce8e742aa6 100644 --- a/widget/cocoa/nsAppShell.mm +++ b/widget/cocoa/nsAppShell.mm @@ -110,7 +110,14 @@ static bool gAppShellMethodsSwizzled = false; [super sendEvent:anEvent]; } +#if defined(MAC_OS_X_VERSION_10_12) && \ + MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 && \ + __LP64__ +// 10.12 changed `mask` to NSEventMask (unsigned long long) for x86_64 builds. +- (NSEvent*)nextEventMatchingMask:(NSEventMask)mask +#else - (NSEvent*)nextEventMatchingMask:(NSUInteger)mask +#endif untilDate:(NSDate*)expiration inMode:(NSString*)mode dequeue:(BOOL)flag @@ -128,7 +135,6 @@ static bool gAppShellMethodsSwizzled = false; @end - // AppShellDelegate // // Cocoa bridge class. An object of this class is registered to receive From acb9007769fc36f4fd1c61bbdd29b74b84d6c07f Mon Sep 17 00:00:00 2001 From: Chris Peterson Date: Wed, 2 Nov 2016 21:20:40 -0700 Subject: [PATCH 35/96] Bug 1313905 - Fix -Wincompatible-pointer-types-discards-qualifiers warnings in webrtc/signaling. r=bwc --- media/webrtc/signaling/src/sdp/sipcc/sdp_attr.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/media/webrtc/signaling/src/sdp/sipcc/sdp_attr.c b/media/webrtc/signaling/src/sdp/sipcc/sdp_attr.c index 0692633e4660..7301d929817c 100644 --- a/media/webrtc/signaling/src/sdp/sipcc/sdp_attr.c +++ b/media/webrtc/signaling/src/sdp/sipcc/sdp_attr.c @@ -429,7 +429,7 @@ sdp_result_e sdp_parse_attr_maxprate (sdp_t *sdp_p, sdp_attr_t *attr_p, * missing. * */ -static void sdp_attr_fmtp_no_value(sdp_t *sdp, char *param_name) +static void sdp_attr_fmtp_no_value(sdp_t *sdp, const char *param_name) { sdp_parse_error(sdp, "%s Warning: No %s value specified for fmtp attribute", @@ -443,9 +443,8 @@ static void sdp_attr_fmtp_no_value(sdp_t *sdp, char *param_name) * incorrect. * */ - -static void sdp_attr_fmtp_invalid_value(sdp_t *sdp, char *param_name, - char* param_value) +static void sdp_attr_fmtp_invalid_value(sdp_t *sdp, const char *param_name, + const char* param_value) { sdp_parse_error(sdp, "%s Warning: Invalid %s: %s specified for fmtp attribute", From bcf48e11c8b1723bb411e448642fc1d13a39a276 Mon Sep 17 00:00:00 2001 From: Franziskus Kiefer Date: Thu, 3 Nov 2016 07:36:09 +0100 Subject: [PATCH 36/96] Bug 1305970 - land NSS 0x5eb5f52b7922, r=me --- security/nss/TAG-INFO | 2 +- security/nss/coreconf/coreconf.dep | 1 - security/nss/gtests/nss_bogo_shim/config.json | 5 +- .../nss/gtests/nss_bogo_shim/nss_bogo_shim.cc | 5 +- .../nss/gtests/ssl_gtest/libssl_internals.c | 20 + .../nss/gtests/ssl_gtest/libssl_internals.h | 1 + .../ssl_gtest/ssl_resumption_unittest.cc | 22 + security/nss/lib/ssl/manifest.mn | 2 + security/nss/lib/ssl/ssl.gyp | 2 + security/nss/lib/ssl/ssl3con.c | 21 +- security/nss/lib/ssl/ssl3ext.c | 3536 +---------------- security/nss/lib/ssl/ssl3exthandle.c | 2447 ++++++++++++ security/nss/lib/ssl/ssl3exthandle.h | 71 + security/nss/lib/ssl/tls13con.c | 33 +- security/nss/lib/ssl/tls13con.h | 1 + security/nss/lib/ssl/tls13exthandle.c | 1013 +++++ security/nss/lib/ssl/tls13exthandle.h | 54 + 17 files changed, 3677 insertions(+), 3559 deletions(-) create mode 100644 security/nss/lib/ssl/ssl3exthandle.c create mode 100644 security/nss/lib/ssl/ssl3exthandle.h create mode 100644 security/nss/lib/ssl/tls13exthandle.c create mode 100644 security/nss/lib/ssl/tls13exthandle.h diff --git a/security/nss/TAG-INFO b/security/nss/TAG-INFO index b38e1b98d9d6..e6cef02af152 100644 --- a/security/nss/TAG-INFO +++ b/security/nss/TAG-INFO @@ -1 +1 @@ -0cccc59d04dd +5eb5f52b7922 diff --git a/security/nss/coreconf/coreconf.dep b/security/nss/coreconf/coreconf.dep index 590d1bfaeee3..5182f75552c8 100644 --- a/security/nss/coreconf/coreconf.dep +++ b/security/nss/coreconf/coreconf.dep @@ -10,4 +10,3 @@ */ #error "Do not include this header file." - diff --git a/security/nss/gtests/nss_bogo_shim/config.json b/security/nss/gtests/nss_bogo_shim/config.json index 48283f942d5c..b70308ec3551 100644 --- a/security/nss/gtests/nss_bogo_shim/config.json +++ b/security/nss/gtests/nss_bogo_shim/config.json @@ -40,12 +40,13 @@ "ClientAuth-SHA1-Fallback-RSA":"We fail when the sig_algs_ext is empty", "Downgrade-TLS12-*":"NSS implements downgrade detection", "TrailingMessageData-*": "Bug 1304575", - "DuplicateKeyShares":"Bug 1304578" + "DuplicateKeyShares":"Bug 1304578", + "Resume-Server-TLS13-TLS13":"Bug 1314351" }, "ErrorMap" : { ":HANDSHAKE_FAILURE_ON_CLIENT_HELLO:":"SSL_ERROR_NO_CYPHER_OVERLAP", ":UNKNOWN_CIPHER_RETURNED:":"SSL_ERROR_NO_CYPHER_OVERLAP", - ":OLD_SESSION_CIPHER_NOT_RETURNED:":"SSL_ERROR_NO_CYPHER_OVERLAP", + ":OLD_SESSION_CIPHER_NOT_RETURNED:":"SSL_ERROR_RX_MALFORMED_SERVER_HELLO", ":NO_SHARED_CIPHER:":"SSL_ERROR_NO_CYPHER_OVERLAP", ":DIGEST_CHECK_FAILED:":"SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE" } diff --git a/security/nss/gtests/nss_bogo_shim/nss_bogo_shim.cc b/security/nss/gtests/nss_bogo_shim/nss_bogo_shim.cc index 7d80ee816c69..ad41eaf20640 100644 --- a/security/nss/gtests/nss_bogo_shim/nss_bogo_shim.cc +++ b/security/nss/gtests/nss_bogo_shim/nss_bogo_shim.cc @@ -265,7 +265,7 @@ std::unique_ptr ReadConfig(int argc, char** argv) { cfg->AddEntry("port", 0); cfg->AddEntry("server", false); - cfg->AddEntry("resume", false); + cfg->AddEntry("resume-count", 0); cfg->AddEntry("key-file", ""); cfg->AddEntry("cert-file", ""); @@ -321,7 +321,8 @@ int main(int argc, char** argv) { // Run a single test cycle. bool success = RunCycle(cfg); - if (success && cfg->get("resume")) { + int resume_count = cfg->get("resume-count"); + while (success && resume_count-- > 0) { std::cout << "Resuming" << std::endl; success = RunCycle(cfg); } diff --git a/security/nss/gtests/ssl_gtest/libssl_internals.c b/security/nss/gtests/ssl_gtest/libssl_internals.c index e922162b7e41..5f923cf400d6 100644 --- a/security/nss/gtests/ssl_gtest/libssl_internals.c +++ b/security/nss/gtests/ssl_gtest/libssl_internals.c @@ -210,6 +210,26 @@ PRBool SSLInt_SendAlert(PRFileDesc *fd, uint8_t level, uint8_t type) { return PR_TRUE; } +PRBool SSLInt_SendNewSessionTicket(PRFileDesc *fd) { + sslSocket *ss = ssl_FindSocket(fd); + if (!ss) { + return PR_FALSE; + } + + ssl_GetSSL3HandshakeLock(ss); + ssl_GetXmitBufLock(ss); + + SECStatus rv = tls13_SendNewSessionTicket(ss); + if (rv == SECSuccess) { + rv = ssl3_FlushHandshake(ss, 0); + } + + ssl_ReleaseXmitBufLock(ss); + ssl_ReleaseSSL3HandshakeLock(ss); + + return rv == SECSuccess; +} + SECStatus SSLInt_AdvanceReadSeqNum(PRFileDesc *fd, PRUint64 to) { PRUint64 epoch; sslSocket *ss; diff --git a/security/nss/gtests/ssl_gtest/libssl_internals.h b/security/nss/gtests/ssl_gtest/libssl_internals.h index bc8b296f8cf4..2f6aa9d1ba12 100644 --- a/security/nss/gtests/ssl_gtest/libssl_internals.h +++ b/security/nss/gtests/ssl_gtest/libssl_internals.h @@ -31,6 +31,7 @@ PRBool SSLInt_DamageEarlyTrafficSecret(PRFileDesc *fd); SECStatus SSLInt_Set0RttAlpn(PRFileDesc *fd, PRUint8 *data, unsigned int len); PRBool SSLInt_HasCertWithAuthType(PRFileDesc *fd, SSLAuthType authType); PRBool SSLInt_SendAlert(PRFileDesc *fd, uint8_t level, uint8_t type); +PRBool SSLInt_SendNewSessionTicket(PRFileDesc *fd); SECStatus SSLInt_AdvanceWriteSeqNum(PRFileDesc *fd, PRUint64 to); SECStatus SSLInt_AdvanceReadSeqNum(PRFileDesc *fd, PRUint64 to); SECStatus SSLInt_AdvanceWriteSeqByAWindow(PRFileDesc *fd, PRInt32 extra); diff --git a/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc index e5b0cf8067ff..08c8f345e51b 100644 --- a/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc @@ -557,4 +557,26 @@ TEST_F(TlsConnectTest, TestTls13ResumptionTwice) { ASSERT_NE(initialTicket, c2->extension()); } +// Check that resumption works after receiving two NST messages. +TEST_F(TlsConnectTest, TestTls13ResumptionDuplicateNST) { + ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); + ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); + Connect(); + + // Clear the session ticket keys to invalidate the old ticket. + SSLInt_ClearSessionTicketKey(); + SSLInt_SendNewSessionTicket(server_->ssl_fd()); + + SendReceive(); // Need to read so that we absorb the session tickets. + CheckKeys(); + + // Resume the connection. + Reset(); + ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); + ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); + ExpectResumption(RESUME_TICKET); + Connect(); + SendReceive(); +} + } // namespace nss_test diff --git a/security/nss/lib/ssl/manifest.mn b/security/nss/lib/ssl/manifest.mn index 3c113cd2858f..e7564edb2bf2 100644 --- a/security/nss/lib/ssl/manifest.mn +++ b/security/nss/lib/ssl/manifest.mn @@ -30,6 +30,7 @@ CSRCS = \ sslerrstrs.c \ sslinit.c \ ssl3ext.c \ + ssl3exthandle.c \ sslmutex.c \ sslnonce.c \ sslreveal.c \ @@ -43,6 +44,7 @@ CSRCS = \ sslinfo.c \ ssl3ecc.c \ tls13con.c \ + tls13exthandle.c \ tls13hkdf.c \ sslcert.c \ sslgrp.c \ diff --git a/security/nss/lib/ssl/ssl.gyp b/security/nss/lib/ssl/ssl.gyp index fc607d25649e..83740d702b5e 100644 --- a/security/nss/lib/ssl/ssl.gyp +++ b/security/nss/lib/ssl/ssl.gyp @@ -17,6 +17,7 @@ 'ssl3con.c', 'ssl3ecc.c', 'ssl3ext.c', + 'ssl3exthandle.c', 'ssl3gthr.c', 'sslauth.c', 'sslcert.c', @@ -37,6 +38,7 @@ 'ssltrace.c', 'sslver.c', 'tls13con.c', + 'tls13exthandle.c', 'tls13hkdf.c', ], 'conditions': [ diff --git a/security/nss/lib/ssl/ssl3con.c b/security/nss/lib/ssl/ssl3con.c index 58b302b20399..e03e0238b0cb 100644 --- a/security/nss/lib/ssl/ssl3con.c +++ b/security/nss/lib/ssl/ssl3con.c @@ -343,7 +343,7 @@ static const ssl3KEADef kea_defs[] = {kea_ecdh_anon, ssl_kea_ecdh, nullKey, ssl_auth_null, PR_TRUE, SEC_OID_TLS_ECDH_ANON}, {kea_ecdhe_psk, ssl_kea_ecdh_psk, nullKey, ssl_auth_psk, PR_TRUE, SEC_OID_TLS_ECDHE_PSK}, {kea_dhe_psk, ssl_kea_dh_psk, nullKey, ssl_auth_psk, PR_TRUE, SEC_OID_TLS_DHE_PSK}, - {kea_tls13_any, ssl_kea_tls13_any, nullKey, ssl_auth_tls13_any, PR_TRUE, SEC_OID_TLS13_KEA_ANY}, + {kea_tls13_any, ssl_kea_tls13_any, nullKey, ssl_auth_tls13_any, PR_TRUE, SEC_OID_TLS13_KEA_ANY}, }; /* must use ssl_LookupCipherSuiteDef to access */ @@ -1557,6 +1557,7 @@ ssl3_SetupPendingCipherSpec(sslSocket *ss) PRBool isTLS; PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + PORT_Assert(ss->version < SSL_LIBRARY_VERSION_TLS_1_3); ssl_GetSpecWriteLock(ss); /*******************************/ @@ -6365,7 +6366,7 @@ ssl_PickSignatureScheme(sslSocket *ss, } /* Skip RSA-PSS schemes when the certificate's private key slot does - * not supporting that mechanism. */ + * not support this signature mechanism. */ if (ssl_IsRsaPssSignatureScheme(preferred) && !slotDoesPss) { continue; } @@ -9338,9 +9339,12 @@ ssl3_SendServerHello(sslSocket *ss) return SECFailure; } } - rv = ssl3_SetupPendingCipherSpec(ss); - if (rv != SECSuccess) { - return rv; /* err set by ssl3_SetupPendingCipherSpec */ + + if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) { + rv = ssl3_SetupPendingCipherSpec(ss); + if (rv != SECSuccess) { + return rv; /* err set by ssl3_SetupPendingCipherSpec */ + } } return SECSuccess; @@ -9552,6 +9556,13 @@ ssl3_EncodeSigAlgs(sslSocket *ss, PRUint8 *buf, unsigned maxLen, PRUint32 *len) SSLHashType hashType = ssl_SignatureSchemeToHashType( ss->ssl3.signatureSchemes[i]); SECOidTag hashOID = ssl3_HashTypeToOID(hashType); + + /* Skip RSA-PSS schemes if there are no tokens to verify them. */ + if (ssl_IsRsaPssSignatureScheme(ss->ssl3.signatureSchemes[i]) && + !PK11_TokenExists(auth_alg_defs[ssl_auth_rsa_pss])) { + continue; + } + if ((NSS_GetAlgorithmPolicy(hashOID, &policy) != SECSuccess) || (policy & NSS_USE_ALG_IN_SSL_KX)) { p = ssl_EncodeUintX((PRUint32)ss->ssl3.signatureSchemes[i], 2, p); diff --git a/security/nss/lib/ssl/ssl3ext.c b/security/nss/lib/ssl/ssl3ext.c index 7e139ea95f8e..75f50e835e1a 100644 --- a/security/nss/lib/ssl/ssl3ext.c +++ b/security/nss/lib/ssl/ssl3ext.c @@ -11,245 +11,10 @@ #include "nssrenam.h" #include "nss.h" #include "ssl.h" -#include "sslproto.h" #include "sslimpl.h" -#include "pk11pub.h" -#include "blapit.h" -#include "prinit.h" - -static unsigned char key_name[SESS_TICKET_KEY_NAME_LEN]; -static PK11SymKey *session_ticket_enc_key = NULL; -static PK11SymKey *session_ticket_mac_key = NULL; - -static PRCallOnceType generate_session_keys_once; - -/* forward static function declarations */ -static SECStatus ssl3_ParseEncryptedSessionTicket(sslSocket *ss, - SECItem *data, EncryptedSessionTicket *enc_session_ticket); -static SECStatus ssl3_AppendToItem(SECItem *item, const unsigned char *buf, - PRUint32 bytes); -static SECStatus ssl3_AppendNumberToItem(SECItem *item, PRUint32 num, - PRInt32 lenSize); -static SECStatus ssl3_GetSessionTicketKeys(sslSocket *ss, - PK11SymKey **aes_key, PK11SymKey **mac_key); -static PRInt32 ssl3_SendRenegotiationInfoXtn(sslSocket *ss, - PRBool append, PRUint32 maxBytes); -static SECStatus ssl3_HandleRenegotiationInfoXtn(sslSocket *ss, - PRUint16 ex_type, SECItem *data); -static SECStatus ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, - PRUint16 ex_type, SECItem *data); -static SECStatus ssl3_ClientHandleAppProtoXtn(sslSocket *ss, - PRUint16 ex_type, SECItem *data); -static SECStatus ssl3_ServerHandleNextProtoNegoXtn(sslSocket *ss, - PRUint16 ex_type, SECItem *data); -static SECStatus ssl3_ServerHandleAppProtoXtn(sslSocket *ss, PRUint16 ex_type, - SECItem *data); -static PRInt32 ssl3_ClientSendNextProtoNegoXtn(sslSocket *ss, PRBool append, - PRUint32 maxBytes); -static PRInt32 ssl3_ClientSendAppProtoXtn(sslSocket *ss, PRBool append, - PRUint32 maxBytes); -static PRInt32 ssl3_ServerSendAppProtoXtn(sslSocket *ss, PRBool append, - PRUint32 maxBytes); -static PRInt32 ssl3_ClientSendUseSRTPXtn(sslSocket *ss, PRBool append, - PRUint32 maxBytes); -static PRInt32 ssl3_ServerSendUseSRTPXtn(sslSocket *ss, PRBool append, - PRUint32 maxBytes); -static SECStatus ssl3_ClientHandleUseSRTPXtn(sslSocket *ss, PRUint16 ex_type, - SECItem *data); -static SECStatus ssl3_ServerHandleUseSRTPXtn(sslSocket *ss, PRUint16 ex_type, - SECItem *data); -static PRInt32 ssl3_ServerSendStatusRequestXtn(sslSocket *ss, - PRBool append, PRUint32 maxBytes); -static PRInt32 tls13_ServerSendStatusRequestXtn(sslSocket *ss, - PRBool append, PRUint32 maxBytes); -static SECStatus ssl3_ServerHandleStatusRequestXtn(sslSocket *ss, - PRUint16 ex_type, SECItem *data); -static SECStatus ssl3_ClientHandleStatusRequestXtn(sslSocket *ss, - PRUint16 ex_type, - SECItem *data); -static PRInt32 ssl3_ClientSendStatusRequestXtn(sslSocket *ss, PRBool append, - PRUint32 maxBytes); -static PRInt32 ssl3_ClientSendSigAlgsXtn(sslSocket *ss, PRBool append, - PRUint32 maxBytes); -static SECStatus ssl3_ServerHandleSigAlgsXtn(sslSocket *ss, PRUint16 ex_type, - SECItem *data); - -static PRInt32 ssl3_ClientSendSignedCertTimestampXtn(sslSocket *ss, - PRBool append, - PRUint32 maxBytes); -static SECStatus ssl3_ClientHandleSignedCertTimestampXtn(sslSocket *ss, - PRUint16 ex_type, - SECItem *data); -static PRInt32 ssl3_ServerSendSignedCertTimestampXtn(sslSocket *ss, - PRBool append, - PRUint32 maxBytes); -static SECStatus ssl3_ServerHandleSignedCertTimestampXtn(sslSocket *ss, - PRUint16 ex_type, - SECItem *data); -static PRInt32 ssl3_SendExtendedMasterSecretXtn(sslSocket *ss, PRBool append, - PRUint32 maxBytes); -static SECStatus ssl3_HandleExtendedMasterSecretXtn(sslSocket *ss, - PRUint16 ex_type, - SECItem *data); -static PRInt32 tls13_ClientSendKeyShareXtn(sslSocket *ss, PRBool append, - PRUint32 maxBytes); -static SECStatus tls13_ClientHandleKeyShareXtn(sslSocket *ss, - PRUint16 ex_type, - SECItem *data); -static SECStatus tls13_ClientHandleKeyShareXtnHrr(sslSocket *ss, - PRUint16 ex_type, - SECItem *data); -static SECStatus tls13_ServerHandleKeyShareXtn(sslSocket *ss, - PRUint16 ex_type, - SECItem *data); -static PRInt32 tls13_ClientSendPreSharedKeyXtn(sslSocket *ss, PRBool append, - PRUint32 maxBytes); -static SECStatus tls13_ServerHandlePreSharedKeyXtn(sslSocket *ss, - PRUint16 ex_type, - SECItem *data); -static SECStatus tls13_ClientHandlePreSharedKeyXtn(sslSocket *ss, - PRUint16 ex_type, - SECItem *data); -static PRInt32 tls13_ClientSendEarlyDataXtn(sslSocket *ss, - PRBool append, - PRUint32 maxBytes); -static SECStatus tls13_ServerHandleEarlyDataXtn(sslSocket *ss, PRUint16 ex_type, - SECItem *data); -static SECStatus tls13_ClientHandleEarlyDataXtn(sslSocket *ss, PRUint16 ex_type, - SECItem *data); -static SECStatus tls13_ClientHandleTicketEarlyDataInfoXtn( - sslSocket *ss, PRUint16 ex_type, - SECItem *data); -static SECStatus tls13_ClientHandleSigAlgsXtn(sslSocket *ss, PRUint16 ex_type, - SECItem *data); -static PRInt32 tls13_ClientSendSupportedVersionsXtn(sslSocket *ss, - PRBool append, - PRUint32 maxBytes); -static SECStatus tls13_ClientHandleHrrCookie(sslSocket *ss, PRUint16 ex_type, - SECItem *data); -static PRInt32 tls13_ClientSendHrrCookieXtn(sslSocket *ss, - PRBool append, - PRUint32 maxBytes); - -/* - * Write bytes. Using this function means the SECItem structure - * cannot be freed. The caller is expected to call this function - * on a shallow copy of the structure. - */ -static SECStatus -ssl3_AppendToItem(SECItem *item, const unsigned char *buf, PRUint32 bytes) -{ - if (bytes > item->len) - return SECFailure; - - PORT_Memcpy(item->data, buf, bytes); - item->data += bytes; - item->len -= bytes; - return SECSuccess; -} - -/* - * Write a number in network byte order. Using this function means the - * SECItem structure cannot be freed. The caller is expected to call - * this function on a shallow copy of the structure. - */ -static SECStatus -ssl3_AppendNumberToItem(SECItem *item, PRUint32 num, PRInt32 lenSize) -{ - SECStatus rv; - PRUint8 b[4]; - PRUint8 *p = b; - - switch (lenSize) { - case 4: - *p++ = (PRUint8)(num >> 24); - case 3: - *p++ = (PRUint8)(num >> 16); - case 2: - *p++ = (PRUint8)(num >> 8); - case 1: - *p = (PRUint8)num; - } - rv = ssl3_AppendToItem(item, &b[0], lenSize); - return rv; -} - -SECStatus -ssl3_SessionTicketShutdown(void *appData, void *nssData) -{ - if (session_ticket_enc_key) { - PK11_FreeSymKey(session_ticket_enc_key); - session_ticket_enc_key = NULL; - } - if (session_ticket_mac_key) { - PK11_FreeSymKey(session_ticket_mac_key); - session_ticket_mac_key = NULL; - } - PORT_Memset(&generate_session_keys_once, 0, - sizeof(generate_session_keys_once)); - return SECSuccess; -} - -static PRStatus -ssl3_GenerateSessionTicketKeys(void *data) -{ - SECStatus rv; - sslSocket *ss = (sslSocket *)data; - sslServerCertType certType = { ssl_auth_rsa_decrypt, NULL }; - const sslServerCert *sc; - SECKEYPrivateKey *svrPrivKey; - SECKEYPublicKey *svrPubKey; - - sc = ssl_FindServerCert(ss, &certType); - if (!sc || !sc->serverKeyPair) { - SSL_DBG(("%d: SSL[%d]: No ssl_auth_rsa_decrypt cert and key pair", - SSL_GETPID(), ss->fd)); - goto loser; - } - svrPrivKey = sc->serverKeyPair->privKey; - svrPubKey = sc->serverKeyPair->pubKey; - if (svrPrivKey == NULL || svrPubKey == NULL) { - SSL_DBG(("%d: SSL[%d]: Pub or priv key(s) is NULL.", - SSL_GETPID(), ss->fd)); - goto loser; - } - - /* Get a copy of the session keys from shared memory. */ - PORT_Memcpy(key_name, SESS_TICKET_KEY_NAME_PREFIX, - sizeof(SESS_TICKET_KEY_NAME_PREFIX)); - if (!ssl_GetSessionTicketKeys(svrPrivKey, svrPubKey, ss->pkcs11PinArg, - &key_name[SESS_TICKET_KEY_NAME_PREFIX_LEN], - &session_ticket_enc_key, &session_ticket_mac_key)) - return PR_FAILURE; - - rv = NSS_RegisterShutdown(ssl3_SessionTicketShutdown, NULL); - if (rv != SECSuccess) - goto loser; - - return PR_SUCCESS; - -loser: - ssl3_SessionTicketShutdown(NULL, NULL); - return PR_FAILURE; -} - -static SECStatus -ssl3_GetSessionTicketKeys(sslSocket *ss, PK11SymKey **aes_key, - PK11SymKey **mac_key) -{ - if (PR_CallOnceWithArg(&generate_session_keys_once, - ssl3_GenerateSessionTicketKeys, ss) != - PR_SUCCESS) - return SECFailure; - - if (session_ticket_enc_key == NULL || - session_ticket_mac_key == NULL) - return SECFailure; - - *aes_key = session_ticket_enc_key; - *mac_key = session_ticket_mac_key; - return SECSuccess; -} +#include "sslproto.h" +#include "ssl3exthandle.h" +#include "tls13exthandle.h" /* Table of handlers for received TLS hello extensions, one per extension. * In the second generation, this table will be dynamic, and functions @@ -380,1714 +145,6 @@ ssl3_ClientExtensionAdvertised(sslSocket *ss, PRUint16 ex_type) xtnData->numAdvertised, ex_type); } -/* Format an SNI extension, using the name from the socket's URL, - * unless that name is a dotted decimal string. - * Used by client and server. - */ -PRInt32 -ssl3_SendServerNameXtn(sslSocket *ss, PRBool append, - PRUint32 maxBytes) -{ - SECStatus rv; - if (!ss) - return 0; - if (!ss->sec.isServer) { - PRUint32 len; - PRNetAddr netAddr; - - /* must have a hostname */ - if (!ss->url || !ss->url[0]) - return 0; - /* must not be an IPv4 or IPv6 address */ - if (PR_SUCCESS == PR_StringToNetAddr(ss->url, &netAddr)) { - /* is an IP address (v4 or v6) */ - return 0; - } - len = PORT_Strlen(ss->url); - if (append && maxBytes >= len + 9) { - /* extension_type */ - rv = ssl3_AppendHandshakeNumber(ss, ssl_server_name_xtn, 2); - if (rv != SECSuccess) - return -1; - /* length of extension_data */ - rv = ssl3_AppendHandshakeNumber(ss, len + 5, 2); - if (rv != SECSuccess) - return -1; - /* length of server_name_list */ - rv = ssl3_AppendHandshakeNumber(ss, len + 3, 2); - if (rv != SECSuccess) - return -1; - /* Name Type (sni_host_name) */ - rv = ssl3_AppendHandshake(ss, "\0", 1); - if (rv != SECSuccess) - return -1; - /* HostName (length and value) */ - rv = ssl3_AppendHandshakeVariable(ss, (PRUint8 *)ss->url, len, 2); - if (rv != SECSuccess) - return -1; - if (!ss->sec.isServer) { - TLSExtensionData *xtnData = &ss->xtnData; - xtnData->advertised[xtnData->numAdvertised++] = - ssl_server_name_xtn; - } - } - return len + 9; - } - /* Server side */ - if (append && maxBytes >= 4) { - rv = ssl3_AppendHandshakeNumber(ss, ssl_server_name_xtn, 2); - if (rv != SECSuccess) - return -1; - /* length of extension_data */ - rv = ssl3_AppendHandshakeNumber(ss, 0, 2); - if (rv != SECSuccess) - return -1; - } - return 4; -} - -/* Handle an incoming SNI extension. */ -SECStatus -ssl3_HandleServerNameXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data) -{ - SECItem *names = NULL; - TLSExtensionData *xtnData = &ss->xtnData; - PRInt32 listLenBytes = 0; - - if (!ss->sec.isServer) { - return SECSuccess; /* ignore extension */ - } - - /* Server side - consume client data and register server sender. */ - /* do not parse the data if don't have user extension handling function. */ - if (!ss->sniSocketConfig) { - return SECSuccess; - } - - /* length of server_name_list */ - listLenBytes = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len); - if (listLenBytes < 0) { - goto loser; /* alert already sent */ - } - if (listLenBytes == 0 || listLenBytes != data->len) { - goto alert_loser; - } - - /* Read ServerNameList. */ - while (data->len > 0) { - SECItem tmp; - SECStatus rv; - PRInt32 type; - - /* Read Name Type. */ - type = ssl3_ConsumeHandshakeNumber(ss, 1, &data->data, &data->len); - if (type < 0) { /* i.e., SECFailure cast to PRint32 */ - /* alert sent in ConsumeHandshakeNumber */ - goto loser; - } - - /* Read ServerName (length and value). */ - rv = ssl3_ConsumeHandshakeVariable(ss, &tmp, 2, &data->data, &data->len); - if (rv != SECSuccess) { - goto loser; - } - - /* Record the value for host_name(0). */ - if (type == sni_nametype_hostname) { - /* Fail if we encounter a second host_name entry. */ - if (names) { - goto alert_loser; - } - - /* Create an array for the only supported NameType. */ - names = PORT_ZNewArray(SECItem, 1); - if (!names) { - goto loser; - } - - /* Copy ServerName into the array. */ - if (SECITEM_CopyItem(NULL, &names[0], &tmp) != SECSuccess) { - goto loser; - } - } - - /* Even if we don't support NameTypes other than host_name at the - * moment, we continue parsing the whole list to check its validity. - * We do not check for duplicate entries with NameType != host_name(0). - */ - } - if (names) { - /* Free old and set the new data. */ - ssl3_FreeSniNameArray(xtnData); - xtnData->sniNameArr = names; - xtnData->sniNameArrSize = 1; - xtnData->negotiated[xtnData->numNegotiated++] = ssl_server_name_xtn; - } - return SECSuccess; - -alert_loser: - (void)ssl3_DecodeError(ss); -loser: - if (names) { - PORT_Free(names); - } - return SECFailure; -} - -/* Frees a given xtnData->sniNameArr and its elements. */ -void -ssl3_FreeSniNameArray(TLSExtensionData *xtnData) -{ - PRUint32 i; - - if (!xtnData->sniNameArr) { - return; - } - - for (i = 0; i < xtnData->sniNameArrSize; i++) { - SECITEM_FreeItem(&xtnData->sniNameArr[i], PR_FALSE); - } - - PORT_Free(xtnData->sniNameArr); - xtnData->sniNameArr = NULL; - xtnData->sniNameArrSize = 0; -} - -/* Called by both clients and servers. - * Clients sends a filled in session ticket if one is available, and otherwise - * sends an empty ticket. Servers always send empty tickets. - */ -PRInt32 -ssl3_SendSessionTicketXtn( - sslSocket *ss, - PRBool append, - PRUint32 maxBytes) -{ - PRInt32 extension_length; - NewSessionTicket *session_ticket = NULL; - sslSessionID *sid = ss->sec.ci.sid; - - /* Never send an extension with a ticket for TLS 1.3, but - * OK to send the empty one in case the server does 1.2. */ - if (sid->cached == in_client_cache && - sid->version >= SSL_LIBRARY_VERSION_TLS_1_3) { - return 0; - } - - /* Ignore the SessionTicket extension if processing is disabled. */ - if (!ss->opt.enableSessionTickets) - return 0; - - /* Empty extension length = extension_type (2-bytes) + - * length(extension_data) (2-bytes) - */ - extension_length = 4; - - /* If we are a client then send a session ticket if one is availble. - * Servers that support the extension and are willing to negotiate the - * the extension always respond with an empty extension. - */ - if (!ss->sec.isServer) { - /* The caller must be holding sid->u.ssl3.lock for reading. We cannot - * just acquire and release the lock within this function because the - * caller will call this function twice, and we need the inputs to be - * consistent between the two calls. Note that currently the caller - * will only be holding the lock when we are the client and when we're - * attempting to resume an existing session. - */ - - session_ticket = &sid->u.ssl3.locked.sessionTicket; - if (session_ticket->ticket.data) { - if (ss->xtnData.ticketTimestampVerified) { - extension_length += session_ticket->ticket.len; - } else if (!append && - (session_ticket->ticket_lifetime_hint == 0 || - (session_ticket->ticket_lifetime_hint + - session_ticket->received_timestamp > - ssl_Time()))) { - extension_length += session_ticket->ticket.len; - ss->xtnData.ticketTimestampVerified = PR_TRUE; - } - } - } - - if (maxBytes < (PRUint32)extension_length) { - PORT_Assert(0); - return 0; - } - if (append) { - SECStatus rv; - /* extension_type */ - rv = ssl3_AppendHandshakeNumber(ss, ssl_session_ticket_xtn, 2); - if (rv != SECSuccess) - goto loser; - if (session_ticket && session_ticket->ticket.data && - ss->xtnData.ticketTimestampVerified) { - rv = ssl3_AppendHandshakeVariable(ss, session_ticket->ticket.data, - session_ticket->ticket.len, 2); - ss->xtnData.ticketTimestampVerified = PR_FALSE; - ss->xtnData.sentSessionTicketInClientHello = PR_TRUE; - } else { - rv = ssl3_AppendHandshakeNumber(ss, 0, 2); - } - if (rv != SECSuccess) - goto loser; - - if (!ss->sec.isServer) { - TLSExtensionData *xtnData = &ss->xtnData; - xtnData->advertised[xtnData->numAdvertised++] = - ssl_session_ticket_xtn; - } - } - return extension_length; - -loser: - ss->xtnData.ticketTimestampVerified = PR_FALSE; - return -1; -} - -/* handle an incoming Next Protocol Negotiation extension. */ -static SECStatus -ssl3_ServerHandleNextProtoNegoXtn(sslSocket *ss, PRUint16 ex_type, - SECItem *data) -{ - if (ss->firstHsDone || data->len != 0) { - /* Clients MUST send an empty NPN extension, if any. */ - PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID); - return SECFailure; - } - - ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; - - /* TODO: server side NPN support would require calling - * ssl3_RegisterServerHelloExtensionSender here in order to echo the - * extension back to the client. */ - - return SECSuccess; -} - -/* ssl3_ValidateNextProtoNego checks that the given block of data is valid: none - * of the lengths may be 0 and the sum of the lengths must equal the length of - * the block. */ -SECStatus -ssl3_ValidateNextProtoNego(const unsigned char *data, unsigned int length) -{ - unsigned int offset = 0; - - while (offset < length) { - unsigned int newOffset = offset + 1 + (unsigned int)data[offset]; - /* Reject embedded nulls to protect against buggy applications that - * store protocol identifiers in null-terminated strings. - */ - if (newOffset > length || data[offset] == 0) { - return SECFailure; - } - offset = newOffset; - } - - return SECSuccess; -} - -/* protocol selection handler for ALPN (server side) and NPN (client side) */ -static SECStatus -ssl3_SelectAppProtocol(sslSocket *ss, PRUint16 ex_type, SECItem *data) -{ - SECStatus rv; - unsigned char resultBuffer[255]; - SECItem result = { siBuffer, resultBuffer, 0 }; - - rv = ssl3_ValidateNextProtoNego(data->data, data->len); - if (rv != SECSuccess) { - (void)SSL3_SendAlert(ss, alert_fatal, decode_error); - PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID); - return rv; - } - - PORT_Assert(ss->nextProtoCallback); - /* For ALPN, the cipher suite isn't selected yet. Note that extensions - * sometimes affect what cipher suite is selected, e.g., for ECC. */ - PORT_Assert((ss->ssl3.hs.preliminaryInfo & - ssl_preinfo_all & ~ssl_preinfo_cipher_suite) == - (ssl_preinfo_all & ~ssl_preinfo_cipher_suite)); - rv = ss->nextProtoCallback(ss->nextProtoArg, ss->fd, data->data, data->len, - result.data, &result.len, sizeof(resultBuffer)); - if (rv != SECSuccess) { - /* Expect callback to call PORT_SetError() */ - (void)SSL3_SendAlert(ss, alert_fatal, internal_error); - return SECFailure; - } - - /* If the callback wrote more than allowed to |result| it has corrupted our - * stack. */ - if (result.len > sizeof(resultBuffer)) { - PORT_SetError(SEC_ERROR_OUTPUT_LEN); - /* TODO: crash */ - return SECFailure; - } - - SECITEM_FreeItem(&ss->ssl3.nextProto, PR_FALSE); - - if (ex_type == ssl_app_layer_protocol_xtn && - ss->ssl3.nextProtoState != SSL_NEXT_PROTO_NEGOTIATED) { - /* The callback might say OK, but then it picks a default value - one - * that was not listed. That's OK for NPN, but not ALPN. */ - (void)SSL3_SendAlert(ss, alert_fatal, no_application_protocol); - PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_NO_PROTOCOL); - return SECFailure; - } - - ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; - return SECITEM_CopyItem(NULL, &ss->ssl3.nextProto, &result); -} - -/* handle an incoming ALPN extension at the server */ -static SECStatus -ssl3_ServerHandleAppProtoXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data) -{ - int count; - SECStatus rv; - - /* We expressly don't want to allow ALPN on renegotiation, - * despite it being permitted by the spec. */ - if (ss->firstHsDone || data->len == 0) { - /* Clients MUST send a non-empty ALPN extension. */ - (void)SSL3_SendAlert(ss, alert_fatal, illegal_parameter); - PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID); - return SECFailure; - } - - /* Unlike NPN, ALPN has extra redundant length information so that - * the extension is the same in both ClientHello and ServerHello. */ - count = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len); - if (count != data->len) { - (void)ssl3_DecodeError(ss); - return SECFailure; - } - - if (!ss->nextProtoCallback) { - /* we're not configured for it */ - return SECSuccess; - } - - rv = ssl3_SelectAppProtocol(ss, ex_type, data); - if (rv != SECSuccess) { - return rv; - } - - /* prepare to send back a response, if we negotiated */ - if (ss->ssl3.nextProtoState == SSL_NEXT_PROTO_NEGOTIATED) { - rv = ssl3_RegisterServerHelloExtensionSender( - ss, ex_type, ssl3_ServerSendAppProtoXtn); - if (rv != SECSuccess) { - (void)SSL3_SendAlert(ss, alert_fatal, internal_error); - PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); - return rv; - } - } - return SECSuccess; -} - -static SECStatus -ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, PRUint16 ex_type, - SECItem *data) -{ - PORT_Assert(!ss->firstHsDone); - - if (ssl3_ExtensionNegotiated(ss, ssl_app_layer_protocol_xtn)) { - /* If the server negotiated ALPN then it has already told us what - * protocol to use, so it doesn't make sense for us to try to negotiate - * a different one by sending the NPN handshake message. However, if - * we've negotiated NPN then we're required to send the NPN handshake - * message. Thus, these two extensions cannot both be negotiated on the - * same connection. */ - (void)SSL3_SendAlert(ss, alert_fatal, illegal_parameter); - PORT_SetError(SSL_ERROR_BAD_SERVER); - return SECFailure; - } - - /* We should only get this call if we sent the extension, so - * ss->nextProtoCallback needs to be non-NULL. However, it is possible - * that an application erroneously cleared the callback between the time - * we sent the ClientHello and now. */ - if (!ss->nextProtoCallback) { - PORT_Assert(0); - (void)SSL3_SendAlert(ss, alert_fatal, internal_error); - PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_NO_CALLBACK); - return SECFailure; - } - - return ssl3_SelectAppProtocol(ss, ex_type, data); -} - -static SECStatus -ssl3_ClientHandleAppProtoXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data) -{ - SECStatus rv; - PRInt32 list_len; - SECItem protocol_name; - - if (ssl3_ExtensionNegotiated(ss, ssl_next_proto_nego_xtn)) { - PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); - return SECFailure; - } - - /* The extension data from the server has the following format: - * uint16 name_list_len; - * uint8 len; // where len >= 1 - * uint8 protocol_name[len]; */ - if (data->len < 4 || data->len > 2 + 1 + 255) { - (void)SSL3_SendAlert(ss, alert_fatal, decode_error); - PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID); - return SECFailure; - } - - list_len = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len); - /* The list has to be the entire extension. */ - if (list_len != data->len) { - (void)SSL3_SendAlert(ss, alert_fatal, decode_error); - PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID); - return SECFailure; - } - - rv = ssl3_ConsumeHandshakeVariable(ss, &protocol_name, 1, - &data->data, &data->len); - /* The list must have exactly one value. */ - if (rv != SECSuccess || data->len != 0) { - (void)SSL3_SendAlert(ss, alert_fatal, decode_error); - PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID); - return SECFailure; - } - - SECITEM_FreeItem(&ss->ssl3.nextProto, PR_FALSE); - ss->ssl3.nextProtoState = SSL_NEXT_PROTO_SELECTED; - ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; - return SECITEM_CopyItem(NULL, &ss->ssl3.nextProto, &protocol_name); -} - -static PRInt32 -ssl3_ClientSendNextProtoNegoXtn(sslSocket *ss, PRBool append, - PRUint32 maxBytes) -{ - PRInt32 extension_length; - - /* Renegotiations do not send this extension. */ - if (!ss->opt.enableNPN || !ss->nextProtoCallback || ss->firstHsDone) { - return 0; - } - - extension_length = 4; - - if (maxBytes < (PRUint32)extension_length) { - return 0; - } - if (append) { - SECStatus rv; - rv = ssl3_AppendHandshakeNumber(ss, ssl_next_proto_nego_xtn, 2); - if (rv != SECSuccess) - goto loser; - rv = ssl3_AppendHandshakeNumber(ss, 0, 2); - if (rv != SECSuccess) - goto loser; - ss->xtnData.advertised[ss->xtnData.numAdvertised++] = - ssl_next_proto_nego_xtn; - } - - return extension_length; - -loser: - return -1; -} - -static PRInt32 -ssl3_ClientSendAppProtoXtn(sslSocket *ss, PRBool append, PRUint32 maxBytes) -{ - PRInt32 extension_length; - unsigned char *alpn_protos = NULL; - - /* Renegotiations do not send this extension. */ - if (!ss->opt.enableALPN || !ss->opt.nextProtoNego.data || ss->firstHsDone) { - return 0; - } - - extension_length = 2 /* extension type */ + 2 /* extension length */ + - 2 /* protocol name list length */ + - ss->opt.nextProtoNego.len; - - if (maxBytes < (PRUint32)extension_length) { - return 0; - } - if (append) { - /* NPN requires that the client's fallback protocol is first in the - * list. However, ALPN sends protocols in preference order. So we - * allocate a buffer and move the first protocol to the end of the - * list. */ - SECStatus rv; - const unsigned int len = ss->opt.nextProtoNego.len; - - alpn_protos = PORT_Alloc(len); - if (alpn_protos == NULL) { - return SECFailure; - } - if (len > 0) { - /* Each protocol string is prefixed with a single byte length. */ - unsigned int i = ss->opt.nextProtoNego.data[0] + 1; - if (i <= len) { - memcpy(alpn_protos, &ss->opt.nextProtoNego.data[i], len - i); - memcpy(alpn_protos + len - i, ss->opt.nextProtoNego.data, i); - } else { - /* This seems to be invalid data so we'll send as-is. */ - memcpy(alpn_protos, ss->opt.nextProtoNego.data, len); - } - } - - rv = ssl3_AppendHandshakeNumber(ss, ssl_app_layer_protocol_xtn, 2); - if (rv != SECSuccess) { - goto loser; - } - rv = ssl3_AppendHandshakeNumber(ss, extension_length - 4, 2); - if (rv != SECSuccess) { - goto loser; - } - rv = ssl3_AppendHandshakeVariable(ss, alpn_protos, len, 2); - PORT_Free(alpn_protos); - alpn_protos = NULL; - if (rv != SECSuccess) { - goto loser; - } - ss->xtnData.advertised[ss->xtnData.numAdvertised++] = - ssl_app_layer_protocol_xtn; - } - - return extension_length; - -loser: - if (alpn_protos) { - PORT_Free(alpn_protos); - } - return -1; -} - -static PRInt32 -ssl3_ServerSendAppProtoXtn(sslSocket *ss, PRBool append, PRUint32 maxBytes) -{ - PRInt32 extension_length; - - /* we're in over our heads if any of these fail */ - PORT_Assert(ss->opt.enableALPN); - PORT_Assert(ss->ssl3.nextProto.data); - PORT_Assert(ss->ssl3.nextProto.len > 0); - PORT_Assert(ss->ssl3.nextProtoState == SSL_NEXT_PROTO_NEGOTIATED); - PORT_Assert(!ss->firstHsDone); - - extension_length = 2 /* extension type */ + 2 /* extension length */ + - 2 /* protocol name list */ + 1 /* name length */ + - ss->ssl3.nextProto.len; - - if (maxBytes < (PRUint32)extension_length) { - return 0; - } - if (append) { - SECStatus rv; - rv = ssl3_AppendHandshakeNumber(ss, ssl_app_layer_protocol_xtn, 2); - if (rv != SECSuccess) { - return -1; - } - rv = ssl3_AppendHandshakeNumber(ss, extension_length - 4, 2); - if (rv != SECSuccess) { - return -1; - } - rv = ssl3_AppendHandshakeNumber(ss, ss->ssl3.nextProto.len + 1, 2); - if (rv != SECSuccess) { - return -1; - } - rv = ssl3_AppendHandshakeVariable(ss, ss->ssl3.nextProto.data, - ss->ssl3.nextProto.len, 1); - if (rv != SECSuccess) { - return -1; - } - } - - return extension_length; -} - -static SECStatus -ssl3_ServerHandleStatusRequestXtn(sslSocket *ss, PRUint16 ex_type, - SECItem *data) -{ - ssl3HelloExtensionSenderFunc sender; - - PORT_Assert(ss->sec.isServer); - - /* remember that we got this extension. */ - ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; - - if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) { - sender = tls13_ServerSendStatusRequestXtn; - } else { - sender = ssl3_ServerSendStatusRequestXtn; - } - return ssl3_RegisterServerHelloExtensionSender(ss, ex_type, sender); -} - -static PRInt32 -ssl3_ServerSendStatusRequestXtn( - sslSocket *ss, - PRBool append, - PRUint32 maxBytes) -{ - PRInt32 extension_length; - const sslServerCert *serverCert = ss->sec.serverCert; - SECStatus rv; - - if (!serverCert->certStatusArray || - !serverCert->certStatusArray->len) { - return 0; - } - - extension_length = 2 + 2; - if (maxBytes < (PRUint32)extension_length) { - return 0; - } - if (append) { - /* extension_type */ - rv = ssl3_AppendHandshakeNumber(ss, ssl_cert_status_xtn, 2); - if (rv != SECSuccess) - return -1; - /* length of extension_data */ - rv = ssl3_AppendHandshakeNumber(ss, 0, 2); - if (rv != SECSuccess) - return -1; - /* The certificate status data is sent in ssl3_SendCertificateStatus. */ - } - - return extension_length; -} - -static PRInt32 -tls13_ServerSendStatusRequestXtn( - sslSocket *ss, - PRBool append, - PRUint32 maxBytes) -{ - PRInt32 extension_length; - const sslServerCert *serverCert = ss->sec.serverCert; - const SECItem *item; - SECStatus rv; - - if (!serverCert->certStatusArray || - !serverCert->certStatusArray->len) { - return 0; - } - - item = &serverCert->certStatusArray->items[0]; - - /* Only send the first entry. */ - extension_length = 2 + 2 + 1 /* status_type */ + 3 + item->len; - if (maxBytes < (PRUint32)extension_length) { - return 0; - } - if (append) { - /* extension_type */ - rv = ssl3_AppendHandshakeNumber(ss, ssl_cert_status_xtn, 2); - if (rv != SECSuccess) - return -1; - /* length of extension_data */ - rv = ssl3_AppendHandshakeNumber(ss, extension_length - 4, 2); - if (rv != SECSuccess) - return -1; - /* status_type == ocsp */ - rv = ssl3_AppendHandshakeNumber(ss, 1 /*ocsp*/, 1); - if (rv != SECSuccess) - return rv; /* err set by AppendHandshake. */ - /* opaque OCSPResponse<1..2^24-1> */ - rv = ssl3_AppendHandshakeVariable(ss, item->data, item->len, 3); - if (rv != SECSuccess) - return rv; /* err set by AppendHandshake. */ - } - - return extension_length; -} - -/* ssl3_ClientSendStatusRequestXtn builds the status_request extension on the - * client side. See RFC 6066 section 8. */ -static PRInt32 -ssl3_ClientSendStatusRequestXtn(sslSocket *ss, PRBool append, - PRUint32 maxBytes) -{ - PRInt32 extension_length; - - if (!ss->opt.enableOCSPStapling) - return 0; - - /* extension_type (2-bytes) + - * length(extension_data) (2-bytes) + - * status_type (1) + - * responder_id_list length (2) + - * request_extensions length (2) - */ - extension_length = 9; - - if (maxBytes < (PRUint32)extension_length) { - PORT_Assert(0); - return 0; - } - if (append) { - SECStatus rv; - TLSExtensionData *xtnData; - - /* extension_type */ - rv = ssl3_AppendHandshakeNumber(ss, ssl_cert_status_xtn, 2); - if (rv != SECSuccess) - return -1; - rv = ssl3_AppendHandshakeNumber(ss, extension_length - 4, 2); - if (rv != SECSuccess) - return -1; - rv = ssl3_AppendHandshakeNumber(ss, 1 /* status_type ocsp */, 1); - if (rv != SECSuccess) - return -1; - /* A zero length responder_id_list means that the responders are - * implicitly known to the server. */ - rv = ssl3_AppendHandshakeNumber(ss, 0, 2); - if (rv != SECSuccess) - return -1; - /* A zero length request_extensions means that there are no extensions. - * Specifically, we don't set the id-pkix-ocsp-nonce extension. This - * means that the server can replay a cached OCSP response to us. */ - rv = ssl3_AppendHandshakeNumber(ss, 0, 2); - if (rv != SECSuccess) - return -1; - - xtnData = &ss->xtnData; - xtnData->advertised[xtnData->numAdvertised++] = ssl_cert_status_xtn; - } - return extension_length; -} - -static SECStatus -ssl3_ClientHandleStatusRequestXtn(sslSocket *ss, PRUint16 ex_type, - SECItem *data) -{ - /* In TLS 1.3, the extension carries the OCSP response. */ - if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) { - SECStatus rv; - rv = ssl_ReadCertificateStatus(ss, data->data, data->len); - if (rv != SECSuccess) { - return SECFailure; /* code already set */ - } - } else if (data->len != 0) { - (void)SSL3_SendAlert(ss, alert_fatal, illegal_parameter); - PORT_SetError(SSL_ERROR_RX_MALFORMED_SERVER_HELLO); - return SECFailure; - } - - /* Keep track of negotiated extensions. */ - ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; - return SECSuccess; -} - -/* - * Called from ssl3_SendNewSessionTicket, tls13_SendNewSessionTicket - */ -SECStatus -ssl3_EncodeSessionTicket(sslSocket *ss, - const NewSessionTicket *ticket, - SECItem *ticket_data) -{ - PRUint32 i; - SECStatus rv; - SECItem plaintext; - SECItem plaintext_item = { 0, NULL, 0 }; - SECItem ciphertext = { 0, NULL, 0 }; - PRUint32 ciphertext_length; - SECItem ticket_buf = { 0, NULL, 0 }; - SECItem ticket_tmp = { 0, NULL, 0 }; - SECItem macParam = { 0, NULL, 0 }; - PRBool ms_is_wrapped; - unsigned char wrapped_ms[SSL3_MASTER_SECRET_LENGTH]; - SECItem ms_item = { 0, NULL, 0 }; - PRUint32 padding_length; - PRUint32 ticket_length; - PRUint32 cert_length = 0; - PRUint8 length_buf[4]; - PRUint32 now; - PK11SymKey *aes_key = NULL; - PK11SymKey *mac_key = NULL; - CK_MECHANISM_TYPE cipherMech = CKM_AES_CBC; - PK11Context *aes_ctx; - CK_MECHANISM_TYPE macMech = CKM_SHA256_HMAC; - PK11Context *hmac_ctx = NULL; - unsigned char computed_mac[TLS_EX_SESS_TICKET_MAC_LENGTH]; - unsigned int computed_mac_length; - unsigned char iv[AES_BLOCK_SIZE]; - SECItem ivItem; - SECItem *srvName = NULL; - PRUint32 srvNameLen = 0; - CK_MECHANISM_TYPE msWrapMech = 0; /* dummy default value, - * must be >= 0 */ - ssl3CipherSpec *spec; - const sslServerCertType *certType; - SECItem alpnSelection = { siBuffer, NULL, 0 }; - - SSL_TRC(3, ("%d: SSL3[%d]: send session_ticket handshake", - SSL_GETPID(), ss->fd)); - - PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); - PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); - - if (ss->opt.requestCertificate && ss->sec.ci.sid->peerCert) { - cert_length = 3 + ss->sec.ci.sid->peerCert->derCert.len; - } - - /* Get IV and encryption keys */ - ivItem.data = iv; - ivItem.len = sizeof(iv); - rv = PK11_GenerateRandom(iv, sizeof(iv)); - if (rv != SECSuccess) - goto loser; - - rv = ssl3_GetSessionTicketKeys(ss, &aes_key, &mac_key); - if (rv != SECSuccess) - goto loser; - - if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) { - spec = ss->ssl3.cwSpec; - } else { - spec = ss->ssl3.pwSpec; - } - if (spec->msItem.len && spec->msItem.data) { - /* The master secret is available unwrapped. */ - ms_item.data = spec->msItem.data; - ms_item.len = spec->msItem.len; - ms_is_wrapped = PR_FALSE; - } else { - /* Extract the master secret wrapped. */ - sslSessionID sid; - PORT_Memset(&sid, 0, sizeof(sslSessionID)); - - rv = ssl3_CacheWrappedMasterSecret(ss, &sid, spec, - ss->ssl3.hs.kea_def->authKeyType); - if (rv == SECSuccess) { - if (sid.u.ssl3.keys.wrapped_master_secret_len > sizeof(wrapped_ms)) - goto loser; - memcpy(wrapped_ms, sid.u.ssl3.keys.wrapped_master_secret, - sid.u.ssl3.keys.wrapped_master_secret_len); - ms_item.data = wrapped_ms; - ms_item.len = sid.u.ssl3.keys.wrapped_master_secret_len; - msWrapMech = sid.u.ssl3.masterWrapMech; - } else { - /* TODO: else send an empty ticket. */ - goto loser; - } - ms_is_wrapped = PR_TRUE; - } - /* Prep to send negotiated name */ - srvName = &ss->sec.ci.sid->u.ssl3.srvName; - if (srvName->data && srvName->len) { - srvNameLen = 2 + srvName->len; /* len bytes + name len */ - } - - if (ss->ssl3.nextProtoState != SSL_NEXT_PROTO_NO_SUPPORT && - ss->ssl3.nextProto.data) { - alpnSelection = ss->ssl3.nextProto; - } - - ciphertext_length = - sizeof(PRUint16) /* ticket_version */ - + sizeof(SSL3ProtocolVersion) /* ssl_version */ - + sizeof(ssl3CipherSuite) /* ciphersuite */ - + 1 /* compression */ - + 10 /* cipher spec parameters */ - + 1 /* certType arguments */ - + 1 /* SessionTicket.ms_is_wrapped */ - + 4 /* msWrapMech */ - + 2 /* master_secret.length */ - + ms_item.len /* master_secret */ - + 1 /* client_auth_type */ - + cert_length /* cert */ - + 1 /* server name type */ - + srvNameLen /* name len + length field */ - + 1 /* extendedMasterSecretUsed */ - + sizeof(ticket->ticket_lifetime_hint) /* ticket lifetime hint */ - + sizeof(ticket->flags) /* ticket flags */ - + 1 + alpnSelection.len; /* npn value + length field. */ - padding_length = AES_BLOCK_SIZE - - (ciphertext_length % - AES_BLOCK_SIZE); - ciphertext_length += padding_length; - - if (SECITEM_AllocItem(NULL, &plaintext_item, ciphertext_length) == NULL) - goto loser; - - plaintext = plaintext_item; - - /* ticket_version */ - rv = ssl3_AppendNumberToItem(&plaintext, TLS_EX_SESS_TICKET_VERSION, - sizeof(PRUint16)); - if (rv != SECSuccess) - goto loser; - - /* ssl_version */ - rv = ssl3_AppendNumberToItem(&plaintext, ss->version, - sizeof(SSL3ProtocolVersion)); - if (rv != SECSuccess) - goto loser; - - /* ciphersuite */ - rv = ssl3_AppendNumberToItem(&plaintext, ss->ssl3.hs.cipher_suite, - sizeof(ssl3CipherSuite)); - if (rv != SECSuccess) - goto loser; - - /* compression */ - rv = ssl3_AppendNumberToItem(&plaintext, ss->ssl3.hs.compression, 1); - if (rv != SECSuccess) - goto loser; - - /* cipher spec parameters */ - rv = ssl3_AppendNumberToItem(&plaintext, ss->sec.authType, 1); - if (rv != SECSuccess) - goto loser; - rv = ssl3_AppendNumberToItem(&plaintext, ss->sec.authKeyBits, 4); - if (rv != SECSuccess) - goto loser; - rv = ssl3_AppendNumberToItem(&plaintext, ss->sec.keaType, 1); - if (rv != SECSuccess) - goto loser; - rv = ssl3_AppendNumberToItem(&plaintext, ss->sec.keaKeyBits, 4); - if (rv != SECSuccess) - goto loser; - - /* certificate type */ - certType = &ss->sec.serverCert->certType; - PORT_Assert(certType->authType == ss->sec.authType); - switch (ss->sec.authType) { - case ssl_auth_ecdsa: - case ssl_auth_ecdh_rsa: - case ssl_auth_ecdh_ecdsa: - PORT_Assert(certType->namedCurve); - PORT_Assert(certType->namedCurve->keaType == ssl_kea_ecdh); - /* EC curves only use the second of the two bytes. */ - PORT_Assert(certType->namedCurve->name < 256); - rv = ssl3_AppendNumberToItem(&plaintext, - certType->namedCurve->name, 1); - break; - default: - rv = ssl3_AppendNumberToItem(&plaintext, 0, 1); - break; - } - if (rv != SECSuccess) - goto loser; - - /* master_secret */ - rv = ssl3_AppendNumberToItem(&plaintext, ms_is_wrapped, 1); - if (rv != SECSuccess) - goto loser; - rv = ssl3_AppendNumberToItem(&plaintext, msWrapMech, 4); - if (rv != SECSuccess) - goto loser; - rv = ssl3_AppendNumberToItem(&plaintext, ms_item.len, 2); - if (rv != SECSuccess) - goto loser; - rv = ssl3_AppendToItem(&plaintext, ms_item.data, ms_item.len); - if (rv != SECSuccess) - goto loser; - - /* client_identity */ - if (ss->opt.requestCertificate && ss->sec.ci.sid->peerCert) { - rv = ssl3_AppendNumberToItem(&plaintext, CLIENT_AUTH_CERTIFICATE, 1); - if (rv != SECSuccess) - goto loser; - rv = ssl3_AppendNumberToItem(&plaintext, - ss->sec.ci.sid->peerCert->derCert.len, 3); - if (rv != SECSuccess) - goto loser; - rv = ssl3_AppendToItem(&plaintext, - ss->sec.ci.sid->peerCert->derCert.data, - ss->sec.ci.sid->peerCert->derCert.len); - if (rv != SECSuccess) - goto loser; - } else { - rv = ssl3_AppendNumberToItem(&plaintext, 0, 1); - if (rv != SECSuccess) - goto loser; - } - - /* timestamp */ - now = ssl_Time(); - rv = ssl3_AppendNumberToItem(&plaintext, now, - sizeof(ticket->ticket_lifetime_hint)); - if (rv != SECSuccess) - goto loser; - - if (srvNameLen) { - /* Name Type (sni_host_name) */ - rv = ssl3_AppendNumberToItem(&plaintext, srvName->type, 1); - if (rv != SECSuccess) - goto loser; - /* HostName (length and value) */ - rv = ssl3_AppendNumberToItem(&plaintext, srvName->len, 2); - if (rv != SECSuccess) - goto loser; - rv = ssl3_AppendToItem(&plaintext, srvName->data, srvName->len); - if (rv != SECSuccess) - goto loser; - } else { - /* No Name */ - rv = ssl3_AppendNumberToItem(&plaintext, (char)TLS_STE_NO_SERVER_NAME, 1); - if (rv != SECSuccess) - goto loser; - } - - /* extendedMasterSecretUsed */ - rv = ssl3_AppendNumberToItem( - &plaintext, ss->sec.ci.sid->u.ssl3.keys.extendedMasterSecretUsed, 1); - if (rv != SECSuccess) - goto loser; - - /* Flags */ - rv = ssl3_AppendNumberToItem(&plaintext, ticket->flags, - sizeof(ticket->flags)); - if (rv != SECSuccess) - goto loser; - - /* NPN value. */ - PORT_Assert(alpnSelection.len < 256); - rv = ssl3_AppendNumberToItem(&plaintext, alpnSelection.len, 1); - if (rv != SECSuccess) - goto loser; - if (alpnSelection.len) { - rv = ssl3_AppendToItem(&plaintext, alpnSelection.data, alpnSelection.len); - if (rv != SECSuccess) - goto loser; - } - - PORT_Assert(plaintext.len == padding_length); - for (i = 0; i < padding_length; i++) - plaintext.data[i] = (unsigned char)padding_length; - - if (SECITEM_AllocItem(NULL, &ciphertext, ciphertext_length) == NULL) { - rv = SECFailure; - goto loser; - } - - /* Generate encrypted portion of ticket. */ - PORT_Assert(aes_key); - aes_ctx = PK11_CreateContextBySymKey(cipherMech, CKA_ENCRYPT, aes_key, &ivItem); - if (!aes_ctx) - goto loser; - - rv = PK11_CipherOp(aes_ctx, ciphertext.data, - (int *)&ciphertext.len, ciphertext.len, - plaintext_item.data, plaintext_item.len); - PK11_Finalize(aes_ctx); - PK11_DestroyContext(aes_ctx, PR_TRUE); - if (rv != SECSuccess) - goto loser; - - /* Convert ciphertext length to network order. */ - length_buf[0] = (ciphertext.len >> 8) & 0xff; - length_buf[1] = (ciphertext.len) & 0xff; - - /* Compute MAC. */ - PORT_Assert(mac_key); - hmac_ctx = PK11_CreateContextBySymKey(macMech, CKA_SIGN, mac_key, &macParam); - if (!hmac_ctx) - goto loser; - - rv = PK11_DigestBegin(hmac_ctx); - if (rv != SECSuccess) - goto loser; - rv = PK11_DigestOp(hmac_ctx, key_name, SESS_TICKET_KEY_NAME_LEN); - if (rv != SECSuccess) - goto loser; - rv = PK11_DigestOp(hmac_ctx, iv, sizeof(iv)); - if (rv != SECSuccess) - goto loser; - rv = PK11_DigestOp(hmac_ctx, (unsigned char *)length_buf, 2); - if (rv != SECSuccess) - goto loser; - rv = PK11_DigestOp(hmac_ctx, ciphertext.data, ciphertext.len); - if (rv != SECSuccess) - goto loser; - rv = PK11_DigestFinal(hmac_ctx, computed_mac, - &computed_mac_length, sizeof(computed_mac)); - if (rv != SECSuccess) - goto loser; - - ticket_length = - +SESS_TICKET_KEY_NAME_LEN /* key_name */ - + AES_BLOCK_SIZE /* iv */ - + 2 /* length field for NewSessionTicket.ticket.encrypted_state */ - + ciphertext_length /* encrypted_state */ - + TLS_EX_SESS_TICKET_MAC_LENGTH; /* mac */ - - if (SECITEM_AllocItem(NULL, &ticket_buf, ticket_length) == NULL) { - rv = SECFailure; - goto loser; - } - ticket_tmp = ticket_buf; /* Shallow copy because AppendToItem is - * destructive. */ - - rv = ssl3_AppendToItem(&ticket_tmp, key_name, SESS_TICKET_KEY_NAME_LEN); - if (rv != SECSuccess) - goto loser; - - rv = ssl3_AppendToItem(&ticket_tmp, iv, sizeof(iv)); - if (rv != SECSuccess) - goto loser; - - rv = ssl3_AppendNumberToItem(&ticket_tmp, ciphertext.len, 2); - if (rv != SECSuccess) - goto loser; - - rv = ssl3_AppendToItem(&ticket_tmp, ciphertext.data, ciphertext.len); - if (rv != SECSuccess) - goto loser; - - rv = ssl3_AppendToItem(&ticket_tmp, computed_mac, computed_mac_length); - if (rv != SECSuccess) - goto loser; - - /* Give ownership of memory to caller. */ - *ticket_data = ticket_buf; - ticket_buf.data = NULL; - -loser: - if (hmac_ctx) { - PK11_DestroyContext(hmac_ctx, PR_TRUE); - } - if (plaintext_item.data) { - SECITEM_FreeItem(&plaintext_item, PR_FALSE); - } - if (ciphertext.data) { - SECITEM_FreeItem(&ciphertext, PR_FALSE); - } - if (ticket_buf.data) { - SECITEM_FreeItem(&ticket_buf, PR_FALSE); - } - - return rv; -} - -/* When a client receives a SessionTicket extension a NewSessionTicket - * message is expected during the handshake. - */ -SECStatus -ssl3_ClientHandleSessionTicketXtn(sslSocket *ss, PRUint16 ex_type, - SECItem *data) -{ - if (data->len != 0) { - return SECSuccess; /* Ignore the extension. */ - } - - /* Keep track of negotiated extensions. */ - ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; - return SECSuccess; -} - -/* Generic ticket processing code, common to TLS 1.0-1.2 and - * TLS 1.3. */ -static SECStatus -ssl3_ProcessSessionTicketCommon(sslSocket *ss, SECItem *data) -{ - SECStatus rv; - SECItem *decrypted_state = NULL; - SessionTicket *parsed_session_ticket = NULL; - sslSessionID *sid = NULL; - SSL3Statistics *ssl3stats; - PRUint32 i; - SECItem extension_data; - EncryptedSessionTicket enc_session_ticket; - unsigned char computed_mac[TLS_EX_SESS_TICKET_MAC_LENGTH]; - unsigned int computed_mac_length; - PK11SymKey *aes_key = NULL; - PK11SymKey *mac_key = NULL; - PK11Context *hmac_ctx; - CK_MECHANISM_TYPE macMech = CKM_SHA256_HMAC; - PK11Context *aes_ctx; - CK_MECHANISM_TYPE cipherMech = CKM_AES_CBC; - unsigned char *padding; - PRUint32 padding_length; - unsigned char *buffer; - unsigned int buffer_len; - PRInt32 temp; - SECItem cert_item; - PRInt8 nameType = TLS_STE_NO_SERVER_NAME; - SECItem macParam = { siBuffer, NULL, 0 }; - SECItem alpn_item; - SECItem ivItem; - - /* Turn off stateless session resumption if the client sends a - * SessionTicket extension, even if the extension turns out to be - * malformed (ss->sec.ci.sid is non-NULL when doing session - * renegotiation.) - */ - if (ss->sec.ci.sid != NULL) { - ss->sec.uncache(ss->sec.ci.sid); - ssl_FreeSID(ss->sec.ci.sid); - ss->sec.ci.sid = NULL; - } - - extension_data.data = data->data; /* Keep a copy for future use. */ - extension_data.len = data->len; - - if (ssl3_ParseEncryptedSessionTicket(ss, data, &enc_session_ticket) != - SECSuccess) { - return SECSuccess; /* Pretend it isn't there */ - } - - /* Get session ticket keys. */ - rv = ssl3_GetSessionTicketKeys(ss, &aes_key, &mac_key); - if (rv != SECSuccess) { - SSL_DBG(("%d: SSL[%d]: Unable to get/generate session ticket keys.", - SSL_GETPID(), ss->fd)); - goto loser; - } - - /* If the ticket sent by the client was generated under a key different - * from the one we have, bypass ticket processing. - */ - if (PORT_Memcmp(enc_session_ticket.key_name, key_name, - SESS_TICKET_KEY_NAME_LEN) != 0) { - SSL_DBG(("%d: SSL[%d]: Session ticket key_name sent mismatch.", - SSL_GETPID(), ss->fd)); - goto no_ticket; - } - - /* Verify the MAC on the ticket. MAC verification may also - * fail if the MAC key has been recently refreshed. - */ - PORT_Assert(mac_key); - hmac_ctx = PK11_CreateContextBySymKey(macMech, CKA_SIGN, mac_key, &macParam); - if (!hmac_ctx) { - SSL_DBG(("%d: SSL[%d]: Unable to create HMAC context: %d.", - SSL_GETPID(), ss->fd, PORT_GetError())); - goto no_ticket; - } else { - SSL_DBG(("%d: SSL[%d]: Successfully created HMAC context.", - SSL_GETPID(), ss->fd)); - } - rv = PK11_DigestBegin(hmac_ctx); - if (rv != SECSuccess) { - PK11_DestroyContext(hmac_ctx, PR_TRUE); - goto no_ticket; - } - rv = PK11_DigestOp(hmac_ctx, extension_data.data, - extension_data.len - - TLS_EX_SESS_TICKET_MAC_LENGTH); - if (rv != SECSuccess) { - PK11_DestroyContext(hmac_ctx, PR_TRUE); - goto no_ticket; - } - rv = PK11_DigestFinal(hmac_ctx, computed_mac, - &computed_mac_length, sizeof(computed_mac)); - PK11_DestroyContext(hmac_ctx, PR_TRUE); - if (rv != SECSuccess) - goto no_ticket; - - if (NSS_SecureMemcmp(computed_mac, enc_session_ticket.mac, - computed_mac_length) != - 0) { - SSL_DBG(("%d: SSL[%d]: Session ticket MAC mismatch.", - SSL_GETPID(), ss->fd)); - goto no_ticket; - } - - /* We ignore key_name for now. - * This is ok as MAC verification succeeded. - */ - - /* Decrypt the ticket. */ - - /* Plaintext is shorter than the ciphertext due to padding. */ - decrypted_state = SECITEM_AllocItem(NULL, NULL, - enc_session_ticket.encrypted_state.len); - - PORT_Assert(aes_key); - ivItem.data = enc_session_ticket.iv; - ivItem.len = AES_BLOCK_SIZE; - aes_ctx = PK11_CreateContextBySymKey(cipherMech, CKA_DECRYPT, - aes_key, &ivItem); - if (!aes_ctx) { - SSL_DBG(("%d: SSL[%d]: Unable to create AES context.", - SSL_GETPID(), ss->fd)); - goto no_ticket; - } - - rv = PK11_CipherOp(aes_ctx, decrypted_state->data, - (int *)&decrypted_state->len, decrypted_state->len, - enc_session_ticket.encrypted_state.data, - enc_session_ticket.encrypted_state.len); - PK11_Finalize(aes_ctx); - PK11_DestroyContext(aes_ctx, PR_TRUE); - if (rv != SECSuccess) - goto no_ticket; - - /* Check padding. */ - padding_length = - (PRUint32)decrypted_state->data[decrypted_state->len - 1]; - if (padding_length == 0 || padding_length > AES_BLOCK_SIZE) - goto no_ticket; - - padding = &decrypted_state->data[decrypted_state->len - padding_length]; - for (i = 0; i < padding_length; i++, padding++) { - if (padding_length != (PRUint32)*padding) - goto no_ticket; - } - - /* Deserialize session state. */ - buffer = decrypted_state->data; - buffer_len = decrypted_state->len; - - parsed_session_ticket = PORT_ZAlloc(sizeof(SessionTicket)); - if (parsed_session_ticket == NULL) { - rv = SECFailure; - goto loser; - } - - /* Read ticket_version and reject if the version is wrong */ - temp = ssl3_ConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len); - if (temp != TLS_EX_SESS_TICKET_VERSION) - goto no_ticket; - - parsed_session_ticket->ticket_version = (SSL3ProtocolVersion)temp; - - /* Read SSLVersion. */ - temp = ssl3_ConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len); - if (temp < 0) - goto no_ticket; - parsed_session_ticket->ssl_version = (SSL3ProtocolVersion)temp; - - /* Read cipher_suite. */ - temp = ssl3_ConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len); - if (temp < 0) - goto no_ticket; - parsed_session_ticket->cipher_suite = (ssl3CipherSuite)temp; - - /* Read compression_method. */ - temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len); - if (temp < 0) - goto no_ticket; - parsed_session_ticket->compression_method = (SSLCompressionMethod)temp; - - /* Read cipher spec parameters. */ - temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len); - if (temp < 0) - goto no_ticket; - parsed_session_ticket->authType = (SSLAuthType)temp; - temp = ssl3_ConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len); - if (temp < 0) - goto no_ticket; - parsed_session_ticket->authKeyBits = (PRUint32)temp; - temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len); - if (temp < 0) - goto no_ticket; - parsed_session_ticket->keaType = (SSLKEAType)temp; - temp = ssl3_ConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len); - if (temp < 0) - goto no_ticket; - parsed_session_ticket->keaKeyBits = (PRUint32)temp; - - /* Read certificate slot */ - parsed_session_ticket->certType.authType = parsed_session_ticket->authType; - temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len); - if (temp < 0) - goto no_ticket; - switch (parsed_session_ticket->authType) { - case ssl_auth_ecdsa: - case ssl_auth_ecdh_rsa: - case ssl_auth_ecdh_ecdsa: { - const sslNamedGroupDef *group = - ssl_LookupNamedGroup((SSLNamedGroup)temp); - if (!group || group->keaType != ssl_kea_ecdh) { - goto no_ticket; - } - parsed_session_ticket->certType.namedCurve = group; - } break; - default: - break; - } - - /* Read wrapped master_secret. */ - temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len); - if (temp < 0) - goto no_ticket; - parsed_session_ticket->ms_is_wrapped = (PRBool)temp; - - temp = ssl3_ConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len); - if (temp < 0) - goto no_ticket; - parsed_session_ticket->msWrapMech = (CK_MECHANISM_TYPE)temp; - - temp = ssl3_ConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len); - if (temp < 0) - goto no_ticket; - parsed_session_ticket->ms_length = (PRUint16)temp; - if (parsed_session_ticket->ms_length == 0 || /* sanity check MS. */ - parsed_session_ticket->ms_length > - sizeof(parsed_session_ticket->master_secret)) - goto no_ticket; - - /* Allow for the wrapped master secret to be longer. */ - if (buffer_len < parsed_session_ticket->ms_length) - goto no_ticket; - PORT_Memcpy(parsed_session_ticket->master_secret, buffer, - parsed_session_ticket->ms_length); - buffer += parsed_session_ticket->ms_length; - buffer_len -= parsed_session_ticket->ms_length; - - /* Read client_identity */ - temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len); - if (temp < 0) - goto no_ticket; - parsed_session_ticket->client_identity.client_auth_type = - (ClientAuthenticationType)temp; - switch (parsed_session_ticket->client_identity.client_auth_type) { - case CLIENT_AUTH_ANONYMOUS: - break; - case CLIENT_AUTH_CERTIFICATE: - rv = ssl3_ConsumeHandshakeVariable(ss, &cert_item, 3, - &buffer, &buffer_len); - if (rv != SECSuccess) - goto no_ticket; - rv = SECITEM_CopyItem(NULL, &parsed_session_ticket->peer_cert, - &cert_item); - if (rv != SECSuccess) - goto no_ticket; - break; - default: - goto no_ticket; - } - /* Read timestamp. */ - temp = ssl3_ConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len); - if (temp < 0) - goto no_ticket; - parsed_session_ticket->timestamp = (PRUint32)temp; - - /* Read server name */ - nameType = - ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len); - if (nameType != TLS_STE_NO_SERVER_NAME) { - SECItem name_item; - rv = ssl3_ConsumeHandshakeVariable(ss, &name_item, 2, &buffer, - &buffer_len); - if (rv != SECSuccess) - goto no_ticket; - rv = SECITEM_CopyItem(NULL, &parsed_session_ticket->srvName, - &name_item); - if (rv != SECSuccess) - goto no_ticket; - parsed_session_ticket->srvName.type = nameType; - } - - /* Read extendedMasterSecretUsed */ - temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len); - if (temp < 0) - goto no_ticket; - PORT_Assert(temp == PR_TRUE || temp == PR_FALSE); - parsed_session_ticket->extendedMasterSecretUsed = (PRBool)temp; - - rv = ssl3_ConsumeHandshake(ss, &parsed_session_ticket->flags, 4, - &buffer, &buffer_len); - if (rv != SECSuccess) - goto no_ticket; - parsed_session_ticket->flags = PR_ntohl(parsed_session_ticket->flags); - - rv = ssl3_ConsumeHandshakeVariable(ss, &alpn_item, 1, &buffer, &buffer_len); - if (rv != SECSuccess) - goto no_ticket; - if (alpn_item.len != 0) { - rv = SECITEM_CopyItem(NULL, &parsed_session_ticket->alpnSelection, - &alpn_item); - if (rv != SECSuccess) - goto no_ticket; - if (alpn_item.len >= 256) - goto no_ticket; - } - - /* Done parsing. Check that all bytes have been consumed. */ - if (buffer_len != padding_length) - goto no_ticket; - - /* Use the ticket if it has not expired, otherwise free the allocated - * memory since the ticket is of no use. - */ - if (parsed_session_ticket->timestamp != 0 && - parsed_session_ticket->timestamp + - TLS_EX_SESS_TICKET_LIFETIME_HINT > - ssl_Time()) { - - sid = ssl3_NewSessionID(ss, PR_TRUE); - if (sid == NULL) { - rv = SECFailure; - goto loser; - } - - /* Copy over parameters. */ - sid->version = parsed_session_ticket->ssl_version; - sid->u.ssl3.cipherSuite = parsed_session_ticket->cipher_suite; - sid->u.ssl3.compression = parsed_session_ticket->compression_method; - sid->authType = parsed_session_ticket->authType; - sid->authKeyBits = parsed_session_ticket->authKeyBits; - sid->keaType = parsed_session_ticket->keaType; - sid->keaKeyBits = parsed_session_ticket->keaKeyBits; - memcpy(&sid->certType, &parsed_session_ticket->certType, - sizeof(sslServerCertType)); - - if (SECITEM_CopyItem(NULL, &sid->u.ssl3.locked.sessionTicket.ticket, - &extension_data) != SECSuccess) - goto no_ticket; - sid->u.ssl3.locked.sessionTicket.flags = parsed_session_ticket->flags; - - if (parsed_session_ticket->ms_length > - sizeof(sid->u.ssl3.keys.wrapped_master_secret)) - goto no_ticket; - PORT_Memcpy(sid->u.ssl3.keys.wrapped_master_secret, - parsed_session_ticket->master_secret, - parsed_session_ticket->ms_length); - sid->u.ssl3.keys.wrapped_master_secret_len = - parsed_session_ticket->ms_length; - sid->u.ssl3.masterWrapMech = parsed_session_ticket->msWrapMech; - sid->u.ssl3.keys.msIsWrapped = - parsed_session_ticket->ms_is_wrapped; - sid->u.ssl3.masterValid = PR_TRUE; - sid->u.ssl3.keys.resumable = PR_TRUE; - sid->u.ssl3.keys.extendedMasterSecretUsed = parsed_session_ticket->extendedMasterSecretUsed; - - /* Copy over client cert from session ticket if there is one. */ - if (parsed_session_ticket->peer_cert.data != NULL) { - if (sid->peerCert != NULL) - CERT_DestroyCertificate(sid->peerCert); - sid->peerCert = CERT_NewTempCertificate(ss->dbHandle, - &parsed_session_ticket->peer_cert, NULL, PR_FALSE, PR_TRUE); - if (sid->peerCert == NULL) { - rv = SECFailure; - goto loser; - } - } - if (parsed_session_ticket->srvName.data != NULL) { - if (sid->u.ssl3.srvName.data) { - SECITEM_FreeItem(&sid->u.ssl3.srvName, PR_FALSE); - } - sid->u.ssl3.srvName = parsed_session_ticket->srvName; - } - if (parsed_session_ticket->alpnSelection.data != NULL) { - sid->u.ssl3.alpnSelection = parsed_session_ticket->alpnSelection; - /* So we don't free below. */ - parsed_session_ticket->alpnSelection.data = NULL; - } - ss->statelessResume = PR_TRUE; - ss->sec.ci.sid = sid; - } - - if (0) { - no_ticket: - SSL_DBG(("%d: SSL[%d]: Session ticket parsing failed.", - SSL_GETPID(), ss->fd)); - ssl3stats = SSL_GetStatistics(); - SSL_AtomicIncrementLong(&ssl3stats->hch_sid_ticket_parse_failures); - } - rv = SECSuccess; - -loser: - /* ss->sec.ci.sid == sid if it did NOT come here via goto statement - * in that case do not free sid - */ - if (sid && (ss->sec.ci.sid != sid)) { - ssl_FreeSID(sid); - sid = NULL; - } - if (decrypted_state != NULL) { - SECITEM_FreeItem(decrypted_state, PR_TRUE); - decrypted_state = NULL; - } - - if (parsed_session_ticket != NULL) { - if (parsed_session_ticket->peer_cert.data) { - SECITEM_FreeItem(&parsed_session_ticket->peer_cert, PR_FALSE); - } - if (parsed_session_ticket->alpnSelection.data) { - SECITEM_FreeItem(&parsed_session_ticket->alpnSelection, PR_FALSE); - } - PORT_ZFree(parsed_session_ticket, sizeof(SessionTicket)); - } - - return rv; -} - -SECStatus -ssl3_ServerHandleSessionTicketXtn(sslSocket *ss, PRUint16 ex_type, - SECItem *data) -{ - - /* Ignore the SessionTicket extension if processing is disabled. */ - if (!ss->opt.enableSessionTickets) { - return SECSuccess; - } - - /* If we are doing TLS 1.3, then ignore this. */ - if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) { - return SECSuccess; - } - - /* Keep track of negotiated extensions. */ - ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; - - /* Parse the received ticket sent in by the client. We are - * lenient about some parse errors, falling back to a fullshake - * instead of terminating the current connection. - */ - if (data->len == 0) { - ss->xtnData.emptySessionTicket = PR_TRUE; - return SECSuccess; - } - - return ssl3_ProcessSessionTicketCommon(ss, data); -} - -/* - * Read bytes. Using this function means the SECItem structure - * cannot be freed. The caller is expected to call this function - * on a shallow copy of the structure. - */ -static SECStatus -ssl3_ConsumeFromItem(SECItem *item, unsigned char **buf, PRUint32 bytes) -{ - if (bytes > item->len) - return SECFailure; - - *buf = item->data; - item->data += bytes; - item->len -= bytes; - return SECSuccess; -} - -static SECStatus -ssl3_ParseEncryptedSessionTicket(sslSocket *ss, SECItem *data, - EncryptedSessionTicket *enc_session_ticket) -{ - if (ssl3_ConsumeFromItem(data, &enc_session_ticket->key_name, - SESS_TICKET_KEY_NAME_LEN) != - SECSuccess) - return SECFailure; - if (ssl3_ConsumeFromItem(data, &enc_session_ticket->iv, - AES_BLOCK_SIZE) != - SECSuccess) - return SECFailure; - if (ssl3_ConsumeHandshakeVariable(ss, &enc_session_ticket->encrypted_state, - 2, &data->data, &data->len) != - SECSuccess) - return SECFailure; - if (ssl3_ConsumeFromItem(data, &enc_session_ticket->mac, - TLS_EX_SESS_TICKET_MAC_LENGTH) != - SECSuccess) - return SECFailure; - if (data->len != 0) /* Make sure that we have consumed all bytes. */ - return SECFailure; - - return SECSuccess; -} - /* Go through hello extensions in |b| and deserialize * them into the list in |ss->ssl3.hs.remoteExtensions|. * The only checking we do in this point is for duplicates. @@ -2342,1593 +399,6 @@ ssl3_CallHelloExtensionSenders(sslSocket *ss, PRBool append, PRUint32 maxBytes, return total_exten_len; } -/* Extension format: - * Extension number: 2 bytes - * Extension length: 2 bytes - * Verify Data Length: 1 byte - * Verify Data (TLS): 12 bytes (client) or 24 bytes (server) - * Verify Data (SSL): 36 bytes (client) or 72 bytes (server) - */ -static PRInt32 -ssl3_SendRenegotiationInfoXtn( - sslSocket *ss, - PRBool append, - PRUint32 maxBytes) -{ - PRInt32 len = 0; - PRInt32 needed; - - /* In draft-ietf-tls-renegotiation-03, it is NOT RECOMMENDED to send - * both the SCSV and the empty RI, so when we send SCSV in - * the initial handshake, we don't also send RI. - */ - if (!ss || ss->ssl3.hs.sendingSCSV) - return 0; - if (ss->firstHsDone) { - len = ss->sec.isServer ? ss->ssl3.hs.finishedBytes * 2 - : ss->ssl3.hs.finishedBytes; - } - needed = 5 + len; - if (maxBytes < (PRUint32)needed) { - return 0; - } - if (append) { - SECStatus rv; - /* extension_type */ - rv = ssl3_AppendHandshakeNumber(ss, ssl_renegotiation_info_xtn, 2); - if (rv != SECSuccess) - return -1; - /* length of extension_data */ - rv = ssl3_AppendHandshakeNumber(ss, len + 1, 2); - if (rv != SECSuccess) - return -1; - /* verify_Data from previous Finished message(s) */ - rv = ssl3_AppendHandshakeVariable(ss, - ss->ssl3.hs.finishedMsgs.data, len, 1); - if (rv != SECSuccess) - return -1; - if (!ss->sec.isServer) { - TLSExtensionData *xtnData = &ss->xtnData; - xtnData->advertised[xtnData->numAdvertised++] = - ssl_renegotiation_info_xtn; - } - } - return needed; -} - -/* This function runs in both the client and server. */ -static SECStatus -ssl3_HandleRenegotiationInfoXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data) -{ - SECStatus rv = SECSuccess; - PRUint32 len = 0; - - if (ss->firstHsDone) { - len = ss->sec.isServer ? ss->ssl3.hs.finishedBytes - : ss->ssl3.hs.finishedBytes * 2; - } - if (data->len != 1 + len || data->data[0] != len) { - (void)ssl3_DecodeError(ss); - return SECFailure; - } - if (len && NSS_SecureMemcmp(ss->ssl3.hs.finishedMsgs.data, - data->data + 1, len)) { - (void)SSL3_SendAlert(ss, alert_fatal, handshake_failure); - PORT_SetError(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE); - return SECFailure; - } - /* remember that we got this extension and it was correct. */ - ss->peerRequestedProtection = 1; - ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; - if (ss->sec.isServer) { - /* prepare to send back the appropriate response */ - rv = ssl3_RegisterServerHelloExtensionSender(ss, ex_type, - ssl3_SendRenegotiationInfoXtn); - } - return rv; -} - -static PRInt32 -ssl3_ClientSendUseSRTPXtn(sslSocket *ss, PRBool append, PRUint32 maxBytes) -{ - PRUint32 ext_data_len; - PRInt16 i; - SECStatus rv; - - if (!ss) - return 0; - - if (!IS_DTLS(ss) || !ss->ssl3.dtlsSRTPCipherCount) - return 0; /* Not relevant */ - - ext_data_len = 2 + 2 * ss->ssl3.dtlsSRTPCipherCount + 1; - - if (append && maxBytes >= 4 + ext_data_len) { - /* Extension type */ - rv = ssl3_AppendHandshakeNumber(ss, ssl_use_srtp_xtn, 2); - if (rv != SECSuccess) - return -1; - /* Length of extension data */ - rv = ssl3_AppendHandshakeNumber(ss, ext_data_len, 2); - if (rv != SECSuccess) - return -1; - /* Length of the SRTP cipher list */ - rv = ssl3_AppendHandshakeNumber(ss, - 2 * ss->ssl3.dtlsSRTPCipherCount, - 2); - if (rv != SECSuccess) - return -1; - /* The SRTP ciphers */ - for (i = 0; i < ss->ssl3.dtlsSRTPCipherCount; i++) { - rv = ssl3_AppendHandshakeNumber(ss, - ss->ssl3.dtlsSRTPCiphers[i], - 2); - if (rv != SECSuccess) - return -1; - } - /* Empty MKI value */ - ssl3_AppendHandshakeVariable(ss, NULL, 0, 1); - - ss->xtnData.advertised[ss->xtnData.numAdvertised++] = - ssl_use_srtp_xtn; - } - - return 4 + ext_data_len; -} - -static PRInt32 -ssl3_ServerSendUseSRTPXtn(sslSocket *ss, PRBool append, PRUint32 maxBytes) -{ - SECStatus rv; - - /* Server side */ - if (!append || maxBytes < 9) { - return 9; - } - - /* Extension type */ - rv = ssl3_AppendHandshakeNumber(ss, ssl_use_srtp_xtn, 2); - if (rv != SECSuccess) - return -1; - /* Length of extension data */ - rv = ssl3_AppendHandshakeNumber(ss, 5, 2); - if (rv != SECSuccess) - return -1; - /* Length of the SRTP cipher list */ - rv = ssl3_AppendHandshakeNumber(ss, 2, 2); - if (rv != SECSuccess) - return -1; - /* The selected cipher */ - rv = ssl3_AppendHandshakeNumber(ss, ss->ssl3.dtlsSRTPCipherSuite, 2); - if (rv != SECSuccess) - return -1; - /* Empty MKI value */ - ssl3_AppendHandshakeVariable(ss, NULL, 0, 1); - - return 9; -} - -static SECStatus -ssl3_ClientHandleUseSRTPXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data) -{ - SECStatus rv; - SECItem ciphers = { siBuffer, NULL, 0 }; - PRUint16 i; - PRUint16 cipher = 0; - PRBool found = PR_FALSE; - SECItem litem; - - if (!data->data || !data->len) { - (void)ssl3_DecodeError(ss); - return SECFailure; - } - - /* Get the cipher list */ - rv = ssl3_ConsumeHandshakeVariable(ss, &ciphers, 2, - &data->data, &data->len); - if (rv != SECSuccess) { - return SECFailure; /* fatal alert already sent */ - } - /* Now check that the server has picked just 1 (i.e., len = 2) */ - if (ciphers.len != 2) { - (void)ssl3_DecodeError(ss); - return SECFailure; - } - - /* Get the selected cipher */ - cipher = (ciphers.data[0] << 8) | ciphers.data[1]; - - /* Now check that this is one of the ciphers we offered */ - for (i = 0; i < ss->ssl3.dtlsSRTPCipherCount; i++) { - if (cipher == ss->ssl3.dtlsSRTPCiphers[i]) { - found = PR_TRUE; - break; - } - } - - if (!found) { - (void)SSL3_SendAlert(ss, alert_fatal, illegal_parameter); - PORT_SetError(SSL_ERROR_RX_MALFORMED_SERVER_HELLO); - return SECFailure; - } - - /* Get the srtp_mki value */ - rv = ssl3_ConsumeHandshakeVariable(ss, &litem, 1, - &data->data, &data->len); - if (rv != SECSuccess) { - return SECFailure; /* alert already sent */ - } - - /* We didn't offer an MKI, so this must be 0 length */ - if (litem.len != 0) { - (void)SSL3_SendAlert(ss, alert_fatal, illegal_parameter); - PORT_SetError(SSL_ERROR_RX_MALFORMED_SERVER_HELLO); - return SECFailure; - } - - /* extra trailing bytes */ - if (data->len != 0) { - (void)ssl3_DecodeError(ss); - return SECFailure; - } - - /* OK, this looks fine. */ - ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ssl_use_srtp_xtn; - ss->ssl3.dtlsSRTPCipherSuite = cipher; - return SECSuccess; -} - -static SECStatus -ssl3_ServerHandleUseSRTPXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data) -{ - SECStatus rv; - SECItem ciphers = { siBuffer, NULL, 0 }; - PRUint16 i; - unsigned int j; - PRUint16 cipher = 0; - PRBool found = PR_FALSE; - SECItem litem; - - if (!IS_DTLS(ss) || !ss->ssl3.dtlsSRTPCipherCount) { - /* Ignore the extension if we aren't doing DTLS or no DTLS-SRTP - * preferences have been set. */ - return SECSuccess; - } - - if (!data->data || data->len < 5) { - (void)ssl3_DecodeError(ss); - return SECFailure; - } - - /* Get the cipher list */ - rv = ssl3_ConsumeHandshakeVariable(ss, &ciphers, 2, - &data->data, &data->len); - if (rv != SECSuccess) { - return SECFailure; /* alert already sent */ - } - /* Check that the list is even length */ - if (ciphers.len % 2) { - (void)ssl3_DecodeError(ss); - return SECFailure; - } - - /* Walk through the offered list and pick the most preferred of our - * ciphers, if any */ - for (i = 0; !found && i < ss->ssl3.dtlsSRTPCipherCount; i++) { - for (j = 0; j + 1 < ciphers.len; j += 2) { - cipher = (ciphers.data[j] << 8) | ciphers.data[j + 1]; - if (cipher == ss->ssl3.dtlsSRTPCiphers[i]) { - found = PR_TRUE; - break; - } - } - } - - /* Get the srtp_mki value */ - rv = ssl3_ConsumeHandshakeVariable(ss, &litem, 1, &data->data, &data->len); - if (rv != SECSuccess) { - return SECFailure; - } - - if (data->len != 0) { - (void)ssl3_DecodeError(ss); /* trailing bytes */ - return SECFailure; - } - - /* Now figure out what to do */ - if (!found) { - /* No matching ciphers, pretend we don't support use_srtp */ - return SECSuccess; - } - - /* OK, we have a valid cipher and we've selected it */ - ss->ssl3.dtlsSRTPCipherSuite = cipher; - ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ssl_use_srtp_xtn; - - return ssl3_RegisterServerHelloExtensionSender(ss, ssl_use_srtp_xtn, - ssl3_ServerSendUseSRTPXtn); -} - -/* ssl3_ServerHandleSigAlgsXtn handles the signature_algorithms extension - * from a client. - * See https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 */ -static SECStatus -ssl3_ServerHandleSigAlgsXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data) -{ - SECStatus rv; - - /* Ignore this extension if we aren't doing TLS 1.2 or greater. */ - if (ss->version < SSL_LIBRARY_VERSION_TLS_1_2) { - return SECSuccess; - } - - if (ss->ssl3.hs.clientSigSchemes) { - PORT_Free(ss->ssl3.hs.clientSigSchemes); - ss->ssl3.hs.clientSigSchemes = NULL; - } - rv = ssl_ParseSignatureSchemes(ss, NULL, - &ss->ssl3.hs.clientSigSchemes, - &ss->ssl3.hs.numClientSigScheme, - &data->data, &data->len); - if (rv != SECSuccess) { - PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO); - return SECFailure; - } - /* Check for trailing data. */ - if (data->len != 0) { - (void)SSL3_SendAlert(ss, alert_fatal, decode_error); - PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO); - return SECFailure; - } - - /* Keep track of negotiated extensions. */ - ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; - return SECSuccess; -} - -/* ssl3_ClientSendSigAlgsXtn sends the signature_algorithm extension for TLS - * 1.2 ClientHellos. */ -static PRInt32 -ssl3_ClientSendSigAlgsXtn(sslSocket *ss, PRBool append, PRUint32 maxBytes) -{ - PRInt32 extension_length; - PRUint8 buf[MAX_SIGNATURE_SCHEMES * 2]; - PRUint32 len; - SECStatus rv; - - if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_2) { - return 0; - } - - rv = ssl3_EncodeSigAlgs(ss, buf, sizeof(buf), &len); - if (rv != SECSuccess) { - return -1; - } - - extension_length = - 2 /* extension type */ + - 2 /* extension length */ + - 2 /* supported_signature_algorithms length */ + - len; - - if (maxBytes < extension_length) { - PORT_Assert(0); - return 0; - } - - if (append) { - SECStatus rv; - rv = ssl3_AppendHandshakeNumber(ss, ssl_signature_algorithms_xtn, 2); - if (rv != SECSuccess) { - return -1; - } - rv = ssl3_AppendHandshakeNumber(ss, len + 2, 2); - if (rv != SECSuccess) { - return -1; - } - - rv = ssl3_AppendHandshakeVariable(ss, buf, len, 2); - if (rv != SECSuccess) { - return -1; - } - - ss->xtnData.advertised[ss->xtnData.numAdvertised++] = - ssl_signature_algorithms_xtn; - } - - return extension_length; -} - -/* Takes the size of the ClientHello, less the record header, and determines how - * much padding is required. */ -unsigned int -ssl3_CalculatePaddingExtensionLength(unsigned int clientHelloLength) -{ - unsigned int recordLength = 1 /* handshake message type */ + - 3 /* handshake message length */ + - clientHelloLength; - unsigned int extensionLength; - - if (recordLength < 256 || recordLength >= 512) { - return 0; - } - - extensionLength = 512 - recordLength; - /* Extensions take at least four bytes to encode. Always include at least - * one byte of data if including the extension. Some servers (e.g. - * WebSphere Application Server 7.0 and Tomcat) will time out or terminate - * the connection if the last extension in the client hello is empty. */ - if (extensionLength < 4 + 1) { - extensionLength = 4 + 1; - } - - return extensionLength; -} - -/* ssl3_AppendPaddingExtension possibly adds an extension which ensures that a - * ClientHello record is either < 256 bytes or is >= 512 bytes. This ensures - * that we don't trigger bugs in F5 products. */ -PRInt32 -ssl3_AppendPaddingExtension(sslSocket *ss, unsigned int extensionLen, - PRUint32 maxBytes) -{ - unsigned int paddingLen = extensionLen - 4; - static unsigned char padding[252]; - - if (extensionLen == 0) { - return 0; - } - - if (extensionLen > maxBytes || - !paddingLen || - paddingLen > sizeof(padding)) { - PORT_Assert(0); - return -1; - } - - if (SECSuccess != ssl3_AppendHandshakeNumber(ss, ssl_padding_xtn, 2)) - return -1; - if (SECSuccess != ssl3_AppendHandshakeVariable(ss, padding, paddingLen, 2)) - return -1; - - return extensionLen; -} - -static PRInt32 -ssl3_SendExtendedMasterSecretXtn(sslSocket *ss, PRBool append, - PRUint32 maxBytes) -{ - PRInt32 extension_length; - - if (!ss->opt.enableExtendedMS) { - return 0; - } - - /* Always send the extension in this function, since the - * client always sends it and this function is only called on - * the server if we negotiated the extension. */ - extension_length = 4; /* Type + length (0) */ - if (maxBytes < extension_length) { - PORT_Assert(0); - return 0; - } - - if (append) { - SECStatus rv; - rv = ssl3_AppendHandshakeNumber(ss, ssl_extended_master_secret_xtn, 2); - if (rv != SECSuccess) - goto loser; - rv = ssl3_AppendHandshakeNumber(ss, 0, 2); - if (rv != SECSuccess) - goto loser; - ss->xtnData.advertised[ss->xtnData.numAdvertised++] = - ssl_extended_master_secret_xtn; - } - - return extension_length; - -loser: - return -1; -} - -static SECStatus -ssl3_HandleExtendedMasterSecretXtn(sslSocket *ss, PRUint16 ex_type, - SECItem *data) -{ - if (ss->version < SSL_LIBRARY_VERSION_TLS_1_0) { - return SECSuccess; - } - - if (!ss->opt.enableExtendedMS) { - return SECSuccess; - } - - if (data->len != 0) { - SSL_TRC(30, ("%d: SSL3[%d]: Bogus extended master secret extension", - SSL_GETPID(), ss->fd)); - return SECFailure; - } - - SSL_DBG(("%d: SSL[%d]: Negotiated extended master secret extension.", - SSL_GETPID(), ss->fd)); - - /* Keep track of negotiated extensions. */ - ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; - - if (ss->sec.isServer) { - return ssl3_RegisterServerHelloExtensionSender( - ss, ex_type, ssl3_SendExtendedMasterSecretXtn); - } - return SECSuccess; -} - -/* ssl3_ClientSendSignedCertTimestampXtn sends the signed_certificate_timestamp - * extension for TLS ClientHellos. */ -static PRInt32 -ssl3_ClientSendSignedCertTimestampXtn(sslSocket *ss, PRBool append, - PRUint32 maxBytes) -{ - PRInt32 extension_length = 2 /* extension_type */ + - 2 /* length(extension_data) */; - - /* Only send the extension if processing is enabled. */ - if (!ss->opt.enableSignedCertTimestamps) - return 0; - - if (append && maxBytes >= extension_length) { - SECStatus rv; - /* extension_type */ - rv = ssl3_AppendHandshakeNumber(ss, - ssl_signed_cert_timestamp_xtn, - 2); - if (rv != SECSuccess) - goto loser; - /* zero length */ - rv = ssl3_AppendHandshakeNumber(ss, 0, 2); - if (rv != SECSuccess) - goto loser; - ss->xtnData.advertised[ss->xtnData.numAdvertised++] = - ssl_signed_cert_timestamp_xtn; - } else if (maxBytes < extension_length) { - PORT_Assert(0); - return 0; - } - - return extension_length; -loser: - return -1; -} - -static SECStatus -ssl3_ClientHandleSignedCertTimestampXtn(sslSocket *ss, PRUint16 ex_type, - SECItem *data) -{ - /* We do not yet know whether we'll be resuming a session or creating - * a new one, so we keep a pointer to the data in the TLSExtensionData - * structure. This pointer is only valid in the scope of - * ssl3_HandleServerHello, and, if not resuming a session, the data is - * copied once a new session structure has been set up. - * All parsing is currently left to the application and we accept - * everything, including empty data. - */ - SECItem *scts = &ss->xtnData.signedCertTimestamps; - PORT_Assert(!scts->data && !scts->len); - - if (!data->len) { - /* Empty extension data: RFC 6962 mandates non-empty contents. */ - return SECFailure; - } - *scts = *data; - /* Keep track of negotiated extensions. */ - ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; - return SECSuccess; -} - -static PRInt32 -ssl3_ServerSendSignedCertTimestampXtn(sslSocket *ss, - PRBool append, - PRUint32 maxBytes) -{ - PRInt32 extension_length; - const SECItem *scts = &ss->sec.serverCert->signedCertTimestamps; - - if (!scts->len) { - /* No timestamps to send */ - return 0; - } - - extension_length = 2 /* extension_type */ + - 2 /* length(extension_data) */ + - scts->len; - - if (maxBytes < extension_length) { - PORT_Assert(0); - return 0; - } - if (append) { - SECStatus rv; - /* extension_type */ - rv = ssl3_AppendHandshakeNumber(ss, - ssl_signed_cert_timestamp_xtn, - 2); - if (rv != SECSuccess) - goto loser; - /* extension_data */ - rv = ssl3_AppendHandshakeVariable(ss, scts->data, scts->len, 2); - if (rv != SECSuccess) - goto loser; - } - - return extension_length; - -loser: - return -1; -} - -static SECStatus -ssl3_ServerHandleSignedCertTimestampXtn(sslSocket *ss, PRUint16 ex_type, - SECItem *data) -{ - ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; - PORT_Assert(ss->sec.isServer); - return ssl3_RegisterServerHelloExtensionSender(ss, ex_type, - ssl3_ServerSendSignedCertTimestampXtn); -} - -/* - * [draft-ietf-tls-tls13-11] Section 6.3.2.3. - * - * struct { - * NamedGroup group; - * opaque key_exchange<1..2^16-1>; - * } KeyShareEntry; - * - * struct { - * select (role) { - * case client: - * KeyShareEntry client_shares<4..2^16-1>; - * - * case server: - * KeyShareEntry server_share; - * } - * } KeyShare; - * - * DH is Section 6.3.2.3.1. - * - * opaque dh_Y<1..2^16-1>; - * - * ECDH is Section 6.3.2.3.2. - * - * opaque point <1..2^8-1>; - */ -static PRUint32 -tls13_SizeOfKeyShareEntry(const SECKEYPublicKey *pubKey) -{ - /* Size = NamedGroup(2) + length(2) + opaque share */ - switch (pubKey->keyType) { - case ecKey: - return 2 + 2 + pubKey->u.ec.publicValue.len; - case dhKey: - return 2 + 2 + pubKey->u.dh.prime.len; - default: - PORT_Assert(0); - } - return 0; -} - -static PRUint32 -tls13_SizeOfClientKeyShareExtension(sslSocket *ss) -{ - PRCList *cursor; - /* Size is: extension(2) + extension_len(2) + client_shares(2) */ - PRUint32 size = 2 + 2 + 2; - for (cursor = PR_NEXT_LINK(&ss->ephemeralKeyPairs); - cursor != &ss->ephemeralKeyPairs; - cursor = PR_NEXT_LINK(cursor)) { - sslEphemeralKeyPair *keyPair = (sslEphemeralKeyPair *)cursor; - size += tls13_SizeOfKeyShareEntry(keyPair->keys->pubKey); - } - return size; -} - -static SECStatus -tls13_EncodeKeyShareEntry(sslSocket *ss, const sslEphemeralKeyPair *keyPair) -{ - SECStatus rv; - SECKEYPublicKey *pubKey = keyPair->keys->pubKey; - unsigned int size = tls13_SizeOfKeyShareEntry(pubKey); - - rv = ssl3_AppendHandshakeNumber(ss, keyPair->group->name, 2); - if (rv != SECSuccess) - return rv; - rv = ssl3_AppendHandshakeNumber(ss, size - 4, 2); - if (rv != SECSuccess) - return rv; - - switch (pubKey->keyType) { - case ecKey: - rv = tls13_EncodeECDHEKeyShareKEX(ss, pubKey); - break; - case dhKey: - rv = ssl_AppendPaddedDHKeyShare(ss, pubKey, PR_FALSE); - break; - default: - PORT_Assert(0); - PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); - break; - } - - return rv; -} - -static PRInt32 -tls13_ClientSendKeyShareXtn(sslSocket *ss, PRBool append, - PRUint32 maxBytes) -{ - PRUint32 extension_length; - - if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3) { - return 0; - } - - /* Optimistically try to send an ECDHE key using the - * preexisting key (in future will be keys) */ - SSL_TRC(3, ("%d: TLS13[%d]: send client key share xtn", - SSL_GETPID(), ss->fd)); - - extension_length = tls13_SizeOfClientKeyShareExtension(ss); - if (maxBytes < extension_length) { - PORT_Assert(0); - return 0; - } - - if (append) { - SECStatus rv; - PRCList *cursor; - - rv = ssl3_AppendHandshakeNumber(ss, ssl_tls13_key_share_xtn, 2); - if (rv != SECSuccess) - goto loser; - - /* The extension length */ - rv = ssl3_AppendHandshakeNumber(ss, extension_length - 4, 2); - if (rv != SECSuccess) - goto loser; - - /* The length of KeyShares */ - rv = ssl3_AppendHandshakeNumber(ss, extension_length - 6, 2); - if (rv != SECSuccess) - goto loser; - - for (cursor = PR_NEXT_LINK(&ss->ephemeralKeyPairs); - cursor != &ss->ephemeralKeyPairs; - cursor = PR_NEXT_LINK(cursor)) { - sslEphemeralKeyPair *keyPair = (sslEphemeralKeyPair *)cursor; - rv = tls13_EncodeKeyShareEntry(ss, keyPair); - if (rv != SECSuccess) - goto loser; - } - - ss->xtnData.advertised[ss->xtnData.numAdvertised++] = - ssl_tls13_key_share_xtn; - } - - return extension_length; - -loser: - return -1; -} - -static SECStatus -tls13_HandleKeyShareEntry(sslSocket *ss, SECItem *data) -{ - SECStatus rv; - PRInt32 group; - const sslNamedGroupDef *groupDef; - TLS13KeyShareEntry *ks = NULL; - SECItem share = { siBuffer, NULL, 0 }; - - group = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len); - if (group < 0) { - PORT_SetError(SSL_ERROR_RX_MALFORMED_KEY_SHARE); - goto loser; - } - groupDef = ssl_LookupNamedGroup(group); - rv = ssl3_ConsumeHandshakeVariable(ss, &share, 2, &data->data, - &data->len); - if (rv != SECSuccess) { - goto loser; - } - /* If the group is disabled, continue. */ - if (!groupDef) { - return SECSuccess; - } - - ks = PORT_ZNew(TLS13KeyShareEntry); - if (!ks) - goto loser; - ks->group = groupDef; - - rv = SECITEM_CopyItem(NULL, &ks->key_exchange, &share); - if (rv != SECSuccess) - goto loser; - - PR_APPEND_LINK(&ks->link, &ss->ssl3.hs.remoteKeyShares); - return SECSuccess; - -loser: - if (ks) - tls13_DestroyKeyShareEntry(ks); - return SECFailure; -} - -/* Handle an incoming KeyShare extension at the client and copy to - * |ss->ssl3.hs.remoteKeyShares| for future use. The key - * share is processed in tls13_HandleServerKeyShare(). */ -static SECStatus -tls13_ClientHandleKeyShareXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data) -{ - SECStatus rv; - - PORT_Assert(!ss->sec.isServer); - if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) { - /* This can't happen because the extension processing - * code filters out TLS 1.3 extensions when not in - * TLS 1.3 mode. */ - PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); - return SECFailure; - } - - SSL_TRC(3, ("%d: SSL3[%d]: handle key_share extension", - SSL_GETPID(), ss->fd)); - - rv = tls13_HandleKeyShareEntry(ss, data); - if (rv != SECSuccess) { - PORT_SetError(SSL_ERROR_RX_MALFORMED_KEY_SHARE); - return SECFailure; - } - - if (data->len) { - PORT_SetError(SSL_ERROR_RX_MALFORMED_KEY_SHARE); - return SECFailure; - } - - return SECSuccess; -} - -static SECStatus -tls13_ClientHandleKeyShareXtnHrr(sslSocket *ss, PRUint16 ex_type, SECItem *data) -{ - SECStatus rv; - PRInt32 tmp; - const sslNamedGroupDef *group; - - PORT_Assert(!ss->sec.isServer); - PORT_Assert(ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3); - - SSL_TRC(3, ("%d: SSL3[%d]: handle key_share extension in HRR", - SSL_GETPID(), ss->fd)); - - tmp = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len); - if (tmp < 0) { - return SECFailure; /* error code already set */ - } - if (data->len) { - (void)SSL3_SendAlert(ss, alert_fatal, decode_error); - PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST); - return SECFailure; - } - - group = ssl_LookupNamedGroup((SSLNamedGroup)tmp); - /* If the group is not enabled, or we already have a share for the - * requested group, abort. */ - if (!ssl_NamedGroupEnabled(ss, group) || - ssl_LookupEphemeralKeyPair(ss, group)) { - (void)SSL3_SendAlert(ss, alert_fatal, illegal_parameter); - PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST); - return SECFailure; - } - - rv = tls13_CreateKeyShare(ss, group); - if (rv != SECSuccess) { - (void)SSL3_SendAlert(ss, alert_fatal, internal_error); - PORT_SetError(SEC_ERROR_KEYGEN_FAIL); - return SECFailure; - } - - return SECSuccess; -} - -/* Handle an incoming KeyShare extension at the server and copy to - * |ss->ssl3.hs.remoteKeyShares| for future use. The key - * share is processed in tls13_HandleClientKeyShare(). */ -static SECStatus -tls13_ServerHandleKeyShareXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data) -{ - SECStatus rv; - PRInt32 length; - - PORT_Assert(ss->sec.isServer); - if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) { - return SECSuccess; - } - - SSL_TRC(3, ("%d: SSL3[%d]: handle key_share extension", - SSL_GETPID(), ss->fd)); - - /* Redundant length because of TLS encoding (this vector consumes - * the entire extension.) */ - length = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, - &data->len); - if (length < 0) - goto loser; - if (length != data->len) { - /* Check for consistency */ - PORT_SetError(SSL_ERROR_RX_MALFORMED_KEY_SHARE); - goto loser; - } - - while (data->len) { - rv = tls13_HandleKeyShareEntry(ss, data); - if (rv != SECSuccess) - goto loser; - } - return SECSuccess; - -loser: - tls13_DestroyKeyShares(&ss->ssl3.hs.remoteKeyShares); - return SECFailure; -} - -PRInt32 -tls13_ServerSendKeyShareXtn(sslSocket *ss, PRBool append, - PRUint32 maxBytes) -{ - PRUint32 extension_length; - PRUint32 entry_length; - SECStatus rv; - sslEphemeralKeyPair *keyPair; - - /* There should be exactly one key share. */ - PORT_Assert(!PR_CLIST_IS_EMPTY(&ss->ephemeralKeyPairs)); - PORT_Assert(PR_PREV_LINK(&ss->ephemeralKeyPairs) == - PR_NEXT_LINK(&ss->ephemeralKeyPairs)); - - keyPair = (sslEphemeralKeyPair *)PR_NEXT_LINK(&ss->ephemeralKeyPairs); - - entry_length = tls13_SizeOfKeyShareEntry(keyPair->keys->pubKey); - extension_length = 2 + 2 + entry_length; /* Type + length + entry_length */ - if (maxBytes < extension_length) { - PORT_Assert(0); - return 0; - } - - if (append) { - rv = ssl3_AppendHandshakeNumber(ss, ssl_tls13_key_share_xtn, 2); - if (rv != SECSuccess) - goto loser; - - rv = ssl3_AppendHandshakeNumber(ss, entry_length, 2); - if (rv != SECSuccess) - goto loser; - - rv = tls13_EncodeKeyShareEntry(ss, keyPair); - if (rv != SECSuccess) - goto loser; - } - - return extension_length; - -loser: - return -1; -} - -/* Called by clients. - * - * struct { - * PskKeyExchangeMode ke_modes<1..255>; - * PskAuthMode auth_modes<1..255>; - * opaque identity<0..2^16-1>; - * } PskIdentity; - * - * struct { - * select (Role) { - * case client: - * PskIdentity identities<2..2^16-1>; - * case server: - * uint16 selected_identity; - * } - * } PreSharedKeyExtension; - * - * Presently the only way to get a PSK is by resumption, so this is - * really a ticket label and there wll be at most one. - */ -static PRInt32 -tls13_ClientSendPreSharedKeyXtn(sslSocket *ss, - PRBool append, - PRUint32 maxBytes) -{ - PRInt32 extension_length; - static const PRUint8 auth_modes[] = { tls13_psk_auth }; - static const unsigned long auth_modes_len = sizeof(auth_modes); - static const PRUint8 ke_modes[] = { tls13_psk_dh_ke }; - static const unsigned long ke_modes_len = sizeof(ke_modes); - NewSessionTicket *session_ticket; - - /* We only set statelessResume on the client in TLS 1.3 code. */ - if (!ss->statelessResume) - return 0; - - PORT_Assert(ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3); - - session_ticket = &ss->sec.ci.sid->u.ssl3.locked.sessionTicket; - - extension_length = - 2 + 2 + 2 + /* Type + length + vector length */ - 1 + ke_modes_len + /* key exchange modes vector */ - 1 + auth_modes_len + /* auth modes vector */ - 2 + session_ticket->ticket.len; /* identity length + ticket len */ - - if (maxBytes < (PRUint32)extension_length) { - PORT_Assert(0); - return 0; - } - - if (append) { - SECStatus rv; - /* extension_type */ - rv = ssl3_AppendHandshakeNumber(ss, ssl_tls13_pre_shared_key_xtn, 2); - if (rv != SECSuccess) - goto loser; - rv = ssl3_AppendHandshakeNumber(ss, extension_length - 4, 2); - if (rv != SECSuccess) - goto loser; - rv = ssl3_AppendHandshakeNumber(ss, extension_length - 6, 2); - if (rv != SECSuccess) - goto loser; - rv = ssl3_AppendHandshakeVariable(ss, ke_modes, ke_modes_len, 1); - if (rv != SECSuccess) - goto loser; - rv = ssl3_AppendHandshakeVariable(ss, auth_modes, auth_modes_len, 1); - if (rv != SECSuccess) - goto loser; - rv = ssl3_AppendHandshakeVariable(ss, session_ticket->ticket.data, - session_ticket->ticket.len, 2); - PRINT_BUF(50, (ss, "Sending PreSharedKey value", - session_ticket->ticket.data, - session_ticket->ticket.len)); - ss->xtnData.sentSessionTicketInClientHello = PR_TRUE; - if (rv != SECSuccess) - goto loser; - - ss->xtnData.advertised[ss->xtnData.numAdvertised++] = - ssl_tls13_pre_shared_key_xtn; - } - return extension_length; - -loser: - ss->xtnData.ticketTimestampVerified = PR_FALSE; - return -1; -} - -/* Handle a TLS 1.3 PreSharedKey Extension. We only accept PSKs - * that contain session tickets. */ -static SECStatus -tls13_ServerHandlePreSharedKeyXtn(sslSocket *ss, PRUint16 ex_type, - SECItem *data) -{ - PRInt32 len; - PRBool first = PR_TRUE; - SECStatus rv; - - SSL_TRC(3, ("%d: SSL3[%d]: handle pre_shared_key extension", - SSL_GETPID(), ss->fd)); - - /* If we are doing < TLS 1.3, then ignore this. */ - if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) { - return SECSuccess; - } - - len = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len); - if (len < 0) - return SECFailure; - - if (len != data->len) { - PORT_SetError(SSL_ERROR_MALFORMED_PRE_SHARED_KEY); - return SECFailure; - } - - while (data->len) { - SECItem label; - - /* IMPORTANT: We aren't copying these values, just setting pointers. - * They will only be valid as long as the ClientHello is in memory. */ - rv = ssl3_ConsumeHandshakeVariable(ss, &ss->xtnData.psk_ke_modes, 1, - &data->data, &data->len); - if (rv != SECSuccess) - return rv; - if (!ss->xtnData.psk_ke_modes.len) { - goto alert_loser; - } - rv = ssl3_ConsumeHandshakeVariable(ss, &ss->xtnData.psk_auth_modes, 1, - &data->data, &data->len); - if (rv != SECSuccess) - return rv; - if (!ss->xtnData.psk_auth_modes.len) { - goto alert_loser; - } - rv = ssl3_ConsumeHandshakeVariable(ss, &label, 2, - &data->data, &data->len); - if (rv != SECSuccess) - return rv; - if (!label.len) { - goto alert_loser; - } - if (first) { - first = PR_FALSE; /* Continue to read through the extension to check - * the format. */ - - PRINT_BUF(50, (ss, "Handling PreSharedKey value", - label.data, label.len)); - rv = ssl3_ProcessSessionTicketCommon(ss, &label); - /* This only happens if we have an internal error, not - * a malformed ticket. Bogus tickets just don't resume - * and return SECSuccess. */ - if (rv != SECSuccess) - return rv; - } - } - - /* Keep track of negotiated extensions. Note that this does not - * mean we are resuming. */ - ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; - - return SECSuccess; - -alert_loser: - (void)SSL3_SendAlert(ss, alert_fatal, illegal_parameter); - PORT_SetError(SSL_ERROR_MALFORMED_PRE_SHARED_KEY); - return SECFailure; -} - -PRInt32 -tls13_ServerSendPreSharedKeyXtn(sslSocket *ss, - PRBool append, - PRUint32 maxBytes) -{ - PRInt32 extension_length = - 2 + 2 + 2; /* type + len + index */ - SECStatus rv; - - if (maxBytes < (PRUint32)extension_length) { - PORT_Assert(0); - return 0; - } - - if (append) { - rv = ssl3_AppendHandshakeNumber(ss, ssl_tls13_pre_shared_key_xtn, 2); - if (rv != SECSuccess) - return -1; - - rv = ssl3_AppendHandshakeNumber(ss, 2, 2); - if (rv != SECSuccess) - return -1; - - /* We only process the first session ticket the client sends, - * so the index is always 0. */ - rv = ssl3_AppendHandshakeNumber(ss, 0, 2); - if (rv != SECSuccess) - return -1; - } - - return extension_length; -} - -/* Handle a TLS 1.3 PreSharedKey Extension. We only accept PSKs - * that contain session tickets. */ -static SECStatus -tls13_ClientHandlePreSharedKeyXtn(sslSocket *ss, PRUint16 ex_type, - SECItem *data) -{ - PRInt32 index; - - SSL_TRC(3, ("%d: SSL3[%d]: handle pre_shared_key extension", - SSL_GETPID(), ss->fd)); - - /* If we are doing < TLS 1.3, then ignore this. */ - if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) { - return SECSuccess; - } - - index = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len); - if (index < 0) - return SECFailure; - - /* This should be the end of the extension. */ - if (data->len) { - PORT_SetError(SSL_ERROR_MALFORMED_PRE_SHARED_KEY); - return SECFailure; - } - - /* We only sent one PSK label so index must be equal to 0 */ - if (index) { - PORT_SetError(SSL_ERROR_MALFORMED_PRE_SHARED_KEY); - return SECFailure; - } - - /* Keep track of negotiated extensions. */ - ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; - - return SECSuccess; -} - -/* - * struct { - * select (Role) { - * case client: - * uint32 obfuscated_ticket_age; - * - * case server: - * struct {}; - * } - * } EarlyDataIndication; - */ -static PRInt32 -tls13_ClientSendEarlyDataXtn(sslSocket *ss, - PRBool append, - PRUint32 maxBytes) -{ - PRInt32 extension_length; - SECStatus rv; - NewSessionTicket *session_ticket; - - if (!tls13_ClientAllow0Rtt(ss, ss->sec.ci.sid)) - return 0; - - /* type + length + obfuscated ticket age. */ - extension_length = 2 + 2 + 4; - - if (maxBytes < (PRUint32)extension_length) { - PORT_Assert(0); - return 0; - } - - session_ticket = &ss->sec.ci.sid->u.ssl3.locked.sessionTicket; - if (append) { - PRUint32 age; - - rv = ssl3_AppendHandshakeNumber(ss, ssl_tls13_early_data_xtn, 2); - if (rv != SECSuccess) - return -1; - - rv = ssl3_AppendHandshakeNumber(ss, extension_length - 4, 2); - if (rv != SECSuccess) - return -1; - - /* Obfuscated age. */ - age = ssl_Time() - session_ticket->received_timestamp; - age += session_ticket->ticket_age_add; - - rv = ssl3_AppendHandshakeNumber(ss, age, 4); - if (rv != SECSuccess) - return -1; - } - - ss->ssl3.hs.zeroRttState = ssl_0rtt_sent; - ss->xtnData.advertised[ss->xtnData.numAdvertised++] = - ssl_tls13_early_data_xtn; - - return extension_length; -} - -static SECStatus -tls13_ServerHandleEarlyDataXtn(sslSocket *ss, PRUint16 ex_type, - SECItem *data) -{ - PRUint32 obfuscated_ticket_age; - SECStatus rv; - - SSL_TRC(3, ("%d: TLS13[%d]: handle early_data extension", - SSL_GETPID(), ss->fd)); - - /* If we are doing < TLS 1.3, then ignore this. */ - if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) { - return SECSuccess; - } - - /* Obfuscated ticket age. Ignore. Bug 1295163. */ - rv = ssl3_ConsumeHandshake(ss, &obfuscated_ticket_age, 4, - &data->data, &data->len); - if (rv != SECSuccess) { - return SECFailure; - } - - if (data->len) { - PORT_SetError(SSL_ERROR_MALFORMED_EARLY_DATA); - return SECFailure; - } - - if (IS_DTLS(ss)) { - /* Save the null spec, which we should be currently reading. We will - * use this when 0-RTT sending is over. */ - ssl_GetSpecReadLock(ss); - ss->ssl3.hs.nullSpec = ss->ssl3.crSpec; - tls13_CipherSpecAddRef(ss->ssl3.hs.nullSpec); - PORT_Assert(ss->ssl3.hs.nullSpec->cipher_def->cipher == cipher_null); - ssl_ReleaseSpecReadLock(ss); - } - - ss->ssl3.hs.zeroRttState = ssl_0rtt_sent; - - ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; - - return SECSuccess; -} - -/* This is only registered if we are sending it. */ -SECStatus -tls13_ServerSendEarlyDataXtn(sslSocket *ss, - PRBool append, - PRUint32 maxBytes) -{ - SSL_TRC(3, ("%d: TLS13[%d]: send early_data extension", - SSL_GETPID(), ss->fd)); - - PORT_Assert(ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted); - if (maxBytes < 4) { - PORT_Assert(0); - return 0; - } - - if (append) { - SECStatus rv; - - rv = ssl3_AppendHandshakeNumber(ss, ssl_tls13_early_data_xtn, 2); - if (rv != SECSuccess) - return -1; - - rv = ssl3_AppendHandshakeNumber(ss, 0, 2); - if (rv != SECSuccess) - return -1; - } - - return 4; -} - -/* This will only be called if we also offered the extension. */ -static SECStatus -tls13_ClientHandleEarlyDataXtn(sslSocket *ss, PRUint16 ex_type, - SECItem *data) -{ - SSL_TRC(3, ("%d: TLS13[%d]: handle early_data extension", - SSL_GETPID(), ss->fd)); - - /* If we are doing < TLS 1.3, then ignore this. */ - if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) { - PORT_SetError(SSL_ERROR_EXTENSION_DISALLOWED_FOR_VERSION); - return SECFailure; - } - - if (data->len) { - PORT_SetError(SSL_ERROR_MALFORMED_EARLY_DATA); - return SECFailure; - } - - /* Keep track of negotiated extensions. */ - ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; - ss->ssl3.hs.zeroRttState = ssl_0rtt_accepted; - - return SECSuccess; -} - -static SECStatus -tls13_ClientHandleTicketEarlyDataInfoXtn(sslSocket *ss, PRUint16 ex_type, - SECItem *data) -{ - PRUint32 utmp; - SECStatus rv; - - SSL_TRC(3, ("%d: TLS13[%d]: handle early_data_info extension", - SSL_GETPID(), ss->fd)); - - /* If we are doing < TLS 1.3, then ignore this. */ - if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) { - PORT_SetError(SSL_ERROR_EXTENSION_DISALLOWED_FOR_VERSION); - return SECFailure; - } - - rv = ssl3_ConsumeHandshake(ss, &utmp, sizeof(utmp), - &data->data, &data->len); - if (rv != SECSuccess) { - PORT_SetError(SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET); - return SECFailure; - } - if (data->len) { - PORT_SetError(SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET); - return SECFailure; - } - - ss->xtnData.ticket_age_add_found = PR_TRUE; - ss->xtnData.ticket_age_add = PR_ntohl(utmp); - - return SECSuccess; -} - -/* This is only registered if we are sending it. */ -SECStatus -tls13_ServerSendSigAlgsXtn(sslSocket *ss, - PRBool append, - PRUint32 maxBytes) -{ - SSL_TRC(3, ("%d: TLS13[%d]: send signature_algorithms extension", - SSL_GETPID(), ss->fd)); - - if (maxBytes < 4) { - PORT_Assert(0); - } - - if (append) { - SECStatus rv; - - rv = ssl3_AppendHandshakeNumber(ss, ssl_signature_algorithms_xtn, 2); - if (rv != SECSuccess) - return -1; - - rv = ssl3_AppendHandshakeNumber(ss, 0, 2); - if (rv != SECSuccess) - return -1; - } - - return 4; -} - -/* This will only be called if we also offered the extension. */ -static SECStatus -tls13_ClientHandleSigAlgsXtn(sslSocket *ss, PRUint16 ex_type, - SECItem *data) -{ - SSL_TRC(3, ("%d: TLS13[%d]: handle signature_algorithms extension", - SSL_GETPID(), ss->fd)); - - /* If we are doing < TLS 1.3, then ignore this. */ - if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) { - PORT_SetError(SSL_ERROR_EXTENSION_DISALLOWED_FOR_VERSION); - return SECFailure; - } - - if (data->len != 0) { - PORT_SetError(SSL_ERROR_RX_MALFORMED_SERVER_HELLO); - return SECFailure; - } - - /* Keep track of negotiated extensions. */ - ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; - - return SECSuccess; -} - -/* - * struct { - * ProtocolVersion versions<2..254>; - * } SupportedVersions; - */ -static PRInt32 -tls13_ClientSendSupportedVersionsXtn(sslSocket *ss, PRBool append, - PRUint32 maxBytes) -{ - PRInt32 extensions_len; - PRUint16 version; - SECStatus rv; - - if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3) { - return 0; - } - - SSL_TRC(3, ("%d: TLS13[%d]: send supported_versions extension", - SSL_GETPID(), ss->fd)); - - /* Extension type, extension len fiels, vector len field, - * length of the values. */ - extensions_len = 2 + 2 + 1 + - 2 * (ss->vrange.max - ss->vrange.min + 1); - - if (maxBytes < (PRUint32)extensions_len) { - PORT_Assert(0); - return 0; - } - - if (append) { - rv = ssl3_AppendHandshakeNumber(ss, ssl_tls13_supported_versions_xtn, 2); - if (rv != SECSuccess) - return -1; - - rv = ssl3_AppendHandshakeNumber(ss, extensions_len - 4, 2); - if (rv != SECSuccess) - return -1; - - rv = ssl3_AppendHandshakeNumber(ss, extensions_len - 5, 1); - if (rv != SECSuccess) - return -1; - - for (version = ss->vrange.max; version >= ss->vrange.min; --version) { - rv = ssl3_AppendHandshakeNumber( - ss, tls13_EncodeDraftVersion(version), 2); - if (rv != SECSuccess) - return -1; - } - } - - return extensions_len; -} - -/* - * struct { - * opaque cookie<1..2^16-1>; - * } Cookie; - */ -SECStatus -tls13_ClientHandleHrrCookie(sslSocket *ss, PRUint16 ex_type, SECItem *data) -{ - SECStatus rv; - - SSL_TRC(3, ("%d: TLS13[%d]: handle cookie extension", - SSL_GETPID(), ss->fd)); - - PORT_Assert(ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3); - - /* IMPORTANT: this is only valid while the HelloRetryRequest is still valid. */ - rv = ssl3_ConsumeHandshakeVariable(ss, &ss->ssl3.hs.cookie, 2, - &data->data, &data->len); - if (rv != SECSuccess) { - PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST); - return SECFailure; - } - if (!ss->ssl3.hs.cookie.len || data->len) { - (void)SSL3_SendAlert(ss, alert_fatal, decode_error); - PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST); - return SECFailure; - } - - return SECSuccess; -} - -PRInt32 -tls13_ClientSendHrrCookieXtn(sslSocket *ss, PRBool append, PRUint32 maxBytes) -{ - PRInt32 extension_len; - - if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3 || - !ss->ssl3.hs.cookie.len) { - return 0; - } - - SSL_TRC(3, ("%d: TLS13[%d]: send cookie extension", SSL_GETPID(), ss->fd)); - - /* Extension type, length, cookie length, cookie value. */ - extension_len = 2 + 2 + 2 + ss->ssl3.hs.cookie.len; - - if (maxBytes < (PRUint32)extension_len) { - PORT_Assert(0); - return 0; - } - - if (append) { - SECStatus rv = ssl3_AppendHandshakeNumber(ss, ssl_tls13_cookie_xtn, 2); - if (rv != SECSuccess) - return -1; - - rv = ssl3_AppendHandshakeNumber(ss, extension_len - 4, 2); - if (rv != SECSuccess) - return -1; - - rv = ssl3_AppendHandshakeVariable(ss, ss->ssl3.hs.cookie.data, - ss->ssl3.hs.cookie.len, 2); - if (rv != SECSuccess) - return -1; - } - return extension_len; -} - void ssl3_DestroyRemoteExtensions(PRCList *list) { diff --git a/security/nss/lib/ssl/ssl3exthandle.c b/security/nss/lib/ssl/ssl3exthandle.c new file mode 100644 index 000000000000..f1891b9121ff --- /dev/null +++ b/security/nss/lib/ssl/ssl3exthandle.c @@ -0,0 +1,2447 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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 "nssrenam.h" +#include "nss.h" +#include "ssl.h" +#include "sslproto.h" +#include "sslimpl.h" +#include "pk11pub.h" +#include "blapit.h" +#include "prinit.h" +#include "ssl3exthandle.h" +#include "tls13exthandle.h" /* For tls13_ServerSendStatusRequestXtn. */ + +static unsigned char key_name[SESS_TICKET_KEY_NAME_LEN]; +static PK11SymKey *session_ticket_enc_key = NULL; +static PK11SymKey *session_ticket_mac_key = NULL; + +static PRCallOnceType generate_session_keys_once; + +static SECStatus ssl3_ParseEncryptedSessionTicket(sslSocket *ss, + SECItem *data, EncryptedSessionTicket *enc_session_ticket); +static SECStatus ssl3_AppendToItem(SECItem *item, const unsigned char *buf, + PRUint32 bytes); +static SECStatus ssl3_ConsumeFromItem(SECItem *item, unsigned char **buf, PRUint32 bytes); +static SECStatus ssl3_AppendNumberToItem(SECItem *item, PRUint32 num, + PRInt32 lenSize); +static SECStatus ssl3_GetSessionTicketKeys(sslSocket *ss, + PK11SymKey **aes_key, PK11SymKey **mac_key); + +/* + * Write bytes. Using this function means the SECItem structure + * cannot be freed. The caller is expected to call this function + * on a shallow copy of the structure. + */ +static SECStatus +ssl3_AppendToItem(SECItem *item, const unsigned char *buf, PRUint32 bytes) +{ + if (bytes > item->len) + return SECFailure; + + PORT_Memcpy(item->data, buf, bytes); + item->data += bytes; + item->len -= bytes; + return SECSuccess; +} + +/* + * Write a number in network byte order. Using this function means the + * SECItem structure cannot be freed. The caller is expected to call + * this function on a shallow copy of the structure. + */ +static SECStatus +ssl3_AppendNumberToItem(SECItem *item, PRUint32 num, PRInt32 lenSize) +{ + SECStatus rv; + PRUint8 b[4]; + PRUint8 *p = b; + + switch (lenSize) { + case 4: + *p++ = (PRUint8)(num >> 24); + case 3: + *p++ = (PRUint8)(num >> 16); + case 2: + *p++ = (PRUint8)(num >> 8); + case 1: + *p = (PRUint8)num; + } + rv = ssl3_AppendToItem(item, &b[0], lenSize); + return rv; +} + +SECStatus +ssl3_SessionTicketShutdown(void *appData, void *nssData) +{ + if (session_ticket_enc_key) { + PK11_FreeSymKey(session_ticket_enc_key); + session_ticket_enc_key = NULL; + } + if (session_ticket_mac_key) { + PK11_FreeSymKey(session_ticket_mac_key); + session_ticket_mac_key = NULL; + } + PORT_Memset(&generate_session_keys_once, 0, + sizeof(generate_session_keys_once)); + return SECSuccess; +} + +static PRStatus +ssl3_GenerateSessionTicketKeys(void *data) +{ + SECStatus rv; + sslSocket *ss = (sslSocket *)data; + sslServerCertType certType = { ssl_auth_rsa_decrypt, NULL }; + const sslServerCert *sc; + SECKEYPrivateKey *svrPrivKey; + SECKEYPublicKey *svrPubKey; + + sc = ssl_FindServerCert(ss, &certType); + if (!sc || !sc->serverKeyPair) { + SSL_DBG(("%d: SSL[%d]: No ssl_auth_rsa_decrypt cert and key pair", + SSL_GETPID(), ss->fd)); + goto loser; + } + svrPrivKey = sc->serverKeyPair->privKey; + svrPubKey = sc->serverKeyPair->pubKey; + if (svrPrivKey == NULL || svrPubKey == NULL) { + SSL_DBG(("%d: SSL[%d]: Pub or priv key(s) is NULL.", + SSL_GETPID(), ss->fd)); + goto loser; + } + + /* Get a copy of the session keys from shared memory. */ + PORT_Memcpy(key_name, SESS_TICKET_KEY_NAME_PREFIX, + sizeof(SESS_TICKET_KEY_NAME_PREFIX)); + if (!ssl_GetSessionTicketKeys(svrPrivKey, svrPubKey, ss->pkcs11PinArg, + &key_name[SESS_TICKET_KEY_NAME_PREFIX_LEN], + &session_ticket_enc_key, &session_ticket_mac_key)) + return PR_FAILURE; + + rv = NSS_RegisterShutdown(ssl3_SessionTicketShutdown, NULL); + if (rv != SECSuccess) + goto loser; + + return PR_SUCCESS; + +loser: + ssl3_SessionTicketShutdown(NULL, NULL); + return PR_FAILURE; +} + +static SECStatus +ssl3_GetSessionTicketKeys(sslSocket *ss, PK11SymKey **aes_key, + PK11SymKey **mac_key) +{ + if (PR_CallOnceWithArg(&generate_session_keys_once, + ssl3_GenerateSessionTicketKeys, ss) != + PR_SUCCESS) + return SECFailure; + + if (session_ticket_enc_key == NULL || + session_ticket_mac_key == NULL) + return SECFailure; + + *aes_key = session_ticket_enc_key; + *mac_key = session_ticket_mac_key; + return SECSuccess; +} +/* Format an SNI extension, using the name from the socket's URL, + * unless that name is a dotted decimal string. + * Used by client and server. + */ +PRInt32 +ssl3_SendServerNameXtn(sslSocket *ss, PRBool append, + PRUint32 maxBytes) +{ + SECStatus rv; + if (!ss) + return 0; + if (!ss->sec.isServer) { + PRUint32 len; + PRNetAddr netAddr; + + /* must have a hostname */ + if (!ss->url || !ss->url[0]) + return 0; + /* must not be an IPv4 or IPv6 address */ + if (PR_SUCCESS == PR_StringToNetAddr(ss->url, &netAddr)) { + /* is an IP address (v4 or v6) */ + return 0; + } + len = PORT_Strlen(ss->url); + if (append && maxBytes >= len + 9) { + /* extension_type */ + rv = ssl3_AppendHandshakeNumber(ss, ssl_server_name_xtn, 2); + if (rv != SECSuccess) + return -1; + /* length of extension_data */ + rv = ssl3_AppendHandshakeNumber(ss, len + 5, 2); + if (rv != SECSuccess) + return -1; + /* length of server_name_list */ + rv = ssl3_AppendHandshakeNumber(ss, len + 3, 2); + if (rv != SECSuccess) + return -1; + /* Name Type (sni_host_name) */ + rv = ssl3_AppendHandshake(ss, "\0", 1); + if (rv != SECSuccess) + return -1; + /* HostName (length and value) */ + rv = ssl3_AppendHandshakeVariable(ss, (PRUint8 *)ss->url, len, 2); + if (rv != SECSuccess) + return -1; + if (!ss->sec.isServer) { + TLSExtensionData *xtnData = &ss->xtnData; + xtnData->advertised[xtnData->numAdvertised++] = + ssl_server_name_xtn; + } + } + return len + 9; + } + /* Server side */ + if (append && maxBytes >= 4) { + rv = ssl3_AppendHandshakeNumber(ss, ssl_server_name_xtn, 2); + if (rv != SECSuccess) + return -1; + /* length of extension_data */ + rv = ssl3_AppendHandshakeNumber(ss, 0, 2); + if (rv != SECSuccess) + return -1; + } + return 4; +} + +/* Handle an incoming SNI extension. */ +SECStatus +ssl3_HandleServerNameXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data) +{ + SECItem *names = NULL; + TLSExtensionData *xtnData = &ss->xtnData; + PRInt32 listLenBytes = 0; + + if (!ss->sec.isServer) { + return SECSuccess; /* ignore extension */ + } + + /* Server side - consume client data and register server sender. */ + /* do not parse the data if don't have user extension handling function. */ + if (!ss->sniSocketConfig) { + return SECSuccess; + } + + /* length of server_name_list */ + listLenBytes = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len); + if (listLenBytes < 0) { + goto loser; /* alert already sent */ + } + if (listLenBytes == 0 || listLenBytes != data->len) { + goto alert_loser; + } + + /* Read ServerNameList. */ + while (data->len > 0) { + SECItem tmp; + SECStatus rv; + PRInt32 type; + + /* Read Name Type. */ + type = ssl3_ConsumeHandshakeNumber(ss, 1, &data->data, &data->len); + if (type < 0) { /* i.e., SECFailure cast to PRint32 */ + /* alert sent in ConsumeHandshakeNumber */ + goto loser; + } + + /* Read ServerName (length and value). */ + rv = ssl3_ConsumeHandshakeVariable(ss, &tmp, 2, &data->data, &data->len); + if (rv != SECSuccess) { + goto loser; + } + + /* Record the value for host_name(0). */ + if (type == sni_nametype_hostname) { + /* Fail if we encounter a second host_name entry. */ + if (names) { + goto alert_loser; + } + + /* Create an array for the only supported NameType. */ + names = PORT_ZNewArray(SECItem, 1); + if (!names) { + goto loser; + } + + /* Copy ServerName into the array. */ + if (SECITEM_CopyItem(NULL, &names[0], &tmp) != SECSuccess) { + goto loser; + } + } + + /* Even if we don't support NameTypes other than host_name at the + * moment, we continue parsing the whole list to check its validity. + * We do not check for duplicate entries with NameType != host_name(0). + */ + } + if (names) { + /* Free old and set the new data. */ + ssl3_FreeSniNameArray(xtnData); + xtnData->sniNameArr = names; + xtnData->sniNameArrSize = 1; + xtnData->negotiated[xtnData->numNegotiated++] = ssl_server_name_xtn; + } + return SECSuccess; + +alert_loser: + (void)ssl3_DecodeError(ss); +loser: + if (names) { + PORT_Free(names); + } + return SECFailure; +} + +/* Frees a given xtnData->sniNameArr and its elements. */ +void +ssl3_FreeSniNameArray(TLSExtensionData *xtnData) +{ + PRUint32 i; + + if (!xtnData->sniNameArr) { + return; + } + + for (i = 0; i < xtnData->sniNameArrSize; i++) { + SECITEM_FreeItem(&xtnData->sniNameArr[i], PR_FALSE); + } + + PORT_Free(xtnData->sniNameArr); + xtnData->sniNameArr = NULL; + xtnData->sniNameArrSize = 0; +} + +/* Called by both clients and servers. + * Clients sends a filled in session ticket if one is available, and otherwise + * sends an empty ticket. Servers always send empty tickets. + */ +PRInt32 +ssl3_SendSessionTicketXtn( + sslSocket *ss, + PRBool append, + PRUint32 maxBytes) +{ + PRInt32 extension_length; + NewSessionTicket *session_ticket = NULL; + sslSessionID *sid = ss->sec.ci.sid; + + /* Never send an extension with a ticket for TLS 1.3, but + * OK to send the empty one in case the server does 1.2. */ + if (sid->cached == in_client_cache && + sid->version >= SSL_LIBRARY_VERSION_TLS_1_3) { + return 0; + } + + /* Ignore the SessionTicket extension if processing is disabled. */ + if (!ss->opt.enableSessionTickets) + return 0; + + /* Empty extension length = extension_type (2-bytes) + + * length(extension_data) (2-bytes) + */ + extension_length = 4; + + /* If we are a client then send a session ticket if one is availble. + * Servers that support the extension and are willing to negotiate the + * the extension always respond with an empty extension. + */ + if (!ss->sec.isServer) { + /* The caller must be holding sid->u.ssl3.lock for reading. We cannot + * just acquire and release the lock within this function because the + * caller will call this function twice, and we need the inputs to be + * consistent between the two calls. Note that currently the caller + * will only be holding the lock when we are the client and when we're + * attempting to resume an existing session. + */ + + session_ticket = &sid->u.ssl3.locked.sessionTicket; + if (session_ticket->ticket.data) { + if (ss->xtnData.ticketTimestampVerified) { + extension_length += session_ticket->ticket.len; + } else if (!append && + (session_ticket->ticket_lifetime_hint == 0 || + (session_ticket->ticket_lifetime_hint + + session_ticket->received_timestamp > + ssl_Time()))) { + extension_length += session_ticket->ticket.len; + ss->xtnData.ticketTimestampVerified = PR_TRUE; + } + } + } + + if (maxBytes < (PRUint32)extension_length) { + PORT_Assert(0); + return 0; + } + if (append) { + SECStatus rv; + /* extension_type */ + rv = ssl3_AppendHandshakeNumber(ss, ssl_session_ticket_xtn, 2); + if (rv != SECSuccess) + goto loser; + if (session_ticket && session_ticket->ticket.data && + ss->xtnData.ticketTimestampVerified) { + rv = ssl3_AppendHandshakeVariable(ss, session_ticket->ticket.data, + session_ticket->ticket.len, 2); + ss->xtnData.ticketTimestampVerified = PR_FALSE; + ss->xtnData.sentSessionTicketInClientHello = PR_TRUE; + } else { + rv = ssl3_AppendHandshakeNumber(ss, 0, 2); + } + if (rv != SECSuccess) + goto loser; + + if (!ss->sec.isServer) { + TLSExtensionData *xtnData = &ss->xtnData; + xtnData->advertised[xtnData->numAdvertised++] = + ssl_session_ticket_xtn; + } + } + return extension_length; + +loser: + ss->xtnData.ticketTimestampVerified = PR_FALSE; + return -1; +} + +static SECStatus +ssl3_ParseEncryptedSessionTicket(sslSocket *ss, SECItem *data, + EncryptedSessionTicket *enc_session_ticket) +{ + if (ssl3_ConsumeFromItem(data, &enc_session_ticket->key_name, + SESS_TICKET_KEY_NAME_LEN) != + SECSuccess) + return SECFailure; + if (ssl3_ConsumeFromItem(data, &enc_session_ticket->iv, + AES_BLOCK_SIZE) != + SECSuccess) + return SECFailure; + if (ssl3_ConsumeHandshakeVariable(ss, &enc_session_ticket->encrypted_state, + 2, &data->data, &data->len) != + SECSuccess) + return SECFailure; + if (ssl3_ConsumeFromItem(data, &enc_session_ticket->mac, + TLS_EX_SESS_TICKET_MAC_LENGTH) != + SECSuccess) + return SECFailure; + if (data->len != 0) /* Make sure that we have consumed all bytes. */ + return SECFailure; + + return SECSuccess; +} + +/* handle an incoming Next Protocol Negotiation extension. */ +SECStatus +ssl3_ServerHandleNextProtoNegoXtn(sslSocket *ss, PRUint16 ex_type, + SECItem *data) +{ + if (ss->firstHsDone || data->len != 0) { + /* Clients MUST send an empty NPN extension, if any. */ + PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID); + return SECFailure; + } + + ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; + + /* TODO: server side NPN support would require calling + * ssl3_RegisterServerHelloExtensionSender here in order to echo the + * extension back to the client. */ + + return SECSuccess; +} + +/* ssl3_ValidateNextProtoNego checks that the given block of data is valid: none + * of the lengths may be 0 and the sum of the lengths must equal the length of + * the block. */ +SECStatus +ssl3_ValidateNextProtoNego(const unsigned char *data, unsigned int length) +{ + unsigned int offset = 0; + + while (offset < length) { + unsigned int newOffset = offset + 1 + (unsigned int)data[offset]; + /* Reject embedded nulls to protect against buggy applications that + * store protocol identifiers in null-terminated strings. + */ + if (newOffset > length || data[offset] == 0) { + return SECFailure; + } + offset = newOffset; + } + + return SECSuccess; +} + +/* protocol selection handler for ALPN (server side) and NPN (client side) */ +static SECStatus +ssl3_SelectAppProtocol(sslSocket *ss, PRUint16 ex_type, SECItem *data) +{ + SECStatus rv; + unsigned char resultBuffer[255]; + SECItem result = { siBuffer, resultBuffer, 0 }; + + rv = ssl3_ValidateNextProtoNego(data->data, data->len); + if (rv != SECSuccess) { + (void)SSL3_SendAlert(ss, alert_fatal, decode_error); + PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID); + return rv; + } + + PORT_Assert(ss->nextProtoCallback); + /* For ALPN, the cipher suite isn't selected yet. Note that extensions + * sometimes affect what cipher suite is selected, e.g., for ECC. */ + PORT_Assert((ss->ssl3.hs.preliminaryInfo & + ssl_preinfo_all & ~ssl_preinfo_cipher_suite) == + (ssl_preinfo_all & ~ssl_preinfo_cipher_suite)); + rv = ss->nextProtoCallback(ss->nextProtoArg, ss->fd, data->data, data->len, + result.data, &result.len, sizeof(resultBuffer)); + if (rv != SECSuccess) { + /* Expect callback to call PORT_SetError() */ + (void)SSL3_SendAlert(ss, alert_fatal, internal_error); + return SECFailure; + } + + /* If the callback wrote more than allowed to |result| it has corrupted our + * stack. */ + if (result.len > sizeof(resultBuffer)) { + PORT_SetError(SEC_ERROR_OUTPUT_LEN); + /* TODO: crash */ + return SECFailure; + } + + SECITEM_FreeItem(&ss->ssl3.nextProto, PR_FALSE); + + if (ex_type == ssl_app_layer_protocol_xtn && + ss->ssl3.nextProtoState != SSL_NEXT_PROTO_NEGOTIATED) { + /* The callback might say OK, but then it picks a default value - one + * that was not listed. That's OK for NPN, but not ALPN. */ + (void)SSL3_SendAlert(ss, alert_fatal, no_application_protocol); + PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_NO_PROTOCOL); + return SECFailure; + } + + ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; + return SECITEM_CopyItem(NULL, &ss->ssl3.nextProto, &result); +} + +/* handle an incoming ALPN extension at the server */ +SECStatus +ssl3_ServerHandleAppProtoXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data) +{ + int count; + SECStatus rv; + + /* We expressly don't want to allow ALPN on renegotiation, + * despite it being permitted by the spec. */ + if (ss->firstHsDone || data->len == 0) { + /* Clients MUST send a non-empty ALPN extension. */ + (void)SSL3_SendAlert(ss, alert_fatal, illegal_parameter); + PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID); + return SECFailure; + } + + /* Unlike NPN, ALPN has extra redundant length information so that + * the extension is the same in both ClientHello and ServerHello. */ + count = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len); + if (count != data->len) { + (void)ssl3_DecodeError(ss); + return SECFailure; + } + + if (!ss->nextProtoCallback) { + /* we're not configured for it */ + return SECSuccess; + } + + rv = ssl3_SelectAppProtocol(ss, ex_type, data); + if (rv != SECSuccess) { + return rv; + } + + /* prepare to send back a response, if we negotiated */ + if (ss->ssl3.nextProtoState == SSL_NEXT_PROTO_NEGOTIATED) { + rv = ssl3_RegisterServerHelloExtensionSender( + ss, ex_type, ssl3_ServerSendAppProtoXtn); + if (rv != SECSuccess) { + (void)SSL3_SendAlert(ss, alert_fatal, internal_error); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return rv; + } + } + return SECSuccess; +} + +SECStatus +ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, PRUint16 ex_type, + SECItem *data) +{ + PORT_Assert(!ss->firstHsDone); + + if (ssl3_ExtensionNegotiated(ss, ssl_app_layer_protocol_xtn)) { + /* If the server negotiated ALPN then it has already told us what + * protocol to use, so it doesn't make sense for us to try to negotiate + * a different one by sending the NPN handshake message. However, if + * we've negotiated NPN then we're required to send the NPN handshake + * message. Thus, these two extensions cannot both be negotiated on the + * same connection. */ + (void)SSL3_SendAlert(ss, alert_fatal, illegal_parameter); + PORT_SetError(SSL_ERROR_BAD_SERVER); + return SECFailure; + } + + /* We should only get this call if we sent the extension, so + * ss->nextProtoCallback needs to be non-NULL. However, it is possible + * that an application erroneously cleared the callback between the time + * we sent the ClientHello and now. */ + if (!ss->nextProtoCallback) { + PORT_Assert(0); + (void)SSL3_SendAlert(ss, alert_fatal, internal_error); + PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_NO_CALLBACK); + return SECFailure; + } + + return ssl3_SelectAppProtocol(ss, ex_type, data); +} + +SECStatus +ssl3_ClientHandleAppProtoXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data) +{ + SECStatus rv; + PRInt32 list_len; + SECItem protocol_name; + + if (ssl3_ExtensionNegotiated(ss, ssl_next_proto_nego_xtn)) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + /* The extension data from the server has the following format: + * uint16 name_list_len; + * uint8 len; // where len >= 1 + * uint8 protocol_name[len]; */ + if (data->len < 4 || data->len > 2 + 1 + 255) { + (void)SSL3_SendAlert(ss, alert_fatal, decode_error); + PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID); + return SECFailure; + } + + list_len = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len); + /* The list has to be the entire extension. */ + if (list_len != data->len) { + (void)SSL3_SendAlert(ss, alert_fatal, decode_error); + PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID); + return SECFailure; + } + + rv = ssl3_ConsumeHandshakeVariable(ss, &protocol_name, 1, + &data->data, &data->len); + /* The list must have exactly one value. */ + if (rv != SECSuccess || data->len != 0) { + (void)SSL3_SendAlert(ss, alert_fatal, decode_error); + PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID); + return SECFailure; + } + + SECITEM_FreeItem(&ss->ssl3.nextProto, PR_FALSE); + ss->ssl3.nextProtoState = SSL_NEXT_PROTO_SELECTED; + ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; + return SECITEM_CopyItem(NULL, &ss->ssl3.nextProto, &protocol_name); +} + +PRInt32 +ssl3_ClientSendNextProtoNegoXtn(sslSocket *ss, PRBool append, + PRUint32 maxBytes) +{ + PRInt32 extension_length; + + /* Renegotiations do not send this extension. */ + if (!ss->opt.enableNPN || !ss->nextProtoCallback || ss->firstHsDone) { + return 0; + } + + extension_length = 4; + + if (maxBytes < (PRUint32)extension_length) { + return 0; + } + if (append) { + SECStatus rv; + rv = ssl3_AppendHandshakeNumber(ss, ssl_next_proto_nego_xtn, 2); + if (rv != SECSuccess) + goto loser; + rv = ssl3_AppendHandshakeNumber(ss, 0, 2); + if (rv != SECSuccess) + goto loser; + ss->xtnData.advertised[ss->xtnData.numAdvertised++] = + ssl_next_proto_nego_xtn; + } + + return extension_length; + +loser: + return -1; +} + +PRInt32 +ssl3_ClientSendAppProtoXtn(sslSocket *ss, PRBool append, PRUint32 maxBytes) +{ + PRInt32 extension_length; + unsigned char *alpn_protos = NULL; + + /* Renegotiations do not send this extension. */ + if (!ss->opt.enableALPN || !ss->opt.nextProtoNego.data || ss->firstHsDone) { + return 0; + } + + extension_length = 2 /* extension type */ + 2 /* extension length */ + + 2 /* protocol name list length */ + + ss->opt.nextProtoNego.len; + + if (maxBytes < (PRUint32)extension_length) { + return 0; + } + if (append) { + /* NPN requires that the client's fallback protocol is first in the + * list. However, ALPN sends protocols in preference order. So we + * allocate a buffer and move the first protocol to the end of the + * list. */ + SECStatus rv; + const unsigned int len = ss->opt.nextProtoNego.len; + + alpn_protos = PORT_Alloc(len); + if (alpn_protos == NULL) { + return SECFailure; + } + if (len > 0) { + /* Each protocol string is prefixed with a single byte length. */ + unsigned int i = ss->opt.nextProtoNego.data[0] + 1; + if (i <= len) { + memcpy(alpn_protos, &ss->opt.nextProtoNego.data[i], len - i); + memcpy(alpn_protos + len - i, ss->opt.nextProtoNego.data, i); + } else { + /* This seems to be invalid data so we'll send as-is. */ + memcpy(alpn_protos, ss->opt.nextProtoNego.data, len); + } + } + + rv = ssl3_AppendHandshakeNumber(ss, ssl_app_layer_protocol_xtn, 2); + if (rv != SECSuccess) { + goto loser; + } + rv = ssl3_AppendHandshakeNumber(ss, extension_length - 4, 2); + if (rv != SECSuccess) { + goto loser; + } + rv = ssl3_AppendHandshakeVariable(ss, alpn_protos, len, 2); + PORT_Free(alpn_protos); + alpn_protos = NULL; + if (rv != SECSuccess) { + goto loser; + } + ss->xtnData.advertised[ss->xtnData.numAdvertised++] = + ssl_app_layer_protocol_xtn; + } + + return extension_length; + +loser: + if (alpn_protos) { + PORT_Free(alpn_protos); + } + return -1; +} + +PRInt32 +ssl3_ServerSendAppProtoXtn(sslSocket *ss, PRBool append, PRUint32 maxBytes) +{ + PRInt32 extension_length; + + /* we're in over our heads if any of these fail */ + PORT_Assert(ss->opt.enableALPN); + PORT_Assert(ss->ssl3.nextProto.data); + PORT_Assert(ss->ssl3.nextProto.len > 0); + PORT_Assert(ss->ssl3.nextProtoState == SSL_NEXT_PROTO_NEGOTIATED); + PORT_Assert(!ss->firstHsDone); + + extension_length = 2 /* extension type */ + 2 /* extension length */ + + 2 /* protocol name list */ + 1 /* name length */ + + ss->ssl3.nextProto.len; + + if (maxBytes < (PRUint32)extension_length) { + return 0; + } + if (append) { + SECStatus rv; + rv = ssl3_AppendHandshakeNumber(ss, ssl_app_layer_protocol_xtn, 2); + if (rv != SECSuccess) { + return -1; + } + rv = ssl3_AppendHandshakeNumber(ss, extension_length - 4, 2); + if (rv != SECSuccess) { + return -1; + } + rv = ssl3_AppendHandshakeNumber(ss, ss->ssl3.nextProto.len + 1, 2); + if (rv != SECSuccess) { + return -1; + } + rv = ssl3_AppendHandshakeVariable(ss, ss->ssl3.nextProto.data, + ss->ssl3.nextProto.len, 1); + if (rv != SECSuccess) { + return -1; + } + } + + return extension_length; +} + +SECStatus +ssl3_ServerHandleStatusRequestXtn(sslSocket *ss, PRUint16 ex_type, + SECItem *data) +{ + ssl3HelloExtensionSenderFunc sender; + + PORT_Assert(ss->sec.isServer); + + /* remember that we got this extension. */ + ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; + + if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) { + sender = tls13_ServerSendStatusRequestXtn; + } else { + sender = ssl3_ServerSendStatusRequestXtn; + } + return ssl3_RegisterServerHelloExtensionSender(ss, ex_type, sender); +} + +PRInt32 +ssl3_ServerSendStatusRequestXtn( + sslSocket *ss, + PRBool append, + PRUint32 maxBytes) +{ + PRInt32 extension_length; + const sslServerCert *serverCert = ss->sec.serverCert; + SECStatus rv; + + if (!serverCert->certStatusArray || + !serverCert->certStatusArray->len) { + return 0; + } + + extension_length = 2 + 2; + if (maxBytes < (PRUint32)extension_length) { + return 0; + } + if (append) { + /* extension_type */ + rv = ssl3_AppendHandshakeNumber(ss, ssl_cert_status_xtn, 2); + if (rv != SECSuccess) + return -1; + /* length of extension_data */ + rv = ssl3_AppendHandshakeNumber(ss, 0, 2); + if (rv != SECSuccess) + return -1; + /* The certificate status data is sent in ssl3_SendCertificateStatus. */ + } + + return extension_length; +} + +/* ssl3_ClientSendStatusRequestXtn builds the status_request extension on the + * client side. See RFC 6066 section 8. */ +PRInt32 +ssl3_ClientSendStatusRequestXtn(sslSocket *ss, PRBool append, + PRUint32 maxBytes) +{ + PRInt32 extension_length; + + if (!ss->opt.enableOCSPStapling) + return 0; + + /* extension_type (2-bytes) + + * length(extension_data) (2-bytes) + + * status_type (1) + + * responder_id_list length (2) + + * request_extensions length (2) + */ + extension_length = 9; + + if (maxBytes < (PRUint32)extension_length) { + PORT_Assert(0); + return 0; + } + if (append) { + SECStatus rv; + TLSExtensionData *xtnData; + + /* extension_type */ + rv = ssl3_AppendHandshakeNumber(ss, ssl_cert_status_xtn, 2); + if (rv != SECSuccess) + return -1; + rv = ssl3_AppendHandshakeNumber(ss, extension_length - 4, 2); + if (rv != SECSuccess) + return -1; + rv = ssl3_AppendHandshakeNumber(ss, 1 /* status_type ocsp */, 1); + if (rv != SECSuccess) + return -1; + /* A zero length responder_id_list means that the responders are + * implicitly known to the server. */ + rv = ssl3_AppendHandshakeNumber(ss, 0, 2); + if (rv != SECSuccess) + return -1; + /* A zero length request_extensions means that there are no extensions. + * Specifically, we don't set the id-pkix-ocsp-nonce extension. This + * means that the server can replay a cached OCSP response to us. */ + rv = ssl3_AppendHandshakeNumber(ss, 0, 2); + if (rv != SECSuccess) + return -1; + + xtnData = &ss->xtnData; + xtnData->advertised[xtnData->numAdvertised++] = ssl_cert_status_xtn; + } + return extension_length; +} + +SECStatus +ssl3_ClientHandleStatusRequestXtn(sslSocket *ss, PRUint16 ex_type, + SECItem *data) +{ + /* In TLS 1.3, the extension carries the OCSP response. */ + if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) { + SECStatus rv; + rv = ssl_ReadCertificateStatus(ss, data->data, data->len); + if (rv != SECSuccess) { + return SECFailure; /* code already set */ + } + } else if (data->len != 0) { + (void)SSL3_SendAlert(ss, alert_fatal, illegal_parameter); + PORT_SetError(SSL_ERROR_RX_MALFORMED_SERVER_HELLO); + return SECFailure; + } + + /* Keep track of negotiated extensions. */ + ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; + return SECSuccess; +} + +/* + * Called from ssl3_SendNewSessionTicket, tls13_SendNewSessionTicket + */ +SECStatus +ssl3_EncodeSessionTicket(sslSocket *ss, + const NewSessionTicket *ticket, + SECItem *ticket_data) +{ + PRUint32 i; + SECStatus rv; + SECItem plaintext; + SECItem plaintext_item = { 0, NULL, 0 }; + SECItem ciphertext = { 0, NULL, 0 }; + PRUint32 ciphertext_length; + SECItem ticket_buf = { 0, NULL, 0 }; + SECItem ticket_tmp = { 0, NULL, 0 }; + SECItem macParam = { 0, NULL, 0 }; + PRBool ms_is_wrapped; + unsigned char wrapped_ms[SSL3_MASTER_SECRET_LENGTH]; + SECItem ms_item = { 0, NULL, 0 }; + PRUint32 padding_length; + PRUint32 ticket_length; + PRUint32 cert_length = 0; + PRUint8 length_buf[4]; + PRUint32 now; + PK11SymKey *aes_key = NULL; + PK11SymKey *mac_key = NULL; + CK_MECHANISM_TYPE cipherMech = CKM_AES_CBC; + PK11Context *aes_ctx; + CK_MECHANISM_TYPE macMech = CKM_SHA256_HMAC; + PK11Context *hmac_ctx = NULL; + unsigned char computed_mac[TLS_EX_SESS_TICKET_MAC_LENGTH]; + unsigned int computed_mac_length; + unsigned char iv[AES_BLOCK_SIZE]; + SECItem ivItem; + SECItem *srvName = NULL; + PRUint32 srvNameLen = 0; + CK_MECHANISM_TYPE msWrapMech = 0; /* dummy default value, + * must be >= 0 */ + ssl3CipherSpec *spec; + const sslServerCertType *certType; + SECItem alpnSelection = { siBuffer, NULL, 0 }; + + SSL_TRC(3, ("%d: SSL3[%d]: send session_ticket handshake", + SSL_GETPID(), ss->fd)); + + PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + + if (ss->opt.requestCertificate && ss->sec.ci.sid->peerCert) { + cert_length = 3 + ss->sec.ci.sid->peerCert->derCert.len; + } + + /* Get IV and encryption keys */ + ivItem.data = iv; + ivItem.len = sizeof(iv); + rv = PK11_GenerateRandom(iv, sizeof(iv)); + if (rv != SECSuccess) + goto loser; + + rv = ssl3_GetSessionTicketKeys(ss, &aes_key, &mac_key); + if (rv != SECSuccess) + goto loser; + + if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) { + spec = ss->ssl3.cwSpec; + } else { + spec = ss->ssl3.pwSpec; + } + if (spec->msItem.len && spec->msItem.data) { + /* The master secret is available unwrapped. */ + ms_item.data = spec->msItem.data; + ms_item.len = spec->msItem.len; + ms_is_wrapped = PR_FALSE; + } else { + /* Extract the master secret wrapped. */ + sslSessionID sid; + PORT_Memset(&sid, 0, sizeof(sslSessionID)); + + rv = ssl3_CacheWrappedMasterSecret(ss, &sid, spec, + ss->ssl3.hs.kea_def->authKeyType); + if (rv == SECSuccess) { + if (sid.u.ssl3.keys.wrapped_master_secret_len > sizeof(wrapped_ms)) + goto loser; + memcpy(wrapped_ms, sid.u.ssl3.keys.wrapped_master_secret, + sid.u.ssl3.keys.wrapped_master_secret_len); + ms_item.data = wrapped_ms; + ms_item.len = sid.u.ssl3.keys.wrapped_master_secret_len; + msWrapMech = sid.u.ssl3.masterWrapMech; + } else { + /* TODO: else send an empty ticket. */ + goto loser; + } + ms_is_wrapped = PR_TRUE; + } + /* Prep to send negotiated name */ + srvName = &ss->sec.ci.sid->u.ssl3.srvName; + if (srvName->data && srvName->len) { + srvNameLen = 2 + srvName->len; /* len bytes + name len */ + } + + if (ss->ssl3.nextProtoState != SSL_NEXT_PROTO_NO_SUPPORT && + ss->ssl3.nextProto.data) { + alpnSelection = ss->ssl3.nextProto; + } + + ciphertext_length = + sizeof(PRUint16) /* ticket_version */ + + sizeof(SSL3ProtocolVersion) /* ssl_version */ + + sizeof(ssl3CipherSuite) /* ciphersuite */ + + 1 /* compression */ + + 10 /* cipher spec parameters */ + + 1 /* certType arguments */ + + 1 /* SessionTicket.ms_is_wrapped */ + + 4 /* msWrapMech */ + + 2 /* master_secret.length */ + + ms_item.len /* master_secret */ + + 1 /* client_auth_type */ + + cert_length /* cert */ + + 1 /* server name type */ + + srvNameLen /* name len + length field */ + + 1 /* extendedMasterSecretUsed */ + + sizeof(ticket->ticket_lifetime_hint) /* ticket lifetime hint */ + + sizeof(ticket->flags) /* ticket flags */ + + 1 + alpnSelection.len; /* npn value + length field. */ + padding_length = AES_BLOCK_SIZE - + (ciphertext_length % + AES_BLOCK_SIZE); + ciphertext_length += padding_length; + + if (SECITEM_AllocItem(NULL, &plaintext_item, ciphertext_length) == NULL) + goto loser; + + plaintext = plaintext_item; + + /* ticket_version */ + rv = ssl3_AppendNumberToItem(&plaintext, TLS_EX_SESS_TICKET_VERSION, + sizeof(PRUint16)); + if (rv != SECSuccess) + goto loser; + + /* ssl_version */ + rv = ssl3_AppendNumberToItem(&plaintext, ss->version, + sizeof(SSL3ProtocolVersion)); + if (rv != SECSuccess) + goto loser; + + /* ciphersuite */ + rv = ssl3_AppendNumberToItem(&plaintext, ss->ssl3.hs.cipher_suite, + sizeof(ssl3CipherSuite)); + if (rv != SECSuccess) + goto loser; + + /* compression */ + rv = ssl3_AppendNumberToItem(&plaintext, ss->ssl3.hs.compression, 1); + if (rv != SECSuccess) + goto loser; + + /* cipher spec parameters */ + rv = ssl3_AppendNumberToItem(&plaintext, ss->sec.authType, 1); + if (rv != SECSuccess) + goto loser; + rv = ssl3_AppendNumberToItem(&plaintext, ss->sec.authKeyBits, 4); + if (rv != SECSuccess) + goto loser; + rv = ssl3_AppendNumberToItem(&plaintext, ss->sec.keaType, 1); + if (rv != SECSuccess) + goto loser; + rv = ssl3_AppendNumberToItem(&plaintext, ss->sec.keaKeyBits, 4); + if (rv != SECSuccess) + goto loser; + + /* certificate type */ + certType = &ss->sec.serverCert->certType; + PORT_Assert(certType->authType == ss->sec.authType); + switch (ss->sec.authType) { + case ssl_auth_ecdsa: + case ssl_auth_ecdh_rsa: + case ssl_auth_ecdh_ecdsa: + PORT_Assert(certType->namedCurve); + PORT_Assert(certType->namedCurve->keaType == ssl_kea_ecdh); + /* EC curves only use the second of the two bytes. */ + PORT_Assert(certType->namedCurve->name < 256); + rv = ssl3_AppendNumberToItem(&plaintext, + certType->namedCurve->name, 1); + break; + default: + rv = ssl3_AppendNumberToItem(&plaintext, 0, 1); + break; + } + if (rv != SECSuccess) + goto loser; + + /* master_secret */ + rv = ssl3_AppendNumberToItem(&plaintext, ms_is_wrapped, 1); + if (rv != SECSuccess) + goto loser; + rv = ssl3_AppendNumberToItem(&plaintext, msWrapMech, 4); + if (rv != SECSuccess) + goto loser; + rv = ssl3_AppendNumberToItem(&plaintext, ms_item.len, 2); + if (rv != SECSuccess) + goto loser; + rv = ssl3_AppendToItem(&plaintext, ms_item.data, ms_item.len); + if (rv != SECSuccess) + goto loser; + + /* client_identity */ + if (ss->opt.requestCertificate && ss->sec.ci.sid->peerCert) { + rv = ssl3_AppendNumberToItem(&plaintext, CLIENT_AUTH_CERTIFICATE, 1); + if (rv != SECSuccess) + goto loser; + rv = ssl3_AppendNumberToItem(&plaintext, + ss->sec.ci.sid->peerCert->derCert.len, 3); + if (rv != SECSuccess) + goto loser; + rv = ssl3_AppendToItem(&plaintext, + ss->sec.ci.sid->peerCert->derCert.data, + ss->sec.ci.sid->peerCert->derCert.len); + if (rv != SECSuccess) + goto loser; + } else { + rv = ssl3_AppendNumberToItem(&plaintext, 0, 1); + if (rv != SECSuccess) + goto loser; + } + + /* timestamp */ + now = ssl_Time(); + rv = ssl3_AppendNumberToItem(&plaintext, now, + sizeof(ticket->ticket_lifetime_hint)); + if (rv != SECSuccess) + goto loser; + + if (srvNameLen) { + /* Name Type (sni_host_name) */ + rv = ssl3_AppendNumberToItem(&plaintext, srvName->type, 1); + if (rv != SECSuccess) + goto loser; + /* HostName (length and value) */ + rv = ssl3_AppendNumberToItem(&plaintext, srvName->len, 2); + if (rv != SECSuccess) + goto loser; + rv = ssl3_AppendToItem(&plaintext, srvName->data, srvName->len); + if (rv != SECSuccess) + goto loser; + } else { + /* No Name */ + rv = ssl3_AppendNumberToItem(&plaintext, (char)TLS_STE_NO_SERVER_NAME, 1); + if (rv != SECSuccess) + goto loser; + } + + /* extendedMasterSecretUsed */ + rv = ssl3_AppendNumberToItem( + &plaintext, ss->sec.ci.sid->u.ssl3.keys.extendedMasterSecretUsed, 1); + if (rv != SECSuccess) + goto loser; + + /* Flags */ + rv = ssl3_AppendNumberToItem(&plaintext, ticket->flags, + sizeof(ticket->flags)); + if (rv != SECSuccess) + goto loser; + + /* NPN value. */ + PORT_Assert(alpnSelection.len < 256); + rv = ssl3_AppendNumberToItem(&plaintext, alpnSelection.len, 1); + if (rv != SECSuccess) + goto loser; + if (alpnSelection.len) { + rv = ssl3_AppendToItem(&plaintext, alpnSelection.data, alpnSelection.len); + if (rv != SECSuccess) + goto loser; + } + + PORT_Assert(plaintext.len == padding_length); + for (i = 0; i < padding_length; i++) + plaintext.data[i] = (unsigned char)padding_length; + + if (SECITEM_AllocItem(NULL, &ciphertext, ciphertext_length) == NULL) { + rv = SECFailure; + goto loser; + } + + /* Generate encrypted portion of ticket. */ + PORT_Assert(aes_key); + aes_ctx = PK11_CreateContextBySymKey(cipherMech, CKA_ENCRYPT, aes_key, &ivItem); + if (!aes_ctx) + goto loser; + + rv = PK11_CipherOp(aes_ctx, ciphertext.data, + (int *)&ciphertext.len, ciphertext.len, + plaintext_item.data, plaintext_item.len); + PK11_Finalize(aes_ctx); + PK11_DestroyContext(aes_ctx, PR_TRUE); + if (rv != SECSuccess) + goto loser; + + /* Convert ciphertext length to network order. */ + length_buf[0] = (ciphertext.len >> 8) & 0xff; + length_buf[1] = (ciphertext.len) & 0xff; + + /* Compute MAC. */ + PORT_Assert(mac_key); + hmac_ctx = PK11_CreateContextBySymKey(macMech, CKA_SIGN, mac_key, &macParam); + if (!hmac_ctx) + goto loser; + + rv = PK11_DigestBegin(hmac_ctx); + if (rv != SECSuccess) + goto loser; + rv = PK11_DigestOp(hmac_ctx, key_name, SESS_TICKET_KEY_NAME_LEN); + if (rv != SECSuccess) + goto loser; + rv = PK11_DigestOp(hmac_ctx, iv, sizeof(iv)); + if (rv != SECSuccess) + goto loser; + rv = PK11_DigestOp(hmac_ctx, (unsigned char *)length_buf, 2); + if (rv != SECSuccess) + goto loser; + rv = PK11_DigestOp(hmac_ctx, ciphertext.data, ciphertext.len); + if (rv != SECSuccess) + goto loser; + rv = PK11_DigestFinal(hmac_ctx, computed_mac, + &computed_mac_length, sizeof(computed_mac)); + if (rv != SECSuccess) + goto loser; + + ticket_length = + +SESS_TICKET_KEY_NAME_LEN /* key_name */ + + AES_BLOCK_SIZE /* iv */ + + 2 /* length field for NewSessionTicket.ticket.encrypted_state */ + + ciphertext_length /* encrypted_state */ + + TLS_EX_SESS_TICKET_MAC_LENGTH; /* mac */ + + if (SECITEM_AllocItem(NULL, &ticket_buf, ticket_length) == NULL) { + rv = SECFailure; + goto loser; + } + ticket_tmp = ticket_buf; /* Shallow copy because AppendToItem is + * destructive. */ + + rv = ssl3_AppendToItem(&ticket_tmp, key_name, SESS_TICKET_KEY_NAME_LEN); + if (rv != SECSuccess) + goto loser; + + rv = ssl3_AppendToItem(&ticket_tmp, iv, sizeof(iv)); + if (rv != SECSuccess) + goto loser; + + rv = ssl3_AppendNumberToItem(&ticket_tmp, ciphertext.len, 2); + if (rv != SECSuccess) + goto loser; + + rv = ssl3_AppendToItem(&ticket_tmp, ciphertext.data, ciphertext.len); + if (rv != SECSuccess) + goto loser; + + rv = ssl3_AppendToItem(&ticket_tmp, computed_mac, computed_mac_length); + if (rv != SECSuccess) + goto loser; + + /* Give ownership of memory to caller. */ + *ticket_data = ticket_buf; + ticket_buf.data = NULL; + +loser: + if (hmac_ctx) { + PK11_DestroyContext(hmac_ctx, PR_TRUE); + } + if (plaintext_item.data) { + SECITEM_FreeItem(&plaintext_item, PR_FALSE); + } + if (ciphertext.data) { + SECITEM_FreeItem(&ciphertext, PR_FALSE); + } + if (ticket_buf.data) { + SECITEM_FreeItem(&ticket_buf, PR_FALSE); + } + + return rv; +} + +/* When a client receives a SessionTicket extension a NewSessionTicket + * message is expected during the handshake. + */ +SECStatus +ssl3_ClientHandleSessionTicketXtn(sslSocket *ss, PRUint16 ex_type, + SECItem *data) +{ + if (data->len != 0) { + return SECSuccess; /* Ignore the extension. */ + } + + /* Keep track of negotiated extensions. */ + ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; + return SECSuccess; +} + +/* Generic ticket processing code, common to TLS 1.0-1.2 and + * TLS 1.3. */ +SECStatus +ssl3_ProcessSessionTicketCommon(sslSocket *ss, SECItem *data) +{ + SECStatus rv; + SECItem *decrypted_state = NULL; + SessionTicket *parsed_session_ticket = NULL; + sslSessionID *sid = NULL; + SSL3Statistics *ssl3stats; + PRUint32 i; + SECItem extension_data; + EncryptedSessionTicket enc_session_ticket; + unsigned char computed_mac[TLS_EX_SESS_TICKET_MAC_LENGTH]; + unsigned int computed_mac_length; + PK11SymKey *aes_key = NULL; + PK11SymKey *mac_key = NULL; + PK11Context *hmac_ctx; + CK_MECHANISM_TYPE macMech = CKM_SHA256_HMAC; + PK11Context *aes_ctx; + CK_MECHANISM_TYPE cipherMech = CKM_AES_CBC; + unsigned char *padding; + PRUint32 padding_length; + unsigned char *buffer; + unsigned int buffer_len; + PRInt32 temp; + SECItem cert_item; + PRInt8 nameType = TLS_STE_NO_SERVER_NAME; + SECItem macParam = { siBuffer, NULL, 0 }; + SECItem alpn_item; + SECItem ivItem; + + /* Turn off stateless session resumption if the client sends a + * SessionTicket extension, even if the extension turns out to be + * malformed (ss->sec.ci.sid is non-NULL when doing session + * renegotiation.) + */ + if (ss->sec.ci.sid != NULL) { + ss->sec.uncache(ss->sec.ci.sid); + ssl_FreeSID(ss->sec.ci.sid); + ss->sec.ci.sid = NULL; + } + + extension_data.data = data->data; /* Keep a copy for future use. */ + extension_data.len = data->len; + + if (ssl3_ParseEncryptedSessionTicket(ss, data, &enc_session_ticket) != + SECSuccess) { + return SECSuccess; /* Pretend it isn't there */ + } + + /* Get session ticket keys. */ + rv = ssl3_GetSessionTicketKeys(ss, &aes_key, &mac_key); + if (rv != SECSuccess) { + SSL_DBG(("%d: SSL[%d]: Unable to get/generate session ticket keys.", + SSL_GETPID(), ss->fd)); + goto loser; + } + + /* If the ticket sent by the client was generated under a key different + * from the one we have, bypass ticket processing. + */ + if (PORT_Memcmp(enc_session_ticket.key_name, key_name, + SESS_TICKET_KEY_NAME_LEN) != 0) { + SSL_DBG(("%d: SSL[%d]: Session ticket key_name sent mismatch.", + SSL_GETPID(), ss->fd)); + goto no_ticket; + } + + /* Verify the MAC on the ticket. MAC verification may also + * fail if the MAC key has been recently refreshed. + */ + PORT_Assert(mac_key); + hmac_ctx = PK11_CreateContextBySymKey(macMech, CKA_SIGN, mac_key, &macParam); + if (!hmac_ctx) { + SSL_DBG(("%d: SSL[%d]: Unable to create HMAC context: %d.", + SSL_GETPID(), ss->fd, PORT_GetError())); + goto no_ticket; + } else { + SSL_DBG(("%d: SSL[%d]: Successfully created HMAC context.", + SSL_GETPID(), ss->fd)); + } + rv = PK11_DigestBegin(hmac_ctx); + if (rv != SECSuccess) { + PK11_DestroyContext(hmac_ctx, PR_TRUE); + goto no_ticket; + } + rv = PK11_DigestOp(hmac_ctx, extension_data.data, + extension_data.len - + TLS_EX_SESS_TICKET_MAC_LENGTH); + if (rv != SECSuccess) { + PK11_DestroyContext(hmac_ctx, PR_TRUE); + goto no_ticket; + } + rv = PK11_DigestFinal(hmac_ctx, computed_mac, + &computed_mac_length, sizeof(computed_mac)); + PK11_DestroyContext(hmac_ctx, PR_TRUE); + if (rv != SECSuccess) + goto no_ticket; + + if (NSS_SecureMemcmp(computed_mac, enc_session_ticket.mac, + computed_mac_length) != + 0) { + SSL_DBG(("%d: SSL[%d]: Session ticket MAC mismatch.", + SSL_GETPID(), ss->fd)); + goto no_ticket; + } + + /* We ignore key_name for now. + * This is ok as MAC verification succeeded. + */ + + /* Decrypt the ticket. */ + + /* Plaintext is shorter than the ciphertext due to padding. */ + decrypted_state = SECITEM_AllocItem(NULL, NULL, + enc_session_ticket.encrypted_state.len); + + PORT_Assert(aes_key); + ivItem.data = enc_session_ticket.iv; + ivItem.len = AES_BLOCK_SIZE; + aes_ctx = PK11_CreateContextBySymKey(cipherMech, CKA_DECRYPT, + aes_key, &ivItem); + if (!aes_ctx) { + SSL_DBG(("%d: SSL[%d]: Unable to create AES context.", + SSL_GETPID(), ss->fd)); + goto no_ticket; + } + + rv = PK11_CipherOp(aes_ctx, decrypted_state->data, + (int *)&decrypted_state->len, decrypted_state->len, + enc_session_ticket.encrypted_state.data, + enc_session_ticket.encrypted_state.len); + PK11_Finalize(aes_ctx); + PK11_DestroyContext(aes_ctx, PR_TRUE); + if (rv != SECSuccess) + goto no_ticket; + + /* Check padding. */ + padding_length = + (PRUint32)decrypted_state->data[decrypted_state->len - 1]; + if (padding_length == 0 || padding_length > AES_BLOCK_SIZE) + goto no_ticket; + + padding = &decrypted_state->data[decrypted_state->len - padding_length]; + for (i = 0; i < padding_length; i++, padding++) { + if (padding_length != (PRUint32)*padding) + goto no_ticket; + } + + /* Deserialize session state. */ + buffer = decrypted_state->data; + buffer_len = decrypted_state->len; + + parsed_session_ticket = PORT_ZAlloc(sizeof(SessionTicket)); + if (parsed_session_ticket == NULL) { + rv = SECFailure; + goto loser; + } + + /* Read ticket_version and reject if the version is wrong */ + temp = ssl3_ConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len); + if (temp != TLS_EX_SESS_TICKET_VERSION) + goto no_ticket; + + parsed_session_ticket->ticket_version = (SSL3ProtocolVersion)temp; + + /* Read SSLVersion. */ + temp = ssl3_ConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len); + if (temp < 0) + goto no_ticket; + parsed_session_ticket->ssl_version = (SSL3ProtocolVersion)temp; + + /* Read cipher_suite. */ + temp = ssl3_ConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len); + if (temp < 0) + goto no_ticket; + parsed_session_ticket->cipher_suite = (ssl3CipherSuite)temp; + + /* Read compression_method. */ + temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len); + if (temp < 0) + goto no_ticket; + parsed_session_ticket->compression_method = (SSLCompressionMethod)temp; + + /* Read cipher spec parameters. */ + temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len); + if (temp < 0) + goto no_ticket; + parsed_session_ticket->authType = (SSLAuthType)temp; + temp = ssl3_ConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len); + if (temp < 0) + goto no_ticket; + parsed_session_ticket->authKeyBits = (PRUint32)temp; + temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len); + if (temp < 0) + goto no_ticket; + parsed_session_ticket->keaType = (SSLKEAType)temp; + temp = ssl3_ConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len); + if (temp < 0) + goto no_ticket; + parsed_session_ticket->keaKeyBits = (PRUint32)temp; + + /* Read certificate slot */ + parsed_session_ticket->certType.authType = parsed_session_ticket->authType; + temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len); + if (temp < 0) + goto no_ticket; + switch (parsed_session_ticket->authType) { + case ssl_auth_ecdsa: + case ssl_auth_ecdh_rsa: + case ssl_auth_ecdh_ecdsa: { + const sslNamedGroupDef *group = + ssl_LookupNamedGroup((SSLNamedGroup)temp); + if (!group || group->keaType != ssl_kea_ecdh) { + goto no_ticket; + } + parsed_session_ticket->certType.namedCurve = group; + } break; + default: + break; + } + + /* Read wrapped master_secret. */ + temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len); + if (temp < 0) + goto no_ticket; + parsed_session_ticket->ms_is_wrapped = (PRBool)temp; + + temp = ssl3_ConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len); + if (temp < 0) + goto no_ticket; + parsed_session_ticket->msWrapMech = (CK_MECHANISM_TYPE)temp; + + temp = ssl3_ConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len); + if (temp < 0) + goto no_ticket; + parsed_session_ticket->ms_length = (PRUint16)temp; + if (parsed_session_ticket->ms_length == 0 || /* sanity check MS. */ + parsed_session_ticket->ms_length > + sizeof(parsed_session_ticket->master_secret)) + goto no_ticket; + + /* Allow for the wrapped master secret to be longer. */ + if (buffer_len < parsed_session_ticket->ms_length) + goto no_ticket; + PORT_Memcpy(parsed_session_ticket->master_secret, buffer, + parsed_session_ticket->ms_length); + buffer += parsed_session_ticket->ms_length; + buffer_len -= parsed_session_ticket->ms_length; + + /* Read client_identity */ + temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len); + if (temp < 0) + goto no_ticket; + parsed_session_ticket->client_identity.client_auth_type = + (ClientAuthenticationType)temp; + switch (parsed_session_ticket->client_identity.client_auth_type) { + case CLIENT_AUTH_ANONYMOUS: + break; + case CLIENT_AUTH_CERTIFICATE: + rv = ssl3_ConsumeHandshakeVariable(ss, &cert_item, 3, + &buffer, &buffer_len); + if (rv != SECSuccess) + goto no_ticket; + rv = SECITEM_CopyItem(NULL, &parsed_session_ticket->peer_cert, + &cert_item); + if (rv != SECSuccess) + goto no_ticket; + break; + default: + goto no_ticket; + } + /* Read timestamp. */ + temp = ssl3_ConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len); + if (temp < 0) + goto no_ticket; + parsed_session_ticket->timestamp = (PRUint32)temp; + + /* Read server name */ + nameType = + ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len); + if (nameType != TLS_STE_NO_SERVER_NAME) { + SECItem name_item; + rv = ssl3_ConsumeHandshakeVariable(ss, &name_item, 2, &buffer, + &buffer_len); + if (rv != SECSuccess) + goto no_ticket; + rv = SECITEM_CopyItem(NULL, &parsed_session_ticket->srvName, + &name_item); + if (rv != SECSuccess) + goto no_ticket; + parsed_session_ticket->srvName.type = nameType; + } + + /* Read extendedMasterSecretUsed */ + temp = ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len); + if (temp < 0) + goto no_ticket; + PORT_Assert(temp == PR_TRUE || temp == PR_FALSE); + parsed_session_ticket->extendedMasterSecretUsed = (PRBool)temp; + + rv = ssl3_ConsumeHandshake(ss, &parsed_session_ticket->flags, 4, + &buffer, &buffer_len); + if (rv != SECSuccess) + goto no_ticket; + parsed_session_ticket->flags = PR_ntohl(parsed_session_ticket->flags); + + rv = ssl3_ConsumeHandshakeVariable(ss, &alpn_item, 1, &buffer, &buffer_len); + if (rv != SECSuccess) + goto no_ticket; + if (alpn_item.len != 0) { + rv = SECITEM_CopyItem(NULL, &parsed_session_ticket->alpnSelection, + &alpn_item); + if (rv != SECSuccess) + goto no_ticket; + if (alpn_item.len >= 256) + goto no_ticket; + } + + /* Done parsing. Check that all bytes have been consumed. */ + if (buffer_len != padding_length) + goto no_ticket; + + /* Use the ticket if it has not expired, otherwise free the allocated + * memory since the ticket is of no use. + */ + if (parsed_session_ticket->timestamp != 0 && + parsed_session_ticket->timestamp + + TLS_EX_SESS_TICKET_LIFETIME_HINT > + ssl_Time()) { + + sid = ssl3_NewSessionID(ss, PR_TRUE); + if (sid == NULL) { + rv = SECFailure; + goto loser; + } + + /* Copy over parameters. */ + sid->version = parsed_session_ticket->ssl_version; + sid->u.ssl3.cipherSuite = parsed_session_ticket->cipher_suite; + sid->u.ssl3.compression = parsed_session_ticket->compression_method; + sid->authType = parsed_session_ticket->authType; + sid->authKeyBits = parsed_session_ticket->authKeyBits; + sid->keaType = parsed_session_ticket->keaType; + sid->keaKeyBits = parsed_session_ticket->keaKeyBits; + memcpy(&sid->certType, &parsed_session_ticket->certType, + sizeof(sslServerCertType)); + + if (SECITEM_CopyItem(NULL, &sid->u.ssl3.locked.sessionTicket.ticket, + &extension_data) != SECSuccess) + goto no_ticket; + sid->u.ssl3.locked.sessionTicket.flags = parsed_session_ticket->flags; + + if (parsed_session_ticket->ms_length > + sizeof(sid->u.ssl3.keys.wrapped_master_secret)) + goto no_ticket; + PORT_Memcpy(sid->u.ssl3.keys.wrapped_master_secret, + parsed_session_ticket->master_secret, + parsed_session_ticket->ms_length); + sid->u.ssl3.keys.wrapped_master_secret_len = + parsed_session_ticket->ms_length; + sid->u.ssl3.masterWrapMech = parsed_session_ticket->msWrapMech; + sid->u.ssl3.keys.msIsWrapped = + parsed_session_ticket->ms_is_wrapped; + sid->u.ssl3.masterValid = PR_TRUE; + sid->u.ssl3.keys.resumable = PR_TRUE; + sid->u.ssl3.keys.extendedMasterSecretUsed = parsed_session_ticket->extendedMasterSecretUsed; + + /* Copy over client cert from session ticket if there is one. */ + if (parsed_session_ticket->peer_cert.data != NULL) { + if (sid->peerCert != NULL) + CERT_DestroyCertificate(sid->peerCert); + sid->peerCert = CERT_NewTempCertificate(ss->dbHandle, + &parsed_session_ticket->peer_cert, NULL, PR_FALSE, PR_TRUE); + if (sid->peerCert == NULL) { + rv = SECFailure; + goto loser; + } + } + if (parsed_session_ticket->srvName.data != NULL) { + if (sid->u.ssl3.srvName.data) { + SECITEM_FreeItem(&sid->u.ssl3.srvName, PR_FALSE); + } + sid->u.ssl3.srvName = parsed_session_ticket->srvName; + } + if (parsed_session_ticket->alpnSelection.data != NULL) { + sid->u.ssl3.alpnSelection = parsed_session_ticket->alpnSelection; + /* So we don't free below. */ + parsed_session_ticket->alpnSelection.data = NULL; + } + ss->statelessResume = PR_TRUE; + ss->sec.ci.sid = sid; + } + + if (0) { + no_ticket: + SSL_DBG(("%d: SSL[%d]: Session ticket parsing failed.", + SSL_GETPID(), ss->fd)); + ssl3stats = SSL_GetStatistics(); + SSL_AtomicIncrementLong(&ssl3stats->hch_sid_ticket_parse_failures); + } + rv = SECSuccess; + +loser: + /* ss->sec.ci.sid == sid if it did NOT come here via goto statement + * in that case do not free sid + */ + if (sid && (ss->sec.ci.sid != sid)) { + ssl_FreeSID(sid); + sid = NULL; + } + if (decrypted_state != NULL) { + SECITEM_FreeItem(decrypted_state, PR_TRUE); + decrypted_state = NULL; + } + + if (parsed_session_ticket != NULL) { + if (parsed_session_ticket->peer_cert.data) { + SECITEM_FreeItem(&parsed_session_ticket->peer_cert, PR_FALSE); + } + if (parsed_session_ticket->alpnSelection.data) { + SECITEM_FreeItem(&parsed_session_ticket->alpnSelection, PR_FALSE); + } + PORT_ZFree(parsed_session_ticket, sizeof(SessionTicket)); + } + + return rv; +} + +SECStatus +ssl3_ServerHandleSessionTicketXtn(sslSocket *ss, PRUint16 ex_type, + SECItem *data) +{ + + /* Ignore the SessionTicket extension if processing is disabled. */ + if (!ss->opt.enableSessionTickets) { + return SECSuccess; + } + + /* If we are doing TLS 1.3, then ignore this. */ + if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) { + return SECSuccess; + } + + /* Keep track of negotiated extensions. */ + ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; + + /* Parse the received ticket sent in by the client. We are + * lenient about some parse errors, falling back to a fullshake + * instead of terminating the current connection. + */ + if (data->len == 0) { + ss->xtnData.emptySessionTicket = PR_TRUE; + return SECSuccess; + } + + return ssl3_ProcessSessionTicketCommon(ss, data); +} + +/* + * Read bytes. Using this function means the SECItem structure + * cannot be freed. The caller is expected to call this function + * on a shallow copy of the structure. + */ +static SECStatus +ssl3_ConsumeFromItem(SECItem *item, unsigned char **buf, PRUint32 bytes) +{ + if (bytes > item->len) + return SECFailure; + + *buf = item->data; + item->data += bytes; + item->len -= bytes; + return SECSuccess; +} + +/* Extension format: + * Extension number: 2 bytes + * Extension length: 2 bytes + * Verify Data Length: 1 byte + * Verify Data (TLS): 12 bytes (client) or 24 bytes (server) + * Verify Data (SSL): 36 bytes (client) or 72 bytes (server) + */ +PRInt32 +ssl3_SendRenegotiationInfoXtn( + sslSocket *ss, + PRBool append, + PRUint32 maxBytes) +{ + PRInt32 len = 0; + PRInt32 needed; + + /* In draft-ietf-tls-renegotiation-03, it is NOT RECOMMENDED to send + * both the SCSV and the empty RI, so when we send SCSV in + * the initial handshake, we don't also send RI. + */ + if (!ss || ss->ssl3.hs.sendingSCSV) + return 0; + if (ss->firstHsDone) { + len = ss->sec.isServer ? ss->ssl3.hs.finishedBytes * 2 + : ss->ssl3.hs.finishedBytes; + } + needed = 5 + len; + if (maxBytes < (PRUint32)needed) { + return 0; + } + if (append) { + SECStatus rv; + /* extension_type */ + rv = ssl3_AppendHandshakeNumber(ss, ssl_renegotiation_info_xtn, 2); + if (rv != SECSuccess) + return -1; + /* length of extension_data */ + rv = ssl3_AppendHandshakeNumber(ss, len + 1, 2); + if (rv != SECSuccess) + return -1; + /* verify_Data from previous Finished message(s) */ + rv = ssl3_AppendHandshakeVariable(ss, + ss->ssl3.hs.finishedMsgs.data, len, 1); + if (rv != SECSuccess) + return -1; + if (!ss->sec.isServer) { + TLSExtensionData *xtnData = &ss->xtnData; + xtnData->advertised[xtnData->numAdvertised++] = + ssl_renegotiation_info_xtn; + } + } + return needed; +} + +/* This function runs in both the client and server. */ +SECStatus +ssl3_HandleRenegotiationInfoXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data) +{ + SECStatus rv = SECSuccess; + PRUint32 len = 0; + + if (ss->firstHsDone) { + len = ss->sec.isServer ? ss->ssl3.hs.finishedBytes + : ss->ssl3.hs.finishedBytes * 2; + } + if (data->len != 1 + len || data->data[0] != len) { + (void)ssl3_DecodeError(ss); + return SECFailure; + } + if (len && NSS_SecureMemcmp(ss->ssl3.hs.finishedMsgs.data, + data->data + 1, len)) { + (void)SSL3_SendAlert(ss, alert_fatal, handshake_failure); + PORT_SetError(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE); + return SECFailure; + } + /* remember that we got this extension and it was correct. */ + ss->peerRequestedProtection = 1; + ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; + if (ss->sec.isServer) { + /* prepare to send back the appropriate response */ + rv = ssl3_RegisterServerHelloExtensionSender(ss, ex_type, + ssl3_SendRenegotiationInfoXtn); + } + return rv; +} + +PRInt32 +ssl3_ClientSendUseSRTPXtn(sslSocket *ss, PRBool append, PRUint32 maxBytes) +{ + PRUint32 ext_data_len; + PRInt16 i; + SECStatus rv; + + if (!ss) + return 0; + + if (!IS_DTLS(ss) || !ss->ssl3.dtlsSRTPCipherCount) + return 0; /* Not relevant */ + + ext_data_len = 2 + 2 * ss->ssl3.dtlsSRTPCipherCount + 1; + + if (append && maxBytes >= 4 + ext_data_len) { + /* Extension type */ + rv = ssl3_AppendHandshakeNumber(ss, ssl_use_srtp_xtn, 2); + if (rv != SECSuccess) + return -1; + /* Length of extension data */ + rv = ssl3_AppendHandshakeNumber(ss, ext_data_len, 2); + if (rv != SECSuccess) + return -1; + /* Length of the SRTP cipher list */ + rv = ssl3_AppendHandshakeNumber(ss, + 2 * ss->ssl3.dtlsSRTPCipherCount, + 2); + if (rv != SECSuccess) + return -1; + /* The SRTP ciphers */ + for (i = 0; i < ss->ssl3.dtlsSRTPCipherCount; i++) { + rv = ssl3_AppendHandshakeNumber(ss, + ss->ssl3.dtlsSRTPCiphers[i], + 2); + if (rv != SECSuccess) + return -1; + } + /* Empty MKI value */ + ssl3_AppendHandshakeVariable(ss, NULL, 0, 1); + + ss->xtnData.advertised[ss->xtnData.numAdvertised++] = + ssl_use_srtp_xtn; + } + + return 4 + ext_data_len; +} + +PRInt32 +ssl3_ServerSendUseSRTPXtn(sslSocket *ss, PRBool append, PRUint32 maxBytes) +{ + SECStatus rv; + + /* Server side */ + if (!append || maxBytes < 9) { + return 9; + } + + /* Extension type */ + rv = ssl3_AppendHandshakeNumber(ss, ssl_use_srtp_xtn, 2); + if (rv != SECSuccess) + return -1; + /* Length of extension data */ + rv = ssl3_AppendHandshakeNumber(ss, 5, 2); + if (rv != SECSuccess) + return -1; + /* Length of the SRTP cipher list */ + rv = ssl3_AppendHandshakeNumber(ss, 2, 2); + if (rv != SECSuccess) + return -1; + /* The selected cipher */ + rv = ssl3_AppendHandshakeNumber(ss, ss->ssl3.dtlsSRTPCipherSuite, 2); + if (rv != SECSuccess) + return -1; + /* Empty MKI value */ + ssl3_AppendHandshakeVariable(ss, NULL, 0, 1); + + return 9; +} + +SECStatus +ssl3_ClientHandleUseSRTPXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data) +{ + SECStatus rv; + SECItem ciphers = { siBuffer, NULL, 0 }; + PRUint16 i; + PRUint16 cipher = 0; + PRBool found = PR_FALSE; + SECItem litem; + + if (!data->data || !data->len) { + (void)ssl3_DecodeError(ss); + return SECFailure; + } + + /* Get the cipher list */ + rv = ssl3_ConsumeHandshakeVariable(ss, &ciphers, 2, + &data->data, &data->len); + if (rv != SECSuccess) { + return SECFailure; /* fatal alert already sent */ + } + /* Now check that the server has picked just 1 (i.e., len = 2) */ + if (ciphers.len != 2) { + (void)ssl3_DecodeError(ss); + return SECFailure; + } + + /* Get the selected cipher */ + cipher = (ciphers.data[0] << 8) | ciphers.data[1]; + + /* Now check that this is one of the ciphers we offered */ + for (i = 0; i < ss->ssl3.dtlsSRTPCipherCount; i++) { + if (cipher == ss->ssl3.dtlsSRTPCiphers[i]) { + found = PR_TRUE; + break; + } + } + + if (!found) { + (void)SSL3_SendAlert(ss, alert_fatal, illegal_parameter); + PORT_SetError(SSL_ERROR_RX_MALFORMED_SERVER_HELLO); + return SECFailure; + } + + /* Get the srtp_mki value */ + rv = ssl3_ConsumeHandshakeVariable(ss, &litem, 1, + &data->data, &data->len); + if (rv != SECSuccess) { + return SECFailure; /* alert already sent */ + } + + /* We didn't offer an MKI, so this must be 0 length */ + if (litem.len != 0) { + (void)SSL3_SendAlert(ss, alert_fatal, illegal_parameter); + PORT_SetError(SSL_ERROR_RX_MALFORMED_SERVER_HELLO); + return SECFailure; + } + + /* extra trailing bytes */ + if (data->len != 0) { + (void)ssl3_DecodeError(ss); + return SECFailure; + } + + /* OK, this looks fine. */ + ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ssl_use_srtp_xtn; + ss->ssl3.dtlsSRTPCipherSuite = cipher; + return SECSuccess; +} + +SECStatus +ssl3_ServerHandleUseSRTPXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data) +{ + SECStatus rv; + SECItem ciphers = { siBuffer, NULL, 0 }; + PRUint16 i; + unsigned int j; + PRUint16 cipher = 0; + PRBool found = PR_FALSE; + SECItem litem; + + if (!IS_DTLS(ss) || !ss->ssl3.dtlsSRTPCipherCount) { + /* Ignore the extension if we aren't doing DTLS or no DTLS-SRTP + * preferences have been set. */ + return SECSuccess; + } + + if (!data->data || data->len < 5) { + (void)ssl3_DecodeError(ss); + return SECFailure; + } + + /* Get the cipher list */ + rv = ssl3_ConsumeHandshakeVariable(ss, &ciphers, 2, + &data->data, &data->len); + if (rv != SECSuccess) { + return SECFailure; /* alert already sent */ + } + /* Check that the list is even length */ + if (ciphers.len % 2) { + (void)ssl3_DecodeError(ss); + return SECFailure; + } + + /* Walk through the offered list and pick the most preferred of our + * ciphers, if any */ + for (i = 0; !found && i < ss->ssl3.dtlsSRTPCipherCount; i++) { + for (j = 0; j + 1 < ciphers.len; j += 2) { + cipher = (ciphers.data[j] << 8) | ciphers.data[j + 1]; + if (cipher == ss->ssl3.dtlsSRTPCiphers[i]) { + found = PR_TRUE; + break; + } + } + } + + /* Get the srtp_mki value */ + rv = ssl3_ConsumeHandshakeVariable(ss, &litem, 1, &data->data, &data->len); + if (rv != SECSuccess) { + return SECFailure; + } + + if (data->len != 0) { + (void)ssl3_DecodeError(ss); /* trailing bytes */ + return SECFailure; + } + + /* Now figure out what to do */ + if (!found) { + /* No matching ciphers, pretend we don't support use_srtp */ + return SECSuccess; + } + + /* OK, we have a valid cipher and we've selected it */ + ss->ssl3.dtlsSRTPCipherSuite = cipher; + ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ssl_use_srtp_xtn; + + return ssl3_RegisterServerHelloExtensionSender(ss, ssl_use_srtp_xtn, + ssl3_ServerSendUseSRTPXtn); +} + +/* ssl3_ServerHandleSigAlgsXtn handles the signature_algorithms extension + * from a client. + * See https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 */ +SECStatus +ssl3_ServerHandleSigAlgsXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data) +{ + SECStatus rv; + + /* Ignore this extension if we aren't doing TLS 1.2 or greater. */ + if (ss->version < SSL_LIBRARY_VERSION_TLS_1_2) { + return SECSuccess; + } + + if (ss->ssl3.hs.clientSigSchemes) { + PORT_Free(ss->ssl3.hs.clientSigSchemes); + ss->ssl3.hs.clientSigSchemes = NULL; + } + rv = ssl_ParseSignatureSchemes(ss, NULL, + &ss->ssl3.hs.clientSigSchemes, + &ss->ssl3.hs.numClientSigScheme, + &data->data, &data->len); + if (rv != SECSuccess) { + PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO); + return SECFailure; + } + /* Check for trailing data. */ + if (data->len != 0) { + (void)SSL3_SendAlert(ss, alert_fatal, decode_error); + PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO); + return SECFailure; + } + + /* Keep track of negotiated extensions. */ + ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; + return SECSuccess; +} + +/* ssl3_ClientSendSigAlgsXtn sends the signature_algorithm extension for TLS + * 1.2 ClientHellos. */ +PRInt32 +ssl3_ClientSendSigAlgsXtn(sslSocket *ss, PRBool append, PRUint32 maxBytes) +{ + PRInt32 extension_length; + PRUint8 buf[MAX_SIGNATURE_SCHEMES * 2]; + PRUint32 len; + SECStatus rv; + + if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_2) { + return 0; + } + + rv = ssl3_EncodeSigAlgs(ss, buf, sizeof(buf), &len); + if (rv != SECSuccess) { + return -1; + } + + extension_length = + 2 /* extension type */ + + 2 /* extension length */ + + 2 /* supported_signature_algorithms length */ + + len; + + if (maxBytes < extension_length) { + PORT_Assert(0); + return 0; + } + + if (append) { + SECStatus rv; + rv = ssl3_AppendHandshakeNumber(ss, ssl_signature_algorithms_xtn, 2); + if (rv != SECSuccess) { + return -1; + } + rv = ssl3_AppendHandshakeNumber(ss, len + 2, 2); + if (rv != SECSuccess) { + return -1; + } + + rv = ssl3_AppendHandshakeVariable(ss, buf, len, 2); + if (rv != SECSuccess) { + return -1; + } + + ss->xtnData.advertised[ss->xtnData.numAdvertised++] = + ssl_signature_algorithms_xtn; + } + + return extension_length; +} + +/* Takes the size of the ClientHello, less the record header, and determines how + * much padding is required. */ +unsigned int +ssl3_CalculatePaddingExtensionLength(unsigned int clientHelloLength) +{ + unsigned int recordLength = 1 /* handshake message type */ + + 3 /* handshake message length */ + + clientHelloLength; + unsigned int extensionLength; + + if (recordLength < 256 || recordLength >= 512) { + return 0; + } + + extensionLength = 512 - recordLength; + /* Extensions take at least four bytes to encode. Always include at least + * one byte of data if including the extension. Some servers (e.g. + * WebSphere Application Server 7.0 and Tomcat) will time out or terminate + * the connection if the last extension in the client hello is empty. */ + if (extensionLength < 4 + 1) { + extensionLength = 4 + 1; + } + + return extensionLength; +} + +/* ssl3_AppendPaddingExtension possibly adds an extension which ensures that a + * ClientHello record is either < 256 bytes or is >= 512 bytes. This ensures + * that we don't trigger bugs in F5 products. */ +PRInt32 +ssl3_AppendPaddingExtension(sslSocket *ss, unsigned int extensionLen, + PRUint32 maxBytes) +{ + unsigned int paddingLen = extensionLen - 4; + static unsigned char padding[252]; + + if (extensionLen == 0) { + return 0; + } + + if (extensionLen > maxBytes || + !paddingLen || + paddingLen > sizeof(padding)) { + PORT_Assert(0); + return -1; + } + + if (SECSuccess != ssl3_AppendHandshakeNumber(ss, ssl_padding_xtn, 2)) + return -1; + if (SECSuccess != ssl3_AppendHandshakeVariable(ss, padding, paddingLen, 2)) + return -1; + + return extensionLen; +} + +PRInt32 +ssl3_SendExtendedMasterSecretXtn(sslSocket *ss, PRBool append, + PRUint32 maxBytes) +{ + PRInt32 extension_length; + + if (!ss->opt.enableExtendedMS) { + return 0; + } + + /* Always send the extension in this function, since the + * client always sends it and this function is only called on + * the server if we negotiated the extension. */ + extension_length = 4; /* Type + length (0) */ + if (maxBytes < extension_length) { + PORT_Assert(0); + return 0; + } + + if (append) { + SECStatus rv; + rv = ssl3_AppendHandshakeNumber(ss, ssl_extended_master_secret_xtn, 2); + if (rv != SECSuccess) + goto loser; + rv = ssl3_AppendHandshakeNumber(ss, 0, 2); + if (rv != SECSuccess) + goto loser; + ss->xtnData.advertised[ss->xtnData.numAdvertised++] = + ssl_extended_master_secret_xtn; + } + + return extension_length; + +loser: + return -1; +} + +SECStatus +ssl3_HandleExtendedMasterSecretXtn(sslSocket *ss, PRUint16 ex_type, + SECItem *data) +{ + if (ss->version < SSL_LIBRARY_VERSION_TLS_1_0) { + return SECSuccess; + } + + if (!ss->opt.enableExtendedMS) { + return SECSuccess; + } + + if (data->len != 0) { + SSL_TRC(30, ("%d: SSL3[%d]: Bogus extended master secret extension", + SSL_GETPID(), ss->fd)); + return SECFailure; + } + + SSL_DBG(("%d: SSL[%d]: Negotiated extended master secret extension.", + SSL_GETPID(), ss->fd)); + + /* Keep track of negotiated extensions. */ + ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; + + if (ss->sec.isServer) { + return ssl3_RegisterServerHelloExtensionSender( + ss, ex_type, ssl3_SendExtendedMasterSecretXtn); + } + return SECSuccess; +} + +/* ssl3_ClientSendSignedCertTimestampXtn sends the signed_certificate_timestamp + * extension for TLS ClientHellos. */ +PRInt32 +ssl3_ClientSendSignedCertTimestampXtn(sslSocket *ss, PRBool append, + PRUint32 maxBytes) +{ + PRInt32 extension_length = 2 /* extension_type */ + + 2 /* length(extension_data) */; + + /* Only send the extension if processing is enabled. */ + if (!ss->opt.enableSignedCertTimestamps) + return 0; + + if (append && maxBytes >= extension_length) { + SECStatus rv; + /* extension_type */ + rv = ssl3_AppendHandshakeNumber(ss, + ssl_signed_cert_timestamp_xtn, + 2); + if (rv != SECSuccess) + goto loser; + /* zero length */ + rv = ssl3_AppendHandshakeNumber(ss, 0, 2); + if (rv != SECSuccess) + goto loser; + ss->xtnData.advertised[ss->xtnData.numAdvertised++] = + ssl_signed_cert_timestamp_xtn; + } else if (maxBytes < extension_length) { + PORT_Assert(0); + return 0; + } + + return extension_length; +loser: + return -1; +} + +SECStatus +ssl3_ClientHandleSignedCertTimestampXtn(sslSocket *ss, PRUint16 ex_type, + SECItem *data) +{ + /* We do not yet know whether we'll be resuming a session or creating + * a new one, so we keep a pointer to the data in the TLSExtensionData + * structure. This pointer is only valid in the scope of + * ssl3_HandleServerHello, and, if not resuming a session, the data is + * copied once a new session structure has been set up. + * All parsing is currently left to the application and we accept + * everything, including empty data. + */ + SECItem *scts = &ss->xtnData.signedCertTimestamps; + PORT_Assert(!scts->data && !scts->len); + + if (!data->len) { + /* Empty extension data: RFC 6962 mandates non-empty contents. */ + return SECFailure; + } + *scts = *data; + /* Keep track of negotiated extensions. */ + ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; + return SECSuccess; +} + +PRInt32 +ssl3_ServerSendSignedCertTimestampXtn(sslSocket *ss, + PRBool append, + PRUint32 maxBytes) +{ + PRInt32 extension_length; + const SECItem *scts = &ss->sec.serverCert->signedCertTimestamps; + + if (!scts->len) { + /* No timestamps to send */ + return 0; + } + + extension_length = 2 /* extension_type */ + + 2 /* length(extension_data) */ + + scts->len; + + if (maxBytes < extension_length) { + PORT_Assert(0); + return 0; + } + if (append) { + SECStatus rv; + /* extension_type */ + rv = ssl3_AppendHandshakeNumber(ss, + ssl_signed_cert_timestamp_xtn, + 2); + if (rv != SECSuccess) + goto loser; + /* extension_data */ + rv = ssl3_AppendHandshakeVariable(ss, scts->data, scts->len, 2); + if (rv != SECSuccess) + goto loser; + } + + return extension_length; + +loser: + return -1; +} + +SECStatus +ssl3_ServerHandleSignedCertTimestampXtn(sslSocket *ss, PRUint16 ex_type, + SECItem *data) +{ + ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; + PORT_Assert(ss->sec.isServer); + return ssl3_RegisterServerHelloExtensionSender(ss, ex_type, + ssl3_ServerSendSignedCertTimestampXtn); +} diff --git a/security/nss/lib/ssl/ssl3exthandle.h b/security/nss/lib/ssl/ssl3exthandle.h new file mode 100644 index 000000000000..ef15260bfe4d --- /dev/null +++ b/security/nss/lib/ssl/ssl3exthandle.h @@ -0,0 +1,71 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is PRIVATE to SSL. + * + * 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 __ssl3exthandle_h_ +#define __ssl3exthandle_h_ + +PRInt32 ssl3_SendRenegotiationInfoXtn(sslSocket *ss, + PRBool append, PRUint32 maxBytes); +SECStatus ssl3_HandleRenegotiationInfoXtn(sslSocket *ss, + PRUint16 ex_type, SECItem *data); +SECStatus ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, + PRUint16 ex_type, SECItem *data); +SECStatus ssl3_ClientHandleAppProtoXtn(sslSocket *ss, + PRUint16 ex_type, SECItem *data); +SECStatus ssl3_ServerHandleNextProtoNegoXtn(sslSocket *ss, + PRUint16 ex_type, SECItem *data); +SECStatus ssl3_ServerHandleAppProtoXtn(sslSocket *ss, PRUint16 ex_type, + SECItem *data); +PRInt32 ssl3_ClientSendNextProtoNegoXtn(sslSocket *ss, PRBool append, + PRUint32 maxBytes); +PRInt32 ssl3_ClientSendAppProtoXtn(sslSocket *ss, PRBool append, + PRUint32 maxBytes); +PRInt32 ssl3_ServerSendAppProtoXtn(sslSocket *ss, PRBool append, + PRUint32 maxBytes); +PRInt32 ssl3_ClientSendUseSRTPXtn(sslSocket *ss, PRBool append, + PRUint32 maxBytes); +PRInt32 ssl3_ServerSendUseSRTPXtn(sslSocket *ss, PRBool append, + PRUint32 maxBytes); +SECStatus ssl3_ClientHandleUseSRTPXtn(sslSocket *ss, PRUint16 ex_type, + SECItem *data); +SECStatus ssl3_ServerHandleUseSRTPXtn(sslSocket *ss, PRUint16 ex_type, + SECItem *data); +PRInt32 ssl3_ServerSendStatusRequestXtn(sslSocket *ss, + PRBool append, PRUint32 maxBytes); +SECStatus ssl3_ServerHandleStatusRequestXtn(sslSocket *ss, + PRUint16 ex_type, SECItem *data); +SECStatus ssl3_ClientHandleStatusRequestXtn(sslSocket *ss, + PRUint16 ex_type, + SECItem *data); +PRInt32 ssl3_ClientSendStatusRequestXtn(sslSocket *ss, PRBool append, + PRUint32 maxBytes); +PRInt32 ssl3_ClientSendSigAlgsXtn(sslSocket *ss, PRBool append, + PRUint32 maxBytes); +SECStatus ssl3_ServerHandleSigAlgsXtn(sslSocket *ss, PRUint16 ex_type, + SECItem *data); + +PRInt32 ssl3_ClientSendSignedCertTimestampXtn(sslSocket *ss, + PRBool append, + PRUint32 maxBytes); +SECStatus ssl3_ClientHandleSignedCertTimestampXtn(sslSocket *ss, + PRUint16 ex_type, + SECItem *data); +PRInt32 ssl3_ServerSendSignedCertTimestampXtn(sslSocket *ss, + PRBool append, + PRUint32 maxBytes); +SECStatus ssl3_ServerHandleSignedCertTimestampXtn(sslSocket *ss, + PRUint16 ex_type, + SECItem *data); +PRInt32 ssl3_SendExtendedMasterSecretXtn(sslSocket *ss, PRBool append, + PRUint32 maxBytes); +SECStatus ssl3_HandleExtendedMasterSecretXtn(sslSocket *ss, + PRUint16 ex_type, + SECItem *data); +SECStatus ssl3_ProcessSessionTicketCommon(sslSocket *ss, SECItem *data); + +#endif diff --git a/security/nss/lib/ssl/tls13con.c b/security/nss/lib/ssl/tls13con.c index 3204a27babe9..695483252a4e 100644 --- a/security/nss/lib/ssl/tls13con.c +++ b/security/nss/lib/ssl/tls13con.c @@ -87,7 +87,6 @@ static SECStatus tls13_ClientHandleFinished(sslSocket *ss, static SECStatus tls13_ServerHandleFinished(sslSocket *ss, SSL3Opaque *b, PRUint32 length, const TLS13CombinedHash *hashes); -static SECStatus tls13_SendNewSessionTicket(sslSocket *ss); static SECStatus tls13_HandleNewSessionTicket(sslSocket *ss, SSL3Opaque *b, PRUint32 length); static void @@ -3354,10 +3353,7 @@ tls13_ServerHandleFinished(sslSocket *ss, SSL3Opaque *b, PRUint32 length, return SECFailure; /* Error code and alerts handled below */ } ssl_GetXmitBufLock(ss); - if (ss->opt.enableSessionTickets && - ss->ssl3.hs.kea_def->authKeyType != ssl_auth_psk) { - /* TODO(ekr@rtfm.com): Add support for new tickets in PSK - * (bug 1281034).*/ + if (ss->opt.enableSessionTickets) { rv = tls13_SendNewSessionTicket(ss); if (rv != SECSuccess) { ssl_ReleaseXmitBufLock(ss); @@ -3541,7 +3537,7 @@ loser: * TicketExtension extensions<0..2^16-2>; * } NewSessionTicket; */ -static SECStatus +SECStatus tls13_SendNewSessionTicket(sslSocket *ss) { PRUint16 message_length; @@ -3726,10 +3722,6 @@ tls13_HandleNewSessionTicket(sslSocket *ss, SSL3Opaque *b, PRUint32 length) return SECFailure; } - /* TODO(ekr@rtfm.com): Re-enable new tickets when PSK mode is - * in use. I believe this works, but I can't test it until the - * server side supports it. Bug 1257047. - */ if (!ss->opt.noCache) { PORT_Assert(ss->sec.ci.sid); @@ -3754,13 +3746,24 @@ tls13_HandleNewSessionTicket(sslSocket *ss, SSL3Opaque *b, PRUint32 length) /* Replace a previous session ticket when * we receive a second NewSessionTicket message. */ if (ss->sec.ci.sid->cached == in_client_cache) { - /* Uncache first. */ - ss->sec.uncache(ss->sec.ci.sid); + /* Create a new session ID. */ + sslSessionID *sid = ssl3_NewSessionID(ss, PR_FALSE); + if (!sid) { + return SECFailure; + } - /* Then destroy and rebuild the SID. */ + /* Copy over the peerCert. */ + PORT_Assert(ss->sec.ci.sid->peerCert); + sid->peerCert = CERT_DupCertificate(ss->sec.ci.sid->peerCert); + if (!sid->peerCert) { + ssl_FreeSID(sid); + return SECFailure; + } + + /* Destroy the old SID. */ + ss->sec.uncache(ss->sec.ci.sid); ssl_FreeSID(ss->sec.ci.sid); - ss->sec.ci.sid = ssl3_NewSessionID(ss, PR_FALSE); - ss->sec.ci.sid->cached = never_cached; + ss->sec.ci.sid = sid; } ssl3_SetSIDSessionTicket(ss->sec.ci.sid, &ticket); diff --git a/security/nss/lib/ssl/tls13con.h b/security/nss/lib/ssl/tls13con.h index d7f073f28d3d..91420ada0169 100644 --- a/security/nss/lib/ssl/tls13con.h +++ b/security/nss/lib/ssl/tls13con.h @@ -74,5 +74,6 @@ PRUint16 tls13_EncodeDraftVersion(SSL3ProtocolVersion version); PRUint16 tls13_DecodeDraftVersion(PRUint16 version); SECStatus tls13_NegotiateVersion(sslSocket *ss, const TLSExtension *supported_versions); +SECStatus tls13_SendNewSessionTicket(sslSocket *ss); #endif /* __tls13con_h_ */ diff --git a/security/nss/lib/ssl/tls13exthandle.c b/security/nss/lib/ssl/tls13exthandle.c new file mode 100644 index 000000000000..570be7b3a55d --- /dev/null +++ b/security/nss/lib/ssl/tls13exthandle.c @@ -0,0 +1,1013 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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 "nssrenam.h" +#include "nss.h" +#include "ssl.h" +#include "sslproto.h" +#include "sslimpl.h" +#include "pk11pub.h" +#include "ssl3exthandle.h" +#include "tls13exthandle.h" + +PRInt32 +tls13_ServerSendStatusRequestXtn( + sslSocket *ss, + PRBool append, + PRUint32 maxBytes) +{ + PRInt32 extension_length; + const sslServerCert *serverCert = ss->sec.serverCert; + const SECItem *item; + SECStatus rv; + + if (!serverCert->certStatusArray || + !serverCert->certStatusArray->len) { + return 0; + } + + item = &serverCert->certStatusArray->items[0]; + + /* Only send the first entry. */ + extension_length = 2 + 2 + 1 /* status_type */ + 3 + item->len; + if (maxBytes < (PRUint32)extension_length) { + return 0; + } + if (append) { + /* extension_type */ + rv = ssl3_AppendHandshakeNumber(ss, ssl_cert_status_xtn, 2); + if (rv != SECSuccess) + return -1; + /* length of extension_data */ + rv = ssl3_AppendHandshakeNumber(ss, extension_length - 4, 2); + if (rv != SECSuccess) + return -1; + /* status_type == ocsp */ + rv = ssl3_AppendHandshakeNumber(ss, 1 /*ocsp*/, 1); + if (rv != SECSuccess) + return rv; /* err set by AppendHandshake. */ + /* opaque OCSPResponse<1..2^24-1> */ + rv = ssl3_AppendHandshakeVariable(ss, item->data, item->len, 3); + if (rv != SECSuccess) + return rv; /* err set by AppendHandshake. */ + } + + return extension_length; +} + +/* + * [draft-ietf-tls-tls13-11] Section 6.3.2.3. + * + * struct { + * NamedGroup group; + * opaque key_exchange<1..2^16-1>; + * } KeyShareEntry; + * + * struct { + * select (role) { + * case client: + * KeyShareEntry client_shares<4..2^16-1>; + * + * case server: + * KeyShareEntry server_share; + * } + * } KeyShare; + * + * DH is Section 6.3.2.3.1. + * + * opaque dh_Y<1..2^16-1>; + * + * ECDH is Section 6.3.2.3.2. + * + * opaque point <1..2^8-1>; + */ +PRUint32 +tls13_SizeOfKeyShareEntry(const SECKEYPublicKey *pubKey) +{ + /* Size = NamedGroup(2) + length(2) + opaque share */ + switch (pubKey->keyType) { + case ecKey: + return 2 + 2 + pubKey->u.ec.publicValue.len; + case dhKey: + return 2 + 2 + pubKey->u.dh.prime.len; + default: + PORT_Assert(0); + } + return 0; +} + +PRUint32 +tls13_SizeOfClientKeyShareExtension(sslSocket *ss) +{ + PRCList *cursor; + /* Size is: extension(2) + extension_len(2) + client_shares(2) */ + PRUint32 size = 2 + 2 + 2; + for (cursor = PR_NEXT_LINK(&ss->ephemeralKeyPairs); + cursor != &ss->ephemeralKeyPairs; + cursor = PR_NEXT_LINK(cursor)) { + sslEphemeralKeyPair *keyPair = (sslEphemeralKeyPair *)cursor; + size += tls13_SizeOfKeyShareEntry(keyPair->keys->pubKey); + } + return size; +} + +SECStatus +tls13_EncodeKeyShareEntry(sslSocket *ss, const sslEphemeralKeyPair *keyPair) +{ + SECStatus rv; + SECKEYPublicKey *pubKey = keyPair->keys->pubKey; + unsigned int size = tls13_SizeOfKeyShareEntry(pubKey); + + rv = ssl3_AppendHandshakeNumber(ss, keyPair->group->name, 2); + if (rv != SECSuccess) + return rv; + rv = ssl3_AppendHandshakeNumber(ss, size - 4, 2); + if (rv != SECSuccess) + return rv; + + switch (pubKey->keyType) { + case ecKey: + rv = tls13_EncodeECDHEKeyShareKEX(ss, pubKey); + break; + case dhKey: + rv = ssl_AppendPaddedDHKeyShare(ss, pubKey, PR_FALSE); + break; + default: + PORT_Assert(0); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + break; + } + + return rv; +} + +PRInt32 +tls13_ClientSendKeyShareXtn(sslSocket *ss, PRBool append, + PRUint32 maxBytes) +{ + PRUint32 extension_length; + + if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3) { + return 0; + } + + /* Optimistically try to send an ECDHE key using the + * preexisting key (in future will be keys) */ + SSL_TRC(3, ("%d: TLS13[%d]: send client key share xtn", + SSL_GETPID(), ss->fd)); + + extension_length = tls13_SizeOfClientKeyShareExtension(ss); + if (maxBytes < extension_length) { + PORT_Assert(0); + return 0; + } + + if (append) { + SECStatus rv; + PRCList *cursor; + + rv = ssl3_AppendHandshakeNumber(ss, ssl_tls13_key_share_xtn, 2); + if (rv != SECSuccess) + goto loser; + + /* The extension length */ + rv = ssl3_AppendHandshakeNumber(ss, extension_length - 4, 2); + if (rv != SECSuccess) + goto loser; + + /* The length of KeyShares */ + rv = ssl3_AppendHandshakeNumber(ss, extension_length - 6, 2); + if (rv != SECSuccess) + goto loser; + + for (cursor = PR_NEXT_LINK(&ss->ephemeralKeyPairs); + cursor != &ss->ephemeralKeyPairs; + cursor = PR_NEXT_LINK(cursor)) { + sslEphemeralKeyPair *keyPair = (sslEphemeralKeyPair *)cursor; + rv = tls13_EncodeKeyShareEntry(ss, keyPair); + if (rv != SECSuccess) + goto loser; + } + + ss->xtnData.advertised[ss->xtnData.numAdvertised++] = + ssl_tls13_key_share_xtn; + } + + return extension_length; + +loser: + return -1; +} + +SECStatus +tls13_HandleKeyShareEntry(sslSocket *ss, SECItem *data) +{ + SECStatus rv; + PRInt32 group; + const sslNamedGroupDef *groupDef; + TLS13KeyShareEntry *ks = NULL; + SECItem share = { siBuffer, NULL, 0 }; + + group = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len); + if (group < 0) { + PORT_SetError(SSL_ERROR_RX_MALFORMED_KEY_SHARE); + goto loser; + } + groupDef = ssl_LookupNamedGroup(group); + rv = ssl3_ConsumeHandshakeVariable(ss, &share, 2, &data->data, + &data->len); + if (rv != SECSuccess) { + goto loser; + } + /* If the group is disabled, continue. */ + if (!groupDef) { + return SECSuccess; + } + + ks = PORT_ZNew(TLS13KeyShareEntry); + if (!ks) + goto loser; + ks->group = groupDef; + + rv = SECITEM_CopyItem(NULL, &ks->key_exchange, &share); + if (rv != SECSuccess) + goto loser; + + PR_APPEND_LINK(&ks->link, &ss->ssl3.hs.remoteKeyShares); + return SECSuccess; + +loser: + if (ks) + tls13_DestroyKeyShareEntry(ks); + return SECFailure; +} + +/* Handle an incoming KeyShare extension at the client and copy to + * |ss->ssl3.hs.remoteKeyShares| for future use. The key + * share is processed in tls13_HandleServerKeyShare(). */ +SECStatus +tls13_ClientHandleKeyShareXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data) +{ + SECStatus rv; + + PORT_Assert(!ss->sec.isServer); + if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) { + /* This can't happen because the extension processing + * code filters out TLS 1.3 extensions when not in + * TLS 1.3 mode. */ + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + SSL_TRC(3, ("%d: SSL3[%d]: handle key_share extension", + SSL_GETPID(), ss->fd)); + + rv = tls13_HandleKeyShareEntry(ss, data); + if (rv != SECSuccess) { + PORT_SetError(SSL_ERROR_RX_MALFORMED_KEY_SHARE); + return SECFailure; + } + + if (data->len) { + PORT_SetError(SSL_ERROR_RX_MALFORMED_KEY_SHARE); + return SECFailure; + } + + return SECSuccess; +} + +SECStatus +tls13_ClientHandleKeyShareXtnHrr(sslSocket *ss, PRUint16 ex_type, SECItem *data) +{ + SECStatus rv; + PRInt32 tmp; + const sslNamedGroupDef *group; + + PORT_Assert(!ss->sec.isServer); + PORT_Assert(ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3); + + SSL_TRC(3, ("%d: SSL3[%d]: handle key_share extension in HRR", + SSL_GETPID(), ss->fd)); + + tmp = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len); + if (tmp < 0) { + return SECFailure; /* error code already set */ + } + if (data->len) { + (void)SSL3_SendAlert(ss, alert_fatal, decode_error); + PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST); + return SECFailure; + } + + group = ssl_LookupNamedGroup((SSLNamedGroup)tmp); + /* If the group is not enabled, or we already have a share for the + * requested group, abort. */ + if (!ssl_NamedGroupEnabled(ss, group) || + ssl_LookupEphemeralKeyPair(ss, group)) { + (void)SSL3_SendAlert(ss, alert_fatal, illegal_parameter); + PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST); + return SECFailure; + } + + rv = tls13_CreateKeyShare(ss, group); + if (rv != SECSuccess) { + (void)SSL3_SendAlert(ss, alert_fatal, internal_error); + PORT_SetError(SEC_ERROR_KEYGEN_FAIL); + return SECFailure; + } + + return SECSuccess; +} + +/* Handle an incoming KeyShare extension at the server and copy to + * |ss->ssl3.hs.remoteKeyShares| for future use. The key + * share is processed in tls13_HandleClientKeyShare(). */ +SECStatus +tls13_ServerHandleKeyShareXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data) +{ + SECStatus rv; + PRInt32 length; + + PORT_Assert(ss->sec.isServer); + if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) { + return SECSuccess; + } + + SSL_TRC(3, ("%d: SSL3[%d]: handle key_share extension", + SSL_GETPID(), ss->fd)); + + /* Redundant length because of TLS encoding (this vector consumes + * the entire extension.) */ + length = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, + &data->len); + if (length < 0) + goto loser; + if (length != data->len) { + /* Check for consistency */ + PORT_SetError(SSL_ERROR_RX_MALFORMED_KEY_SHARE); + goto loser; + } + + while (data->len) { + rv = tls13_HandleKeyShareEntry(ss, data); + if (rv != SECSuccess) + goto loser; + } + return SECSuccess; + +loser: + tls13_DestroyKeyShares(&ss->ssl3.hs.remoteKeyShares); + return SECFailure; +} + +PRInt32 +tls13_ServerSendKeyShareXtn(sslSocket *ss, PRBool append, + PRUint32 maxBytes) +{ + PRUint32 extension_length; + PRUint32 entry_length; + SECStatus rv; + sslEphemeralKeyPair *keyPair; + + /* There should be exactly one key share. */ + PORT_Assert(!PR_CLIST_IS_EMPTY(&ss->ephemeralKeyPairs)); + PORT_Assert(PR_PREV_LINK(&ss->ephemeralKeyPairs) == + PR_NEXT_LINK(&ss->ephemeralKeyPairs)); + + keyPair = (sslEphemeralKeyPair *)PR_NEXT_LINK(&ss->ephemeralKeyPairs); + + entry_length = tls13_SizeOfKeyShareEntry(keyPair->keys->pubKey); + extension_length = 2 + 2 + entry_length; /* Type + length + entry_length */ + if (maxBytes < extension_length) { + PORT_Assert(0); + return 0; + } + + if (append) { + rv = ssl3_AppendHandshakeNumber(ss, ssl_tls13_key_share_xtn, 2); + if (rv != SECSuccess) + goto loser; + + rv = ssl3_AppendHandshakeNumber(ss, entry_length, 2); + if (rv != SECSuccess) + goto loser; + + rv = tls13_EncodeKeyShareEntry(ss, keyPair); + if (rv != SECSuccess) + goto loser; + } + + return extension_length; + +loser: + return -1; +} + +/* Called by clients. + * + * struct { + * PskKeyExchangeMode ke_modes<1..255>; + * PskAuthMode auth_modes<1..255>; + * opaque identity<0..2^16-1>; + * } PskIdentity; + * + * struct { + * select (Role) { + * case client: + * PskIdentity identities<2..2^16-1>; + * case server: + * uint16 selected_identity; + * } + * } PreSharedKeyExtension; + * + * Presently the only way to get a PSK is by resumption, so this is + * really a ticket label and there wll be at most one. + */ +PRInt32 +tls13_ClientSendPreSharedKeyXtn(sslSocket *ss, + PRBool append, + PRUint32 maxBytes) +{ + PRInt32 extension_length; + static const PRUint8 auth_modes[] = { tls13_psk_auth }; + static const unsigned long auth_modes_len = sizeof(auth_modes); + static const PRUint8 ke_modes[] = { tls13_psk_dh_ke }; + static const unsigned long ke_modes_len = sizeof(ke_modes); + NewSessionTicket *session_ticket; + + /* We only set statelessResume on the client in TLS 1.3 code. */ + if (!ss->statelessResume) + return 0; + + PORT_Assert(ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3); + + session_ticket = &ss->sec.ci.sid->u.ssl3.locked.sessionTicket; + + extension_length = + 2 + 2 + 2 + /* Type + length + vector length */ + 1 + ke_modes_len + /* key exchange modes vector */ + 1 + auth_modes_len + /* auth modes vector */ + 2 + session_ticket->ticket.len; /* identity length + ticket len */ + + if (maxBytes < (PRUint32)extension_length) { + PORT_Assert(0); + return 0; + } + + if (append) { + SECStatus rv; + /* extension_type */ + rv = ssl3_AppendHandshakeNumber(ss, ssl_tls13_pre_shared_key_xtn, 2); + if (rv != SECSuccess) + goto loser; + rv = ssl3_AppendHandshakeNumber(ss, extension_length - 4, 2); + if (rv != SECSuccess) + goto loser; + rv = ssl3_AppendHandshakeNumber(ss, extension_length - 6, 2); + if (rv != SECSuccess) + goto loser; + rv = ssl3_AppendHandshakeVariable(ss, ke_modes, ke_modes_len, 1); + if (rv != SECSuccess) + goto loser; + rv = ssl3_AppendHandshakeVariable(ss, auth_modes, auth_modes_len, 1); + if (rv != SECSuccess) + goto loser; + rv = ssl3_AppendHandshakeVariable(ss, session_ticket->ticket.data, + session_ticket->ticket.len, 2); + PRINT_BUF(50, (ss, "Sending PreSharedKey value", + session_ticket->ticket.data, + session_ticket->ticket.len)); + ss->xtnData.sentSessionTicketInClientHello = PR_TRUE; + if (rv != SECSuccess) + goto loser; + + ss->xtnData.advertised[ss->xtnData.numAdvertised++] = + ssl_tls13_pre_shared_key_xtn; + } + return extension_length; + +loser: + ss->xtnData.ticketTimestampVerified = PR_FALSE; + return -1; +} + +/* Handle a TLS 1.3 PreSharedKey Extension. We only accept PSKs + * that contain session tickets. */ +SECStatus +tls13_ServerHandlePreSharedKeyXtn(sslSocket *ss, PRUint16 ex_type, + SECItem *data) +{ + PRInt32 len; + PRBool first = PR_TRUE; + SECStatus rv; + + SSL_TRC(3, ("%d: SSL3[%d]: handle pre_shared_key extension", + SSL_GETPID(), ss->fd)); + + /* If we are doing < TLS 1.3, then ignore this. */ + if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) { + return SECSuccess; + } + + len = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len); + if (len < 0) + return SECFailure; + + if (len != data->len) { + PORT_SetError(SSL_ERROR_MALFORMED_PRE_SHARED_KEY); + return SECFailure; + } + + while (data->len) { + SECItem label; + + /* IMPORTANT: We aren't copying these values, just setting pointers. + * They will only be valid as long as the ClientHello is in memory. */ + rv = ssl3_ConsumeHandshakeVariable(ss, &ss->xtnData.psk_ke_modes, 1, + &data->data, &data->len); + if (rv != SECSuccess) + return rv; + if (!ss->xtnData.psk_ke_modes.len) { + goto alert_loser; + } + rv = ssl3_ConsumeHandshakeVariable(ss, &ss->xtnData.psk_auth_modes, 1, + &data->data, &data->len); + if (rv != SECSuccess) + return rv; + if (!ss->xtnData.psk_auth_modes.len) { + goto alert_loser; + } + rv = ssl3_ConsumeHandshakeVariable(ss, &label, 2, + &data->data, &data->len); + if (rv != SECSuccess) + return rv; + if (!label.len) { + goto alert_loser; + } + if (first) { + first = PR_FALSE; /* Continue to read through the extension to check + * the format. */ + + PRINT_BUF(50, (ss, "Handling PreSharedKey value", + label.data, label.len)); + rv = ssl3_ProcessSessionTicketCommon(ss, &label); + /* This only happens if we have an internal error, not + * a malformed ticket. Bogus tickets just don't resume + * and return SECSuccess. */ + if (rv != SECSuccess) + return rv; + } + } + + /* Keep track of negotiated extensions. Note that this does not + * mean we are resuming. */ + ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; + + return SECSuccess; + +alert_loser: + (void)SSL3_SendAlert(ss, alert_fatal, illegal_parameter); + PORT_SetError(SSL_ERROR_MALFORMED_PRE_SHARED_KEY); + return SECFailure; +} + +PRInt32 +tls13_ServerSendPreSharedKeyXtn(sslSocket *ss, + PRBool append, + PRUint32 maxBytes) +{ + PRInt32 extension_length = + 2 + 2 + 2; /* type + len + index */ + SECStatus rv; + + if (maxBytes < (PRUint32)extension_length) { + PORT_Assert(0); + return 0; + } + + if (append) { + rv = ssl3_AppendHandshakeNumber(ss, ssl_tls13_pre_shared_key_xtn, 2); + if (rv != SECSuccess) + return -1; + + rv = ssl3_AppendHandshakeNumber(ss, 2, 2); + if (rv != SECSuccess) + return -1; + + /* We only process the first session ticket the client sends, + * so the index is always 0. */ + rv = ssl3_AppendHandshakeNumber(ss, 0, 2); + if (rv != SECSuccess) + return -1; + } + + return extension_length; +} + +/* Handle a TLS 1.3 PreSharedKey Extension. We only accept PSKs + * that contain session tickets. */ +SECStatus +tls13_ClientHandlePreSharedKeyXtn(sslSocket *ss, PRUint16 ex_type, + SECItem *data) +{ + PRInt32 index; + + SSL_TRC(3, ("%d: SSL3[%d]: handle pre_shared_key extension", + SSL_GETPID(), ss->fd)); + + /* If we are doing < TLS 1.3, then ignore this. */ + if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) { + return SECSuccess; + } + + index = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len); + if (index < 0) + return SECFailure; + + /* This should be the end of the extension. */ + if (data->len) { + PORT_SetError(SSL_ERROR_MALFORMED_PRE_SHARED_KEY); + return SECFailure; + } + + /* We only sent one PSK label so index must be equal to 0 */ + if (index) { + PORT_SetError(SSL_ERROR_MALFORMED_PRE_SHARED_KEY); + return SECFailure; + } + + /* Keep track of negotiated extensions. */ + ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; + + return SECSuccess; +} + +/* + * struct { + * select (Role) { + * case client: + * uint32 obfuscated_ticket_age; + * + * case server: + * struct {}; + * } + * } EarlyDataIndication; + */ +PRInt32 +tls13_ClientSendEarlyDataXtn(sslSocket *ss, + PRBool append, + PRUint32 maxBytes) +{ + PRInt32 extension_length; + SECStatus rv; + NewSessionTicket *session_ticket; + + if (!tls13_ClientAllow0Rtt(ss, ss->sec.ci.sid)) + return 0; + + /* type + length + obfuscated ticket age. */ + extension_length = 2 + 2 + 4; + + if (maxBytes < (PRUint32)extension_length) { + PORT_Assert(0); + return 0; + } + + session_ticket = &ss->sec.ci.sid->u.ssl3.locked.sessionTicket; + if (append) { + PRUint32 age; + + rv = ssl3_AppendHandshakeNumber(ss, ssl_tls13_early_data_xtn, 2); + if (rv != SECSuccess) + return -1; + + rv = ssl3_AppendHandshakeNumber(ss, extension_length - 4, 2); + if (rv != SECSuccess) + return -1; + + /* Obfuscated age. */ + age = ssl_Time() - session_ticket->received_timestamp; + age += session_ticket->ticket_age_add; + + rv = ssl3_AppendHandshakeNumber(ss, age, 4); + if (rv != SECSuccess) + return -1; + } + + ss->ssl3.hs.zeroRttState = ssl_0rtt_sent; + ss->xtnData.advertised[ss->xtnData.numAdvertised++] = + ssl_tls13_early_data_xtn; + + return extension_length; +} + +SECStatus +tls13_ServerHandleEarlyDataXtn(sslSocket *ss, PRUint16 ex_type, + SECItem *data) +{ + PRUint32 obfuscated_ticket_age; + SECStatus rv; + + SSL_TRC(3, ("%d: TLS13[%d]: handle early_data extension", + SSL_GETPID(), ss->fd)); + + /* If we are doing < TLS 1.3, then ignore this. */ + if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) { + return SECSuccess; + } + + /* Obfuscated ticket age. Ignore. Bug 1295163. */ + rv = ssl3_ConsumeHandshake(ss, &obfuscated_ticket_age, 4, + &data->data, &data->len); + if (rv != SECSuccess) { + return SECFailure; + } + + if (data->len) { + PORT_SetError(SSL_ERROR_MALFORMED_EARLY_DATA); + return SECFailure; + } + + if (IS_DTLS(ss)) { + /* Save the null spec, which we should be currently reading. We will + * use this when 0-RTT sending is over. */ + ssl_GetSpecReadLock(ss); + ss->ssl3.hs.nullSpec = ss->ssl3.crSpec; + tls13_CipherSpecAddRef(ss->ssl3.hs.nullSpec); + PORT_Assert(ss->ssl3.hs.nullSpec->cipher_def->cipher == cipher_null); + ssl_ReleaseSpecReadLock(ss); + } + + ss->ssl3.hs.zeroRttState = ssl_0rtt_sent; + + ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; + + return SECSuccess; +} + +/* This is only registered if we are sending it. */ +SECStatus +tls13_ServerSendEarlyDataXtn(sslSocket *ss, + PRBool append, + PRUint32 maxBytes) +{ + SSL_TRC(3, ("%d: TLS13[%d]: send early_data extension", + SSL_GETPID(), ss->fd)); + + PORT_Assert(ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted); + if (maxBytes < 4) { + PORT_Assert(0); + return 0; + } + + if (append) { + SECStatus rv; + + rv = ssl3_AppendHandshakeNumber(ss, ssl_tls13_early_data_xtn, 2); + if (rv != SECSuccess) + return -1; + + rv = ssl3_AppendHandshakeNumber(ss, 0, 2); + if (rv != SECSuccess) + return -1; + } + + return 4; +} + +/* This will only be called if we also offered the extension. */ +SECStatus +tls13_ClientHandleEarlyDataXtn(sslSocket *ss, PRUint16 ex_type, + SECItem *data) +{ + SSL_TRC(3, ("%d: TLS13[%d]: handle early_data extension", + SSL_GETPID(), ss->fd)); + + /* If we are doing < TLS 1.3, then ignore this. */ + if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) { + PORT_SetError(SSL_ERROR_EXTENSION_DISALLOWED_FOR_VERSION); + return SECFailure; + } + + if (data->len) { + PORT_SetError(SSL_ERROR_MALFORMED_EARLY_DATA); + return SECFailure; + } + + /* Keep track of negotiated extensions. */ + ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; + ss->ssl3.hs.zeroRttState = ssl_0rtt_accepted; + + return SECSuccess; +} + +SECStatus +tls13_ClientHandleTicketEarlyDataInfoXtn(sslSocket *ss, PRUint16 ex_type, + SECItem *data) +{ + PRUint32 utmp; + SECStatus rv; + + SSL_TRC(3, ("%d: TLS13[%d]: handle early_data_info extension", + SSL_GETPID(), ss->fd)); + + /* If we are doing < TLS 1.3, then ignore this. */ + if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) { + PORT_SetError(SSL_ERROR_EXTENSION_DISALLOWED_FOR_VERSION); + return SECFailure; + } + + rv = ssl3_ConsumeHandshake(ss, &utmp, sizeof(utmp), + &data->data, &data->len); + if (rv != SECSuccess) { + PORT_SetError(SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET); + return SECFailure; + } + if (data->len) { + PORT_SetError(SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET); + return SECFailure; + } + + ss->xtnData.ticket_age_add_found = PR_TRUE; + ss->xtnData.ticket_age_add = PR_ntohl(utmp); + + return SECSuccess; +} + +/* This is only registered if we are sending it. */ +SECStatus +tls13_ServerSendSigAlgsXtn(sslSocket *ss, + PRBool append, + PRUint32 maxBytes) +{ + SSL_TRC(3, ("%d: TLS13[%d]: send signature_algorithms extension", + SSL_GETPID(), ss->fd)); + + if (maxBytes < 4) { + PORT_Assert(0); + } + + if (append) { + SECStatus rv; + + rv = ssl3_AppendHandshakeNumber(ss, ssl_signature_algorithms_xtn, 2); + if (rv != SECSuccess) + return -1; + + rv = ssl3_AppendHandshakeNumber(ss, 0, 2); + if (rv != SECSuccess) + return -1; + } + + return 4; +} + +/* This will only be called if we also offered the extension. */ +SECStatus +tls13_ClientHandleSigAlgsXtn(sslSocket *ss, PRUint16 ex_type, + SECItem *data) +{ + SSL_TRC(3, ("%d: TLS13[%d]: handle signature_algorithms extension", + SSL_GETPID(), ss->fd)); + + /* If we are doing < TLS 1.3, then ignore this. */ + if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) { + PORT_SetError(SSL_ERROR_EXTENSION_DISALLOWED_FOR_VERSION); + return SECFailure; + } + + if (data->len != 0) { + PORT_SetError(SSL_ERROR_RX_MALFORMED_SERVER_HELLO); + return SECFailure; + } + + /* Keep track of negotiated extensions. */ + ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; + + return SECSuccess; +} + +/* + * struct { + * ProtocolVersion versions<2..254>; + * } SupportedVersions; + */ +PRInt32 +tls13_ClientSendSupportedVersionsXtn(sslSocket *ss, PRBool append, + PRUint32 maxBytes) +{ + PRInt32 extensions_len; + PRUint16 version; + SECStatus rv; + + if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3) { + return 0; + } + + SSL_TRC(3, ("%d: TLS13[%d]: send supported_versions extension", + SSL_GETPID(), ss->fd)); + + /* Extension type, extension len fiels, vector len field, + * length of the values. */ + extensions_len = 2 + 2 + 1 + + 2 * (ss->vrange.max - ss->vrange.min + 1); + + if (maxBytes < (PRUint32)extensions_len) { + PORT_Assert(0); + return 0; + } + + if (append) { + rv = ssl3_AppendHandshakeNumber(ss, ssl_tls13_supported_versions_xtn, 2); + if (rv != SECSuccess) + return -1; + + rv = ssl3_AppendHandshakeNumber(ss, extensions_len - 4, 2); + if (rv != SECSuccess) + return -1; + + rv = ssl3_AppendHandshakeNumber(ss, extensions_len - 5, 1); + if (rv != SECSuccess) + return -1; + + for (version = ss->vrange.max; version >= ss->vrange.min; --version) { + rv = ssl3_AppendHandshakeNumber( + ss, tls13_EncodeDraftVersion(version), 2); + if (rv != SECSuccess) + return -1; + } + } + + return extensions_len; +} + +/* + * struct { + * opaque cookie<1..2^16-1>; + * } Cookie; + */ +SECStatus +tls13_ClientHandleHrrCookie(sslSocket *ss, PRUint16 ex_type, SECItem *data) +{ + SECStatus rv; + + SSL_TRC(3, ("%d: TLS13[%d]: handle cookie extension", + SSL_GETPID(), ss->fd)); + + PORT_Assert(ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3); + + /* IMPORTANT: this is only valid while the HelloRetryRequest is still valid. */ + rv = ssl3_ConsumeHandshakeVariable(ss, &ss->ssl3.hs.cookie, 2, + &data->data, &data->len); + if (rv != SECSuccess) { + PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST); + return SECFailure; + } + if (!ss->ssl3.hs.cookie.len || data->len) { + (void)SSL3_SendAlert(ss, alert_fatal, decode_error); + PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST); + return SECFailure; + } + + return SECSuccess; +} + +PRInt32 +tls13_ClientSendHrrCookieXtn(sslSocket *ss, PRBool append, PRUint32 maxBytes) +{ + PRInt32 extension_len; + + if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3 || + !ss->ssl3.hs.cookie.len) { + return 0; + } + + SSL_TRC(3, ("%d: TLS13[%d]: send cookie extension", SSL_GETPID(), ss->fd)); + + /* Extension type, length, cookie length, cookie value. */ + extension_len = 2 + 2 + 2 + ss->ssl3.hs.cookie.len; + + if (maxBytes < (PRUint32)extension_len) { + PORT_Assert(0); + return 0; + } + + if (append) { + SECStatus rv = ssl3_AppendHandshakeNumber(ss, ssl_tls13_cookie_xtn, 2); + if (rv != SECSuccess) + return -1; + + rv = ssl3_AppendHandshakeNumber(ss, extension_len - 4, 2); + if (rv != SECSuccess) + return -1; + + rv = ssl3_AppendHandshakeVariable(ss, ss->ssl3.hs.cookie.data, + ss->ssl3.hs.cookie.len, 2); + if (rv != SECSuccess) + return -1; + } + return extension_len; +} diff --git a/security/nss/lib/ssl/tls13exthandle.h b/security/nss/lib/ssl/tls13exthandle.h new file mode 100644 index 000000000000..c6b6c8dcdfd7 --- /dev/null +++ b/security/nss/lib/ssl/tls13exthandle.h @@ -0,0 +1,54 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is PRIVATE to SSL. + * + * 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 __tls13exthandle_h_ +#define __tls13exthandle_h_ + +PRInt32 tls13_ServerSendStatusRequestXtn(sslSocket *ss, + PRBool append, PRUint32 maxBytes); +PRInt32 tls13_ClientSendKeyShareXtn(sslSocket *ss, PRBool append, + PRUint32 maxBytes); +SECStatus tls13_ClientHandleKeyShareXtn(sslSocket *ss, + PRUint16 ex_type, + SECItem *data); +SECStatus tls13_ClientHandleKeyShareXtnHrr(sslSocket *ss, + PRUint16 ex_type, + SECItem *data); +SECStatus tls13_ServerHandleKeyShareXtn(sslSocket *ss, + PRUint16 ex_type, + SECItem *data); +PRInt32 tls13_ClientSendPreSharedKeyXtn(sslSocket *ss, PRBool append, + PRUint32 maxBytes); +SECStatus tls13_ServerHandlePreSharedKeyXtn(sslSocket *ss, + PRUint16 ex_type, + SECItem *data); +SECStatus tls13_ClientHandlePreSharedKeyXtn(sslSocket *ss, + PRUint16 ex_type, + SECItem *data); +PRInt32 tls13_ClientSendEarlyDataXtn(sslSocket *ss, + PRBool append, + PRUint32 maxBytes); +SECStatus tls13_ServerHandleEarlyDataXtn(sslSocket *ss, PRUint16 ex_type, + SECItem *data); +SECStatus tls13_ClientHandleEarlyDataXtn(sslSocket *ss, PRUint16 ex_type, + SECItem *data); +SECStatus tls13_ClientHandleTicketEarlyDataInfoXtn( + sslSocket *ss, PRUint16 ex_type, + SECItem *data); +SECStatus tls13_ClientHandleSigAlgsXtn(sslSocket *ss, PRUint16 ex_type, + SECItem *data); +PRInt32 tls13_ClientSendSupportedVersionsXtn(sslSocket *ss, + PRBool append, + PRUint32 maxBytes); +SECStatus tls13_ClientHandleHrrCookie(sslSocket *ss, PRUint16 ex_type, + SECItem *data); +PRInt32 tls13_ClientSendHrrCookieXtn(sslSocket *ss, + PRBool append, + PRUint32 maxBytes); + +#endif From 6db54bcb2f5c0d37d79f32449d078baa39c6a213 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 3 Nov 2016 07:55:17 +0100 Subject: [PATCH 37/96] Bug 1284987 - Entries API - part 1 - DOMString to USVString, r=smaug --- dom/filesystem/compat/FileSystemDirectoryEntry.h | 10 ++++++---- dom/webidl/File.webidl | 2 +- dom/webidl/FileSystem.webidl | 2 +- dom/webidl/FileSystemDirectoryEntry.webidl | 4 ++-- dom/webidl/FileSystemEntry.webidl | 15 ++------------- 5 files changed, 12 insertions(+), 21 deletions(-) diff --git a/dom/filesystem/compat/FileSystemDirectoryEntry.h b/dom/filesystem/compat/FileSystemDirectoryEntry.h index ce5848f5dd17..e5567d91814d 100644 --- a/dom/filesystem/compat/FileSystemDirectoryEntry.h +++ b/dom/filesystem/compat/FileSystemDirectoryEntry.h @@ -46,19 +46,21 @@ public: CreateReader() const; void - GetFile(const nsAString& aPath, const FileSystemFlags& aFlag, + GetFile(const Optional& aPath, const FileSystemFlags& aFlag, const Optional>& aSuccessCallback, const Optional>& aErrorCallback) const { - GetInternal(aPath, aFlag, aSuccessCallback, aErrorCallback, eGetFile); + GetInternal(aPath.WasPassed() ? aPath.Value() : EmptyString(), + aFlag, aSuccessCallback, aErrorCallback, eGetFile); } void - GetDirectory(const nsAString& aPath, const FileSystemFlags& aFlag, + GetDirectory(const Optional& aPath, const FileSystemFlags& aFlag, const Optional>& aSuccessCallback, const Optional>& aErrorCallback) const { - GetInternal(aPath, aFlag, aSuccessCallback, aErrorCallback, eGetDirectory); + GetInternal(aPath.WasPassed() ? aPath.Value() : EmptyString(), + aFlag, aSuccessCallback, aErrorCallback, eGetDirectory); } void diff --git a/dom/webidl/File.webidl b/dom/webidl/File.webidl index fef124f3a31f..5cdfdbb002b6 100644 --- a/dom/webidl/File.webidl +++ b/dom/webidl/File.webidl @@ -41,7 +41,7 @@ partial interface File { readonly attribute Date lastModifiedDate; [BinaryName="path", Func="mozilla::dom::Directory::WebkitBlinkDirectoryPickerEnabled"] - readonly attribute DOMString webkitRelativePath; + readonly attribute USVString webkitRelativePath; [GetterThrows, ChromeOnly] readonly attribute DOMString mozFullPath; diff --git a/dom/webidl/FileSystem.webidl b/dom/webidl/FileSystem.webidl index 02b6cc6890ac..8eb451521f7e 100644 --- a/dom/webidl/FileSystem.webidl +++ b/dom/webidl/FileSystem.webidl @@ -24,6 +24,6 @@ callback interface ErrorCallback { }; interface FileSystem { - readonly attribute DOMString name; + readonly attribute USVString name; readonly attribute FileSystemDirectoryEntry root; }; diff --git a/dom/webidl/FileSystemDirectoryEntry.webidl b/dom/webidl/FileSystemDirectoryEntry.webidl index 4aeb0c4fb2ac..0e41e869ce26 100644 --- a/dom/webidl/FileSystemDirectoryEntry.webidl +++ b/dom/webidl/FileSystemDirectoryEntry.webidl @@ -7,12 +7,12 @@ interface FileSystemDirectoryEntry : FileSystemEntry { FileSystemDirectoryReader createReader(); - void getFile(DOMString? path, + void getFile(optional USVString? path, optional FileSystemFlags options, optional FileSystemEntryCallback successCallback, optional ErrorCallback errorCallback); - void getDirectory(DOMString? path, + void getDirectory(optional USVString? path, optional FileSystemFlags options, optional FileSystemEntryCallback successCallback, optional ErrorCallback errorCallback); diff --git a/dom/webidl/FileSystemEntry.webidl b/dom/webidl/FileSystemEntry.webidl index 30348d3c6842..98197174e47c 100644 --- a/dom/webidl/FileSystemEntry.webidl +++ b/dom/webidl/FileSystemEntry.webidl @@ -9,25 +9,14 @@ interface FileSystemEntry { readonly attribute boolean isDirectory; [GetterThrows] - readonly attribute DOMString name; + readonly attribute USVString name; [GetterThrows] - readonly attribute DOMString fullPath; + readonly attribute USVString fullPath; readonly attribute FileSystem filesystem; /** Not implemented: - * void getMetadata(MetadataCallback successCallback, - * optional ErrorCallback errorCallback); - * void moveTo(FileSystemDirectoryEntry parent, optional DOMString? name, - * optional FileSystemEntryCallback successCallback, - * optional ErrorCallback errorCallback); - * void copyTo(FileSystemDirectoryEntry parent, optional DOMString? name, - * optional FileSystemEntryCallback successCallback, - * optional ErrorCallback errorCallback); - * DOMString toURL(); - * void remove(VoidCallback successCallback, - * optional ErrorCallback errorCallback); * void getParent(optional FileSystemEntryCallback successCallback, * optional ErrorCallback errorCallback); */ From 3a9551a834118765fa9f77e46b847fc8804543e9 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 3 Nov 2016 07:55:30 +0100 Subject: [PATCH 38/96] Bug 1284987 - Entries API - part 2 - FileSystemEntry.getParent, r=smaug --- dom/events/DataTransferItem.cpp | 4 +- dom/filesystem/compat/CallbackRunnables.cpp | 30 +++++++++++--- dom/filesystem/compat/CallbackRunnables.h | 12 +++++- .../compat/FileSystemDirectoryEntry.cpp | 11 ++--- .../compat/FileSystemDirectoryEntry.h | 10 ++--- .../compat/FileSystemDirectoryReader.cpp | 28 +++++++------ .../compat/FileSystemDirectoryReader.h | 8 ++-- dom/filesystem/compat/FileSystemEntry.cpp | 23 ++++++++++- dom/filesystem/compat/FileSystemEntry.h | 7 ++++ dom/filesystem/compat/FileSystemFileEntry.cpp | 3 +- dom/filesystem/compat/FileSystemFileEntry.h | 4 +- .../compat/FileSystemRootDirectoryEntry.cpp | 9 ++--- .../compat/FileSystemRootDirectoryEntry.h | 4 +- .../compat/FileSystemRootDirectoryReader.cpp | 6 +-- .../compat/FileSystemRootDirectoryReader.h | 2 +- dom/filesystem/compat/tests/test_basic.html | 40 +++++++++++++++++-- dom/webidl/FileSystemEntry.webidl | 6 +-- 17 files changed, 147 insertions(+), 60 deletions(-) diff --git a/dom/events/DataTransferItem.cpp b/dom/events/DataTransferItem.cpp index 3c5563c06f58..ebd13a867dd4 100644 --- a/dom/events/DataTransferItem.cpp +++ b/dom/events/DataTransferItem.cpp @@ -326,9 +326,9 @@ DataTransferItem::GetAsEntry(nsIPrincipal& aSubjectPrincipal, } RefPtr directory = Directory::Create(global, directoryFile); - entry = new FileSystemDirectoryEntry(global, directory, fs); + entry = new FileSystemDirectoryEntry(global, directory, nullptr, fs); } else { - entry = new FileSystemFileEntry(global, file, fs); + entry = new FileSystemFileEntry(global, file, nullptr, fs); } Sequence> entries; diff --git a/dom/filesystem/compat/CallbackRunnables.cpp b/dom/filesystem/compat/CallbackRunnables.cpp index 03f80a3693ae..1cd2b85b4889 100644 --- a/dom/filesystem/compat/CallbackRunnables.cpp +++ b/dom/filesystem/compat/CallbackRunnables.cpp @@ -74,18 +74,18 @@ EmptyEntriesCallbackRunnable::Run() return NS_OK; } -GetEntryHelper::GetEntryHelper(nsIGlobalObject* aGlobalObject, +GetEntryHelper::GetEntryHelper(FileSystemDirectoryEntry* aParentEntry, FileSystem* aFileSystem, FileSystemEntryCallback* aSuccessCallback, ErrorCallback* aErrorCallback, FileSystemDirectoryEntry::GetInternalType aType) - : mGlobal(aGlobalObject) + : mParentEntry(aParentEntry) , mFileSystem(aFileSystem) , mSuccessCallback(aSuccessCallback) , mErrorCallback(aErrorCallback) , mType(aType) { - MOZ_ASSERT(aGlobalObject); + MOZ_ASSERT(aParentEntry); MOZ_ASSERT(aFileSystem); MOZ_ASSERT(aSuccessCallback || aErrorCallback); } @@ -110,7 +110,8 @@ GetEntryHelper::ResolvedCallback(JSContext* aCx, JS::Handle aValue) } RefPtr entry = - new FileSystemFileEntry(mGlobal, file, mFileSystem); + new FileSystemFileEntry(mParentEntry->GetParentObject(), file, + mParentEntry, mFileSystem); mSuccessCallback->HandleEvent(*entry); return; } @@ -124,7 +125,8 @@ GetEntryHelper::ResolvedCallback(JSContext* aCx, JS::Handle aValue) } RefPtr entry = - new FileSystemDirectoryEntry(mGlobal, directory, mFileSystem); + new FileSystemDirectoryEntry(mParentEntry->GetParentObject(), directory, + mParentEntry, mFileSystem); mSuccessCallback->HandleEvent(*entry); } @@ -141,7 +143,8 @@ GetEntryHelper::Error(nsresult aError) if (mErrorCallback) { RefPtr runnable = - new ErrorCallbackRunnable(mGlobal, mErrorCallback, aError); + new ErrorCallbackRunnable(mParentEntry->GetParentObject(), + mErrorCallback, aError); DebugOnly rv = NS_DispatchToMainThread(runnable); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed"); } @@ -149,6 +152,21 @@ GetEntryHelper::Error(nsresult aError) NS_IMPL_ISUPPORTS0(GetEntryHelper); +/* static */ void +FileSystemEntryCallbackHelper::Call(const Optional>& aEntryCallback, + FileSystemEntry* aEntry) +{ + MOZ_ASSERT(aEntry); + + if (aEntryCallback.WasPassed()) { + RefPtr runnable = + new EntryCallbackRunnable(&aEntryCallback.Value(), aEntry); + + DebugOnly rv = NS_DispatchToMainThread(runnable); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed"); + } +} + /* static */ void ErrorCallbackHelper::Call(nsIGlobalObject* aGlobal, const Optional>& aErrorCallback, diff --git a/dom/filesystem/compat/CallbackRunnables.h b/dom/filesystem/compat/CallbackRunnables.h index c24e2e08172f..3e4e3ceac2e9 100644 --- a/dom/filesystem/compat/CallbackRunnables.h +++ b/dom/filesystem/compat/CallbackRunnables.h @@ -65,7 +65,7 @@ class GetEntryHelper final : public PromiseNativeHandler public: NS_DECL_ISUPPORTS - GetEntryHelper(nsIGlobalObject* aGlobalObject, + GetEntryHelper(FileSystemDirectoryEntry* aParentEntry, FileSystem* aFileSystem, FileSystemEntryCallback* aSuccessCallback, ErrorCallback* aErrorCallback, @@ -83,13 +83,21 @@ private: void Error(nsresult aError); - nsCOMPtr mGlobal; + RefPtr mParentEntry; RefPtr mFileSystem; RefPtr mSuccessCallback; RefPtr mErrorCallback; FileSystemDirectoryEntry::GetInternalType mType; }; +class FileSystemEntryCallbackHelper +{ +public: + static void + Call(const Optional>& aEntryCallback, + FileSystemEntry* aEntry); +}; + class ErrorCallbackHelper { public: diff --git a/dom/filesystem/compat/FileSystemDirectoryEntry.cpp b/dom/filesystem/compat/FileSystemDirectoryEntry.cpp index 49a956e0a750..c2b7285fe5de 100644 --- a/dom/filesystem/compat/FileSystemDirectoryEntry.cpp +++ b/dom/filesystem/compat/FileSystemDirectoryEntry.cpp @@ -25,8 +25,9 @@ NS_INTERFACE_MAP_END_INHERITING(FileSystemEntry) FileSystemDirectoryEntry::FileSystemDirectoryEntry(nsIGlobalObject* aGlobal, Directory* aDirectory, + FileSystemDirectoryEntry* aParentEntry, FileSystem* aFileSystem) - : FileSystemEntry(aGlobal, aFileSystem) + : FileSystemEntry(aGlobal, aParentEntry, aFileSystem) , mDirectory(aDirectory) { MOZ_ASSERT(aGlobal); @@ -57,12 +58,12 @@ FileSystemDirectoryEntry::GetFullPath(nsAString& aPath, ErrorResult& aRv) const } already_AddRefed -FileSystemDirectoryEntry::CreateReader() const +FileSystemDirectoryEntry::CreateReader() { MOZ_ASSERT(mDirectory); RefPtr reader = - new FileSystemDirectoryReader(GetParentObject(), Filesystem(), mDirectory); + new FileSystemDirectoryReader(this, Filesystem(), mDirectory); return reader.forget(); } @@ -71,7 +72,7 @@ FileSystemDirectoryEntry::GetInternal(const nsAString& aPath, const FileSystemFlags& aFlag, const Optional>& aSuccessCallback, const Optional>& aErrorCallback, - GetInternalType aType) const + GetInternalType aType) { MOZ_ASSERT(mDirectory); @@ -101,7 +102,7 @@ FileSystemDirectoryEntry::GetInternal(const nsAString& aPath, } RefPtr handler = - new GetEntryHelper(GetParentObject(), Filesystem(), + new GetEntryHelper(this, Filesystem(), aSuccessCallback.WasPassed() ? &aSuccessCallback.Value() : nullptr, aErrorCallback.WasPassed() diff --git a/dom/filesystem/compat/FileSystemDirectoryEntry.h b/dom/filesystem/compat/FileSystemDirectoryEntry.h index e5567d91814d..67d80cab5877 100644 --- a/dom/filesystem/compat/FileSystemDirectoryEntry.h +++ b/dom/filesystem/compat/FileSystemDirectoryEntry.h @@ -7,7 +7,6 @@ #ifndef mozilla_dom_FileSystemDirectoryEntry_h #define mozilla_dom_FileSystemDirectoryEntry_h -#include "mozilla/dom/FileSystemBinding.h" #include "mozilla/dom/FileSystemEntry.h" namespace mozilla { @@ -25,6 +24,7 @@ public: FileSystemDirectoryEntry(nsIGlobalObject* aGlobalObject, Directory* aDirectory, + FileSystemDirectoryEntry* aParentEntry, FileSystem* aFileSystem); virtual JSObject* @@ -43,12 +43,12 @@ public: GetFullPath(nsAString& aFullPath, ErrorResult& aRv) const override; virtual already_AddRefed - CreateReader() const; + CreateReader(); void GetFile(const Optional& aPath, const FileSystemFlags& aFlag, const Optional>& aSuccessCallback, - const Optional>& aErrorCallback) const + const Optional>& aErrorCallback) { GetInternal(aPath.WasPassed() ? aPath.Value() : EmptyString(), aFlag, aSuccessCallback, aErrorCallback, eGetFile); @@ -57,7 +57,7 @@ public: void GetDirectory(const Optional& aPath, const FileSystemFlags& aFlag, const Optional>& aSuccessCallback, - const Optional>& aErrorCallback) const + const Optional>& aErrorCallback) { GetInternal(aPath.WasPassed() ? aPath.Value() : EmptyString(), aFlag, aSuccessCallback, aErrorCallback, eGetDirectory); @@ -73,7 +73,7 @@ public: GetInternal(const nsAString& aPath, const FileSystemFlags& aFlag, const Optional>& aSuccessCallback, const Optional>& aErrorCallback, - GetInternalType aType) const; + GetInternalType aType); protected: virtual ~FileSystemDirectoryEntry(); diff --git a/dom/filesystem/compat/FileSystemDirectoryReader.cpp b/dom/filesystem/compat/FileSystemDirectoryReader.cpp index 1969cb2b073a..1374373783d5 100644 --- a/dom/filesystem/compat/FileSystemDirectoryReader.cpp +++ b/dom/filesystem/compat/FileSystemDirectoryReader.cpp @@ -12,7 +12,6 @@ #include "mozilla/dom/DirectoryBinding.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/PromiseNativeHandler.h" -#include "nsIGlobalObject.h" namespace mozilla { namespace dom { @@ -24,16 +23,16 @@ class PromiseHandler final : public PromiseNativeHandler public: NS_DECL_ISUPPORTS - PromiseHandler(nsIGlobalObject* aGlobalObject, + PromiseHandler(FileSystemDirectoryEntry* aParentEntry, FileSystem* aFileSystem, FileSystemEntriesCallback* aSuccessCallback, ErrorCallback* aErrorCallback) - : mGlobal(aGlobalObject) + : mParentEntry(aParentEntry) , mFileSystem(aFileSystem) , mSuccessCallback(aSuccessCallback) , mErrorCallback(aErrorCallback) { - MOZ_ASSERT(aGlobalObject); + MOZ_ASSERT(aParentEntry); MOZ_ASSERT(aFileSystem); MOZ_ASSERT(aSuccessCallback); } @@ -72,7 +71,8 @@ public: RefPtr file; if (NS_SUCCEEDED(UNWRAP_OBJECT(File, valueObj, file))) { RefPtr entry = - new FileSystemFileEntry(mGlobal, file, mFileSystem); + new FileSystemFileEntry(mParentEntry->GetParentObject(), file, + mParentEntry, mFileSystem); sequence[i] = entry; continue; } @@ -84,7 +84,8 @@ public: } RefPtr entry = - new FileSystemDirectoryEntry(mGlobal, directory, mFileSystem); + new FileSystemDirectoryEntry(mParentEntry->GetParentObject(), directory, + mParentEntry, mFileSystem); sequence[i] = entry; } @@ -96,7 +97,8 @@ public: { if (mErrorCallback) { RefPtr runnable = - new ErrorCallbackRunnable(mGlobal, mErrorCallback, + new ErrorCallbackRunnable(mParentEntry->GetParentObject(), + mErrorCallback, NS_ERROR_DOM_INVALID_STATE_ERR); DebugOnly rv = NS_DispatchToMainThread(runnable); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed"); @@ -106,7 +108,7 @@ public: private: ~PromiseHandler() {} - nsCOMPtr mGlobal; + RefPtr mParentEntry; RefPtr mFileSystem; RefPtr mSuccessCallback; RefPtr mErrorCallback; @@ -116,7 +118,7 @@ NS_IMPL_ISUPPORTS0(PromiseHandler); } // anonymous namespace -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FileSystemDirectoryReader, mParent, +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FileSystemDirectoryReader, mParentEntry, mDirectory, mFileSystem) NS_IMPL_CYCLE_COLLECTING_ADDREF(FileSystemDirectoryReader) @@ -127,15 +129,15 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FileSystemDirectoryReader) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END -FileSystemDirectoryReader::FileSystemDirectoryReader(nsIGlobalObject* aGlobal, +FileSystemDirectoryReader::FileSystemDirectoryReader(FileSystemDirectoryEntry* aParentEntry, FileSystem* aFileSystem, Directory* aDirectory) - : mParent(aGlobal) + : mParentEntry(aParentEntry) , mFileSystem(aFileSystem) , mDirectory(aDirectory) , mAlreadyRead(false) { - MOZ_ASSERT(aGlobal); + MOZ_ASSERT(aParentEntry); MOZ_ASSERT(aFileSystem); } @@ -176,7 +178,7 @@ FileSystemDirectoryReader::ReadEntries(FileSystemEntriesCallback& aSuccessCallba } RefPtr handler = - new PromiseHandler(GetParentObject(), mFileSystem, &aSuccessCallback, + new PromiseHandler(mParentEntry, mFileSystem, &aSuccessCallback, aErrorCallback.WasPassed() ? &aErrorCallback.Value() : nullptr); promise->AppendNativeHandler(handler); diff --git a/dom/filesystem/compat/FileSystemDirectoryReader.h b/dom/filesystem/compat/FileSystemDirectoryReader.h index ac3a543acc87..d568990f6a46 100644 --- a/dom/filesystem/compat/FileSystemDirectoryReader.h +++ b/dom/filesystem/compat/FileSystemDirectoryReader.h @@ -13,8 +13,6 @@ #include "nsCycleCollectionParticipant.h" #include "nsWrapperCache.h" -class nsIGlobalObject; - namespace mozilla { namespace dom { @@ -30,14 +28,14 @@ public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(FileSystemDirectoryReader) - explicit FileSystemDirectoryReader(nsIGlobalObject* aGlobalObject, + explicit FileSystemDirectoryReader(FileSystemDirectoryEntry* aDirectoryEntry, FileSystem* aFileSystem, Directory* aDirectory); nsIGlobalObject* GetParentObject() const { - return mParent; + return mParentEntry->GetParentObject(); } virtual JSObject* @@ -52,7 +50,7 @@ protected: virtual ~FileSystemDirectoryReader(); private: - nsCOMPtr mParent; + RefPtr mParentEntry; RefPtr mFileSystem; RefPtr mDirectory; diff --git a/dom/filesystem/compat/FileSystemEntry.cpp b/dom/filesystem/compat/FileSystemEntry.cpp index 6f93f15d28e3..638c2c6db9b0 100644 --- a/dom/filesystem/compat/FileSystemEntry.cpp +++ b/dom/filesystem/compat/FileSystemEntry.cpp @@ -13,7 +13,8 @@ namespace mozilla { namespace dom { -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FileSystemEntry, mParent, mFileSystem) +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FileSystemEntry, mParent, mParentEntry, + mFileSystem) NS_IMPL_CYCLE_COLLECTING_ADDREF(FileSystemEntry) NS_IMPL_CYCLE_COLLECTING_RELEASE(FileSystemEntry) @@ -35,11 +36,13 @@ FileSystemEntry::Create(nsIGlobalObject* aGlobalObject, if (aFileOrDirectory.IsFile()) { entry = new FileSystemFileEntry(aGlobalObject, aFileOrDirectory.GetAsFile(), + nullptr, aFileSystem); } else { MOZ_ASSERT(aFileOrDirectory.IsDirectory()); entry = new FileSystemDirectoryEntry(aGlobalObject, aFileOrDirectory.GetAsDirectory(), + nullptr, aFileSystem); } @@ -47,8 +50,10 @@ FileSystemEntry::Create(nsIGlobalObject* aGlobalObject, } FileSystemEntry::FileSystemEntry(nsIGlobalObject* aGlobal, + FileSystemEntry* aParentEntry, FileSystem* aFileSystem) : mParent(aGlobal) + , mParentEntry(aParentEntry) , mFileSystem(aFileSystem) { MOZ_ASSERT(aGlobal); @@ -64,5 +69,21 @@ FileSystemEntry::WrapObject(JSContext* aCx, JS::Handle aGivenProto) return FileSystemEntryBinding::Wrap(aCx, this, aGivenProto); } +void +FileSystemEntry::GetParent(const Optional>& aSuccessCallback, + const Optional>& aErrorCallback) +{ + if (!aSuccessCallback.WasPassed() && !aErrorCallback.WasPassed()) { + return; + } + + if (mParentEntry) { + FileSystemEntryCallbackHelper::Call(aSuccessCallback, mParentEntry); + return; + } + + FileSystemEntryCallbackHelper::Call(aSuccessCallback, this); +} + } // dom namespace } // mozilla namespace diff --git a/dom/filesystem/compat/FileSystemEntry.h b/dom/filesystem/compat/FileSystemEntry.h index 38ee9fd98c52..769fb8f3f95f 100644 --- a/dom/filesystem/compat/FileSystemEntry.h +++ b/dom/filesystem/compat/FileSystemEntry.h @@ -10,6 +10,7 @@ #include "mozilla/Attributes.h" #include "mozilla/ErrorResult.h" #include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/FileSystemBinding.h" #include "nsCycleCollectionParticipant.h" #include "nsIGlobalObject.h" #include "nsWrapperCache.h" @@ -60,6 +61,10 @@ public: virtual void GetFullPath(nsAString& aFullPath, ErrorResult& aRv) const = 0; + void + GetParent(const Optional>& aSuccessCallback, + const Optional>& aErrorCallback); + FileSystem* Filesystem() const { @@ -68,11 +73,13 @@ public: protected: FileSystemEntry(nsIGlobalObject* aGlobalObject, + FileSystemEntry* aParentEntry, FileSystem* aFileSystem); virtual ~FileSystemEntry(); private: nsCOMPtr mParent; + RefPtr mParentEntry; RefPtr mFileSystem; }; diff --git a/dom/filesystem/compat/FileSystemFileEntry.cpp b/dom/filesystem/compat/FileSystemFileEntry.cpp index 56e5fe056946..03747fe4e317 100644 --- a/dom/filesystem/compat/FileSystemFileEntry.cpp +++ b/dom/filesystem/compat/FileSystemFileEntry.cpp @@ -49,8 +49,9 @@ NS_INTERFACE_MAP_END_INHERITING(FileSystemEntry) FileSystemFileEntry::FileSystemFileEntry(nsIGlobalObject* aGlobal, File* aFile, + FileSystemDirectoryEntry* aParentEntry, FileSystem* aFileSystem) - : FileSystemEntry(aGlobal, aFileSystem) + : FileSystemEntry(aGlobal, aParentEntry, aFileSystem) , mFile(aFile) { MOZ_ASSERT(aGlobal); diff --git a/dom/filesystem/compat/FileSystemFileEntry.h b/dom/filesystem/compat/FileSystemFileEntry.h index eb214496ff11..b1e1251a48b2 100644 --- a/dom/filesystem/compat/FileSystemFileEntry.h +++ b/dom/filesystem/compat/FileSystemFileEntry.h @@ -12,8 +12,9 @@ namespace mozilla { namespace dom { -class File; class BlobCallback; +class File; +class FileSystemDirectoryEntry; class FileSystemFileEntry final : public FileSystemEntry { @@ -22,6 +23,7 @@ public: NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FileSystemFileEntry, FileSystemEntry) FileSystemFileEntry(nsIGlobalObject* aGlobalObject, File* aFile, + FileSystemDirectoryEntry* aParentEntry, FileSystem* aFileSystem); virtual JSObject* diff --git a/dom/filesystem/compat/FileSystemRootDirectoryEntry.cpp b/dom/filesystem/compat/FileSystemRootDirectoryEntry.cpp index 417df7e15f37..68ce62aa291c 100644 --- a/dom/filesystem/compat/FileSystemRootDirectoryEntry.cpp +++ b/dom/filesystem/compat/FileSystemRootDirectoryEntry.cpp @@ -23,7 +23,7 @@ NS_INTERFACE_MAP_END_INHERITING(FileSystemDirectoryEntry) FileSystemRootDirectoryEntry::FileSystemRootDirectoryEntry(nsIGlobalObject* aGlobal, const Sequence>& aEntries, FileSystem* aFileSystem) - : FileSystemDirectoryEntry(aGlobal, nullptr, aFileSystem) + : FileSystemDirectoryEntry(aGlobal, nullptr, nullptr, aFileSystem) , mEntries(aEntries) { MOZ_ASSERT(aGlobal); @@ -45,11 +45,10 @@ FileSystemRootDirectoryEntry::GetFullPath(nsAString& aPath, ErrorResult& aRv) co } already_AddRefed -FileSystemRootDirectoryEntry::CreateReader() const +FileSystemRootDirectoryEntry::CreateReader() { RefPtr reader = - new FileSystemRootDirectoryReader(GetParentObject(), Filesystem(), - mEntries); + new FileSystemRootDirectoryReader(this, Filesystem(), mEntries); return reader.forget(); } @@ -58,7 +57,7 @@ FileSystemRootDirectoryEntry::GetInternal(const nsAString& aPath, const FileSystemFlags& aFlag, const Optional>& aSuccessCallback, const Optional>& aErrorCallback, - GetInternalType aType) const + GetInternalType aType) { if (!aSuccessCallback.WasPassed() && !aErrorCallback.WasPassed()) { return; diff --git a/dom/filesystem/compat/FileSystemRootDirectoryEntry.h b/dom/filesystem/compat/FileSystemRootDirectoryEntry.h index 0af6384a3e65..28c151ea2452 100644 --- a/dom/filesystem/compat/FileSystemRootDirectoryEntry.h +++ b/dom/filesystem/compat/FileSystemRootDirectoryEntry.h @@ -29,7 +29,7 @@ public: GetFullPath(nsAString& aFullPath, ErrorResult& aRv) const override; virtual already_AddRefed - CreateReader() const override; + CreateReader() override; private: ~FileSystemRootDirectoryEntry(); @@ -38,7 +38,7 @@ private: GetInternal(const nsAString& aPath, const FileSystemFlags& aFlag, const Optional>& aSuccessCallback, const Optional>& aErrorCallback, - GetInternalType aType) const override; + GetInternalType aType) override; void Error(const Optional>& aErrorCallback, diff --git a/dom/filesystem/compat/FileSystemRootDirectoryReader.cpp b/dom/filesystem/compat/FileSystemRootDirectoryReader.cpp index 16b3417f5364..5b4a417527de 100644 --- a/dom/filesystem/compat/FileSystemRootDirectoryReader.cpp +++ b/dom/filesystem/compat/FileSystemRootDirectoryReader.cpp @@ -56,14 +56,14 @@ NS_IMPL_RELEASE_INHERITED(FileSystemRootDirectoryReader, NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FileSystemRootDirectoryReader) NS_INTERFACE_MAP_END_INHERITING(FileSystemDirectoryReader) -FileSystemRootDirectoryReader::FileSystemRootDirectoryReader(nsIGlobalObject* aGlobal, +FileSystemRootDirectoryReader::FileSystemRootDirectoryReader(FileSystemDirectoryEntry* aParentEntry, FileSystem* aFileSystem, const Sequence>& aEntries) - : FileSystemDirectoryReader(aGlobal, aFileSystem, nullptr) + : FileSystemDirectoryReader(aParentEntry, aFileSystem, nullptr) , mEntries(aEntries) , mAlreadyRead(false) { - MOZ_ASSERT(aGlobal); + MOZ_ASSERT(aParentEntry); MOZ_ASSERT(aFileSystem); } diff --git a/dom/filesystem/compat/FileSystemRootDirectoryReader.h b/dom/filesystem/compat/FileSystemRootDirectoryReader.h index 790aabd84899..54bca7726fd6 100644 --- a/dom/filesystem/compat/FileSystemRootDirectoryReader.h +++ b/dom/filesystem/compat/FileSystemRootDirectoryReader.h @@ -19,7 +19,7 @@ public: NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FileSystemRootDirectoryReader, FileSystemDirectoryReader) - explicit FileSystemRootDirectoryReader(nsIGlobalObject* aGlobalObject, + explicit FileSystemRootDirectoryReader(FileSystemDirectoryEntry* aParentEntry, FileSystem* aFileSystem, const Sequence>& aEntries); diff --git a/dom/filesystem/compat/tests/test_basic.html b/dom/filesystem/compat/tests/test_basic.html index 50ef3bf53ea7..085878162d8e 100644 --- a/dom/filesystem/compat/tests/test_basic.html +++ b/dom/filesystem/compat/tests/test_basic.html @@ -80,6 +80,15 @@ function test_fileEntry_createWriter() { }); } +function test_fileEntry_getParent() { + fileEntry.getParent(function(entry) { + is(fileEntry.fullPath, entry.fullPath, "Top level FileEntry should return itself as parent."); + next(); + }, function() { + ok(false, "This is wrong."); + }); +} + function test_directoryEntry() { ok("name" in directoryEntry, "We have a name."); ok("fullPath" in directoryEntry, "We have a fullPath."); @@ -115,6 +124,15 @@ function test_directoryEntry_createReader() { }); } +function test_directoryEntry_getParent() { + directoryEntry.getParent(function(entry) { + is(directoryEntry.fullPath, entry.fullPath, "Top level FileEntry should return itself as parent."); + next(); + }, function() { + ok(false, "This is wrong."); + }); +} + function test_directoryEntry_getFile_securityError() { directoryEntry.getFile("foo", { create: true }, function() { @@ -159,7 +177,7 @@ function test_directoryEntry_getFile_simple() { directoryEntry.getFile("foo.txt", {}, function(e) { is(e.name, "foo.txt", "We have the right FileEntry."); - next(); + test_getParent(e, directoryEntry); }, function(e) { ok(false, "This should not happen."); }); @@ -169,7 +187,7 @@ function test_directoryEntry_getFile_deep() { directoryEntry.getFile("subdir/bar.txt", {}, function(e) { is(e.name, "bar.txt", "We have the right FileEntry."); - next(); + test_getParent(e, null); }, function(e) { ok(false, "This should not happen."); }); @@ -219,7 +237,7 @@ function test_directoryEntry_getDirectory_simple() { directoryEntry.getDirectory("subdir", {}, function(e) { is(e.name, "subdir", "We have the right DirectoryEntry."); - next(); + test_getParent(e, directoryEntry); }, function(e) { ok(false, "This should not happen."); }); @@ -229,7 +247,7 @@ function test_directoryEntry_getDirectory_deep() { directoryEntry.getDirectory("subdir/subsubdir", {}, function(e) { is(e.name, "subsubdir", "We have the right DirectoryEntry."); - next(); + test_getParent(e, directoryEntry); }, function(e) { ok(false, "This should not happen."); }); @@ -385,6 +403,18 @@ function cleanUpTestingFiles() { script.sendAsyncMessage("entries.delete"); } +function test_getParent(entry, parentEntry) { + entry.getParent(function(e) { + ok(e, "We have a parent Entry."); + if (parentEntry) { + is (e, parentEntry, "Parent entry matches"); + } + next(); + }, function(e) { + ok(false, "This should not happen."); + }); +} + var tests = [ setup_tests, populate_entries, @@ -394,9 +424,11 @@ var tests = [ test_fileEntry, test_fileEntry_file, test_fileEntry_createWriter, + test_fileEntry_getParent, test_directoryEntry, test_directoryEntry_createReader, + test_directoryEntry_getParent, test_directoryEntry_getFile_securityError, test_directoryEntry_getFile_typeMismatchError, diff --git a/dom/webidl/FileSystemEntry.webidl b/dom/webidl/FileSystemEntry.webidl index 98197174e47c..af112282d2b0 100644 --- a/dom/webidl/FileSystemEntry.webidl +++ b/dom/webidl/FileSystemEntry.webidl @@ -16,8 +16,6 @@ interface FileSystemEntry { readonly attribute FileSystem filesystem; -/** Not implemented: - * void getParent(optional FileSystemEntryCallback successCallback, - * optional ErrorCallback errorCallback); - */ + void getParent(optional FileSystemEntryCallback successCallback, + optional ErrorCallback errorCallback); }; From d7a6ec8806e890038ecdbce024e8d26c10c17185 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 3 Nov 2016 07:55:51 +0100 Subject: [PATCH 39/96] Bug 1284987 - Entries API - part 3 - FileSystemEntry.getParent recursion, r=smaug --- dom/filesystem/compat/CallbackRunnables.cpp | 64 ++++++++++++++++++- dom/filesystem/compat/CallbackRunnables.h | 15 +++++ .../compat/FileSystemDirectoryEntry.cpp | 14 +--- dom/filesystem/compat/tests/test_basic.html | 16 +++-- 4 files changed, 89 insertions(+), 20 deletions(-) diff --git a/dom/filesystem/compat/CallbackRunnables.cpp b/dom/filesystem/compat/CallbackRunnables.cpp index 1cd2b85b4889..fd09d6692f7e 100644 --- a/dom/filesystem/compat/CallbackRunnables.cpp +++ b/dom/filesystem/compat/CallbackRunnables.cpp @@ -5,6 +5,7 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "CallbackRunnables.h" +#include "mozilla/dom/Directory.h" #include "mozilla/dom/DirectoryBinding.h" #include "mozilla/dom/DOMError.h" #include "mozilla/dom/File.h" @@ -75,17 +76,23 @@ EmptyEntriesCallbackRunnable::Run() } GetEntryHelper::GetEntryHelper(FileSystemDirectoryEntry* aParentEntry, + Directory* aDirectory, + nsTArray& aParts, FileSystem* aFileSystem, FileSystemEntryCallback* aSuccessCallback, ErrorCallback* aErrorCallback, FileSystemDirectoryEntry::GetInternalType aType) : mParentEntry(aParentEntry) + , mDirectory(aDirectory) + , mParts(aParts) , mFileSystem(aFileSystem) , mSuccessCallback(aSuccessCallback) , mErrorCallback(aErrorCallback) , mType(aType) { MOZ_ASSERT(aParentEntry); + MOZ_ASSERT(aDirectory); + MOZ_ASSERT(!aParts.IsEmpty()); MOZ_ASSERT(aFileSystem); MOZ_ASSERT(aSuccessCallback || aErrorCallback); } @@ -93,6 +100,23 @@ GetEntryHelper::GetEntryHelper(FileSystemDirectoryEntry* aParentEntry, GetEntryHelper::~GetEntryHelper() {} +void +GetEntryHelper::Run() +{ + MOZ_ASSERT(!mParts.IsEmpty()); + + ErrorResult rv; + RefPtr promise = mDirectory->Get(mParts[0], rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + Error(NS_ERROR_DOM_INVALID_STATE_ERR); + return; + } + + mParts.RemoveElementAt(0); + promise->AppendNativeHandler(this); +} + void GetEntryHelper::ResolvedCallback(JSContext* aCx, JS::Handle aValue) { @@ -102,9 +126,23 @@ GetEntryHelper::ResolvedCallback(JSContext* aCx, JS::Handle aValue) JS::Rooted obj(aCx, &aValue.toObject()); + // This is not the last part of the path. + if (!mParts.IsEmpty()) { + ContinueRunning(obj); + return; + } + + CompleteOperation(obj); +} + +void +GetEntryHelper::CompleteOperation(JSObject* aObj) +{ + MOZ_ASSERT(mParts.IsEmpty()); + if (mType == FileSystemDirectoryEntry::eGetFile) { RefPtr file; - if (NS_FAILED(UNWRAP_OBJECT(File, obj, file))) { + if (NS_FAILED(UNWRAP_OBJECT(File, aObj, file))) { Error(NS_ERROR_DOM_TYPE_MISMATCH_ERR); return; } @@ -119,7 +157,7 @@ GetEntryHelper::ResolvedCallback(JSContext* aCx, JS::Handle aValue) MOZ_ASSERT(mType == FileSystemDirectoryEntry::eGetDirectory); RefPtr directory; - if (NS_FAILED(UNWRAP_OBJECT(Directory, obj, directory))) { + if (NS_FAILED(UNWRAP_OBJECT(Directory, aObj, directory))) { Error(NS_ERROR_DOM_TYPE_MISMATCH_ERR); return; } @@ -130,6 +168,28 @@ GetEntryHelper::ResolvedCallback(JSContext* aCx, JS::Handle aValue) mSuccessCallback->HandleEvent(*entry); } +void +GetEntryHelper::ContinueRunning(JSObject* aObj) +{ + MOZ_ASSERT(!mParts.IsEmpty()); + + RefPtr directory; + if (NS_FAILED(UNWRAP_OBJECT(Directory, aObj, directory))) { + Error(NS_ERROR_DOM_TYPE_MISMATCH_ERR); + return; + } + + RefPtr entry = + new FileSystemDirectoryEntry(mParentEntry->GetParentObject(), directory, + mParentEntry, mFileSystem); + + // Update the internal values. + mParentEntry = entry; + mDirectory = directory; + + Run(); +} + void GetEntryHelper::RejectedCallback(JSContext* aCx, JS::Handle aValue) { diff --git a/dom/filesystem/compat/CallbackRunnables.h b/dom/filesystem/compat/CallbackRunnables.h index 3e4e3ceac2e9..3ff77f1a9113 100644 --- a/dom/filesystem/compat/CallbackRunnables.h +++ b/dom/filesystem/compat/CallbackRunnables.h @@ -66,11 +66,16 @@ public: NS_DECL_ISUPPORTS GetEntryHelper(FileSystemDirectoryEntry* aParentEntry, + Directory* aDirectory, + nsTArray& aParts, FileSystem* aFileSystem, FileSystemEntryCallback* aSuccessCallback, ErrorCallback* aErrorCallback, FileSystemDirectoryEntry::GetInternalType aType); + void + Run(); + virtual void ResolvedCallback(JSContext* aCx, JS::Handle aValue) override; @@ -83,10 +88,20 @@ private: void Error(nsresult aError); + void + ContinueRunning(JSObject* aObj); + + void + CompleteOperation(JSObject* aObj); + RefPtr mParentEntry; + RefPtr mDirectory; + nsTArray mParts; RefPtr mFileSystem; + RefPtr mSuccessCallback; RefPtr mErrorCallback; + FileSystemDirectoryEntry::GetInternalType mType; }; diff --git a/dom/filesystem/compat/FileSystemDirectoryEntry.cpp b/dom/filesystem/compat/FileSystemDirectoryEntry.cpp index c2b7285fe5de..e4816ac288e9 100644 --- a/dom/filesystem/compat/FileSystemDirectoryEntry.cpp +++ b/dom/filesystem/compat/FileSystemDirectoryEntry.cpp @@ -93,22 +93,14 @@ FileSystemDirectoryEntry::GetInternal(const nsAString& aPath, return; } - ErrorResult error; - RefPtr promise = mDirectory->Get(aPath, error); - if (NS_WARN_IF(error.Failed())) { - ErrorCallbackHelper::Call(GetParentObject(), aErrorCallback, - error.StealNSResult()); - return; - } - - RefPtr handler = - new GetEntryHelper(this, Filesystem(), + RefPtr helper = + new GetEntryHelper(this, mDirectory, parts, Filesystem(), aSuccessCallback.WasPassed() ? &aSuccessCallback.Value() : nullptr, aErrorCallback.WasPassed() ? &aErrorCallback.Value() : nullptr, aType); - promise->AppendNativeHandler(handler); + helper->Run(); } void diff --git a/dom/filesystem/compat/tests/test_basic.html b/dom/filesystem/compat/tests/test_basic.html index 085878162d8e..2812918c7360 100644 --- a/dom/filesystem/compat/tests/test_basic.html +++ b/dom/filesystem/compat/tests/test_basic.html @@ -177,7 +177,7 @@ function test_directoryEntry_getFile_simple() { directoryEntry.getFile("foo.txt", {}, function(e) { is(e.name, "foo.txt", "We have the right FileEntry."); - test_getParent(e, directoryEntry); + test_getParent(e, directoryEntry, /* nested */ false); }, function(e) { ok(false, "This should not happen."); }); @@ -187,7 +187,7 @@ function test_directoryEntry_getFile_deep() { directoryEntry.getFile("subdir/bar.txt", {}, function(e) { is(e.name, "bar.txt", "We have the right FileEntry."); - test_getParent(e, null); + test_getParent(e, directoryEntry, /* nested */ true); }, function(e) { ok(false, "This should not happen."); }); @@ -237,7 +237,7 @@ function test_directoryEntry_getDirectory_simple() { directoryEntry.getDirectory("subdir", {}, function(e) { is(e.name, "subdir", "We have the right DirectoryEntry."); - test_getParent(e, directoryEntry); + test_getParent(e, directoryEntry, /* nested */ false); }, function(e) { ok(false, "This should not happen."); }); @@ -247,7 +247,7 @@ function test_directoryEntry_getDirectory_deep() { directoryEntry.getDirectory("subdir/subsubdir", {}, function(e) { is(e.name, "subsubdir", "We have the right DirectoryEntry."); - test_getParent(e, directoryEntry); + test_getParent(e, directoryEntry, /* nested */ true); }, function(e) { ok(false, "This should not happen."); }); @@ -403,13 +403,15 @@ function cleanUpTestingFiles() { script.sendAsyncMessage("entries.delete"); } -function test_getParent(entry, parentEntry) { +function test_getParent(entry, parentEntry, nested) { entry.getParent(function(e) { ok(e, "We have a parent Entry."); - if (parentEntry) { + if (!nested) { is (e, parentEntry, "Parent entry matches"); + next(); + } else { + test_getParent(e, parentEntry, false); } - next(); }, function(e) { ok(false, "This should not happen."); }); From 05dea8999788c765941c01692549584db28e199f Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 3 Nov 2016 07:56:28 +0100 Subject: [PATCH 40/96] Bug 1284987 - Entries API - part 4 - Use of DOMException, r=smaug --- dom/filesystem/compat/CallbackRunnables.cpp | 6 +++--- dom/webidl/FileSystem.webidl | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/dom/filesystem/compat/CallbackRunnables.cpp b/dom/filesystem/compat/CallbackRunnables.cpp index fd09d6692f7e..6fb9f866d5f3 100644 --- a/dom/filesystem/compat/CallbackRunnables.cpp +++ b/dom/filesystem/compat/CallbackRunnables.cpp @@ -7,7 +7,7 @@ #include "CallbackRunnables.h" #include "mozilla/dom/Directory.h" #include "mozilla/dom/DirectoryBinding.h" -#include "mozilla/dom/DOMError.h" +#include "mozilla/dom/DOMException.h" #include "mozilla/dom/File.h" #include "mozilla/dom/FileBinding.h" #include "mozilla/dom/FileSystemDirectoryReaderBinding.h" @@ -56,8 +56,8 @@ ErrorCallbackRunnable::Run() return NS_ERROR_FAILURE; } - RefPtr error = new DOMError(window, mError); - mCallback->HandleEvent(*error); + RefPtr exception = DOMException::Create(mError); + mCallback->HandleEvent(*exception); return NS_OK; } diff --git a/dom/webidl/FileSystem.webidl b/dom/webidl/FileSystem.webidl index 8eb451521f7e..06b88c5ce91a 100644 --- a/dom/webidl/FileSystem.webidl +++ b/dom/webidl/FileSystem.webidl @@ -19,8 +19,7 @@ callback interface VoidCallback { }; callback interface ErrorCallback { - // This should be FileError but we are implementing just a subset of this API. - void handleEvent(DOMError error); + void handleEvent(DOMException err); }; interface FileSystem { From a69d834b0d2d480be1f891d384af4d267ac9c2d0 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 3 Nov 2016 07:57:09 +0100 Subject: [PATCH 41/96] Bug 1284987 - Entries API - part 5 - Get rid of remove methods, r=smaug --- dom/filesystem/compat/FileSystemDirectoryEntry.cpp | 8 -------- dom/filesystem/compat/FileSystemDirectoryEntry.h | 4 ---- dom/webidl/FileSystemDirectoryEntry.webidl | 5 ----- 3 files changed, 17 deletions(-) diff --git a/dom/filesystem/compat/FileSystemDirectoryEntry.cpp b/dom/filesystem/compat/FileSystemDirectoryEntry.cpp index e4816ac288e9..3157ef654aea 100644 --- a/dom/filesystem/compat/FileSystemDirectoryEntry.cpp +++ b/dom/filesystem/compat/FileSystemDirectoryEntry.cpp @@ -103,13 +103,5 @@ FileSystemDirectoryEntry::GetInternal(const nsAString& aPath, helper->Run(); } -void -FileSystemDirectoryEntry::RemoveRecursively(VoidCallback& aSuccessCallback, - const Optional>& aErrorCallback) const -{ - ErrorCallbackHelper::Call(GetParentObject(), aErrorCallback, - NS_ERROR_DOM_SECURITY_ERR); -} - } // dom namespace } // mozilla namespace diff --git a/dom/filesystem/compat/FileSystemDirectoryEntry.h b/dom/filesystem/compat/FileSystemDirectoryEntry.h index 67d80cab5877..96dc218316dc 100644 --- a/dom/filesystem/compat/FileSystemDirectoryEntry.h +++ b/dom/filesystem/compat/FileSystemDirectoryEntry.h @@ -63,10 +63,6 @@ public: aFlag, aSuccessCallback, aErrorCallback, eGetDirectory); } - void - RemoveRecursively(VoidCallback& aSuccessCallback, - const Optional>& aErrorCallback) const; - enum GetInternalType { eGetFile, eGetDirectory }; virtual void diff --git a/dom/webidl/FileSystemDirectoryEntry.webidl b/dom/webidl/FileSystemDirectoryEntry.webidl index 0e41e869ce26..f972d216fc3f 100644 --- a/dom/webidl/FileSystemDirectoryEntry.webidl +++ b/dom/webidl/FileSystemDirectoryEntry.webidl @@ -16,9 +16,4 @@ interface FileSystemDirectoryEntry : FileSystemEntry { optional FileSystemFlags options, optional FileSystemEntryCallback successCallback, optional ErrorCallback errorCallback); - - // This method is not implemented. ErrorCallback will be called - // with SecurityError - void removeRecursively(VoidCallback successCallback, - optional ErrorCallback errorCallback); }; From 9188b61d962e8140c37bb4f745be53d3f6ffb5ab Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 3 Nov 2016 07:57:34 +0100 Subject: [PATCH 42/96] Bug 1284987 - Entries API - part 6 - BlobCallback renamed, r=smaug --- dom/canvas/CanvasRenderingContextHelper.cpp | 12 ++++++------ dom/canvas/CanvasRenderingContextHelper.h | 4 ++-- dom/filesystem/compat/FileSystemFileEntry.cpp | 14 +++++++------- dom/filesystem/compat/FileSystemFileEntry.h | 4 ++-- dom/html/HTMLCanvasElement.cpp | 2 +- dom/html/HTMLCanvasElement.h | 4 ++-- dom/webidl/FileSystemFileEntry.webidl | 6 +++--- dom/webidl/HTMLCanvasElement.webidl | 4 ++-- 8 files changed, 25 insertions(+), 25 deletions(-) diff --git a/dom/canvas/CanvasRenderingContextHelper.cpp b/dom/canvas/CanvasRenderingContextHelper.cpp index e2b57abbc287..9ebb8973b393 100644 --- a/dom/canvas/CanvasRenderingContextHelper.cpp +++ b/dom/canvas/CanvasRenderingContextHelper.cpp @@ -22,7 +22,7 @@ namespace dom { void CanvasRenderingContextHelper::ToBlob(JSContext* aCx, nsIGlobalObject* aGlobal, - FileCallback& aCallback, + BlobCallback& aCallback, const nsAString& aType, JS::Handle aParams, ErrorResult& aRv) @@ -31,9 +31,9 @@ CanvasRenderingContextHelper::ToBlob(JSContext* aCx, class EncodeCallback : public EncodeCompleteCallback { public: - EncodeCallback(nsIGlobalObject* aGlobal, FileCallback* aCallback) + EncodeCallback(nsIGlobalObject* aGlobal, BlobCallback* aCallback) : mGlobal(aGlobal) - , mFileCallback(aCallback) {} + , mBlobCallback(aCallback) {} // This is called on main thread. nsresult ReceiveBlob(already_AddRefed aBlob) @@ -53,16 +53,16 @@ CanvasRenderingContextHelper::ToBlob(JSContext* aCx, RefPtr newBlob = Blob::Create(mGlobal, blob->Impl()); - mFileCallback->Call(*newBlob, rv); + mBlobCallback->Call(*newBlob, rv); mGlobal = nullptr; - mFileCallback = nullptr; + mBlobCallback = nullptr; return rv.StealNSResult(); } nsCOMPtr mGlobal; - RefPtr mFileCallback; + RefPtr mBlobCallback; }; RefPtr callback = diff --git a/dom/canvas/CanvasRenderingContextHelper.h b/dom/canvas/CanvasRenderingContextHelper.h index b047d60fcfc1..49f27d1d21cf 100644 --- a/dom/canvas/CanvasRenderingContextHelper.h +++ b/dom/canvas/CanvasRenderingContextHelper.h @@ -18,8 +18,8 @@ class ErrorResult; namespace dom { +class BlobCallback; class EncodeCompleteCallback; -class FileCallback; enum class CanvasContextType : uint8_t { NoContext, @@ -55,7 +55,7 @@ protected: nsAString& outParams, bool* const outCustomParseOptions); - void ToBlob(JSContext* aCx, nsIGlobalObject* global, FileCallback& aCallback, + void ToBlob(JSContext* aCx, nsIGlobalObject* global, BlobCallback& aCallback, const nsAString& aType, JS::Handle aParams, ErrorResult& aRv); diff --git a/dom/filesystem/compat/FileSystemFileEntry.cpp b/dom/filesystem/compat/FileSystemFileEntry.cpp index 03747fe4e317..e9a10e08fb84 100644 --- a/dom/filesystem/compat/FileSystemFileEntry.cpp +++ b/dom/filesystem/compat/FileSystemFileEntry.cpp @@ -14,10 +14,10 @@ namespace dom { namespace { -class BlobCallbackRunnable final : public Runnable +class FileCallbackRunnable final : public Runnable { public: - BlobCallbackRunnable(BlobCallback* aCallback, File* aFile) + FileCallbackRunnable(FileCallback* aCallback, File* aFile) : mCallback(aCallback) , mFile(aFile) { @@ -28,12 +28,12 @@ public: NS_IMETHOD Run() override { - mCallback->HandleEvent(mFile); + mCallback->HandleEvent(*mFile); return NS_OK; } private: - RefPtr mCallback; + RefPtr mCallback; RefPtr mFile; }; @@ -99,11 +99,11 @@ FileSystemFileEntry::CreateWriter(VoidCallback& aSuccessCallback, } void -FileSystemFileEntry::GetFile(BlobCallback& aSuccessCallback, +FileSystemFileEntry::GetFile(FileCallback& aSuccessCallback, const Optional>& aErrorCallback) const { - RefPtr runnable = - new BlobCallbackRunnable(&aSuccessCallback, mFile); + RefPtr runnable = + new FileCallbackRunnable(&aSuccessCallback, mFile); DebugOnly rv = NS_DispatchToMainThread(runnable); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed"); } diff --git a/dom/filesystem/compat/FileSystemFileEntry.h b/dom/filesystem/compat/FileSystemFileEntry.h index b1e1251a48b2..018337b3b3c3 100644 --- a/dom/filesystem/compat/FileSystemFileEntry.h +++ b/dom/filesystem/compat/FileSystemFileEntry.h @@ -12,8 +12,8 @@ namespace mozilla { namespace dom { -class BlobCallback; class File; +class FileCallback; class FileSystemDirectoryEntry; class FileSystemFileEntry final : public FileSystemEntry @@ -46,7 +46,7 @@ public: const Optional>& aErrorCallback) const; void - GetFile(BlobCallback& aSuccessCallback, + GetFile(FileCallback& aSuccessCallback, const Optional>& aErrorCallback) const; private: diff --git a/dom/html/HTMLCanvasElement.cpp b/dom/html/HTMLCanvasElement.cpp index 6b6e798513bb..88d500af54a2 100644 --- a/dom/html/HTMLCanvasElement.cpp +++ b/dom/html/HTMLCanvasElement.cpp @@ -805,7 +805,7 @@ HTMLCanvasElement::ToDataURLImpl(JSContext* aCx, void HTMLCanvasElement::ToBlob(JSContext* aCx, - FileCallback& aCallback, + BlobCallback& aCallback, const nsAString& aType, JS::Handle aParams, ErrorResult& aRv) diff --git a/dom/html/HTMLCanvasElement.h b/dom/html/HTMLCanvasElement.h index 485c32c9a41b..81c141d3cc00 100644 --- a/dom/html/HTMLCanvasElement.h +++ b/dom/html/HTMLCanvasElement.h @@ -41,9 +41,9 @@ class VRLayerChild; } // namespace gfx namespace dom { +class BlobCallback; class CanvasCaptureMediaStream; class File; -class FileCallback; class HTMLCanvasPrintState; class OffscreenCanvas; class PrintCallback; @@ -182,7 +182,7 @@ public: } void ToBlob(JSContext* aCx, - FileCallback& aCallback, + BlobCallback& aCallback, const nsAString& aType, JS::Handle aParams, ErrorResult& aRv); diff --git a/dom/webidl/FileSystemFileEntry.webidl b/dom/webidl/FileSystemFileEntry.webidl index e41c59613dd2..9dee6c44fdda 100644 --- a/dom/webidl/FileSystemFileEntry.webidl +++ b/dom/webidl/FileSystemFileEntry.webidl @@ -4,8 +4,8 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ -callback interface BlobCallback { - void handleEvent(Blob? blob); +callback interface FileCallback { + void handleEvent(File file); }; interface FileSystemFileEntry : FileSystemEntry { @@ -15,6 +15,6 @@ interface FileSystemFileEntry : FileSystemEntry { optional ErrorCallback errorCallback); [BinaryName="GetFile"] - void file (BlobCallback successCallback, + void file (FileCallback successCallback, optional ErrorCallback errorCallback); }; diff --git a/dom/webidl/HTMLCanvasElement.webidl b/dom/webidl/HTMLCanvasElement.webidl index e0e27ec522e9..15e94f15459d 100644 --- a/dom/webidl/HTMLCanvasElement.webidl +++ b/dom/webidl/HTMLCanvasElement.webidl @@ -26,7 +26,7 @@ interface HTMLCanvasElement : HTMLElement { DOMString toDataURL(optional DOMString type = "", optional any encoderOptions); [Throws] - void toBlob(FileCallback _callback, + void toBlob(BlobCallback _callback, optional DOMString type = "", optional any encoderOptions); }; @@ -64,4 +64,4 @@ interface MozCanvasPrintState callback PrintCallback = void(MozCanvasPrintState ctx); -callback FileCallback = void(Blob file); +callback BlobCallback = void(Blob blob); From 137c6280559ff4ab35bc1344e77f2f1e42888a25 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 3 Nov 2016 08:18:51 +0100 Subject: [PATCH 43/96] Bug 1289001 - NeckoParent::GetValidatedAppInfo should consider ServiceWorkers when validating HttpChannel requests, r=bkelly, r=valentin, f=asuth --- dom/workers/ServiceWorkerManager.cpp | 25 ++++ dom/workers/ServiceWorkerManager.h | 23 +++- netwerk/ipc/NeckoParent.cpp | 110 +++++++++++++++--- netwerk/ipc/NeckoParent.h | 2 + .../protocol/wyciwyg/WyciwygChannelParent.cpp | 1 + 5 files changed, 142 insertions(+), 19 deletions(-) diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp index fa9016b61bf8..55a49c1cba16 100644 --- a/dom/workers/ServiceWorkerManager.cpp +++ b/dom/workers/ServiceWorkerManager.cpp @@ -3753,6 +3753,31 @@ public: NS_IMPL_ISUPPORTS(UpdateTimerCallback, nsITimerCallback) +bool +ServiceWorkerManager::MayHaveActiveServiceWorkerInstance(ContentParent* aContent, + nsIPrincipal* aPrincipal) +{ + AssertIsOnMainThread(); + MOZ_ASSERT(aPrincipal); + + if (mShuttingDown) { + return false; + } + + nsAutoCString scopeKey; + nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + RegistrationDataPerPrincipal* data; + if (!mRegistrationInfos.Get(scopeKey, &data)) { + return false; + } + + return true; +} + void ServiceWorkerManager::ScheduleUpdateTimer(nsIPrincipal* aPrincipal, const nsACString& aScope) diff --git a/dom/workers/ServiceWorkerManager.h b/dom/workers/ServiceWorkerManager.h index 02b286c1b309..e9333ef3c1a7 100644 --- a/dom/workers/ServiceWorkerManager.h +++ b/dom/workers/ServiceWorkerManager.h @@ -131,6 +131,25 @@ public: bool IsControlled(nsIDocument* aDocument, ErrorResult& aRv); + // Return true if the given content process could potentially be executing + // service worker code with the given principal. At the current time, this + // just means that we have any registration for the origin, regardless of + // scope. This is a very weak guarantee but is the best we can do when push + // notifications can currently spin up a service worker in content processes + // without our involvement in the parent process. + // + // In the future when there is only a single ServiceWorkerManager in the + // parent process that is entirely in control of spawning and running service + // worker code, we will be able to authoritatively indicate whether there is + // an activate service worker in the given content process. At that time we + // will rename this method HasActiveServiceWorkerInstance and provide + // semantics that ensure this method returns true until the worker is known to + // have shut down in order to allow the caller to induce a crash for security + // reasons without having to worry about shutdown races with the worker. + bool + MayHaveActiveServiceWorkerInstance(ContentParent* aContent, + nsIPrincipal* aPrincipal); + void DispatchFetchEvent(const PrincipalOriginAttributes& aOriginAttributes, nsIDocument* aDoc, @@ -268,8 +287,8 @@ public: static already_AddRefed GetInstance(); - void - LoadRegistration(const ServiceWorkerRegistrationData& aRegistration); + void + LoadRegistration(const ServiceWorkerRegistrationData& aRegistration); void LoadRegistrations(const nsTArray& aRegistrations); diff --git a/netwerk/ipc/NeckoParent.cpp b/netwerk/ipc/NeckoParent.cpp index d99964707b7d..52eb9a8f5116 100644 --- a/netwerk/ipc/NeckoParent.cpp +++ b/netwerk/ipc/NeckoParent.cpp @@ -30,6 +30,7 @@ #include "mozilla/dom/network/TCPSocketParent.h" #include "mozilla/dom/network/TCPServerSocketParent.h" #include "mozilla/dom/network/UDPSocketParent.h" +#include "mozilla/dom/workers/ServiceWorkerManager.h" #include "mozilla/LoadContext.h" #include "mozilla/AppProcessChecker.h" #include "nsPrintfCString.h" @@ -55,6 +56,9 @@ using mozilla::net::PTCPServerSocketParent; using mozilla::dom::TCPServerSocketParent; using mozilla::net::PUDPSocketParent; using mozilla::dom::UDPSocketParent; +using mozilla::dom::workers::ServiceWorkerManager; +using mozilla::ipc::OptionalPrincipalInfo; +using mozilla::ipc::PrincipalInfo; using IPC::SerializedLoadContext; namespace mozilla { @@ -94,6 +98,49 @@ PBOverrideStatusFromLoadContext(const SerializedLoadContext& aSerialized) return kPBOverride_Unset; } +static already_AddRefed +GetRequestingPrincipal(const OptionalLoadInfoArgs aOptionalLoadInfoArgs) +{ + if (aOptionalLoadInfoArgs.type() != OptionalLoadInfoArgs::TLoadInfoArgs) { + return nullptr; + } + + const LoadInfoArgs& loadInfoArgs = aOptionalLoadInfoArgs.get_LoadInfoArgs(); + const OptionalPrincipalInfo& optionalPrincipalInfo = + loadInfoArgs.requestingPrincipalInfo(); + + if (optionalPrincipalInfo.type() != OptionalPrincipalInfo::TPrincipalInfo) { + return nullptr; + } + + const PrincipalInfo& principalInfo = + optionalPrincipalInfo.get_PrincipalInfo(); + + return PrincipalInfoToPrincipal(principalInfo); +} + +static already_AddRefed +GetRequestingPrincipal(const HttpChannelCreationArgs& aArgs) +{ + if (aArgs.type() != HttpChannelCreationArgs::THttpChannelOpenArgs) { + return nullptr; + } + + const HttpChannelOpenArgs& args = aArgs.get_HttpChannelOpenArgs(); + return GetRequestingPrincipal(args.loadInfo()); +} + +static already_AddRefed +GetRequestingPrincipal(const FTPChannelCreationArgs& aArgs) +{ + if (aArgs.type() != FTPChannelCreationArgs::TFTPChannelOpenArgs) { + return nullptr; + } + + const FTPChannelOpenArgs& args = aArgs.get_FTPChannelOpenArgs(); + return GetRequestingPrincipal(args.loadInfo()); +} + // Bug 1289001 - If GetValidatedAppInfo returns an error string, that usually // leads to a content crash with very little info about the cause. // We prefer to crash on the parent, so we get the reason in the crash report. @@ -109,6 +156,7 @@ void CrashWithReason(const char * reason) const char* NeckoParent::GetValidatedAppInfo(const SerializedLoadContext& aSerialized, PContentParent* aContent, + nsIPrincipal* aRequestingPrincipal, DocShellOriginAttributes& aAttrs) { if (!aSerialized.IsNotNull()) { @@ -125,24 +173,12 @@ NeckoParent::GetValidatedAppInfo(const SerializedLoadContext& aSerialized, nsTArray contextArray = static_cast(aContent)->GetManagedTabContext(); - if (contextArray.IsEmpty()) { - if (UsingNeckoIPCSecurity()) { - CrashWithReason("GetValidatedAppInfo | ContentParent does not have any PBrowsers"); - return "ContentParent does not have any PBrowsers"; - } - - // We are running xpcshell tests - aAttrs = aSerialized.mOriginAttributes; - return nullptr; - } nsAutoCString debugString; for (uint32_t i = 0; i < contextArray.Length(); i++) { TabContext tabContext = contextArray[i]; uint32_t appId = tabContext.OwnOrContainingAppId(); - bool inBrowserElement = aSerialized.IsNotNull() ? - aSerialized.mOriginAttributes.mInIsolatedMozBrowser : - tabContext.IsIsolatedMozBrowserElement(); + bool inBrowserElement = aSerialized.mOriginAttributes.mInIsolatedMozBrowser; if (appId == NECKO_UNKNOWN_APP_ID) { debugString.Append("u,"); @@ -179,6 +215,34 @@ NeckoParent::GetValidatedAppInfo(const SerializedLoadContext& aSerialized, return nullptr; } + // This may be a ServiceWorker: when a push notification is received, FF wakes + // up the corrisponding service worker so that it can manage the PushEvent. At + // that time we probably don't have any valid tabcontext, but still, we want + // to support http channel requests coming from that ServiceWorker. + if (aRequestingPrincipal) { + MOZ_ASSERT(BasePrincipal::Cast(aRequestingPrincipal)->OriginAttributesRef() == + aSerialized.mOriginAttributes); + + RefPtr swm = ServiceWorkerManager::GetInstance(); + if (swm && + swm->MayHaveActiveServiceWorkerInstance(static_cast(aContent), + aRequestingPrincipal)) { + aAttrs = aSerialized.mOriginAttributes; + return nullptr; + } + } + + if (contextArray.IsEmpty()) { + if (UsingNeckoIPCSecurity()) { + CrashWithReason("GetValidatedAppInfo | ContentParent does not have any PBrowsers"); + return "ContentParent does not have any PBrowsers"; + } + + // We are running xpcshell tests + aAttrs = aSerialized.mOriginAttributes; + return nullptr; + } + nsAutoCString errorString; errorString.Append("GetValidatedAppInfo | App does not have permission -"); errorString.Append(debugString); @@ -195,10 +259,12 @@ const char * NeckoParent::CreateChannelLoadContext(const PBrowserOrId& aBrowser, PContentParent* aContent, const SerializedLoadContext& aSerialized, + nsIPrincipal* aRequestingPrincipal, nsCOMPtr &aResult) { DocShellOriginAttributes attrs; - const char* error = GetValidatedAppInfo(aSerialized, aContent, attrs); + const char* error = GetValidatedAppInfo(aSerialized, aContent, + aRequestingPrincipal, attrs); if (error) { return error; } @@ -244,9 +310,13 @@ NeckoParent::AllocPHttpChannelParent(const PBrowserOrId& aBrowser, const SerializedLoadContext& aSerialized, const HttpChannelCreationArgs& aOpenArgs) { + nsCOMPtr requestingPrincipal = + GetRequestingPrincipal(aOpenArgs); + nsCOMPtr loadContext; const char *error = CreateChannelLoadContext(aBrowser, Manager(), - aSerialized, loadContext); + aSerialized, requestingPrincipal, + loadContext); if (error) { printf_stderr("NeckoParent::AllocPHttpChannelParent: " "FATAL error: %s: KILLING CHILD PROCESS\n", @@ -307,9 +377,13 @@ NeckoParent::AllocPFTPChannelParent(const PBrowserOrId& aBrowser, const SerializedLoadContext& aSerialized, const FTPChannelCreationArgs& aOpenArgs) { + nsCOMPtr requestingPrincipal = + GetRequestingPrincipal(aOpenArgs); + nsCOMPtr loadContext; const char *error = CreateChannelLoadContext(aBrowser, Manager(), - aSerialized, loadContext); + aSerialized, requestingPrincipal, + loadContext); if (error) { printf_stderr("NeckoParent::AllocPFTPChannelParent: " "FATAL error: %s: KILLING CHILD PROCESS\n", @@ -377,7 +451,9 @@ NeckoParent::AllocPWebSocketParent(const PBrowserOrId& browser, { nsCOMPtr loadContext; const char *error = CreateChannelLoadContext(browser, Manager(), - serialized, loadContext); + serialized, + nullptr, + loadContext); if (error) { printf_stderr("NeckoParent::AllocPWebSocketParent: " "FATAL error: %s: KILLING CHILD PROCESS\n", diff --git a/netwerk/ipc/NeckoParent.h b/netwerk/ipc/NeckoParent.h index 0e15d30b4c97..f0adf12f64a0 100644 --- a/netwerk/ipc/NeckoParent.h +++ b/netwerk/ipc/NeckoParent.h @@ -37,6 +37,7 @@ public: static const char * GetValidatedAppInfo(const SerializedLoadContext& aSerialized, PContentParent* aBrowser, + nsIPrincipal* aRequestingPrincipal, mozilla::DocShellOriginAttributes& aAttrs); /* @@ -51,6 +52,7 @@ public: CreateChannelLoadContext(const PBrowserOrId& aBrowser, PContentParent* aContent, const SerializedLoadContext& aSerialized, + nsIPrincipal* aRequestingPrincipal, nsCOMPtr &aResult); virtual void ActorDestroy(ActorDestroyReason aWhy) override; diff --git a/netwerk/protocol/wyciwyg/WyciwygChannelParent.cpp b/netwerk/protocol/wyciwyg/WyciwygChannelParent.cpp index 487b77102da2..c5412e75080c 100644 --- a/netwerk/protocol/wyciwyg/WyciwygChannelParent.cpp +++ b/netwerk/protocol/wyciwyg/WyciwygChannelParent.cpp @@ -145,6 +145,7 @@ WyciwygChannelParent::SetupAppData(const IPC::SerializedLoadContext& loadContext const char* error = NeckoParent::CreateChannelLoadContext(aParent, Manager()->Manager(), loadContext, + nullptr, mLoadContext); if (error) { printf_stderr("WyciwygChannelParent::SetupAppData: FATAL ERROR: %s\n", From 94917617953da52168a79effde9e19269c1b6a77 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 3 Nov 2016 08:19:16 +0100 Subject: [PATCH 44/96] Bug 1289001 - Test about Containers, SW and PushNotifications, r=valentin --- .../test/browser/browser.ini | 4 + .../test/browser/browser_pushnotification.js | 91 +++++++++++++++++++ .../test/browser/pushserver.sjs | 25 +++++ .../test/browser/pushworker.js | 4 + 4 files changed, 124 insertions(+) create mode 100644 browser/components/contextualidentity/test/browser/browser_pushnotification.js create mode 100644 browser/components/contextualidentity/test/browser/pushserver.sjs create mode 100644 browser/components/contextualidentity/test/browser/pushworker.js diff --git a/browser/components/contextualidentity/test/browser/browser.ini b/browser/components/contextualidentity/test/browser/browser.ini index 7d614d1347b7..9cf30bbc4942 100644 --- a/browser/components/contextualidentity/test/browser/browser.ini +++ b/browser/components/contextualidentity/test/browser/browser.ini @@ -7,6 +7,8 @@ support-files = file_set_storages.html serviceworker.html worker.js + pushworker.js + pushserver.sjs [browser_aboutURLs.js] [browser_eme.js] @@ -28,3 +30,5 @@ tags = openwindow [browser_middleClick.js] [browser_imageCache.js] [browser_count_and_remove.js] +[browser_pushnotification.js] +run-if = e10s diff --git a/browser/components/contextualidentity/test/browser/browser_pushnotification.js b/browser/components/contextualidentity/test/browser/browser_pushnotification.js new file mode 100644 index 000000000000..daa68a6e8ac7 --- /dev/null +++ b/browser/components/contextualidentity/test/browser/browser_pushnotification.js @@ -0,0 +1,91 @@ +let { classes: Cc, interfaces: Ci } = Components; + +const URI = "http://example.com/browser/browser/components/contextualidentity/test/browser/"; + +add_task(function* setup() { + return SpecialPowers.pushPrefEnv({"set": [ + ["privacy.userContext.enabled", true], + ["dom.push.enabled", true], + ["dom.push.connection.enabled", true], + ["dom.push.maxRecentMessageIDsPerSubscription", 0], + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ]}); +}); + +add_task(function* test() { + info("Creation of a container tab..."); + let tab = gBrowser.addTab(URI + "empty_file.html", {userContextId: 1}); + let browser = gBrowser.getBrowserForTab(tab); + yield BrowserTestUtils.browserLoaded(browser); + + info("Let's creating a second tab..."); + let tab2 = gBrowser.addTab(URI + "empty_file.html", {userContextId: 2}); + let browser2 = gBrowser.getBrowserForTab(tab2); + yield BrowserTestUtils.browserLoaded(browser2); + + info("Registration of the ServiceWorker..."); + let data = yield ContentTask.spawn(browser, URI, function(url) { + return new Promise((resolve, reject) => { + content.navigator.serviceWorker.register(url + "pushworker.js", {scope: url}) + .then( + (registration) => { + resolve({ scope: registration.scope, + oa: content.document.nodePrincipal.originAttributes}); }, + () => { reject(); }); + }); + }); + + info("Closing the tab..."); + yield BrowserTestUtils.removeTab(tab); + + info("Creating originAttributes suffix..."); + let originAttributes = self.ChromeUtils.originAttributesToSuffix(data.oa); + + info("Sending push event..."); + let swm = Cc["@mozilla.org/serviceworkers/manager;1"] + .getService(Ci.nsIServiceWorkerManager); + swm.sendPushEvent(originAttributes, data.scope); + + info("Let's wait for an answer on the second tab..."); + yield ContentTask.spawn(browser2, URI, function(url) { + return new Promise((resolve, reject) => { + function testResponse() { + let xhr = new content.window.XMLHttpRequest(); + xhr.open("GET", url + "pushserver.sjs"); + xhr.send(); + xhr.onload = function() { + if (xhr.response == 42) { + ok("The SW answered correctly."); + resolve(); + } else { + setTimeout(testResponse, 1000); + } + } + } + + testResponse(); + }); + }); + + info("Creation of a container tab..."); + tab = gBrowser.addTab(URI + "empty_file.html", {userContextId: 1}); + browser = gBrowser.getBrowserForTab(tab); + yield BrowserTestUtils.browserLoaded(browser); + + info("Unregistration of the ServiceWorker..."); + yield ContentTask.spawn(browser, URI, function(url) { + return new Promise((resolve, reject) => { + content.navigator.serviceWorker.getRegistration(url) + .then((reg) => { return reg.unregister(url); }) + .then(resolve); + }); + }); + + info("Closing the first tab..."); + yield BrowserTestUtils.removeTab(tab); + + info("Closing the second tab..."); + yield BrowserTestUtils.removeTab(tab2); +}); diff --git a/browser/components/contextualidentity/test/browser/pushserver.sjs b/browser/components/contextualidentity/test/browser/pushserver.sjs new file mode 100644 index 000000000000..93ca3e4661c1 --- /dev/null +++ b/browser/components/contextualidentity/test/browser/pushserver.sjs @@ -0,0 +1,25 @@ +const CC = Components.Constructor; +const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream"); + +function handleRequest(request, response) +{ + response.setHeader("Content-Type", "text/plain", false); + + if (request.method == "POST") { + var body = new BinaryInputStream(request.bodyInputStream); + + var avail; + var bytes = []; + while ((avail = body.available()) > 0) { + Array.prototype.push.apply(bytes, body.readByteArray(avail)); + } + + var data = String.fromCharCode.apply(null, bytes); + setState("response", data); + response.write("sure!"); + } else { + response.write(getState("response")); + } +} diff --git a/browser/components/contextualidentity/test/browser/pushworker.js b/browser/components/contextualidentity/test/browser/pushworker.js new file mode 100644 index 000000000000..0547df2df224 --- /dev/null +++ b/browser/components/contextualidentity/test/browser/pushworker.js @@ -0,0 +1,4 @@ +self.onpush = function() { + fetch("http://example.com/browser/browser/components/contextualidentity/test/browser/pushserver.sjs", + { method: "POST", body: "42" }); +} From aecb80d69bffb226d3e78820b7550e0dc30e916a Mon Sep 17 00:00:00 2001 From: Jork K Date: Wed, 2 Nov 2016 08:24:00 +0100 Subject: [PATCH 45/96] Bug 1314568 - Silence warning from nsEffectiveTLDService::GetBaseDomain(). r=mystor --- dom/base/DocGroup.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dom/base/DocGroup.cpp b/dom/base/DocGroup.cpp index e73c4405f6ca..226879985cb8 100644 --- a/dom/base/DocGroup.cpp +++ b/dom/base/DocGroup.cpp @@ -16,7 +16,9 @@ DocGroup::GetKey(nsIPrincipal* aPrincipal, nsACString& aKey) aKey.Truncate(); nsCOMPtr uri; nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri)); - if (NS_SUCCEEDED(rv)) { + // GetBaseDomain works fine if |uri| is null, but it outputs a warning + // which ends up cluttering the logs. + if (NS_SUCCEEDED(rv) && uri) { nsCOMPtr tldService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID); if (tldService) { From 93ad132f709d4658ec4c911fd313532d254e9bdc Mon Sep 17 00:00:00 2001 From: Mike Taylor Date: Tue, 1 Nov 2016 07:53:00 +0100 Subject: [PATCH 46/96] Bug 1314214 - Remove Yahoo! Japan finance and homepage UA overrides. r=karlcow --- mobile/android/app/ua-update.json.in | 5 ----- 1 file changed, 5 deletions(-) diff --git a/mobile/android/app/ua-update.json.in b/mobile/android/app/ua-update.json.in index 6b9108e4f20f..6aaa90fa9652 100644 --- a/mobile/android/app/ua-update.json.in +++ b/mobile/android/app/ua-update.json.in @@ -3,11 +3,6 @@ // Send these sites a custom user-agent. Bugs should be included with an entry. // NOTE: trailing commas are not valid JSON and will prevent the CDN from syncing. { - // bug 1177298, yahoo.co.jp properties - "www.yahoo.co.jp": "Mozilla/5.0 (Linux; Android 5.0.2; Galaxy Nexus Build/IMM76B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.93 Mobile Safari/537.36", - // send override to m. in addition to www. to prevent infinite 302 loop - "m.yahoo.co.jp": "Mozilla/5.0 (Linux; Android 5.0.2; Galaxy Nexus Build/IMM76B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.93 Mobile Safari/537.36", - "finance.yahoo.co.jp": "Mozilla/5.0 (Linux; Android 5.0.2; Galaxy Nexus Build/IMM76B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.93 Mobile Safari/537.36", "weather.yahoo.co.jp": "Mozilla/5.0 (Linux; Android 5.0.2; Galaxy Nexus Build/IMM76B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.93 Mobile Safari/537.36", // bug 1177298, lohaco.jp "lohaco.jp": "Mozilla/5.0 (Linux; Android 5.0.2; Galaxy Nexus Build/IMM76B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.93 Mobile Safari/537.36", From fb3bba701b43438db1850b2f3b5ba5e7db82eade Mon Sep 17 00:00:00 2001 From: Tobias Schneider Date: Wed, 2 Nov 2016 00:25:10 -0700 Subject: [PATCH 47/96] Bug 1314027 - r=mstange --- dom/base/DOMIntersectionObserver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dom/base/DOMIntersectionObserver.cpp b/dom/base/DOMIntersectionObserver.cpp index 1fbbad4d4609..29b3aeba888f 100644 --- a/dom/base/DOMIntersectionObserver.cpp +++ b/dom/base/DOMIntersectionObserver.cpp @@ -241,8 +241,8 @@ EdgeInclusiveIntersection(const nsRect& aRect, const nsRect& aOtherRect) void DOMIntersectionObserver::Update(nsIDocument* aDocument, DOMHighResTimeStamp time) { - Element* root; - nsIFrame* rootFrame; + Element* root = nullptr; + nsIFrame* rootFrame = nullptr; nsRect rootRect; if (mRoot) { From b30144022d9cbe50382e1e004e245fdd5705b655 Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Wed, 2 Nov 2016 07:26:41 -0500 Subject: [PATCH 48/96] Bug 1312100 - Enable a11y with e10s for Linux/OSX on all channels. r=felipe MozReview-Commit-ID: Ex7T5S6MNJR --- toolkit/xre/nsAppRunner.cpp | 76 +++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 41 deletions(-) diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index 4ef710c23ce3..c34e9025f19b 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -4820,17 +4820,18 @@ enum { const char* kAccessibilityLastRunDatePref = "accessibility.lastLoadDate"; const char* kAccessibilityLoadedLastSessionPref = "accessibility.loadedInLastSession"; +#if defined(XP_WIN) static inline uint32_t PRTimeToSeconds(PRTime t_usec) { PRTime usec_per_sec = PR_USEC_PER_SEC; return uint32_t(t_usec /= usec_per_sec); } +#endif const char* kForceEnableE10sPref = "browser.tabs.remote.force-enable"; const char* kForceDisableE10sPref = "browser.tabs.remote.force-disable"; - uint32_t MultiprocessBlockPolicy() { if (gMultiprocessBlockPolicyInitialized) { @@ -4855,50 +4856,43 @@ MultiprocessBlockPolicy() { return gMultiprocessBlockPolicy; } - bool disabledForA11y = false; - - /** - * Avoids enabling e10s if accessibility has recently loaded. Performs the - * following checks: - * 1) Checks a pref indicating if a11y loaded in the last session. This pref - * is set in nsBrowserGlue.js. If a11y was loaded in the last session we - * do not enable e10s in this session. - * 2) Accessibility stores a last run date (PR_IntervalNow) when it is - * initialized (see nsBaseWidget.cpp). We check if this pref exists and - * compare it to now. If a11y hasn't run in an extended period of time or - * if the date pref does not exist we load e10s. - */ - disabledForA11y = Preferences::GetBool(kAccessibilityLoadedLastSessionPref, false); - if (!disabledForA11y && - Preferences::HasUserValue(kAccessibilityLastRunDatePref)) { - #define ONE_WEEK_IN_SECONDS (60*60*24*7) - uint32_t a11yRunDate = Preferences::GetInt(kAccessibilityLastRunDatePref, 0); - MOZ_ASSERT(0 != a11yRunDate); - // If a11y hasn't run for a period of time, clear the pref and load e10s - uint32_t now = PRTimeToSeconds(PR_Now()); - uint32_t difference = now - a11yRunDate; - if (difference > ONE_WEEK_IN_SECONDS || !a11yRunDate) { - Preferences::ClearUser(kAccessibilityLastRunDatePref); - } else { - disabledForA11y = true; +#if defined(XP_WIN) + // These checks are currently only in use under WinXP + if (!IsVistaOrLater()) { + bool disabledForA11y = false; + /** + * Avoids enabling e10s if accessibility has recently loaded. Performs the + * following checks: + * 1) Checks a pref indicating if a11y loaded in the last session. This pref + * is set in nsBrowserGlue.js. If a11y was loaded in the last session we + * do not enable e10s in this session. + * 2) Accessibility stores a last run date (PR_IntervalNow) when it is + * initialized (see nsBaseWidget.cpp). We check if this pref exists and + * compare it to now. If a11y hasn't run in an extended period of time or + * if the date pref does not exist we load e10s. + */ + disabledForA11y = Preferences::GetBool(kAccessibilityLoadedLastSessionPref, false); + if (!disabledForA11y && + Preferences::HasUserValue(kAccessibilityLastRunDatePref)) { + #define ONE_WEEK_IN_SECONDS (60*60*24*7) + uint32_t a11yRunDate = Preferences::GetInt(kAccessibilityLastRunDatePref, 0); + MOZ_ASSERT(0 != a11yRunDate); + // If a11y hasn't run for a period of time, clear the pref and load e10s + uint32_t now = PRTimeToSeconds(PR_Now()); + uint32_t difference = now - a11yRunDate; + if (difference > ONE_WEEK_IN_SECONDS || !a11yRunDate) { + Preferences::ClearUser(kAccessibilityLastRunDatePref); + } else { + disabledForA11y = true; + } + } + if (disabledForA11y) { + gMultiprocessBlockPolicy = kE10sDisabledForAccessibility; + return gMultiprocessBlockPolicy; } } - - bool doAccessibilityCheck = true; -#if defined(MOZ_WIDGET_GTK) && !defined(RELEASE_OR_BETA) - // For linux nightly and aurora builds skip accessibility - // checks. - doAccessibilityCheck = false; -#elif defined(XP_WIN) - // For Windows Vista and up, skip accessibility checks. - doAccessibilityCheck = !IsVistaOrLater(); #endif - if (doAccessibilityCheck && disabledForA11y) { - gMultiprocessBlockPolicy = kE10sDisabledForAccessibility; - return gMultiprocessBlockPolicy; - } - /** * Avoids enabling e10s for Windows XP users on the release channel. */ From 54662d0a8cdc44a8b71547a085190e69e4095a53 Mon Sep 17 00:00:00 2001 From: Arthur Edelstein Date: Thu, 3 Nov 2016 00:19:00 +0100 Subject: [PATCH 49/96] Bug 1312541 - Test isolation of cookies. r=baku, r=timhuang --- .../originattributes/test/browser/browser.ini | 1 + .../test/browser/browser_cookieIsolation.js | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 browser/components/originattributes/test/browser/browser_cookieIsolation.js diff --git a/browser/components/originattributes/test/browser/browser.ini b/browser/components/originattributes/test/browser/browser.ini index a3bc78538982..8c0c8d219aa5 100644 --- a/browser/components/originattributes/test/browser/browser.ini +++ b/browser/components/originattributes/test/browser/browser.ini @@ -33,6 +33,7 @@ support-files = worker_deblobify.js [browser_broadcastChannel.js] +[browser_cookieIsolation.js] [browser_favicon_firstParty.js] [browser_favicon_userContextId.js] [browser_firstPartyIsolation.js] diff --git a/browser/components/originattributes/test/browser/browser_cookieIsolation.js b/browser/components/originattributes/test/browser/browser_cookieIsolation.js new file mode 100644 index 000000000000..6259723ba1d2 --- /dev/null +++ b/browser/components/originattributes/test/browser/browser_cookieIsolation.js @@ -0,0 +1,31 @@ +/** + * Bug 1312541 - A test case for document.cookie isolation. + */ + +const TEST_PAGE = "http://mochi.test:8888/browser/browser/components/" + + "originattributes/test/browser/file_firstPartyBasic.html"; + +// Use a random key so we don't access it in later tests. +const key = "key" + Math.random().toString(); +const re = new RegExp(key + "=([0-9\.]+)"); + +// Define the testing function +function* doTest(aBrowser) { + return yield ContentTask.spawn(aBrowser, {key, re}, + function ({key, re}) { + let result = re.exec(content.document.cookie); + if (result) { + return result[1]; + } + // No value is found, so we create one. + let value = Math.random().toString(); + content.document.cookie = key + "=" + value; + return value; + }); +} + +registerCleanupFunction(() => { + Services.cookies.removeAll(); +}); + +IsolationTestTools.runTests(TEST_PAGE, doTest); From ed70f39ba246ca4f0b02498e593398c2c7c17f44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Tue, 1 Nov 2016 08:54:19 -0700 Subject: [PATCH 50/96] Bug 1073307: Add tests for timeZone support in Date.prototype.toLocaleString. r=Waldo --- js/src/tests/Intl/Date/browser.js | 0 js/src/tests/Intl/Date/shell.js | 0 .../Intl/Date/toLocaleDateString_timeZone.js | 77 +++++++++++++++++++ .../Intl/Date/toLocaleString_timeZone.js | 77 +++++++++++++++++++ .../Intl/Date/toLocaleTimeString_timeZone.js | 77 +++++++++++++++++++ 5 files changed, 231 insertions(+) create mode 100644 js/src/tests/Intl/Date/browser.js create mode 100644 js/src/tests/Intl/Date/shell.js create mode 100644 js/src/tests/Intl/Date/toLocaleDateString_timeZone.js create mode 100644 js/src/tests/Intl/Date/toLocaleString_timeZone.js create mode 100644 js/src/tests/Intl/Date/toLocaleTimeString_timeZone.js diff --git a/js/src/tests/Intl/Date/browser.js b/js/src/tests/Intl/Date/browser.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/js/src/tests/Intl/Date/shell.js b/js/src/tests/Intl/Date/shell.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/js/src/tests/Intl/Date/toLocaleDateString_timeZone.js b/js/src/tests/Intl/Date/toLocaleDateString_timeZone.js new file mode 100644 index 000000000000..47b77dbe89d9 --- /dev/null +++ b/js/src/tests/Intl/Date/toLocaleDateString_timeZone.js @@ -0,0 +1,77 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const defaultLocale = "en-US"; +const defaultDate = Date.UTC(2012, 12-1, 6, 12, 0, 0); +const defaultOptions = { timeZoneName: "short" }; + +const tests = [ + { + timeZone: "UTC", + result: "12/6/2012, GMT", + }, + { + timeZone: "America/Los_Angeles", + result: "12/6/2012, PST", + }, + { + timeZone: "Europe/Berlin", locale: "de", + options: { timeZoneName: "short" }, + result: "6.12.2012, MEZ", + }, + { + timeZone: "Europe/Paris", locale: "fr", + options: { timeZoneName: "long" }, + result: "06/12/2012 Ć  heure normale dā€™Europe centrale", + }, + { + timeZone: "Asia/Shanghai", locale: "zh-Hans-CN", + options: { timeZoneName: "long" }, + result: "2012/12/6 äø­å›½ę ‡å‡†ę—¶é—“", + }, + { + timeZone: { toString: () => "Australia/Melbourne" }, locale: "en-AU", + result: "06/12/2012, AEDT", + }, +]; + +for (let {timeZone, result, locale = defaultLocale, date = defaultDate, options = defaultOptions} of tests) { + let s = new Date(date).toLocaleDateString(locale, Object.assign({timeZone}, options)); + assertEq(s, result); +} + + +// |undefined| or absent "timeZone" option selects the default time zone. +{ + let locale = defaultLocale; + let date = defaultDate; + let options = defaultOptions; + + let absentTz = new Date(date).toLocaleDateString(locale, Object.assign({}, options)); + let undefinedTz = new Date(date).toLocaleDateString(locale, Object.assign({timeZone: undefined}, options)); + assertEq(undefinedTz, absentTz); +} + + +// RangeError is thrown for invalid time zone names. +for (let timeZone of ["", "undefined", "UTC\0", "Vienna", "Africa", "America/NewYork"]) { + assertThrowsInstanceOf(() => { + new Date(defaultDate).toLocaleDateString(undefined, {timeZone}); + }, RangeError); +} + +// RangeError is thrown for these values, because ToString() +// isn't a valid time zone name. +for (let timeZone of [null, 0, 0.5, true, false, [], {}, function() {}]) { + assertThrowsInstanceOf(() => { + new Date(defaultDate).toLocaleDateString(undefined, {timeZone}); + }, RangeError); +} + +// ToString() throws TypeError. +assertThrowsInstanceOf(() => { + new Date(defaultDate).toLocaleDateString(undefined, {timeZone: Symbol()}); +}, TypeError); + + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/Intl/Date/toLocaleString_timeZone.js b/js/src/tests/Intl/Date/toLocaleString_timeZone.js new file mode 100644 index 000000000000..63e370034280 --- /dev/null +++ b/js/src/tests/Intl/Date/toLocaleString_timeZone.js @@ -0,0 +1,77 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const defaultLocale = "en-US"; +const defaultDate = Date.UTC(2012, 12-1, 6, 12, 0, 0); +const defaultOptions = {}; + +const tests = [ + { + timeZone: "UTC", + result: "12/6/2012, 12:00:00 PM", + }, + { + timeZone: "America/Los_Angeles", + result: "12/6/2012, 4:00:00 AM", + }, + { + timeZone: "Europe/Berlin", locale: "de", + options: { timeZoneName: "short" }, + result: "6.12.2012, 13:00:00 MEZ", + }, + { + timeZone: "Europe/Paris", locale: "fr", + options: { timeZoneName: "long" }, + result: "06/12/2012 Ć  13:00:00 heure normale dā€™Europe centrale", + }, + { + timeZone: "Asia/Shanghai", locale: "zh-Hans-CN", + options: { timeZoneName: "long" }, + result: "2012/12/6 äø­å›½ę ‡å‡†ę—¶é—“ äø‹åˆ8:00:00", + }, + { + timeZone: { toString: () => "Australia/Melbourne" }, locale: "en-AU", + result: "06/12/2012, 11:00:00 pm", + }, +]; + +for (let {timeZone, result, locale = defaultLocale, date = defaultDate, options = defaultOptions} of tests) { + let s = new Date(date).toLocaleString(locale, Object.assign({timeZone}, options)); + assertEq(s, result); +} + + +// |undefined| or absent "timeZone" option selects the default time zone. +{ + let locale = defaultLocale; + let date = defaultDate; + let options = defaultOptions; + + let absentTz = new Date(date).toLocaleString(locale, Object.assign({}, options)); + let undefinedTz = new Date(date).toLocaleString(locale, Object.assign({timeZone: undefined}, options)); + assertEq(undefinedTz, absentTz); +} + + +// RangeError is thrown for invalid time zone names. +for (let timeZone of ["", "undefined", "UTC\0", "Vienna", "Africa", "America/NewYork"]) { + assertThrowsInstanceOf(() => { + new Date(defaultDate).toLocaleString(undefined, {timeZone}); + }, RangeError); +} + +// RangeError is thrown for these values, because ToString() +// isn't a valid time zone name. +for (let timeZone of [null, 0, 0.5, true, false, [], {}, function() {}]) { + assertThrowsInstanceOf(() => { + new Date(defaultDate).toLocaleString(undefined, {timeZone}); + }, RangeError); +} + +// ToString() throws TypeError. +assertThrowsInstanceOf(() => { + new Date(defaultDate).toLocaleString(undefined, {timeZone: Symbol()}); +}, TypeError); + + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/Intl/Date/toLocaleTimeString_timeZone.js b/js/src/tests/Intl/Date/toLocaleTimeString_timeZone.js new file mode 100644 index 000000000000..14799a28747e --- /dev/null +++ b/js/src/tests/Intl/Date/toLocaleTimeString_timeZone.js @@ -0,0 +1,77 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +const defaultLocale = "en-US"; +const defaultDate = Date.UTC(2012, 12-1, 6, 12, 0, 0); +const defaultOptions = {}; + +const tests = [ + { + timeZone: "UTC", + result: "12:00:00 PM", + }, + { + timeZone: "America/Los_Angeles", + result: "4:00:00 AM", + }, + { + timeZone: "Europe/Berlin", locale: "de", + options: { timeZoneName: "short" }, + result: "13:00:00 MEZ", + }, + { + timeZone: "Europe/Paris", locale: "fr", + options: { timeZoneName: "long" }, + result: "13:00:00 heure normale dā€™Europe centrale", + }, + { + timeZone: "Asia/Shanghai", locale: "zh-Hans-CN", + options: { timeZoneName: "long" }, + result: "äø­å›½ę ‡å‡†ę—¶é—“ äø‹åˆ8:00:00", + }, + { + timeZone: { toString: () => "Australia/Melbourne" }, locale: "en-AU", + result: "11:00:00 pm", + }, +]; + +for (let {timeZone, result, locale = defaultLocale, date = defaultDate, options = defaultOptions} of tests) { + let s = new Date(date).toLocaleTimeString(locale, Object.assign({timeZone}, options)); + assertEq(s, result); +} + + +// |undefined| or absent "timeZone" option selects the default time zone. +{ + let locale = defaultLocale; + let date = defaultDate; + let options = defaultOptions; + + let absentTz = new Date(date).toLocaleTimeString(locale, Object.assign({}, options)); + let undefinedTz = new Date(date).toLocaleTimeString(locale, Object.assign({timeZone: undefined}, options)); + assertEq(undefinedTz, absentTz); +} + + +// RangeError is thrown for invalid time zone names. +for (let timeZone of ["", "undefined", "UTC\0", "Vienna", "Africa", "America/NewYork"]) { + assertThrowsInstanceOf(() => { + new Date(defaultDate).toLocaleTimeString(undefined, {timeZone}); + }, RangeError); +} + +// RangeError is thrown for these values, because ToString() +// isn't a valid time zone name. +for (let timeZone of [null, 0, 0.5, true, false, [], {}, function() {}]) { + assertThrowsInstanceOf(() => { + new Date(defaultDate).toLocaleTimeString(undefined, {timeZone}); + }, RangeError); +} + +// ToString() throws TypeError. +assertThrowsInstanceOf(() => { + new Date(defaultDate).toLocaleTimeString(undefined, {timeZone: Symbol()}); +}, TypeError); + + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); From 03da3ee9bf27d57d4ff13db41d2509c78e3e8021 Mon Sep 17 00:00:00 2001 From: Deepjyoti Mondal Date: Tue, 20 Sep 2016 09:30:00 +0200 Subject: [PATCH 51/96] Bug 1215856 - "":" should not be selected or copied in Title field". r=dtownsend --- browser/base/content/pageinfo/pageInfo.js | 2 +- browser/base/content/pageinfo/pageInfo.xul | 6 +++++- browser/locales/en-US/chrome/browser/pageInfo.dtd | 1 + browser/locales/en-US/chrome/browser/pageInfo.properties | 1 - 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/browser/base/content/pageinfo/pageInfo.js b/browser/base/content/pageinfo/pageInfo.js index 247061fbfa32..3ae927896560 100644 --- a/browser/base/content/pageinfo/pageInfo.js +++ b/browser/base/content/pageinfo/pageInfo.js @@ -529,7 +529,7 @@ function openCacheEntry(key, cb) function makeGeneralTab(metaViewRows, docInfo) { - var title = (docInfo.title) ? gBundle.getFormattedString("pageTitle", [docInfo.title]) : gBundle.getString("noPageTitle"); + var title = (docInfo.title) ? docInfo.title : gBundle.getString("noPageTitle"); document.getElementById("titletext").value = title; var url = docInfo.location; diff --git a/browser/base/content/pageinfo/pageInfo.xul b/browser/base/content/pageinfo/pageInfo.xul index 12d4f526f341..8352a8aa72a3 100644 --- a/browser/base/content/pageinfo/pageInfo.xul +++ b/browser/base/content/pageinfo/pageInfo.xul @@ -88,7 +88,6 @@ - @@ -96,6 +95,11 @@ + +