summaryrefslogtreecommitdiffstats
path: root/layout/style
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:14:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:14:29 +0000
commitfbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8 (patch)
tree4c1ccaf5486d4f2009f9a338a98a83e886e29c97 /layout/style
parentReleasing progress-linux version 124.0.1-1~progress7.99u1. (diff)
downloadfirefox-fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8.tar.xz
firefox-fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8.zip
Merging upstream version 125.0.1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'layout/style')
-rw-r--r--layout/style/FontFaceSet.cpp1
-rw-r--r--layout/style/GeckoBindings.cpp2
-rw-r--r--layout/style/Loader.cpp208
-rw-r--r--layout/style/Loader.h5
-rw-r--r--layout/style/PseudoStyleType.h1
-rw-r--r--layout/style/ServoBindings.toml60
-rw-r--r--layout/style/ServoCSSParser.cpp10
-rw-r--r--layout/style/ServoCSSParser.h21
-rw-r--r--layout/style/ServoStyleConstsInlines.h68
-rw-r--r--layout/style/ServoStyleSet.h4
-rw-r--r--layout/style/SheetLoadData.h6
-rw-r--r--layout/style/StreamLoader.cpp114
-rw-r--r--layout/style/StreamLoader.h10
-rw-r--r--layout/style/StyleSheet.cpp47
-rw-r--r--layout/style/StyleSheet.h31
-rw-r--r--layout/style/contenteditable.css4
-rw-r--r--layout/style/crashtests/1397439-1.html6
-rw-r--r--layout/style/crashtests/1403465.html24
-rw-r--r--layout/style/crashtests/411603-1.html7
-rw-r--r--layout/style/crashtests/413274-1.xhtml18
-rw-r--r--layout/style/crashtests/418007-1.xhtml24
-rw-r--r--layout/style/crashtests/crashtests.list5
-rw-r--r--layout/style/nsCSSAnonBoxList.h1
-rw-r--r--layout/style/nsComputedDOMStyle.cpp28
-rw-r--r--layout/style/nsComputedDOMStyle.h6
-rw-r--r--layout/style/nsICSSDeclaration.h5
-rw-r--r--layout/style/nsMediaFeatures.cpp3
-rw-r--r--layout/style/nsStyleStruct.cpp11
-rw-r--r--layout/style/nsStyleStruct.h7
-rw-r--r--layout/style/nsStyleTransformMatrix.cpp3
-rw-r--r--layout/style/nsTransitionManager.cpp12
-rw-r--r--layout/style/nsTransitionManager.h1
-rw-r--r--layout/style/res/forms.css63
-rw-r--r--layout/style/res/html.css133
-rw-r--r--layout/style/test/ParseCSS.cpp1
-rw-r--r--layout/style/test/animation_utils.js2
-rw-r--r--layout/style/test/chrome/bug418986-2.js2
-rw-r--r--layout/style/test/mochitest.toml1
-rw-r--r--layout/style/test/property_database.js84
-rw-r--r--layout/style/test/test_shorthand_property_getters.html14
40 files changed, 576 insertions, 477 deletions
diff --git a/layout/style/FontFaceSet.cpp b/layout/style/FontFaceSet.cpp
index f350e5210a..ddc6240216 100644
--- a/layout/style/FontFaceSet.cpp
+++ b/layout/style/FontFaceSet.cpp
@@ -439,7 +439,6 @@ void FontFaceSet::MaybeResolve() {
break;
case FontFaceLoadStatus::Loading:
// We should've returned above at MightHavePendingFontLoads()!
- case FontFaceLoadStatus::EndGuard_:
MOZ_ASSERT_UNREACHABLE("unexpected FontFaceLoadStatus");
break;
}
diff --git a/layout/style/GeckoBindings.cpp b/layout/style/GeckoBindings.cpp
index 5c7992f8db..dc43e9cf6a 100644
--- a/layout/style/GeckoBindings.cpp
+++ b/layout/style/GeckoBindings.cpp
@@ -27,7 +27,6 @@
#include "mozilla/dom/DocumentInlines.h"
#include "nsILoadContext.h"
#include "nsIFrame.h"
-#include "nsIMozBrowserFrame.h"
#include "nsINode.h"
#include "nsIURI.h"
#include "nsFontMetrics.h"
@@ -44,6 +43,7 @@
#include "mozilla/css/ImageLoader.h"
#include "mozilla/DeclarationBlock.h"
#include "mozilla/AttributeStyles.h"
+#include "mozilla/ClearOnShutdown.h"
#include "mozilla/EffectCompositor.h"
#include "mozilla/EffectSet.h"
#include "mozilla/FontPropertyTypes.h"
diff --git a/layout/style/Loader.cpp b/layout/style/Loader.cpp
index ebbc934466..1ea37b094f 100644
--- a/layout/style/Loader.cpp
+++ b/layout/style/Loader.cpp
@@ -8,7 +8,9 @@
#include "mozilla/css/Loader.h"
+#include "MainThreadUtils.h"
#include "mozilla/ArrayUtils.h"
+#include "mozilla/css/ErrorReporter.h"
#include "mozilla/dom/DocGroup.h"
#include "mozilla/dom/FetchPriority.h"
#include "mozilla/dom/SRILogHelper.h"
@@ -22,6 +24,7 @@
#include "mozilla/SchedulerGroup.h"
#include "mozilla/URLPreloader.h"
#include "nsIChildChannel.h"
+#include "nsIPrincipal.h"
#include "nsISupportsPriority.h"
#include "nsITimedChannel.h"
#include "nsICachingChannel.h"
@@ -34,7 +37,6 @@
#include "nsICookieJarSettings.h"
#include "mozilla/dom/Document.h"
#include "nsIURI.h"
-#include "nsNetUtil.h"
#include "nsContentUtils.h"
#include "nsIScriptSecurityManager.h"
#include "nsContentPolicyUtils.h"
@@ -64,6 +66,7 @@
#include "mozilla/css/StreamLoader.h"
#include "mozilla/SharedStyleSheetCache.h"
#include "mozilla/StaticPrefs_layout.h"
+#include "mozilla/StaticPrefs_network.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/StaticPrefs_network.h"
#include "mozilla/Try.h"
@@ -308,7 +311,10 @@ SheetLoadData::SheetLoadData(
mNonce(aNonce),
mFetchPriority{aFetchPriority},
mGuessedEncoding(GetFallbackEncoding(*aLoader, aOwningNode, nullptr)),
- mCompatMode(aLoader->CompatMode(aPreloadKind)) {
+ mCompatMode(aLoader->CompatMode(aPreloadKind)),
+ mRecordErrors(
+ aLoader && aLoader->GetDocument() &&
+ css::ErrorReporter::ShouldReportErrors(*aLoader->GetDocument())) {
MOZ_ASSERT(!aOwningNode || dom::LinkStyle::FromNode(*aOwningNode),
"Must implement LinkStyle");
MOZ_ASSERT(mTriggeringPrincipal);
@@ -349,7 +355,10 @@ SheetLoadData::SheetLoadData(css::Loader* aLoader, nsIURI* aURI,
mFetchPriority(FetchPriority::Auto),
mGuessedEncoding(GetFallbackEncoding(
*aLoader, nullptr, aParentData ? aParentData->mEncoding : nullptr)),
- mCompatMode(aLoader->CompatMode(mPreloadKind)) {
+ mCompatMode(aLoader->CompatMode(mPreloadKind)),
+ mRecordErrors(
+ aLoader && aLoader->GetDocument() &&
+ css::ErrorReporter::ShouldReportErrors(*aLoader->GetDocument())) {
MOZ_ASSERT(mLoader, "Must have a loader!");
MOZ_ASSERT(mTriggeringPrincipal);
MOZ_ASSERT(!mUseSystemPrincipal || mSyncLoad,
@@ -391,7 +400,10 @@ SheetLoadData::SheetLoadData(
mFetchPriority(aFetchPriority),
mGuessedEncoding(
GetFallbackEncoding(*aLoader, nullptr, aPreloadEncoding)),
- mCompatMode(aLoader->CompatMode(aPreloadKind)) {
+ mCompatMode(aLoader->CompatMode(aPreloadKind)),
+ mRecordErrors(
+ aLoader && aLoader->GetDocument() &&
+ css::ErrorReporter::ShouldReportErrors(*aLoader->GetDocument())) {
MOZ_ASSERT(mTriggeringPrincipal);
MOZ_ASSERT(mLoader, "Must have a loader!");
MOZ_ASSERT(!mUseSystemPrincipal || mSyncLoad,
@@ -639,43 +651,54 @@ static bool AllLoadsCanceled(const SheetLoadData& aData) {
* page and check the mimetype on the channel to make sure we're not
* loading non-text/css data in standards mode.
*/
-nsresult SheetLoadData::VerifySheetReadyToParse(nsresult aStatus,
- const nsACString& aBytes1,
- const nsACString& aBytes2,
- nsIChannel* aChannel) {
+nsresult SheetLoadData::VerifySheetReadyToParse(
+ nsresult aStatus, const nsACString& aBytes1, const nsACString& aBytes2,
+ nsIChannel* aChannel, nsIURI* aFinalChannelURI,
+ nsIPrincipal* aChannelResultPrincipal) {
LOG(("SheetLoadData::VerifySheetReadyToParse"));
- NS_ASSERTION(!mLoader->mSyncCallback, "Synchronous callback from necko");
+ NS_ASSERTION((!NS_IsMainThread() || !mLoader->mSyncCallback),
+ "Synchronous callback from necko");
if (AllLoadsCanceled(*this)) {
- LOG_WARN((" All loads are canceled, dropping"));
- mLoader->SheetComplete(*this, NS_BINDING_ABORTED);
+ if (NS_IsMainThread()) {
+ LOG_WARN((" All loads are canceled, dropping"));
+ mLoader->SheetComplete(*this, NS_BINDING_ABORTED);
+ }
+ return NS_OK;
+ }
+
+ if (!NS_IsMainThread() && mRecordErrors) {
+ // we cannot parse sheet OMT if we need to record errors
return NS_OK;
}
if (NS_FAILED(aStatus)) {
- LOG_WARN(
- (" Load failed: status 0x%" PRIx32, static_cast<uint32_t>(aStatus)));
- // Handle sheet not loading error because source was a tracking URL (or
- // fingerprinting, cryptomining, etc).
- // We make a note of this sheet node by including it in a dedicated
- // array of blocked tracking nodes under its parent document.
- //
- // Multiple sheet load instances might be tied to this request,
- // we annotate each one linked to a valid owning element (node).
- if (net::UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(
- aStatus)) {
- if (Document* doc = mLoader->GetDocument()) {
- for (SheetLoadData* data = this; data; data = data->mNext) {
- // owner node may be null but AddBlockTrackingNode can cope
- doc->AddBlockedNodeByClassifier(data->mSheet->GetOwnerNode());
+ if (NS_IsMainThread()) {
+ LOG_WARN(
+ (" Load failed: status 0x%" PRIx32, static_cast<uint32_t>(aStatus)));
+ // Handle sheet not loading error because source was a tracking URL (or
+ // fingerprinting, cryptomining, etc).
+ // We make a note of this sheet node by including it in a dedicated
+ // array of blocked tracking nodes under its parent document.
+ //
+ // Multiple sheet load instances might be tied to this request,
+ // we annotate each one linked to a valid owning element (node).
+ if (net::UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(
+ aStatus)) {
+ if (Document* doc = mLoader->GetDocument()) {
+ for (SheetLoadData* data = this; data; data = data->mNext) {
+ // owner node may be null but AddBlockTrackingNode can cope
+ doc->AddBlockedNodeByClassifier(data->mSheet->GetOwnerNode());
+ }
}
}
+ mLoader->SheetComplete(*this, aStatus);
}
- mLoader->SheetComplete(*this, aStatus);
return NS_OK;
}
if (!aChannel) {
+ MOZ_ASSERT(NS_IsMainThread());
mLoader->SheetComplete(*this, NS_OK);
return NS_OK;
}
@@ -688,10 +711,8 @@ nsresult SheetLoadData::VerifySheetReadyToParse(nsresult aStatus,
// having a chrome URI. (Whether or not chrome stylesheets come through
// this codepath seems nondeterministic.)
// Otherwise we want the potentially-HTTP-redirected URI.
- nsCOMPtr<nsIURI> channelURI;
- NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
-
- if (!channelURI || !originalURI) {
+ if (!aFinalChannelURI || !originalURI) {
+ MOZ_ASSERT(NS_IsMainThread());
NS_ERROR("Someone just violated the nsIRequest contract");
LOG_WARN((" Channel without a URI. Bad!"));
mLoader->SheetComplete(*this, NS_ERROR_UNEXPECTED);
@@ -705,32 +726,33 @@ nsresult SheetLoadData::VerifySheetReadyToParse(nsresult aStatus,
if (mUseSystemPrincipal) {
result = secMan->GetSystemPrincipal(getter_AddRefs(principal));
} else {
- result = secMan->GetChannelResultPrincipal(aChannel,
- getter_AddRefs(principal));
+ if (aChannelResultPrincipal) {
+ principal = aChannelResultPrincipal;
+ result = NS_OK;
+ }
}
}
if (NS_FAILED(result)) {
- LOG_WARN((" Couldn't get principal"));
- mLoader->SheetComplete(*this, result);
+ if (NS_IsMainThread()) {
+ LOG_WARN((" Couldn't get principal"));
+ mLoader->SheetComplete(*this, result);
+ }
return NS_OK;
}
mSheet->SetPrincipal(principal);
- if (mSheet->GetCORSMode() == CORS_NONE &&
- !mTriggeringPrincipal->Subsumes(principal)) {
- mIsCrossOriginNoCORS = true;
- }
-
// If it's an HTTP channel, we want to make sure this is not an
// error document we got.
if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel)) {
bool requestSucceeded;
result = httpChannel->GetRequestSucceeded(&requestSucceeded);
if (NS_SUCCEEDED(result) && !requestSucceeded) {
- LOG((" Load returned an error page"));
- mLoader->SheetComplete(*this, NS_ERROR_NOT_AVAILABLE);
+ if (NS_IsMainThread()) {
+ LOG((" Load returned an error page"));
+ mLoader->SheetComplete(*this, NS_ERROR_NOT_AVAILABLE);
+ }
return NS_OK;
}
@@ -753,6 +775,9 @@ nsresult SheetLoadData::VerifySheetReadyToParse(nsresult aStatus,
contentType.IsEmpty();
if (!validType) {
+ if (!NS_IsMainThread()) {
+ return NS_OK;
+ }
const char* errorMessage;
uint32_t errorFlag;
bool sameOrigin = true;
@@ -772,7 +797,8 @@ nsresult SheetLoadData::VerifySheetReadyToParse(nsresult aStatus,
}
AutoTArray<nsString, 2> strings;
- CopyUTF8toUTF16(channelURI->GetSpecOrDefault(), *strings.AppendElement());
+ CopyUTF8toUTF16(aFinalChannelURI->GetSpecOrDefault(),
+ *strings.AppendElement());
CopyASCIItoUTF16(contentType, *strings.AppendElement());
nsCOMPtr<nsIURI> referrer = ReferrerInfo()->GetOriginalReferrer();
@@ -791,6 +817,13 @@ nsresult SheetLoadData::VerifySheetReadyToParse(nsresult aStatus,
SRIMetadata sriMetadata;
mSheet->GetIntegrity(sriMetadata);
if (!sriMetadata.IsEmpty()) {
+ if (!NS_IsMainThread()) {
+ // We dont process any further in OMT.
+ // This is because we have some main-thread only accesses below.
+ // We need to find a way to optimize this handling.
+ // See Bug 1882046.
+ return NS_OK;
+ }
nsAutoCString sourceUri;
if (mLoader->mDocument && mLoader->mDocument->GetDocumentURI()) {
mLoader->mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
@@ -815,9 +848,13 @@ nsresult SheetLoadData::VerifySheetReadyToParse(nsresult aStatus,
}
}
+ if (mSheet->GetCORSMode() == CORS_NONE &&
+ !mTriggeringPrincipal->Subsumes(principal)) {
+ mIsCrossOriginNoCORS = true;
+ }
// Enough to set the URIs on mSheet, since any sibling datas we have share
// the same mInner as mSheet and will thus get the same URI.
- mSheet->SetURIs(channelURI, originalURI, channelURI);
+ mSheet->SetURIs(aFinalChannelURI, originalURI, aFinalChannelURI);
ReferrerPolicy policy =
nsContentUtils::GetReferrerPolicyFromChannel(aChannel);
@@ -1353,11 +1390,11 @@ nsresult Loader::LoadSheet(SheetLoadData& aLoadData, SheetState aSheetState,
void Loader::AdjustPriority(const SheetLoadData& aLoadData,
nsIChannel* aChannel) {
- if (!StaticPrefs::network_fetchpriority_enabled()) {
- if (!aLoadData.ShouldDefer() && aLoadData.IsLinkRelPreload()) {
- SheetLoadData::PrioritizeAsPreload(aChannel);
- }
+ if (!aLoadData.ShouldDefer() && aLoadData.IsLinkRelPreload()) {
+ SheetLoadData::PrioritizeAsPreload(aChannel);
+ }
+ if (!StaticPrefs::network_fetchpriority_enabled()) {
return;
}
@@ -1367,32 +1404,27 @@ void Loader::AdjustPriority(const SheetLoadData& aLoadData,
return;
}
- // Adjusting priorites is specified as implementation-defined. To align with
- // other browsers for potentially important cases, some adjustments are made
- // according to
- // <https://web.dev/articles/fetch-priority#browser_priority_and_fetchpriority>.
- const int32_t supportsPriority = [&]() {
+ // Adjusting priorites is specified as implementation-defined.
+ // See corresponding preferences in StaticPrefList.yaml for more context.
+ const int32_t supportsPriorityDelta = [&]() {
if (aLoadData.ShouldDefer()) {
- return nsISupportsPriority::PRIORITY_LOW;
+ return FETCH_PRIORITY_ADJUSTMENT_FOR(deferred_style,
+ aLoadData.mFetchPriority);
}
- switch (aLoadData.mFetchPriority) {
- case FetchPriority::Auto: {
- return nsISupportsPriority::PRIORITY_HIGHEST;
- }
- case FetchPriority::High: {
- return nsISupportsPriority::PRIORITY_HIGHEST;
- }
- case FetchPriority::Low: {
- return nsISupportsPriority::PRIORITY_HIGH;
- }
+ if (aLoadData.IsLinkRelPreload()) {
+ return FETCH_PRIORITY_ADJUSTMENT_FOR(link_preload_style,
+ aLoadData.mFetchPriority);
}
-
- MOZ_ASSERT_UNREACHABLE();
- return nsISupportsPriority::PRIORITY_HIGHEST;
+ return FETCH_PRIORITY_ADJUSTMENT_FOR(non_deferred_style,
+ aLoadData.mFetchPriority);
}();
- LogPriorityMapping(sCssLoaderLog, aLoadData.mFetchPriority, supportsPriority);
- sp->SetPriority(supportsPriority);
+ sp->AdjustPriority(supportsPriorityDelta);
+#ifdef DEBUG
+ int32_t adjustedPriority;
+ sp->GetPriority(&adjustedPriority);
+ LogPriorityMapping(sCssLoaderLog, aLoadData.mFetchPriority, adjustedPriority);
+#endif
}
nsresult Loader::LoadSheetAsyncInternal(SheetLoadData& aLoadData,
@@ -1582,32 +1614,35 @@ nsresult Loader::LoadSheetAsyncInternal(SheetLoadData& aLoadData,
/**
* ParseSheet handles parsing the data stream.
*/
-Loader::Completed Loader::ParseSheet(const nsACString& aBytes,
- SheetLoadData& aLoadData,
- AllowAsyncParse aAllowAsync) {
+Loader::Completed Loader::ParseSheet(
+ const nsACString& aBytes, const RefPtr<SheetLoadDataHolder>& aLoadData,
+ AllowAsyncParse aAllowAsync) {
LOG(("css::Loader::ParseSheet"));
- if (aLoadData.mURI) {
- LOG_URI(" Load succeeded for URI: '%s', parsing", aLoadData.mURI);
+ SheetLoadData* loadData = aLoadData->get();
+ MOZ_ASSERT(loadData);
+
+ if (loadData->mURI) {
+ LOG_URI(" Load succeeded for URI: '%s', parsing", loadData->mURI);
}
AUTO_PROFILER_LABEL_CATEGORY_PAIR_RELEVANT_FOR_JS(LAYOUT_CSSParsing);
++mParsedSheetCount;
- aLoadData.mIsBeingParsed = true;
+ loadData->mIsBeingParsed = true;
- StyleSheet* sheet = aLoadData.mSheet;
+ StyleSheet* sheet = loadData->mSheet;
MOZ_ASSERT(sheet);
// Some cases, like inline style and UA stylesheets, need to be parsed
// synchronously. The former may trigger child loads, the latter must not.
- if (aLoadData.mSyncLoad || aAllowAsync == AllowAsyncParse::No) {
- sheet->ParseSheetSync(this, aBytes, &aLoadData);
- aLoadData.mIsBeingParsed = false;
+ if (loadData->mSyncLoad || aAllowAsync == AllowAsyncParse::No) {
+ sheet->ParseSheetSync(this, aBytes, loadData);
+ loadData->mIsBeingParsed = false;
- bool noPendingChildren = aLoadData.mPendingChildren == 0;
- MOZ_ASSERT_IF(aLoadData.mSyncLoad, noPendingChildren);
+ bool noPendingChildren = loadData->mPendingChildren == 0;
+ MOZ_ASSERT_IF(loadData->mSyncLoad, noPendingChildren);
if (noPendingChildren) {
- SheetComplete(aLoadData, NS_OK);
+ SheetComplete(*loadData, NS_OK);
return Completed::Yes;
}
return Completed::No;
@@ -1621,9 +1656,9 @@ Loader::Completed Loader::ParseSheet(const nsACString& aBytes,
sheet->ParseSheet(*this, aBytes, aLoadData)
->Then(
GetMainThreadSerialEventTarget(), __func__,
- [loadData = RefPtr<SheetLoadData>(&aLoadData)](bool aDummy) {
+ [loadData = aLoadData](bool aDummy) {
MOZ_ASSERT(NS_IsMainThread());
- loadData->SheetFinishedParsingAsync();
+ loadData->get()->SheetFinishedParsingAsync();
},
[] { MOZ_CRASH("rejected parse promise"); });
return Completed::No;
@@ -1847,7 +1882,10 @@ Result<Loader::LoadSheetResult, nsresult> Loader::LoadInlineStyle(
// effects of inline stylesheets are visible immediately (aside from
// @imports).
NS_ConvertUTF16toUTF8 utf8(aBuffer);
- completed = ParseSheet(utf8, *data, AllowAsyncParse::No);
+ RefPtr<SheetLoadDataHolder> holder(
+ new nsMainThreadPtrHolder<css::SheetLoadData>(__func__, data.get(),
+ true));
+ completed = ParseSheet(utf8, holder, AllowAsyncParse::No);
if (completed == Completed::Yes) {
if (isWorthCaching) {
mInlineSheets.InsertOrUpdate(aBuffer, std::move(sheet));
diff --git a/layout/style/Loader.h b/layout/style/Loader.h
index ba60218e94..70eefffb4a 100644
--- a/layout/style/Loader.h
+++ b/layout/style/Loader.h
@@ -590,7 +590,8 @@ class Loader final {
//
// If this function returns Completed::Yes, then ParseSheet also called
// SheetComplete on aLoadData.
- Completed ParseSheet(const nsACString&, SheetLoadData&, AllowAsyncParse);
+ Completed ParseSheet(const nsACString&, const RefPtr<SheetLoadDataHolder>&,
+ AllowAsyncParse);
// The load of the sheet in the load data is done, one way or another.
// Do final cleanup.
@@ -646,7 +647,7 @@ class Loader final {
uint32_t mPendingLoadCount = 0;
// The number of stylesheets that we have parsed, for testing purposes.
- uint32_t mParsedSheetCount = 0;
+ Atomic<uint32_t, MemoryOrdering::Relaxed> mParsedSheetCount{0};
bool mEnabled = true;
diff --git a/layout/style/PseudoStyleType.h b/layout/style/PseudoStyleType.h
index 6804b500c3..88eb4e4cf8 100644
--- a/layout/style/PseudoStyleType.h
+++ b/layout/style/PseudoStyleType.h
@@ -7,6 +7,7 @@
#ifndef mozilla_PseudoStyleType_h
#define mozilla_PseudoStyleType_h
+#include <cstddef>
#include <cstdint>
#include <iosfwd>
diff --git a/layout/style/ServoBindings.toml b/layout/style/ServoBindings.toml
index cc6d13fb5e..20e1ca4116 100644
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -186,16 +186,9 @@ allowlist-vars = [
"NODE_.*",
"ELEMENT_.*",
"NS_FONT_.*",
- "NS_STYLE_.*",
- "NS_MATHML_.*",
- "NS_RADIUS_.*",
- "BORDER_COLOR_.*",
- "BORDER_STYLE_.*",
"CSS_PSEUDO_ELEMENT_.*",
"SERVO_CSS_PSEUDO_ELEMENT_FLAGS_.*",
"kNameSpaceID_.*",
- "kGenericFont_.*",
- "kPresContext_.*",
"nsNameSpaceManager_.*",
"GECKO_IS_NIGHTLY",
"NS_SAME_AS_FOREGROUND_COLOR",
@@ -205,9 +198,7 @@ allowlist-vars = [
# TODO(emilio): A bunch of types here can go away once we generate bindings and
# structs together.
allowlist-types = [
- "ServoCssRules",
"nsFontFaceRuleContainer",
- "Matrix4x4Components",
"mozilla::ComputedKeyframeValues",
"mozilla::Keyframe",
"mozilla::PropertyValuePair",
@@ -223,7 +214,6 @@ allowlist-types = [
"mozilla::ServoElementSnapshot.*",
"mozilla::ComputedStyle",
"mozilla::StyleSheet",
- "mozilla::ServoStyleSheetInner",
"mozilla::ServoStyleSetSizes",
"mozilla::ServoTraversalStatistics",
"mozilla::css::LoaderReusableStyleSheets",
@@ -247,27 +237,14 @@ allowlist-types = [
"mozilla::gfx::Float",
"mozilla::gfx::FontVariation",
"mozilla::gfx::FontPaletteValueSet",
- "mozilla::gfx::FontPaletteValueSet::PaletteValeus",
"mozilla::StyleImageLayerAttachment",
"gfxFontFeature",
"gfxFontVariation",
- ".*ThreadSafe.*Holder",
- "AnonymousContent",
- "AudioContext",
- "DefaultDelete",
- "DOMIntersectionObserverEntry",
"Element",
- "mozilla::FontSizePrefs",
- "FragmentOrURL",
- "FrameRequestCallback",
- "GeckoParserExtraData",
"GeckoFontMetrics",
"gfxFontFeatureValueSet",
- "GridNamedArea",
"mozilla::HalfCorner",
"Image",
- "ImageURL",
- "Keyframe",
"mozilla::MediumFeaturesChangedResult",
"nsAttrName",
"nsAttrValue",
@@ -280,15 +257,7 @@ allowlist-types = [
"nsCSSPropertyID",
"nsCSSPropertyIDSet",
"nsCSSProps",
- "nsCSSShadowArray",
"nsCSSValue",
- "nsCSSValueList",
- "nsCSSValueList_heap",
- "nsCSSValuePair_heap",
- "nsCSSValuePairList",
- "nsCSSValuePairList_heap",
- "nsCSSValueTriplet_heap",
- "nsCursorImage",
"nsFont",
"nsAtom",
"nsDynamicAtom",
@@ -299,19 +268,11 @@ allowlist-types = [
"nsSize",
"nsStyleBackground",
"nsStyleBorder",
- "nsStyleColor",
"nsStyleColumn",
"nsStyleContent",
- "nsStyleContentData",
- "ComputedStyle",
- "nsStyleCounterData",
"nsStyleDisplay",
"nsStyleEffects",
- "nsStyleFilter",
"nsStyleFont",
- "nsStyleGradient",
- "nsStyleGridTemplate",
- "nsStyleImage",
"nsStyleImageLayers",
"nsStyleList",
"nsStyleMargin",
@@ -319,37 +280,17 @@ allowlist-types = [
"nsStylePadding",
"nsStylePage",
"nsStylePosition",
- "nsStyleSides",
"nsStyleSVG",
- "nsStyleSVGOpacitySource",
"nsStyleSVGReset",
"nsStyleTable",
"nsStyleTableBorder",
"nsStyleText",
"nsStyleTextReset",
"nsStyleUIReset",
- "nsStyleUnion",
"nsStyleUI",
"nsStyleVisibility",
"nsStyleXUL",
"nsTArrayHeader",
- "Position",
- "PropertyValuePair",
- "Runnable",
- "ServoAttrSnapshot",
- "ServoComputedData",
- "ServoComputedDataBorrowed",
- "ServoElementSnapshot",
- "ComputedStyleStrong",
- "ComputedStyleBorrowed",
- "ComputedStyleBorrowedOrNull",
- "SheetParsingMode",
- "StaticRefPtr",
- "StyleAnimation",
- "StyleGeometryBox",
- "StyleShapeSource",
- "StyleTransition",
- "ThemeWidgetType",
"mozilla::UniquePtr",
"mozilla::DeclarationBlock",
"mozilla::DefaultDelete",
@@ -430,6 +371,7 @@ opaque-types = [
cbindgen-types = [
{ gecko = "StyleAnimationIterationCount", servo = "crate::values::computed::AnimationIterationCount" },
{ gecko = "StyleAnimationTimeline", servo = "crate::values::computed::AnimationTimeline" },
+ { gecko = "StyleTransitionBehavior", servo = "crate::values::computed::TransitionBehavior" },
{ gecko = "StyleAppearance", servo = "crate::values::specified::Appearance" },
{ gecko = "StyleAspectRatio", servo = "crate::values::computed::position::AspectRatio" },
{ gecko = "StyleAtom", servo = "crate::Atom" },
diff --git a/layout/style/ServoCSSParser.cpp b/layout/style/ServoCSSParser.cpp
index 92e1271f2c..89f17f1733 100644
--- a/layout/style/ServoCSSParser.cpp
+++ b/layout/style/ServoCSSParser.cpp
@@ -33,6 +33,16 @@ bool ServoCSSParser::ComputeColor(ServoStyleSet* aStyleSet,
}
/* static */
+bool ServoCSSParser::ColorTo(const nsACString& aFromColor,
+ const nsACString& aToColorSpace,
+ nsACString* aResultColor,
+ nsTArray<float>* aResultComponents,
+ bool* aResultAdjusted, css::Loader* aLoader) {
+ return Servo_ColorTo(&aFromColor, &aToColorSpace, aResultColor,
+ aResultComponents, aResultAdjusted, aLoader);
+}
+
+/* static */
already_AddRefed<StyleLockedDeclarationBlock> ServoCSSParser::ParseProperty(
nsCSSPropertyID aProperty, const nsACString& aValue,
const ParsingEnvironment& aParsingEnvironment,
diff --git a/layout/style/ServoCSSParser.h b/layout/style/ServoCSSParser.h
index 4bbde6a0e8..583a2eebd9 100644
--- a/layout/style/ServoCSSParser.h
+++ b/layout/style/ServoCSSParser.h
@@ -80,6 +80,27 @@ class ServoCSSParser {
css::Loader* aLoader = nullptr);
/**
+ * Takes a CSS <color> and convert it to another color space.
+ *
+ * @param aStyleSet The style set whose nsPresContext will be used to
+ * compute system colors and other special color values.
+ * @param aFromColor The CSS <color> we use to convert from.
+ * @param aToColorSpace The CSS <color-space> to convert the color into.
+ * @param aResultColor The resulting converted color value.
+ * @param aResultAdjusted Whether the color was adjusted to fit into the SRGB
+ color space.
+ * @param aLoader The CSS loader for document we're parsing a color for,
+ * so that parse errors can be reported to the console. If nullptr, errors
+ * won't be reported to the console.
+ * @return Whether aFromColor and aToColorSpace was successfully parsed and
+ * aResultColor and aResultAdjusted was set.
+ */
+ static bool ColorTo(const nsACString& aFromColor,
+ const nsACString& aToColorSpace, nsACString* aResultColor,
+ nsTArray<float>* aResultComponents, bool* aResultAdjusted,
+ css::Loader* aLoader = nullptr);
+
+ /**
* Parse a string representing a CSS property value into a
* StyleLockedDeclarationBlock.
*
diff --git a/layout/style/ServoStyleConstsInlines.h b/layout/style/ServoStyleConstsInlines.h
index b6a574037a..1985e8fbeb 100644
--- a/layout/style/ServoStyleConstsInlines.h
+++ b/layout/style/ServoStyleConstsInlines.h
@@ -507,20 +507,15 @@ StyleCSSPixelLength StyleCSSPixelLength::ScaledBy(float aScale) const {
return FromPixels(ToCSSPixels() * aScale);
}
-nscoord StyleCSSPixelLength::ToAppUnits() const {
- // We want to resolve the length part of the calc() expression rounding 0.5
- // away from zero, instead of the default behavior of
- // NSToCoordRound{,WithClamp} which do floor(x + 0.5).
- //
- // This is what the rust code in the app_units crate does, and not doing this
- // would regress bug 1323735, for example.
- //
- // FIXME(emilio, bug 1528114): Probably we should do something smarter.
- if (IsZero()) {
- // Avoid the expensive FP math below.
- return 0;
- }
- float length = _0 * float(mozilla::AppUnitsPerCSSPixel());
+namespace detail {
+static inline nscoord DefaultPercentLengthToAppUnits(float aPixelLength) {
+ return NSToCoordTruncClamped(aPixelLength);
+}
+
+static inline nscoord DefaultLengthToAppUnits(float aPixelLength) {
+ // We want to round lengths rounding 0.5 away from zero, instead of the
+ // default behavior of NSToCoordRound{,WithClamp} which do floor(x + 0.5).
+ float length = aPixelLength * float(mozilla::AppUnitsPerCSSPixel());
if (length >= float(nscoord_MAX)) {
return nscoord_MAX;
}
@@ -529,6 +524,15 @@ nscoord StyleCSSPixelLength::ToAppUnits() const {
}
return NSToIntRound(length);
}
+} // namespace detail
+
+nscoord StyleCSSPixelLength::ToAppUnits() const {
+ if (IsZero()) {
+ // Avoid the expensive FP math below.
+ return 0;
+ }
+ return detail::DefaultLengthToAppUnits(_0);
+}
bool LengthPercentage::IsLength() const { return Tag() == TAG_LENGTH; }
@@ -693,6 +697,11 @@ CSSCoord StyleCalcLengthPercentage::ResolveToCSSPixels(CSSCoord aBasis) const {
return Servo_ResolveCalcLengthPercentage(this, aBasis);
}
+nscoord StyleCalcLengthPercentage::Resolve(nscoord aBasis) const {
+ return detail::DefaultLengthToAppUnits(
+ ResolveToCSSPixels(CSSPixel::FromAppUnits(aBasis)));
+}
+
template <>
void StyleCalcNode::ScaleLengthsBy(float);
@@ -708,20 +717,18 @@ CSSCoord LengthPercentage::ResolveToCSSPixels(CSSCoord aPercentageBasis) const {
template <typename T>
CSSCoord LengthPercentage::ResolveToCSSPixelsWith(T aPercentageGetter) const {
- static_assert(std::is_same<decltype(aPercentageGetter()), CSSCoord>::value,
- "Should return CSS pixels");
+ static_assert(std::is_same_v<decltype(aPercentageGetter()), CSSCoord>);
if (ConvertsToLength()) {
return ToLengthInCSSPixels();
}
return ResolveToCSSPixels(aPercentageGetter());
}
-template <typename T, typename U>
-nscoord LengthPercentage::Resolve(T aPercentageGetter, U aRounder) const {
- static_assert(std::is_same<decltype(aPercentageGetter()), nscoord>::value,
- "Should return app units");
- static_assert(std::is_same<decltype(aRounder(1.0f)), nscoord>::value,
- "Should return app units");
+template <typename T, typename PercentRounder>
+nscoord LengthPercentage::Resolve(T aPercentageGetter,
+ PercentRounder aPercentRounder) const {
+ static_assert(std::is_same_v<decltype(aPercentageGetter()), nscoord>);
+ static_assert(std::is_same_v<decltype(aPercentRounder(1.0f)), nscoord>);
if (ConvertsToLength()) {
return ToLength();
}
@@ -730,29 +737,26 @@ nscoord LengthPercentage::Resolve(T aPercentageGetter, U aRounder) const {
}
nscoord basis = aPercentageGetter();
if (IsPercentage()) {
- return aRounder(basis * AsPercentage()._0);
+ return aPercentRounder(basis * AsPercentage()._0);
}
- return AsCalc().Resolve(basis, aRounder);
+ return AsCalc().Resolve(basis);
}
-// Note: the static_cast<> wrappers below are needed to disambiguate between
-// the versions of NSToCoordTruncClamped that take float vs. double as the arg.
nscoord LengthPercentage::Resolve(nscoord aPercentageBasis) const {
return Resolve([=] { return aPercentageBasis; },
- static_cast<nscoord (*)(float)>(NSToCoordTruncClamped));
+ detail::DefaultPercentLengthToAppUnits);
}
template <typename T>
nscoord LengthPercentage::Resolve(T aPercentageGetter) const {
- return Resolve(aPercentageGetter,
- static_cast<nscoord (*)(float)>(NSToCoordTruncClamped));
+ return Resolve(aPercentageGetter, detail::DefaultPercentLengthToAppUnits);
}
-template <typename T>
+template <typename PercentRounder>
nscoord LengthPercentage::Resolve(nscoord aPercentageBasis,
- T aPercentageRounder) const {
+ PercentRounder aPercentRounder) const {
return Resolve([aPercentageBasis] { return aPercentageBasis; },
- aPercentageRounder);
+ aPercentRounder);
}
void LengthPercentage::ScaleLengthsBy(float aScale) {
diff --git a/layout/style/ServoStyleSet.h b/layout/style/ServoStyleSet.h
index d7225349cc..eee6cba0f7 100644
--- a/layout/style/ServoStyleSet.h
+++ b/layout/style/ServoStyleSet.h
@@ -686,8 +686,8 @@ class ServoStyleSet {
// Stores pointers to our cached ComputedStyles for non-inheriting anonymous
// boxes.
- EnumeratedArray<nsCSSAnonBoxes::NonInheriting,
- nsCSSAnonBoxes::NonInheriting::_Count, RefPtr<ComputedStyle>>
+ EnumeratedArray<nsCSSAnonBoxes::NonInheriting, RefPtr<ComputedStyle>,
+ size_t(nsCSSAnonBoxes::NonInheriting::_Count)>
mNonInheritingComputedStyles;
public:
diff --git a/layout/style/SheetLoadData.h b/layout/style/SheetLoadData.h
index c817e4c1f3..6621af35bd 100644
--- a/layout/style/SheetLoadData.h
+++ b/layout/style/SheetLoadData.h
@@ -90,7 +90,9 @@ class SheetLoadData final
// so aBytes1 and aBytes2 refer to those pieces.
nsresult VerifySheetReadyToParse(nsresult aStatus, const nsACString& aBytes1,
const nsACString& aBytes2,
- nsIChannel* aChannel);
+ nsIChannel* aChannel,
+ nsIURI* aFinalChannelURI,
+ nsIPrincipal* aPrincipal);
NS_DECL_ISUPPORTS
@@ -237,6 +239,8 @@ class SheetLoadData final
// listening for the load.
bool mIntentionallyDropped = false;
+ const bool mRecordErrors;
+
bool ShouldDefer() const { return mWasAlternate || !mMediaMatched; }
RefPtr<StyleSheet> ValueForCache() const;
diff --git a/layout/style/StreamLoader.cpp b/layout/style/StreamLoader.cpp
index 3e8bd37d76..3c614d7d0c 100644
--- a/layout/style/StreamLoader.cpp
+++ b/layout/style/StreamLoader.cpp
@@ -5,8 +5,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/css/StreamLoader.h"
-
+#include "mozilla/StaticPrefs_network.h"
#include "mozilla/Encoding.h"
+#include "mozilla/glean/GleanMetrics.h"
#include "mozilla/TaskQueue.h"
#include "nsContentUtils.h"
#include "nsIChannel.h"
@@ -14,16 +15,21 @@
#include "nsIThreadRetargetableRequest.h"
#include "nsIStreamTransportService.h"
#include "nsNetCID.h"
+#include "nsNetUtil.h"
+#include "nsProxyRelease.h"
#include "nsServiceManagerUtils.h"
namespace mozilla::css {
StreamLoader::StreamLoader(SheetLoadData& aSheetLoadData)
- : mSheetLoadData(&aSheetLoadData), mStatus(NS_OK) {}
+ : mSheetLoadData(&aSheetLoadData),
+ mStatus(NS_OK),
+ mMainThreadSheetLoadData(new nsMainThreadPtrHolder<SheetLoadData>(
+ "StreamLoader::SheetLoadData", mSheetLoadData, false)) {}
StreamLoader::~StreamLoader() {
#ifdef NIGHTLY_BUILD
- MOZ_RELEASE_ASSERT(mOnStopRequestCalled || mChannelOpenFailed);
+ MOZ_RELEASE_ASSERT(mOnStopProcessingDone || mChannelOpenFailed);
#endif
}
@@ -34,6 +40,7 @@ NS_IMPL_ISUPPORTS(StreamLoader, nsIStreamListener,
NS_IMETHODIMP
StreamLoader::OnStartRequest(nsIRequest* aRequest) {
MOZ_ASSERT(aRequest);
+ mRequest = aRequest;
mSheetLoadData->NotifyStart(aRequest);
// It's kinda bad to let Web content send a number that results
@@ -52,6 +59,12 @@ StreamLoader::OnStartRequest(nsIRequest* aRequest) {
return (mStatus = NS_ERROR_OUT_OF_MEMORY);
}
}
+ NS_GetFinalChannelURI(channel, getter_AddRefs(mFinalChannelURI));
+ nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
+ // we dont return on error here as the error is handled in
+ // SheetLoadData::VerifySheetReadyToParse
+ Unused << secMan->GetChannelResultPrincipal(
+ channel, getter_AddRefs(mChannelResultPrincipal));
}
if (nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(aRequest)) {
nsCOMPtr<nsIEventTarget> sts =
@@ -73,6 +86,13 @@ StreamLoader::OnStartRequest(nsIRequest* aRequest) {
return *info.mExpirationTime;
}();
+ // We need to block block resolution of parse promise until we receive
+ // OnStopRequest on Main thread. This is necessary because parse promise
+ // resolution fires OnLoad event OnLoad event must not be dispatched until
+ // OnStopRequest in main thread is processed, for stuff like performance
+ // resource entries.
+ mSheetLoadData->mSheet->BlockParsePromise();
+
return NS_OK;
}
@@ -81,39 +101,80 @@ StreamLoader::CheckListenerChain() { return NS_OK; }
NS_IMETHODIMP
StreamLoader::OnStopRequest(nsIRequest* aRequest, nsresult aStatus) {
-#ifdef NIGHTLY_BUILD
- MOZ_RELEASE_ASSERT(!mOnStopRequestCalled);
- mOnStopRequestCalled = true;
-#endif
+ MOZ_ASSERT_IF(!StaticPrefs::network_send_OnDataFinished_cssLoader(),
+ !mOnStopProcessingDone);
+
+ // StreamLoader::OnStopRequest can get triggered twice for a request.
+ // Once from the path
+ // nsIThreadRetargetableStreamListener::OnDataFinished->StreamLoader::OnDataFinished
+ // (non-main thread) and
+ // once from nsIRequestObserver::OnStopRequest path (main thread). It is
+ // guaranteed that we will always get
+ // nsIThreadRetargetableStreamListener::OnDataFinished trigger first and this
+ // is always followed by nsIRequestObserver::OnStopRequest
+
+ // If we are executing OnStopRequest OMT, we need to block resolution of parse
+ // promise and unblock again if we are executing this in main thread.
+ // Resolution of parse promise fires onLoadEvent and this should not happen
+ // before main thread OnStopRequest is dispatched.
+ if (NS_IsMainThread()) {
+ if (mOnDataFinishedTime) {
+ // collect telemetry for the delta between OnDataFinished and
+ // OnStopRequest
+ TimeDuration delta = (TimeStamp::Now() - mOnDataFinishedTime);
+ glean::networking::http_content_cssloader_ondatafinished_to_onstop_delay
+ .AccumulateRawDuration(delta);
+ }
+ mSheetLoadData->mSheet->UnblockParsePromise();
+ }
+
+ if (mOnStopProcessingDone) {
+ return NS_OK;
+ }
+ mOnStopProcessingDone = true;
nsresult rv = mStatus;
// Decoded data
nsCString utf8String;
{
- // Hold the nsStringBuffer for the bytes from the stack to ensure release
- // no matter which return branch is taken.
- nsCString bytes = std::move(mBytes);
-
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
if (NS_FAILED(mStatus)) {
- mSheetLoadData->VerifySheetReadyToParse(mStatus, ""_ns, ""_ns, channel);
+ mSheetLoadData->VerifySheetReadyToParse(mStatus, ""_ns, ""_ns, channel,
+ mFinalChannelURI,
+ mChannelResultPrincipal);
+
+ if (!NS_IsMainThread()) {
+ // When processing OMT, we have code paths in VerifySheetReadyToParse
+ // that are main-thread only. We bail on such scenarios and continue
+ // processing them on main thread OnStopRequest.
+ mOnStopProcessingDone = false;
+ }
return mStatus;
}
- rv = mSheetLoadData->VerifySheetReadyToParse(aStatus, mBOMBytes, bytes,
- channel);
+ rv = mSheetLoadData->VerifySheetReadyToParse(aStatus, mBOMBytes, mBytes,
+ channel, mFinalChannelURI,
+ mChannelResultPrincipal);
if (rv != NS_OK_PARSE_SHEET) {
+ if (!NS_IsMainThread()) {
+ mOnStopProcessingDone = false;
+ }
return rv;
}
- // BOM detection generally happens during the write callback, but that won't
- // have happened if fewer than three bytes were received.
+ // At this point all the conditions that requires us to run on main
+ // are checked in VerifySheetReadyToParse
+
+ // BOM detection generally happens during the write callback, but that
+ // won't have happened if fewer than three bytes were received.
if (mEncodingFromBOM.isNothing()) {
HandleBOM();
MOZ_ASSERT(mEncodingFromBOM.isSome());
}
-
+ // Hold the nsStringBuffer for the bytes from the stack to ensure release
+ // after its scope ends
+ nsCString bytes = std::move(mBytes);
// The BOM handling has happened, but we still may not have an encoding if
// there was no BOM. Ensure we have one.
const Encoding* encoding = mEncodingFromBOM.value();
@@ -142,9 +203,11 @@ StreamLoader::OnStopRequest(nsIRequest* aRequest, nsresult aStatus) {
// For reasons I don't understand, factoring the below lines into
// a method on SheetLoadData resulted in a linker error. Hence,
// accessing fields of mSheetLoadData from here.
- mSheetLoadData->mLoader->ParseSheet(utf8String, *mSheetLoadData,
+ mSheetLoadData->mLoader->ParseSheet(utf8String, mMainThreadSheetLoadData,
Loader::AllowAsyncParse::Yes);
+ mRequest = nullptr;
+
return NS_OK;
}
@@ -159,9 +222,6 @@ StreamLoader::OnDataAvailable(nsIRequest*, nsIInputStream* aInputStream,
return aInputStream->ReadSegments(WriteSegmentFun, this, aCount, &dummy);
}
-NS_IMETHODIMP
-StreamLoader::OnDataFinished(nsresult aStatus) { return NS_OK; }
-
void StreamLoader::HandleBOM() {
MOZ_ASSERT(mEncodingFromBOM.isNothing());
MOZ_ASSERT(mBytes.IsEmpty());
@@ -176,6 +236,18 @@ void StreamLoader::HandleBOM() {
mBOMBytes.Truncate(bomLength);
}
+NS_IMETHODIMP
+StreamLoader::OnDataFinished(nsresult aResult) {
+ if (StaticPrefs::network_send_OnDataFinished_cssLoader()) {
+ MOZ_ASSERT(mOnDataFinishedTime.IsNull(),
+ "OnDataFinished should only be called once");
+ mOnDataFinishedTime = TimeStamp::Now();
+ return OnStopRequest(mRequest, aResult);
+ }
+
+ return NS_OK;
+}
+
nsresult StreamLoader::WriteSegmentFun(nsIInputStream*, void* aClosure,
const char* aSegment, uint32_t,
uint32_t aCount, uint32_t* aWriteCount) {
diff --git a/layout/style/StreamLoader.h b/layout/style/StreamLoader.h
index a34117625a..e940fc7936 100644
--- a/layout/style/StreamLoader.h
+++ b/layout/style/StreamLoader.h
@@ -9,6 +9,7 @@
#include "nsIStreamListener.h"
#include "nsIThreadRetargetableStreamListener.h"
+#include "nsIURI.h"
#include "nsString.h"
#include "mozilla/css/SheetLoadData.h"
#include "mozilla/Assertions.h"
@@ -52,10 +53,17 @@ class StreamLoader : public nsIThreadRetargetableStreamListener {
// mBytes, and store all subsequent data in that buffer.
nsCString mBytes;
nsAutoCStringN<3> mBOMBytes;
+ nsCOMPtr<nsIRequest> mRequest;
+ nsCOMPtr<nsIURI> mFinalChannelURI;
+ nsCOMPtr<nsIPrincipal> mChannelResultPrincipal;
+ // flag to indicate that we can skip processing of data in OnStopRequest
+ bool mOnStopProcessingDone{false};
+ RefPtr<SheetLoadDataHolder> mMainThreadSheetLoadData;
+
+ mozilla::TimeStamp mOnDataFinishedTime;
#ifdef NIGHTLY_BUILD
bool mChannelOpenFailed = false;
- bool mOnStopRequestCalled = false;
#endif
};
diff --git a/layout/style/StyleSheet.cpp b/layout/style/StyleSheet.cpp
index 494618d879..b1ce1d66e2 100644
--- a/layout/style/StyleSheet.cpp
+++ b/layout/style/StyleSheet.cpp
@@ -28,7 +28,6 @@
#include "mozilla/css/SheetLoadData.h"
#include "mozAutoDocUpdate.h"
-#include "SheetLoadData.h"
namespace mozilla {
@@ -136,8 +135,12 @@ already_AddRefed<StyleSheet> StyleSheet::Constructor(
// 3. Set the sheet's disabled flag according to aOptions.
sheet->SetDisabled(aOptions.mDisabled);
+ sheet->SetURLExtraData();
sheet->SetComplete();
+ sheet->ReplaceSync(""_ns, aRv);
+ MOZ_ASSERT(!aRv.Failed());
+
// 4. Return sheet.
return sheet.forget();
}
@@ -740,7 +743,9 @@ already_AddRefed<dom::Promise> StyleSheet::Replace(const nsACString& aText,
loadData->mIsBeingParsed = true;
MOZ_ASSERT(!mReplacePromise);
mReplacePromise = promise;
- ParseSheet(*loader, aText, *loadData)
+ RefPtr<css::SheetLoadDataHolder> holder(
+ new css::SheetLoadDataHolder(__func__, loadData, false));
+ ParseSheet(*loader, aText, holder)
->Then(
target, __func__,
[loadData] { loadData->SheetFinishedParsingAsync(); },
@@ -769,7 +774,6 @@ void StyleSheet::ReplaceSync(const nsACString& aText, ErrorResult& aRv) {
// 3. Parse aText into rules.
// 4. If rules contain @imports, skip them and continue parsing.
auto* loader = mConstructorDocument->CSSLoader();
- SetURLExtraData();
RefPtr<const StyleStylesheetContents> rawContent =
Servo_StyleSheet_FromUTF8Bytes(
loader, this,
@@ -1159,28 +1163,19 @@ already_AddRefed<StyleSheet> StyleSheet::CreateEmptyChildSheet(
return child.forget();
}
-// We disable parallel stylesheet parsing if the browser is recording CSS errors
-// (which parallel parsing can't handle).
-static bool AllowParallelParse(css::Loader& aLoader, URLExtraData* aUrlData) {
- Document* doc = aLoader.GetDocument();
- if (doc && css::ErrorReporter::ShouldReportErrors(*doc)) {
- return false;
- }
- // Otherwise we can parse in parallel.
- return true;
-}
-
RefPtr<StyleSheetParsePromise> StyleSheet::ParseSheet(
css::Loader& aLoader, const nsACString& aBytes,
- css::SheetLoadData& aLoadData) {
+ const RefPtr<css::SheetLoadDataHolder>& aLoadData) {
MOZ_ASSERT(mParsePromise.IsEmpty());
+ MOZ_ASSERT_IF(NS_IsMainThread(), mAsyncParseBlockers == 0);
+
RefPtr<StyleSheetParsePromise> p = mParsePromise.Ensure(__func__);
- if (!aLoadData.ShouldDefer()) {
+ if (!aLoadData->get()->ShouldDefer()) {
mParsePromise.SetTaskPriority(nsIRunnablePriority::PRIORITY_RENDER_BLOCKING,
__func__);
}
+ BlockParsePromise();
SetURLExtraData();
-
// @import rules are disallowed due to this decision:
// https://github.com/WICG/construct-stylesheets/issues/119#issuecomment-588352418
// We may allow @import rules again in the future.
@@ -1191,26 +1186,26 @@ RefPtr<StyleSheetParsePromise> StyleSheet::ParseSheet(
const bool shouldRecordCounters =
aLoader.GetDocument() && aLoader.GetDocument()->GetStyleUseCounters() &&
!urlData->ChromeRulesEnabled();
- if (!AllowParallelParse(aLoader, urlData)) {
+
+ if (aLoadData->get()->mRecordErrors) {
+ MOZ_ASSERT(NS_IsMainThread());
UniquePtr<StyleUseCounters> counters;
if (shouldRecordCounters) {
counters.reset(Servo_UseCounters_Create());
}
-
RefPtr<StyleStylesheetContents> contents =
Servo_StyleSheet_FromUTF8Bytes(
- &aLoader, this, &aLoadData, &aBytes, mParsingMode, urlData,
- aLoadData.mCompatMode,
+ &aLoader, this, aLoadData->get(), &aBytes, mParsingMode, urlData,
+ aLoadData->get()->mCompatMode,
/* reusable_sheets = */ nullptr, counters.get(), allowImportRules,
StyleSanitizationKind::None,
/* sanitized_output = */ nullptr)
.Consume();
FinishAsyncParse(contents.forget(), std::move(counters));
} else {
- auto holder = MakeRefPtr<css::SheetLoadDataHolder>(__func__, &aLoadData);
- Servo_StyleSheet_FromUTF8BytesAsync(holder, urlData, &aBytes, mParsingMode,
- aLoadData.mCompatMode,
- shouldRecordCounters, allowImportRules);
+ Servo_StyleSheet_FromUTF8BytesAsync(
+ aLoadData, urlData, &aBytes, mParsingMode,
+ aLoadData->get()->mCompatMode, shouldRecordCounters, allowImportRules);
}
return p;
@@ -1224,7 +1219,7 @@ void StyleSheet::FinishAsyncParse(
Inner().mContents = aSheetContents;
Inner().mUseCounters = std::move(aUseCounters);
FixUpRuleListAfterContentsChangeIfNeeded();
- mParsePromise.Resolve(true, __func__);
+ UnblockParsePromise();
}
void StyleSheet::ParseSheetSync(
diff --git a/layout/style/StyleSheet.h b/layout/style/StyleSheet.h
index 1dbcad1e01..873ff2f4fc 100644
--- a/layout/style/StyleSheet.h
+++ b/layout/style/StyleSheet.h
@@ -7,6 +7,7 @@
#ifndef mozilla_StyleSheet_h
#define mozilla_StyleSheet_h
+#include "mozilla/Assertions.h"
#include "mozilla/css/SheetParsingMode.h"
#include "mozilla/dom/CSSStyleSheetBinding.h"
#include "mozilla/dom/SRIMetadata.h"
@@ -15,11 +16,13 @@
#include "mozilla/RefPtr.h"
#include "mozilla/ServoBindingTypes.h"
#include "mozilla/ServoTypes.h"
+#include "mozilla/StaticPrefs_network.h"
#include "mozilla/StyleSheetInfo.h"
#include "nsICSSLoaderObserver.h"
#include "nsIPrincipal.h"
#include "nsWrapperCache.h"
#include "nsStringFwd.h"
+#include "nsProxyRelease.h"
class nsIGlobalObject;
class nsINode;
@@ -44,6 +47,7 @@ class Loader;
class LoaderReusableStyleSheets;
class Rule;
class SheetLoadData;
+using SheetLoadDataHolder = nsMainThreadPtrHolder<SheetLoadData>;
} // namespace css
namespace dom {
@@ -112,9 +116,9 @@ class StyleSheet final : public nsICSSLoaderObserver, public nsWrapperCache {
// SheetLoadData for this stylesheet.
// NOTE: ParseSheet can run synchronously or asynchronously
// based on the result of `AllowParallelParse`
- RefPtr<StyleSheetParsePromise> ParseSheet(css::Loader&,
- const nsACString& aBytes,
- css::SheetLoadData&);
+ RefPtr<StyleSheetParsePromise> ParseSheet(
+ css::Loader&, const nsACString& aBytes,
+ const RefPtr<css::SheetLoadDataHolder>& aLoadData);
// Common code that needs to be called after servo finishes parsing. This is
// shared between the parallel and sequential paths.
@@ -301,7 +305,7 @@ class StyleSheet final : public nsICSSLoaderObserver, public nsWrapperCache {
*/
void SetPrincipal(nsIPrincipal* aPrincipal) {
StyleSheetInfo& info = Inner();
- MOZ_ASSERT(!info.mPrincipalSet, "Should only set principal once");
+ MOZ_ASSERT_IF(info.mPrincipalSet, info.mPrincipal == aPrincipal);
if (aPrincipal) {
info.mPrincipal = aPrincipal;
#ifdef DEBUG
@@ -468,6 +472,23 @@ class StyleSheet final : public nsICSSLoaderObserver, public nsWrapperCache {
// Gets the relevant global if exists.
nsISupports* GetRelevantGlobal() const;
+ // Blocks/Unblocks resolution of parse promise
+ void BlockParsePromise() {
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ uint32_t count =
+#endif
+ ++mAsyncParseBlockers;
+ MOZ_DIAGNOSTIC_ASSERT(count);
+ }
+
+ void UnblockParsePromise() {
+ uint32_t count = --mAsyncParseBlockers;
+ MOZ_DIAGNOSTIC_ASSERT(count != UINT32_MAX);
+ if (!count && !mParsePromise.IsEmpty()) {
+ mParsePromise.Resolve(true, __func__);
+ }
+ }
+
private:
void SetModifiedRules() {
mState |= State::ModifiedRules | State::ModifiedRulesForDevtools;
@@ -591,6 +612,8 @@ class StyleSheet final : public nsICSSLoaderObserver, public nsWrapperCache {
State mState;
+ Atomic<uint32_t, ReleaseAcquire> mAsyncParseBlockers{0};
+
// Core information we get from parsed sheets, which are shared amongst
// StyleSheet clones.
//
diff --git a/layout/style/contenteditable.css b/layout/style/contenteditable.css
index a2e2ca8712..7e9a197740 100644
--- a/layout/style/contenteditable.css
+++ b/layout/style/contenteditable.css
@@ -77,10 +77,6 @@ input[contenteditable="true"][type="hidden"] {
visibility: visible !important;
}
-*|*::-moz-display-comboboxcontrol-frame {
- user-select: text;
-}
-
option:read-write {
user-select: text;
}
diff --git a/layout/style/crashtests/1397439-1.html b/layout/style/crashtests/1397439-1.html
deleted file mode 100644
index b617f8e0ed..0000000000
--- a/layout/style/crashtests/1397439-1.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<!DOCTYPE html>
-<math>
-<mstyle scriptlevel=101>
-<mstyle scriptlevel=-204>
-</math>
-
diff --git a/layout/style/crashtests/1403465.html b/layout/style/crashtests/1403465.html
deleted file mode 100644
index 924392757b..0000000000
--- a/layout/style/crashtests/1403465.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<!DOCTYPE html>
-<html>
-<body>
- <math class="hidden">
- <mi>x</mi>
- <mo>=</mo>
- </math>
-<script>
-window.onload = function() {
- let s = document.createElement("style");
- s.textContent = `
- body {
- line-height: 1.42857143;
- }
-
- .hidden {
- display: none;
- }
- `;
- document.body.appendChild(s);
-};
-</script>
-</body>
-</html>
diff --git a/layout/style/crashtests/411603-1.html b/layout/style/crashtests/411603-1.html
deleted file mode 100644
index 596565fbc3..0000000000
--- a/layout/style/crashtests/411603-1.html
+++ /dev/null
@@ -1,7 +0,0 @@
-<html xmlns="http://www.w3.org/1999/xhtml" xmlns:math="http://www.w3.org/1998/Math/MathML">
-<head>
-</head>
-<body onload="document.getElementById('ms').setAttribute('scriptminsize', '9em');">
-<math:mstyle id="ms" /><span />
-</body>
-</html>
diff --git a/layout/style/crashtests/413274-1.xhtml b/layout/style/crashtests/413274-1.xhtml
deleted file mode 100644
index 19d8fab0fd..0000000000
--- a/layout/style/crashtests/413274-1.xhtml
+++ /dev/null
@@ -1,18 +0,0 @@
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-</head>
-<body>
-
-<math xmlns="http://www.w3.org/1998/Math/MathML">
- <mstyle scriptsizemultiplier="8205" scriptlevel="15">
- <mroot>
- <mrow/>
- <mrow>
- <span xmlns="http://www.w3.org/1999/xhtml"/>
- </mrow>
- </mroot>
- </mstyle>
-</math>
-
-</body>
-</html>
diff --git a/layout/style/crashtests/418007-1.xhtml b/layout/style/crashtests/418007-1.xhtml
deleted file mode 100644
index f07a693444..0000000000
--- a/layout/style/crashtests/418007-1.xhtml
+++ /dev/null
@@ -1,24 +0,0 @@
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head></head>
-<body>
-
-<math xmlns="http://www.w3.org/1998/Math/MathML">
- <ms fontsize="8179em">
- <span xmlns="http://www.w3.org/1999/xhtml">
- <span>
- <td>
- <mfrac xmlns="http://www.w3.org/1998/Math/MathML">
- <mfrac>
- <mrow/>
- <mrow/>
- </mfrac>
- <mrow/>
- </mfrac>
- </td>
- </span>
- </span>
- </ms>
-</math>
-
-</body>
-</html>
diff --git a/layout/style/crashtests/crashtests.list b/layout/style/crashtests/crashtests.list
index 3e7d1e2669..d54f053f1b 100644
--- a/layout/style/crashtests/crashtests.list
+++ b/layout/style/crashtests/crashtests.list
@@ -16,11 +16,8 @@ load 391034-1.xhtml
load 397022-1.html
load 399289-1.svg
load 404470-1.html
-load 411603-1.html
load 412588-1.html
-load 413274-1.xhtml
skip-if(Android) load chrome://reftest/content/crashtests/layout/style/crashtests/416461-1.xhtml
-load 418007-1.xhtml
load chrome://reftest/content/crashtests/layout/style/crashtests/431705-1.xhtml
load 432561-1.html
load 437170-1.html
@@ -229,7 +226,6 @@ load 1384232.html
load 1395725.html
load 1396041.html
pref(dom.animations-api.compositing.enabled,true) load 1397363-1.html
-load 1397439-1.html
load 1395719.html
load 1397091.html
load 1398479.html
@@ -251,7 +247,6 @@ load 1402419.html
load 1402472.html
load 1403028.html
load 1403433.html
-load 1403465.html
load 1403592.html
load 1403615.html
load 1403712.html
diff --git a/layout/style/nsCSSAnonBoxList.h b/layout/style/nsCSSAnonBoxList.h
index 891b34814b..4d62f5bd79 100644
--- a/layout/style/nsCSSAnonBoxList.h
+++ b/layout/style/nsCSSAnonBoxList.h
@@ -99,7 +99,6 @@ CSS_ANON_BOX(buttonContent, ":-moz-button-content")
CSS_ANON_BOX(cellContent, ":-moz-cell-content")
CSS_ANON_BOX(dropDownList, ":-moz-dropdown-list")
CSS_ANON_BOX(fieldsetContent, ":-moz-fieldset-content")
-CSS_ANON_BOX(mozDisplayComboboxControlFrame, ":-moz-display-comboboxcontrol-frame")
CSS_ANON_BOX(htmlCanvasContent, ":-moz-html-canvas-content")
CSS_WRAPPER_ANON_BOX(inlineTable, ":-moz-inline-table")
diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp
index 3b25ff9726..d40d4bc801 100644
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -733,6 +733,16 @@ static void CollectImageURLsForProperty(nsCSSPropertyID aProp,
}
}
+float nsComputedDOMStyle::UsedFontSize() {
+ UpdateCurrentStyleSources(eCSSProperty_font_size);
+
+ if (!mComputedStyle) {
+ return -1.0;
+ }
+
+ return mComputedStyle->StyleFont()->mFont.size.ToCSSPixels();
+}
+
void nsComputedDOMStyle::GetCSSImageURLs(const nsACString& aPropertyName,
nsTArray<nsCString>& aImageURLs,
mozilla::ErrorResult& aRv) {
@@ -990,6 +1000,19 @@ bool nsComputedDOMStyle::NeedsToFlushLayout(nsCSSPropertyID aPropID) const {
}
}
+bool nsComputedDOMStyle::NeedsToFlushLayoutForContainerQuery() const {
+ const auto* outerFrame = GetOuterFrame();
+ if (!outerFrame) {
+ return false;
+ }
+ const auto* innerFrame = nsLayoutUtils::GetStyleFrame(outerFrame);
+ MOZ_ASSERT(innerFrame, "No valid inner frame?");
+ // It's possible that potential containers are styled but not yet reflowed,
+ // i.e. They don't have a correct size, which makes any container query
+ // evaluation against them invalid.
+ return innerFrame->HasUnreflowedContainerQueryAncestor();
+}
+
void nsComputedDOMStyle::Flush(Document& aDocument, FlushType aFlushType) {
MOZ_ASSERT(mElement->IsInComposedDoc());
MOZ_ASSERT(mDocumentWeak == &aDocument);
@@ -1051,8 +1074,9 @@ void nsComputedDOMStyle::UpdateCurrentStyleSources(nsCSSPropertyID aPropID) {
Flush(*document, FlushType::Frames);
}
- if (NeedsToFlushLayout(aPropID)) {
- MOZ_ASSERT(MayNeedToFlushLayout(aPropID));
+ const bool needsToFlushLayoutForProp = NeedsToFlushLayout(aPropID);
+ if (needsToFlushLayoutForProp || NeedsToFlushLayoutForContainerQuery()) {
+ MOZ_ASSERT_IF(needsToFlushLayoutForProp, MayNeedToFlushLayout(aPropID));
didFlush = true;
Flush(*document, FlushType::Layout);
#ifdef DEBUG
diff --git a/layout/style/nsComputedDOMStyle.h b/layout/style/nsComputedDOMStyle.h
index 4a6fec785d..4f135a3aca 100644
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -120,6 +120,8 @@ class nsComputedDOMStyle final : public nsDOMCSSDeclaration,
mExposeVisitedStyle = aExpose;
}
+ float UsedFontSize() final;
+
void GetCSSImageURLs(const nsACString& aPropertyName,
nsTArray<nsCString>& aImageURLs,
mozilla::ErrorResult& aRv) final;
@@ -312,6 +314,10 @@ class nsComputedDOMStyle final : public nsDOMCSSDeclaration,
// Find out if we need to flush layout of the document, depending on the
// property that was requested.
bool NeedsToFlushLayout(nsCSSPropertyID) const;
+ // Find out if we need to flush layout of the document due to container
+ // query being made before relevant query containers are reflowed at least
+ // once.
+ bool NeedsToFlushLayoutForContainerQuery() const;
// Flushes the given document, which must be our document, and potentially the
// mElement's document.
void Flush(Document&, mozilla::FlushType);
diff --git a/layout/style/nsICSSDeclaration.h b/layout/style/nsICSSDeclaration.h
index 1750fd75c3..53dcbed4ec 100644
--- a/layout/style/nsICSSDeclaration.h
+++ b/layout/style/nsICSSDeclaration.h
@@ -86,6 +86,11 @@ class nsICSSDeclaration : public nsISupports, public nsWrapperCache {
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
}
+ // [Chrome only]
+ // Used font-size (taking account of the min-font-size prefs), if available;
+ // returns -1.0 on failure to retrieve a value.
+ virtual float UsedFontSize() { return -1.0; }
+
// WebIDL interface for CSSStyleDeclaration
virtual void SetCssText(const nsACString& aString,
nsIPrincipal* aSubjectPrincipal,
diff --git a/layout/style/nsMediaFeatures.cpp b/layout/style/nsMediaFeatures.cpp
index c99eecb486..f888c127d4 100644
--- a/layout/style/nsMediaFeatures.cpp
+++ b/layout/style/nsMediaFeatures.cpp
@@ -248,6 +248,9 @@ bool Gecko_MediaFeatures_MatchesPlatform(StylePlatform aPlatform) {
#elif defined(XP_MACOSX)
case StylePlatform::Macos:
return true;
+#elif defined(XP_IOS)
+ case StylePlatform::Ios:
+ return true;
#else
# error "Unknown platform?"
#endif
diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp
index 123a1b3304..39f5b1a760 100644
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -2020,7 +2020,7 @@ StyleTransition::StyleTransition(const StyleTransition& aCopy) = default;
bool StyleTransition::operator==(const StyleTransition& aOther) const {
return mTimingFunction == aOther.mTimingFunction &&
mDuration == aOther.mDuration && mDelay == aOther.mDelay &&
- mProperty == aOther.mProperty;
+ mProperty == aOther.mProperty && mBehavior == aOther.mBehavior;
}
StyleAnimation::StyleAnimation(const StyleAnimation& aCopy) = default;
@@ -3079,6 +3079,7 @@ nsStyleUIReset::nsStyleUIReset()
mTransitionDurationCount(1),
mTransitionDelayCount(1),
mTransitionPropertyCount(1),
+ mTransitionBehaviorCount(1),
mAnimations(
nsStyleAutoArray<StyleAnimation>::WITH_SINGLE_INITIAL_ELEMENT),
mAnimationTimingFunctionCount(1),
@@ -3120,6 +3121,7 @@ nsStyleUIReset::nsStyleUIReset(const nsStyleUIReset& aSource)
mTransitionDurationCount(aSource.mTransitionDurationCount),
mTransitionDelayCount(aSource.mTransitionDelayCount),
mTransitionPropertyCount(aSource.mTransitionPropertyCount),
+ mTransitionBehaviorCount(aSource.mTransitionBehaviorCount),
mAnimations(aSource.mAnimations.Clone()),
mAnimationTimingFunctionCount(aSource.mAnimationTimingFunctionCount),
mAnimationDurationCount(aSource.mAnimationDurationCount),
@@ -3178,6 +3180,7 @@ nsChangeHint nsStyleUIReset::CalcDifference(
mTransitionDurationCount != aNewData.mTransitionDurationCount ||
mTransitionDelayCount != aNewData.mTransitionDelayCount ||
mTransitionPropertyCount != aNewData.mTransitionPropertyCount ||
+ mTransitionBehaviorCount != aNewData.mTransitionBehaviorCount ||
mAnimations != aNewData.mAnimations ||
mAnimationTimingFunctionCount !=
aNewData.mAnimationTimingFunctionCount ||
@@ -3461,12 +3464,6 @@ void StyleCalcNode::ScaleLengthsBy(float aScale) {
}
}
-nscoord StyleCalcLengthPercentage::Resolve(nscoord aBasis,
- CoordRounder aRounder) const {
- CSSCoord result = ResolveToCSSPixels(CSSPixel::FromAppUnits(aBasis));
- return aRounder(result * AppUnitsPerCSSPixel());
-}
-
bool nsStyleDisplay::PrecludesSizeContainmentOrContentVisibilityWithFrame(
const nsIFrame& aFrame) const {
// The spec says that in the case of SVG, the contain property only applies
diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h
index b2d68a5976..8835934eaf 100644
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1120,6 +1120,7 @@ struct StyleTransition {
const StyleTime& GetDelay() const { return mDelay; }
const StyleTime& GetDuration() const { return mDuration; }
const StyleTransitionProperty& GetProperty() const { return mProperty; }
+ StyleTransitionBehavior GetBehavior() const { return mBehavior; }
bool operator==(const StyleTransition& aOther) const;
bool operator!=(const StyleTransition& aOther) const {
@@ -1133,6 +1134,7 @@ struct StyleTransition {
StyleTime mDelay{0.0};
StyleTransitionProperty mProperty{StyleTransitionProperty::NonCustom(
StyleNonCustomPropertyId{uint16_t(eCSSProperty_all)})};
+ StyleTransitionBehavior mBehavior = StyleTransitionBehavior::Normal;
};
struct StyleAnimation {
@@ -1647,6 +1649,10 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleUIReset {
return mTransitions[aIndex % mTransitionTimingFunctionCount]
.GetTimingFunction();
}
+ mozilla::StyleTransitionBehavior GetTransitionBehavior(
+ uint32_t aIndex) const {
+ return mTransitions[aIndex % mTransitionBehaviorCount].GetBehavior();
+ }
mozilla::StyleTime GetTransitionCombinedDuration(uint32_t aIndex) const {
// https://drafts.csswg.org/css-transitions/#transition-combined-duration
return {std::max(GetTransitionDuration(aIndex).seconds, 0.0f) +
@@ -1708,6 +1714,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleUIReset {
uint32_t mTransitionDurationCount;
uint32_t mTransitionDelayCount;
uint32_t mTransitionPropertyCount;
+ uint32_t mTransitionBehaviorCount;
nsStyleAutoArray<mozilla::StyleAnimation> mAnimations;
// The number of elements in mAnimations that are not from repeating
// a list due to another property being longer.
diff --git a/layout/style/nsStyleTransformMatrix.cpp b/layout/style/nsStyleTransformMatrix.cpp
index 593bcbb39d..71f8f826c2 100644
--- a/layout/style/nsStyleTransformMatrix.cpp
+++ b/layout/style/nsStyleTransformMatrix.cpp
@@ -101,7 +101,8 @@ static nsRect GetSVGBox(const nsIFrame* aFrame) {
// FIXME: Bug 1849054. We may have to update
// SVGGeometryFrame::GetBBoxContribution() to get tighter stroke bounds.
nsRect strokeBox = nsLayoutUtils::ComputeSVGReferenceRect(
- const_cast<nsIFrame*>(aFrame), StyleGeometryBox::StrokeBox);
+ const_cast<nsIFrame*>(aFrame), StyleGeometryBox::StrokeBox,
+ nsLayoutUtils::MayHaveNonScalingStrokeCyclicDependency::Yes);
// The |nsIFrame::mRect| includes markers, so we have to compute the
// offsets without markers.
return nsRect{strokeBox.x - aFrame->GetPosition().x,
diff --git a/layout/style/nsTransitionManager.cpp b/layout/style/nsTransitionManager.cpp
index ad3809c37f..66f1004354 100644
--- a/layout/style/nsTransitionManager.cpp
+++ b/layout/style/nsTransitionManager.cpp
@@ -116,6 +116,7 @@ bool nsTransitionManager::DoUpdateTransitions(
continue;
}
+ const auto behavior = aStyle.GetTransitionBehavior(i);
ExpandTransitionProperty(aStyle.GetTransitionProperty(i),
[&](const AnimatedPropertyID& aProperty) {
// We might have something to transition. See if
@@ -123,8 +124,9 @@ bool nsTransitionManager::DoUpdateTransitions(
// are animatable.
startedAny |= ConsiderInitiatingTransition(
aProperty, aStyle, i, delay, duration,
- aElement, aPseudoType, aElementTransitions,
- aOldStyle, aNewStyle, propertiesChecked);
+ behavior, aElement, aPseudoType,
+ aElementTransitions, aOldStyle, aNewStyle,
+ propertiesChecked);
});
}
@@ -248,8 +250,8 @@ GetReplacedTransitionProperties(const CSSTransition* aTransition,
bool nsTransitionManager::ConsiderInitiatingTransition(
const AnimatedPropertyID& aProperty, const nsStyleUIReset& aStyle,
uint32_t aTransitionIndex, float aDelay, float aDuration,
- dom::Element* aElement, PseudoStyleType aPseudoType,
- CSSTransitionCollection*& aElementTransitions,
+ mozilla::StyleTransitionBehavior aBehavior, dom::Element* aElement,
+ PseudoStyleType aPseudoType, CSSTransitionCollection*& aElementTransitions,
const ComputedStyle& aOldStyle, const ComputedStyle& aNewStyle,
AnimatedPropertyIDSet& aPropertiesChecked) {
// IsShorthand itself will assert if aProperty is not a property.
@@ -294,7 +296,7 @@ bool nsTransitionManager::ConsiderInitiatingTransition(
AnimationValue startValue, endValue;
const StyleShouldTransitionResult result =
Servo_ComputedValues_ShouldTransition(
- &aOldStyle, &aNewStyle, &property,
+ &aOldStyle, &aNewStyle, &property, aBehavior,
oldTransition ? oldTransition->ToValue().mServo.get() : nullptr,
&startValue.mServo, &endValue.mServo);
diff --git a/layout/style/nsTransitionManager.h b/layout/style/nsTransitionManager.h
index e87a0109a2..02a577690d 100644
--- a/layout/style/nsTransitionManager.h
+++ b/layout/style/nsTransitionManager.h
@@ -63,6 +63,7 @@ class nsTransitionManager final
bool ConsiderInitiatingTransition(
const mozilla::AnimatedPropertyID&, const nsStyleUIReset& aStyle,
uint32_t aTransitionIndex, float aDelay, float aDuration,
+ mozilla::StyleTransitionBehavior aBehavior,
mozilla::dom::Element* aElement, mozilla::PseudoStyleType aPseudoType,
CSSTransitionCollection*& aElementTransitions,
const mozilla::ComputedStyle& aOldStyle,
diff --git a/layout/style/res/forms.css b/layout/style/res/forms.css
index 9fb833aa74..a2d026639b 100644
--- a/layout/style/res/forms.css
+++ b/layout/style/res/forms.css
@@ -259,40 +259,27 @@ select:-moz-select-list-box {
}
select > button {
- inline-size: 12px;
- white-space: nowrap;
- position: static;
+ padding: 0;
+ border: 0;
appearance: auto;
-moz-default-appearance: -moz-menulist-arrow-button;
- /* Make sure to size correctly if the combobox has a non-auto height. */
- block-size: 100%;
- box-sizing: border-box;
-
/* Draw the arrow in the select's color */
color: inherit;
- /*
- Make sure to align properly with the display frame. Note that we
- want the baseline of the combobox to match the baseline of the
- display frame, so the dropmarker is what gets the vertical-align.
- */
+ /* We don't want the button to grow the line-height */
+ font: inherit;
+ max-block-size: 100%;
+
+ /* Make sure to align properly with the display frame. Note that we want the
+ * baseline of the combobox to match the baseline of the label, so the
+ * dropmarker is what gets the vertical-align. */
vertical-align: top;
}
-*|*::-moz-display-comboboxcontrol-frame {
- content: inherit;
+select > label {
+ display: inline-block;
overflow: clip;
- color: unset;
- white-space: nowrap;
- text-align: unset;
- user-select: none;
- /* Make sure to size correctly if the combobox has a non-auto block-size. */
- block-size: 100%;
- /* Try to always display our own text */
- min-inline-size: max-content;
- box-sizing: border-box;
- line-height: -moz-block-height;
}
option[label]::before {
@@ -668,16 +655,15 @@ input[type=file] > label {
* inherit into the ':-moz-button-content' pseudo-element.
*
* <select>:
- * inherit into the ':-moz-display-comboboxcontrol-frame' pseudo-element and
- * the <optgroup>'s ':before' pseudo-element, which is where the label of
- * the <optgroup> gets displayed. The <option>s don't use anonymous boxes,
- * so they need no special rules.
+ * inherit into the label and the <optgroup>'s ':before' pseudo-element,
+ * which is where the label of the <optgroup> gets displayed. The <option>s
+ * don't use anonymous boxes, so they need no special rules.
*/
::placeholder,
::-moz-text-control-editing-root,
*|*::-moz-button-content,
-*|*::-moz-display-comboboxcontrol-frame,
-optgroup:before {
+select > label,
+optgroup::before {
unicode-bidi: inherit;
text-overflow: inherit;
}
@@ -868,6 +854,13 @@ input[type=number]::-moz-number-spin-box {
overflow: clip;
}
+/* stylelint-disable-next-line media-query-no-invalid */
+@media (-moz-bool-pref: "dom.forms.number.hide_spin_buttons_when_no_hover_or_focus") {
+ input[type=number]:not(:hover, :focus)::-moz-number-spin-box {
+ opacity: 0;
+ }
+}
+
input[type=number]::-moz-number-spin-up,
input[type=number]::-moz-number-spin-down {
writing-mode: horizontal-tb;
@@ -922,11 +915,7 @@ input:is([type=date], [type=time], [type=datetime-local]):is(:disabled, :read-on
}
input:autofill, select:autofill {
- /* The idea behind using background-image instead of plain background-color
- * is that it's less likely to be overridden by the page. */
- background-image: linear-gradient(-moz-autofill-background, -moz-autofill-background);
-}
-
-input:-moz-autofill-preview, select:-moz-autofill-preview {
- color: GrayText;
+ background-color: -moz-autofill-background !important;
+ background-image: none !important;
+ color: FieldText !important;
}
diff --git a/layout/style/res/html.css b/layout/style/res/html.css
index 383aa35f7b..ff58ecd4d1 100644
--- a/layout/style/res/html.css
+++ b/layout/style/res/html.css
@@ -149,10 +149,8 @@ dd {
blockquote, figure {
display: block;
- margin-block-start: 1em;
- margin-block-end: 1em;
- margin-inline-start: 40px;
- margin-inline-end: 40px;
+ margin-block: 1em;
+ margin-inline: 40px;
}
address {
@@ -169,68 +167,85 @@ h1 {
display: block;
font-size: 2em;
font-weight: bold;
- margin-block-start: .67em;
- margin-block-end: .67em;
+ margin-block: .67em;
}
-h2,
-:is(article, aside, nav, section)
-h1 {
+h2 {
display: block;
font-size: 1.5em;
font-weight: bold;
- margin-block-start: .83em;
- margin-block-end: .83em;
+ margin-block: .83em;
}
-h3,
-:is(article, aside, nav, section)
-:is(article, aside, nav, section)
-h1 {
+h3 {
display: block;
font-size: 1.17em;
font-weight: bold;
- margin-block-start: 1em;
- margin-block-end: 1em;
+ margin-block: 1em;
}
-h4,
-:is(article, aside, nav, section)
-:is(article, aside, nav, section)
-:is(article, aside, nav, section)
-h1 {
+h4 {
display: block;
font-size: 1.00em;
font-weight: bold;
- margin-block-start: 1.33em;
- margin-block-end: 1.33em;
+ margin-block: 1.33em;
}
-h5,
-:is(article, aside, nav, section)
-:is(article, aside, nav, section)
-:is(article, aside, nav, section)
-:is(article, aside, nav, section)
-h1 {
+h5 {
display: block;
font-size: 0.83em;
font-weight: bold;
- margin-block-start: 1.67em;
- margin-block-end: 1.67em;
+ margin-block: 1.67em;
}
-h6,
-:is(article, aside, nav, section)
-:is(article, aside, nav, section)
-:is(article, aside, nav, section)
-:is(article, aside, nav, section)
-:is(article, aside, nav, section)
-h1 {
+h6 {
display: block;
font-size: 0.67em;
font-weight: bold;
- margin-block-start: 2.33em;
- margin-block-end: 2.33em;
+ margin-block: 2.33em;
+}
+
+/* stylelint-disable-next-line media-query-no-invalid */
+@media (-moz-bool-pref: "layout.css.h1-in-section-ua-styles.enabled") {
+ :is(article, aside, nav, section)
+ h1 {
+ margin-block: 0.83em;
+ font-size: 1.50em;
+ }
+
+ :is(article, aside, nav, section)
+ :is(article, aside, nav, section)
+ h1 {
+ margin-block: 1.00em;
+ font-size: 1.17em;
+ }
+
+ :is(article, aside, nav, section)
+ :is(article, aside, nav, section)
+ :is(article, aside, nav, section)
+ h1 {
+ margin-block: 1.33em;
+ font-size: 1.00em;
+ }
+
+ :is(article, aside, nav, section)
+ :is(article, aside, nav, section)
+ :is(article, aside, nav, section)
+ :is(article, aside, nav, section)
+ h1 {
+ margin-block: 1.67em;
+ font-size: 0.83em;
+ }
+
+ :is(article, aside, nav, section)
+ :is(article, aside, nav, section)
+ :is(article, aside, nav, section)
+ :is(article, aside, nav, section)
+ :is(article, aside, nav, section)
+ h1 {
+ margin-block: 2.33em;
+ font-size: 0.67em;
+ }
}
listing {
@@ -238,16 +253,14 @@ listing {
font-family: -moz-fixed;
font-size: medium;
white-space: pre;
- margin-block-start: 1em;
- margin-block-end: 1em;
+ margin-block: 1em;
}
xmp, pre, plaintext {
display: block;
font-family: -moz-fixed;
white-space: pre;
- margin-block-start: 1em;
- margin-block-end: 1em;
+ margin-block: 1em;
}
/* tables */
@@ -380,25 +393,19 @@ table[rules][rules="cols"] > tr > td,
table[rules][rules="cols"] > * > tr > td,
table[rules][rules="cols"] > tr > th,
table[rules][rules="cols"] > * > tr > th {
- border-inline-start-width: thin;
- border-inline-end-width: thin;
- border-inline-start-style: solid;
- border-inline-end-style: solid;
+ border-inline-width: thin;
+ border-inline-style: solid;
}
table[rules][rules="groups"] > colgroup {
- border-inline-start-width: thin;
- border-inline-end-width: thin;
- border-inline-start-style: solid;
- border-inline-end-style: solid;
+ border-inline-width: thin;
+ border-inline-style: solid;
}
table[rules][rules="groups"] > tfoot,
table[rules][rules="groups"] > thead,
table[rules][rules="groups"] > tbody {
- border-block-start-width: thin;
- border-block-end-width: thin;
- border-block-start-style: solid;
- border-block-end-style: solid;
+ border-block-width: thin;
+ border-block-style: solid;
}
@@ -409,8 +416,7 @@ caption {
}
table[align="center"] > caption {
- margin-inline-start: auto;
- margin-inline-end: auto;
+ margin-inline: auto;
}
table[align="center"] > caption[align="left"]:dir(ltr) {
@@ -589,8 +595,7 @@ li {
:is(ul, ol, dir, menu, dl) dir,
:is(ul, ol, dir, menu, dl) menu,
:is(ul, ol, dir, menu, dl) dl {
- margin-block-start: 0;
- margin-block-end: 0;
+ margin-block: 0;
}
/* 2 deep unordered lists use a circle */
@@ -618,10 +623,8 @@ hr {
color: gray;
border-width: 1px;
border-style: inset;
- margin-block-start: 0.5em;
- margin-block-end: 0.5em;
- margin-inline-start: auto;
- margin-inline-end: auto;
+ margin-block: 0.5em;
+ margin-inline: auto;
overflow: hidden;
/* FIXME: This is not really per spec */
diff --git a/layout/style/test/ParseCSS.cpp b/layout/style/test/ParseCSS.cpp
index 04e37d48e2..e201d191a9 100644
--- a/layout/style/test/ParseCSS.cpp
+++ b/layout/style/test/ParseCSS.cpp
@@ -18,7 +18,6 @@
#include "nsIFile.h"
#include "nsNetUtil.h"
-#include "nsContentCID.h"
#include "mozilla/StyleSheetInlines.h"
#include "mozilla/css/Loader.h"
diff --git a/layout/style/test/animation_utils.js b/layout/style/test/animation_utils.js
index 6f7ededcd4..7239885e7c 100644
--- a/layout/style/test/animation_utils.js
+++ b/layout/style/test/animation_utils.js
@@ -4,6 +4,8 @@
//
//----------------------------------------------------------------------
+/* eslint-disable mozilla/no-comparison-or-assignment-inside-ok */
+
function advance_clock(milliseconds) {
SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(milliseconds);
}
diff --git a/layout/style/test/chrome/bug418986-2.js b/layout/style/test/chrome/bug418986-2.js
index 6d2af235c3..c945eecca0 100644
--- a/layout/style/test/chrome/bug418986-2.js
+++ b/layout/style/test/chrome/bug418986-2.js
@@ -1,5 +1,7 @@
// # Bug 418986, part 2.
+/* eslint-disable mozilla/no-comparison-or-assignment-inside-ok */
+
const is_chrome_window = window.location.protocol === "chrome:";
const HTML_NS = "http://www.w3.org/1999/xhtml";
diff --git a/layout/style/test/mochitest.toml b/layout/style/test/mochitest.toml
index 9df86ea539..54ad9736f2 100644
--- a/layout/style/test/mochitest.toml
+++ b/layout/style/test/mochitest.toml
@@ -18,6 +18,7 @@ prefs = [
"layout.css.basic-shape-rect.enabled=true",
"layout.css.basic-shape-xywh.enabled=true",
"layout.css.transform-box-content-stroke.enabled=true",
+ "layout.css.transition-behavior.enabled=true",
]
support-files = [
"animation_utils.js",
diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js
index 422f2ffe5c..2e8b4c71a3 100644
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -13604,36 +13604,32 @@ if (IsCSSPropertyPrefEnabled("layout.css.backdrop-filter.enabled")) {
};
}
-if (IsCSSPropertyPrefEnabled("layout.css.math-depth.enabled")) {
- gCSSProperties["math-depth"] = {
- domProp: "mathDepth",
- inherited: true,
- type: CSS_TYPE_LONGHAND,
- initial_values: ["0"],
- other_values: [
- // auto-add cannot be tested here because it has no effect when the
- // inherited math-style is equal to the default (normal).
- "123",
- "-123",
- "add(123)",
- "add(-123)",
- "calc(1 + 2*3)",
- "add(calc(4 - 2/3))",
- ],
- invalid_values: ["auto", "1,23", "1.23", "add(1,23)", "add(1.23)"],
- };
-}
+gCSSProperties["math-depth"] = {
+ domProp: "mathDepth",
+ inherited: true,
+ type: CSS_TYPE_LONGHAND,
+ initial_values: ["0"],
+ other_values: [
+ // auto-add cannot be tested here because it has no effect when the
+ // inherited math-style is equal to the default (normal).
+ "123",
+ "-123",
+ "add(123)",
+ "add(-123)",
+ "calc(1 + 2*3)",
+ "add(calc(4 - 2/3))",
+ ],
+ invalid_values: ["auto", "1,23", "1.23", "add(1,23)", "add(1.23)"],
+};
-if (IsCSSPropertyPrefEnabled("layout.css.math-style.enabled")) {
- gCSSProperties["math-style"] = {
- domProp: "mathStyle",
- inherited: true,
- type: CSS_TYPE_LONGHAND,
- initial_values: ["normal"],
- other_values: ["compact"],
- invalid_values: [],
- };
-}
+gCSSProperties["math-style"] = {
+ domProp: "mathStyle",
+ inherited: true,
+ type: CSS_TYPE_LONGHAND,
+ initial_values: ["normal"],
+ other_values: ["compact"],
+ invalid_values: [],
+};
if (IsCSSPropertyPrefEnabled("layout.css.forced-color-adjust.enabled")) {
gCSSProperties["forced-color-adjust"] = {
@@ -14088,6 +14084,36 @@ if (IsCSSPropertyPrefEnabled("layout.css.prefixes.transitions")) {
});
}
+if (IsCSSPropertyPrefEnabled("layout.css.transition-behavior.enabled")) {
+ Object.assign(gCSSProperties, {
+ "transition-behavior": {
+ domProp: "transitionBehavior",
+ inherited: false,
+ type: CSS_TYPE_LONGHAND,
+ applies_to_marker: true,
+ initial_values: ["normal"],
+ other_values: ["allow-discrete"],
+ invalid_values: ["none", "auto", "discrete"],
+ },
+ });
+
+ gCSSProperties["transition"].subproperties.push("transition-behavior");
+ gCSSProperties["transition"].initial_values.push("normal");
+ gCSSProperties["transition"].other_values.push(
+ "allow-discrete",
+ "width allow-discrete",
+ "1s allow-discrete",
+ "linear allow-discrete"
+ );
+ gCSSProperties["-webkit-transition"].subproperties.push(
+ "transition-behavior"
+ );
+
+ if (IsCSSPropertyPrefEnabled("layout.css.prefixes.transitions")) {
+ gCSSProperties["-moz-transition"].subproperties.push("transition-behavior");
+ }
+}
+
// Copy aliased properties' fields from their alias targets. Keep this logic
// at the bottom of this file to ensure all the aliased properties are
// processed.
diff --git a/layout/style/test/test_shorthand_property_getters.html b/layout/style/test/test_shorthand_property_getters.html
index cade526183..b5db9519a6 100644
--- a/layout/style/test/test_shorthand_property_getters.html
+++ b/layout/style/test/test_shorthand_property_getters.html
@@ -183,17 +183,19 @@ e.setAttribute("style", "background-position-x: 0px; background-position-y: top
is(e.style.backgroundPosition, "left 0px top 0px", "should serialize to 4-value form if 3-value form would only have one edge");
// Check that we only serialize transition when the lists are the same length.
-e.setAttribute("style", "transition-property: color, width; transition-duration: 1s, 200ms; transition-timing-function: ease-in, linear; transition-delay: 0s, 1s");
+e.setAttribute("style", "transition-property: color, width; transition-duration: 1s, 200ms; transition-timing-function: ease-in, linear; transition-delay: 0s, 1s; transition-behavior: normal, allow-discrete;");
isnot(e.style.transition, "", "should have transition shorthand (lists same length)");
-e.setAttribute("style", "transition-property: color, width, left; transition-duration: 1s, 200ms; transition-timing-function: ease-in, linear; transition-delay: 0s, 1s");
+e.setAttribute("style", "transition-property: color, width, left; transition-duration: 1s, 200ms; transition-timing-function: ease-in, linear; transition-delay: 0s, 1s; transition-behavior: normal, allow-discrete;");
is(e.style.transition, "", "should not have transition shorthand (lists different lengths)");
-e.setAttribute("style", "transition-property: all; transition-duration: 1s, 200ms; transition-timing-function: ease-in, linear; transition-delay: 0s, 1s");
+e.setAttribute("style", "transition-property: all; transition-duration: 1s, 200ms; transition-timing-function: ease-in, linear; transition-delay: 0s, 1s; transition-behavior: normal, allow-discrete;");
is(e.style.transition, "", "should not have transition shorthand (lists different lengths)");
-e.setAttribute("style", "transition-property: color, width; transition-duration: 1s, 200ms, 300ms; transition-timing-function: ease-in, linear; transition-delay: 0s, 1s");
+e.setAttribute("style", "transition-property: color, width; transition-duration: 1s, 200ms, 300ms; transition-timing-function: ease-in, linear; transition-delay: 0s, 1s; transition-behavior: normal, allow-discrete;");
is(e.style.transition, "", "should not have transition shorthand (lists different lengths)");
-e.setAttribute("style", "transition-property: color, width; transition-duration: 1s, 200ms; transition-timing-function: ease-in, linear, ease-out; transition-delay: 0s, 1s");
+e.setAttribute("style", "transition-property: color, width; transition-duration: 1s, 200ms; transition-timing-function: ease-in, linear, ease-out; transition-delay: 0s, 1s; transition-behavior: normal, allow-discrete;");
is(e.style.transition, "", "should not have transition shorthand (lists different lengths)");
-e.setAttribute("style", "transition-property: color, width; transition-duration: 1s, 200ms; transition-timing-function: ease-in, linear; transition-delay: 0s, 1s, 0s");
+e.setAttribute("style", "transition-property: color, width; transition-duration: 1s, 200ms; transition-timing-function: ease-in, linear; transition-delay: 0s, 1s, 0s; transition-behavior: normal, allow-discrete;");
+is(e.style.transition, "", "should not have transition shorthand (lists different lengths)");
+e.setAttribute("style", "transition-property: color, width; transition-duration: 1s, 200ms; transition-timing-function: ease-in, linear; transition-delay: 0s, 1s; transition-behavior: normal, allow-discrete, normal;");
is(e.style.transition, "", "should not have transition shorthand (lists different lengths)");
e.setAttribute("style", "transition: color, width; transition-delay: 0s");
is(e.style.transition, "", "should not have transition shorthand (lists different lengths)");