diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/accessible/atk/AccessibleWrap.cpp firefox-trunk-95.0~a1~hg20211020r596404/accessible/atk/AccessibleWrap.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/accessible/atk/AccessibleWrap.cpp 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/accessible/atk/AccessibleWrap.cpp 2021-10-20 19:28:14.000000000 +0000 @@ -737,7 +737,7 @@ return nullptr; } - LocalAccessible* accChild = accWrap->GetEmbeddedChildAt(aChildIndex); + LocalAccessible* accChild = accWrap->EmbeddedChildAt(aChildIndex); if (accChild) { childAtkObj = AccessibleWrap::GetAtkObject(accChild); } else { @@ -752,8 +752,10 @@ return nullptr; } - RemoteAccessible* child = proxy->EmbeddedChildAt(aChildIndex); - if (child) childAtkObj = GetWrapperFor(child); + Accessible* child = proxy->EmbeddedChildAt(aChildIndex); + if (child) { + childAtkObj = GetWrapperFor(child->AsRemote()); + } } else { return nullptr; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/accessible/basetypes/Accessible.h firefox-trunk-95.0~a1~hg20211020r596404/accessible/basetypes/Accessible.h --- firefox-trunk-95.0~a1~hg20211017r596111/accessible/basetypes/Accessible.h 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/accessible/basetypes/Accessible.h 2021-10-20 19:28:14.000000000 +0000 @@ -110,6 +110,11 @@ */ bool HasGenericType(AccGenericType aType) const; + /** + * Return embedded accessible child at the given index. + */ + virtual Accessible* EmbeddedChildAt(uint32_t aIndex) = 0; + // Methods that potentially access a cache. /* diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/accessible/basetypes/HyperTextAccessibleBase.cpp firefox-trunk-95.0~a1~hg20211020r596404/accessible/basetypes/HyperTextAccessibleBase.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/accessible/basetypes/HyperTextAccessibleBase.cpp 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/accessible/basetypes/HyperTextAccessibleBase.cpp 2021-10-20 19:28:14.000000000 +0000 @@ -288,4 +288,8 @@ startOffset <= endOffset && endOffset <= CharacterCount(); } +Accessible* HyperTextAccessibleBase::LinkAt(uint32_t aIndex) { + return Acc()->EmbeddedChildAt(aIndex); +} + } // namespace mozilla::a11y diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/accessible/basetypes/HyperTextAccessibleBase.h firefox-trunk-95.0~a1~hg20211020r596404/accessible/basetypes/HyperTextAccessibleBase.h --- firefox-trunk-95.0~a1~hg20211017r596111/accessible/basetypes/HyperTextAccessibleBase.h 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/accessible/basetypes/HyperTextAccessibleBase.h 2021-10-20 19:28:14.000000000 +0000 @@ -119,6 +119,11 @@ bool IsValidOffset(int32_t aOffset); bool IsValidRange(int32_t aStartOffset, int32_t aEndOffset); + /** + * Return link accessible at the given index. + */ + Accessible* LinkAt(uint32_t aIndex); + protected: virtual const Accessible* Acc() const = 0; Accessible* Acc() { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/accessible/generic/HyperTextAccessible.h firefox-trunk-95.0~a1~hg20211020r596404/accessible/generic/HyperTextAccessible.h --- firefox-trunk-95.0~a1~hg20211017r596111/accessible/generic/HyperTextAccessible.h 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/accessible/generic/HyperTextAccessible.h 2021-10-20 19:28:14.000000000 +0000 @@ -82,9 +82,7 @@ /** * Return link accessible at the given index. */ - LocalAccessible* LinkAt(uint32_t aIndex) { - return GetEmbeddedChildAt(aIndex); - } + LocalAccessible* LinkAt(uint32_t aIndex) { return EmbeddedChildAt(aIndex); } /** * Return index for the given link accessible. diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/accessible/generic/LocalAccessible.cpp firefox-trunk-95.0~a1~hg20211020r596404/accessible/generic/LocalAccessible.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/accessible/generic/LocalAccessible.cpp 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/accessible/generic/LocalAccessible.cpp 2021-10-20 19:28:14.000000000 +0000 @@ -2683,7 +2683,7 @@ return ChildCount(); } -LocalAccessible* LocalAccessible::GetEmbeddedChildAt(uint32_t aIndex) { +LocalAccessible* LocalAccessible::EmbeddedChildAt(uint32_t aIndex) { if (mStateFlags & eHasTextKids) { if (!mEmbeddedObjCollector) { mEmbeddedObjCollector.reset(new EmbeddedObjCollector(this)); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/accessible/generic/LocalAccessible.h firefox-trunk-95.0~a1~hg20211020r596404/accessible/generic/LocalAccessible.h --- firefox-trunk-95.0~a1~hg20211017r596111/accessible/generic/LocalAccessible.h 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/accessible/generic/LocalAccessible.h 2021-10-20 19:28:14.000000000 +0000 @@ -401,7 +401,7 @@ /** * Return embedded accessible child at the given index. */ - LocalAccessible* GetEmbeddedChildAt(uint32_t aIndex); + virtual LocalAccessible* EmbeddedChildAt(uint32_t aIndex) override; /** * Return index of the given embedded accessible child. diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/accessible/ipc/RemoteAccessibleBase.cpp firefox-trunk-95.0~a1~hg20211020r596404/accessible/ipc/RemoteAccessibleBase.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/accessible/ipc/RemoteAccessibleBase.cpp 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/accessible/ipc/RemoteAccessibleBase.cpp 2021-10-20 19:28:14.000000000 +0000 @@ -113,7 +113,7 @@ } template -Derived* RemoteAccessibleBase::EmbeddedChildAt(size_t aChildIdx) { +Accessible* RemoteAccessibleBase::EmbeddedChildAt(uint32_t aChildIdx) { size_t index = 0, kids = mChildren.Length(); for (size_t i = 0; i < kids; i++) { if (!mChildren[i]->IsEmbeddedObject()) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/accessible/ipc/RemoteAccessibleBase.h firefox-trunk-95.0~a1~hg20211020r596404/accessible/ipc/RemoteAccessibleBase.h --- firefox-trunk-95.0~a1~hg20211017r596111/accessible/ipc/RemoteAccessibleBase.h 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/accessible/ipc/RemoteAccessibleBase.h 2021-10-20 19:28:14.000000000 +0000 @@ -110,7 +110,7 @@ } uint32_t EmbeddedChildCount() const; int32_t IndexOfEmbeddedChild(const Derived* aChild); - Derived* EmbeddedChildAt(size_t aChildIdx); + virtual Accessible* EmbeddedChildAt(uint32_t aChildIdx) override; void Shutdown(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/accessible/tests/browser/bounds/browser.ini firefox-trunk-95.0~a1~hg20211020r596404/accessible/tests/browser/bounds/browser.ini --- firefox-trunk-95.0~a1~hg20211017r596111/accessible/tests/browser/bounds/browser.ini 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/accessible/tests/browser/bounds/browser.ini 2021-10-20 19:28:14.000000000 +0000 @@ -7,8 +7,9 @@ !/accessible/tests/mochitest/letters.gif [browser_test_resolution.js] -skip-if = (e10s && os == 'win') || (webrender) # bug 1372296, bug 1734271 +skip-if = e10s && os == 'win' # bug 1372296 [browser_test_zoom.js] +skip-if = webrender # Bug 1734271 [browser_test_zoom_text.js] https_first_disabled = true skip-if = e10s && os == 'win' # bug 1372296 diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/accessible/tests/browser/e10s/browser_caching_text.js firefox-trunk-95.0~a1~hg20211020r596404/accessible/tests/browser/e10s/browser_caching_text.js --- firefox-trunk-95.0~a1~hg20211017r596111/accessible/tests/browser/e10s/browser_caching_text.js 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/accessible/tests/browser/e10s/browser_caching_text.js 2021-10-20 19:28:14.000000000 +0000 @@ -11,6 +11,9 @@ "accessibility.cache.enabled", false ); +// Some RemoteAccessible methods aren't supported on Windows when the cache is +// disabled. +const isWinNoCache = !isCacheEnabled && AppConstants.platform == "win"; /** * Test line and word offsets for various cases for both local and remote @@ -30,7 +33,7 @@ async function(browser, docAcc) { for (const id of ["br", "pre"]) { const acc = findAccessibleChildByID(docAcc, id); - if (!isCacheEnabled && AppConstants.platform == "win") { + if (isWinNoCache) { todo( false, "Cache disabled, so RemoteAccessible doesn't support CharacterCount on Windows" @@ -77,3 +80,24 @@ }, { chrome: true, topLevel: true, iframe: true, remoteIframe: true } ); + +/** + * Test HyperText embedded object methods. + */ +addAccessibleTask( + `
abc
`, + async function(browser, docAcc) { + const container = findAccessibleChildByID(docAcc, "container", [ + nsIAccessibleHyperText, + ]); + let embeddedAcc = container.getLinkAt(0); + queryInterfaces(embeddedAcc, [nsIAccessible]); + is(getAccessibleDOMNodeID(embeddedAcc), "link", "LinkAt 0 is the link"); + }, + { + chrome: true, + topLevel: !isWinNoCache, + iframe: !isWinNoCache, + remoteIframe: !isWinNoCache, + } +); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/accessible/tests/mochitest/events/slow_image.sjs firefox-trunk-95.0~a1~hg20211020r596404/accessible/tests/mochitest/events/slow_image.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/accessible/tests/mochitest/events/slow_image.sjs 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/accessible/tests/mochitest/events/slow_image.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -12,7 +12,10 @@ // stolen from file_blocked_script.sjs function setGlobalState(data, key) { - x = { data: data, QueryInterface: function(iid) { return this } }; + x = { + data, + QueryInterface: ChromeUtils.generateQI([]), + }; x.wrappedJSObject = x; setObjectState(key, x); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/accessible/windows/ia2/ia2Accessible.cpp firefox-trunk-95.0~a1~hg20211020r596404/accessible/windows/ia2/ia2Accessible.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/accessible/windows/ia2/ia2Accessible.cpp 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/accessible/windows/ia2/ia2Accessible.cpp 2021-10-20 19:28:14.000000000 +0000 @@ -284,7 +284,10 @@ } AccessibleWrap* acc = LocalAcc(); if (!acc) { - return E_NOTIMPL; // XXX Not supported for RemoteAccessible yet. + // XXX Not supported for RemoteAccessible yet. However, don't return + // E_NOTIMPL because returning an error here will cause screen readers to + // assume defunct. + return S_OK; } uint64_t state; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/accessible/windows/ia2/ia2AccessibleHyperlink.cpp firefox-trunk-95.0~a1~hg20211020r596404/accessible/windows/ia2/ia2AccessibleHyperlink.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/accessible/windows/ia2/ia2AccessibleHyperlink.cpp 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/accessible/windows/ia2/ia2AccessibleHyperlink.cpp 2021-10-20 19:28:14.000000000 +0000 @@ -14,6 +14,10 @@ using namespace mozilla::a11y; +Accessible* ia2AccessibleHyperlink::Acc() { + return static_cast(this)->Acc(); +} + AccessibleWrap* ia2AccessibleHyperlink::LocalAcc() { return static_cast(this)->LocalAcc(); } @@ -27,8 +31,8 @@ *ppv = nullptr; if (IID_IAccessibleHyperlink == iid) { - auto accWrap = LocalAcc(); - if (!accWrap || !accWrap->IsLink()) { + Accessible* acc = Acc(); + if (!acc || !acc->IsLink()) { return E_NOINTERFACE; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/accessible/windows/ia2/ia2AccessibleHyperlink.h firefox-trunk-95.0~a1~hg20211020r596404/accessible/windows/ia2/ia2AccessibleHyperlink.h --- firefox-trunk-95.0~a1~hg20211017r596111/accessible/windows/ia2/ia2AccessibleHyperlink.h 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/accessible/windows/ia2/ia2AccessibleHyperlink.h 2021-10-20 19:28:14.000000000 +0000 @@ -15,6 +15,7 @@ namespace mozilla { namespace a11y { +class Accessible; class AccessibleWrap; class ia2AccessibleHyperlink : public ia2AccessibleAction, @@ -44,6 +45,7 @@ /* [retval][out] */ boolean* valid); private: + Accessible* Acc(); AccessibleWrap* LocalAcc(); }; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/accessible/windows/ia2/ia2AccessibleHypertext.cpp firefox-trunk-95.0~a1~hg20211020r596404/accessible/windows/ia2/ia2AccessibleHypertext.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/accessible/windows/ia2/ia2AccessibleHypertext.cpp 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/accessible/windows/ia2/ia2AccessibleHypertext.cpp 2021-10-20 19:28:14.000000000 +0000 @@ -14,7 +14,12 @@ using namespace mozilla::a11y; -HyperTextAccessibleWrap* ia2AccessibleHypertext::TextAcc() { +HyperTextAccessibleBase* ia2AccessibleHypertext::TextAcc() { + Accessible* acc = Acc(); + return acc ? acc->AsHyperTextBase() : nullptr; +} + +HyperTextAccessibleWrap* ia2AccessibleHypertext::LocalTextAcc() { AccessibleWrap* acc = LocalAcc(); return static_cast(acc); } @@ -32,7 +37,7 @@ if (aIID == IID_IAccessibleText) { *aInstancePtr = static_cast(static_cast(this)); - } else if (aIID == IID_IAccessibleHypertext && isLocal) { + } else if (aIID == IID_IAccessibleHypertext) { *aInstancePtr = static_cast(this); } else if (aIID == IID_IAccessibleHypertext2 && isLocal) { *aInstancePtr = static_cast(this); @@ -57,7 +62,7 @@ *aHyperlinkCount = 0; - HyperTextAccessibleWrap* hyperText = TextAcc(); + HyperTextAccessibleWrap* hyperText = LocalTextAcc(); if (!hyperText) return CO_E_OBJNOTCONNECTED; *aHyperlinkCount = hyperText->LinkCount(); @@ -71,19 +76,15 @@ *aHyperlink = nullptr; - LocalAccessible* hyperLink; - HyperTextAccessibleWrap* hyperText = TextAcc(); + HyperTextAccessibleBase* hyperText = TextAcc(); if (!hyperText) { return CO_E_OBJNOTCONNECTED; } - hyperLink = hyperText->LinkAt(aLinkIndex); + Accessible* hyperLink = hyperText->LinkAt(aLinkIndex); if (!hyperLink) return E_FAIL; - // GetNativeInterface returns an IAccessible, but we need an - // IAccessibleHyperlink, so use MsaaAccessible::GetFrom instead and let - // RefPtr cast it. RefPtr result = MsaaAccessible::GetFrom(hyperLink); result.forget(aHyperlink); return S_OK; @@ -96,7 +97,7 @@ *aHyperlinkIndex = 0; - HyperTextAccessibleWrap* hyperAcc = TextAcc(); + HyperTextAccessibleWrap* hyperAcc = LocalTextAcc(); if (!hyperAcc) return CO_E_OBJNOTCONNECTED; *aHyperlinkIndex = hyperAcc->LinkIndexAtOffset(aCharIndex); @@ -113,7 +114,7 @@ *aHyperlinks = nullptr; *aNHyperlinks = 0; - HyperTextAccessibleWrap* hyperText = TextAcc(); + HyperTextAccessibleWrap* hyperText = LocalTextAcc(); if (!hyperText) { return CO_E_OBJNOTCONNECTED; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/accessible/windows/ia2/ia2AccessibleHypertext.h firefox-trunk-95.0~a1~hg20211020r596404/accessible/windows/ia2/ia2AccessibleHypertext.h --- firefox-trunk-95.0~a1~hg20211017r596111/accessible/windows/ia2/ia2AccessibleHypertext.h 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/accessible/windows/ia2/ia2AccessibleHypertext.h 2021-10-20 19:28:14.000000000 +0000 @@ -18,6 +18,7 @@ namespace mozilla { namespace a11y { +class HyperTextAccessibleBase; class HyperTextAccessibleWrap; class ia2AccessibleHypertext : public ia2AccessibleText, @@ -59,7 +60,8 @@ using MsaaAccessible::MsaaAccessible; private: - HyperTextAccessibleWrap* TextAcc(); + HyperTextAccessibleBase* TextAcc(); + HyperTextAccessibleWrap* LocalTextAcc(); }; } // namespace a11y diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/accessible/windows/ia2/ia2AccessibleValue.cpp firefox-trunk-95.0~a1~hg20211020r596404/accessible/windows/ia2/ia2AccessibleValue.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/accessible/windows/ia2/ia2AccessibleValue.cpp 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/accessible/windows/ia2/ia2AccessibleValue.cpp 2021-10-20 19:28:14.000000000 +0000 @@ -34,7 +34,7 @@ *ppv = nullptr; if (IID_IAccessibleValue == iid) { - AccessibleWrap* valueAcc = LocalAcc(); + Accessible* valueAcc = Acc(); if (valueAcc && valueAcc->HasNumericValue()) { RefPtr result = this; result.forget(ppv); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/accessible/windows/msaa/MsaaAccessible.cpp firefox-trunk-95.0~a1~hg20211020r596404/accessible/windows/msaa/MsaaAccessible.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/accessible/windows/msaa/MsaaAccessible.cpp 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/accessible/windows/msaa/MsaaAccessible.cpp 2021-10-20 19:28:14.000000000 +0000 @@ -860,12 +860,12 @@ if (SUCCEEDED(hr)) return hr; } - if (!*ppv && localAcc) { + if (!*ppv) { HRESULT hr = ia2AccessibleHyperlink::QueryInterface(iid, ppv); if (SUCCEEDED(hr)) return hr; } - if (!*ppv && localAcc) { + if (!*ppv) { HRESULT hr = ia2AccessibleValue::QueryInterface(iid, ppv); if (SUCCEEDED(hr)) return hr; } @@ -1161,8 +1161,16 @@ if (accessible) { return accessible->get_accState(kVarChildIdSelf, pvarState); } + if (mAcc->IsRemote()) { - return E_NOTIMPL; // XXX Not supported for RemoteAccessible yet. + // XXX Not supported for RemoteAccessible yet. + if (mAcc->IsDoc()) { + // XXX We don't cache whether a document is editable yet. Most documents + // aren't. To facilitate testing of the cache with screen readers, always + // expose READONLY here. + pvarState->lVal |= STATE_SYSTEM_READONLY; + } + return S_OK; } // MSAA only has 31 states and the lowest 31 bits of our state bit mask @@ -1523,13 +1531,7 @@ kVarChildIdSelf); } - LocalAccessible* localAcc = LocalAcc(); - if (!localAcc) { - return E_NOTIMPL; // XXX Not supported for RemoteAccessible yet. - } - - nsIntRect rect = localAcc->Bounds(); - + nsIntRect rect = Acc()->Bounds(); *pxLeft = rect.X(); *pyTop = rect.Y(); *pcxWidth = rect.Width(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/accessible/xpcom/xpcAccessibleHyperText.cpp firefox-trunk-95.0~a1~hg20211020r596404/accessible/xpcom/xpcAccessibleHyperText.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/accessible/xpcom/xpcAccessibleHyperText.cpp 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/accessible/xpcom/xpcAccessibleHyperText.cpp 2021-10-20 19:28:14.000000000 +0000 @@ -674,15 +674,14 @@ if (!mIntl) return NS_ERROR_FAILURE; - if (mIntl->IsLocal()) { - NS_IF_ADDREF(*aLink = ToXPC(IntlLocal()->LinkAt(aIndex))); - } else { #if defined(XP_WIN) + if (mIntl->IsRemote() && + !StaticPrefs::accessibility_cache_enabled_AtStartup()) { return NS_ERROR_NOT_IMPLEMENTED; -#else - NS_IF_ADDREF(*aLink = ToXPC(mIntl->AsRemote()->LinkAt(aIndex))); -#endif } +#endif + + NS_IF_ADDREF(*aLink = ToXPC(Intl()->LinkAt(aIndex))); return NS_OK; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/app/nsBrowserApp.cpp firefox-trunk-95.0~a1~hg20211020r596404/browser/app/nsBrowserApp.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/browser/app/nsBrowserApp.cpp 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/app/nsBrowserApp.cpp 2021-10-20 19:28:14.000000000 +0000 @@ -352,6 +352,9 @@ // even if the last window is closed. ::putenv(const_cast("MOZ_APP_ALLOW_WINDOWLESS=1")); # endif +# if defined(XP_MACOSX) + ::putenv(const_cast("MOZ_APP_NO_DOCK=1")); +# endif } #endif diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/app/profile/firefox.js firefox-trunk-95.0~a1~hg20211020r596404/browser/app/profile/firefox.js --- firefox-trunk-95.0~a1~hg20211017r596111/browser/app/profile/firefox.js 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/app/profile/firefox.js 2021-10-20 19:28:14.000000000 +0000 @@ -1208,6 +1208,16 @@ // content process is killed when all windows are closed, so a change will // take effect when the 1st window is opened. pref("security.sandbox.content.level", 3); + + // Disconnect content processes from the window server. Depends on + // out-of-process WebGL and non-native theming. i.e., both in-process WebGL + // and native theming depend on content processes having a connection to the + // window server. Window server disconnection is automatically disabled (and + // this pref overridden) if OOP WebGL is disabled. OOP WebGL is disabled + // for some tests. + #if defined(NIGHTLY_BUILD) + pref("security.sandbox.content.mac.disconnect-windowserver", true); + #endif #endif #if defined(XP_LINUX) && defined(MOZ_SANDBOX) diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/browser.css firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/browser.css --- firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/browser.css 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/browser.css 2021-10-20 19:28:14.000000000 +0000 @@ -1349,15 +1349,6 @@ animation-duration: 2s; } -/* This effectively simulates a focus outline. */ -#UITourHighlight[active="focus-outline"] { - background-color: transparent; - border: 2px solid var(--focus-outline-color); - border-radius: 4px; - - animation-name: none; -} - /* Combined context-menu items */ #context-navigation > .menuitem-iconic > .menu-iconic-text, #context-navigation > .menuitem-iconic > .menu-accel-container { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/about/csp_iframe.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/about/csp_iframe.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/about/csp_iframe.sjs 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/about/csp_iframe.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -3,7 +3,11 @@ function handleRequest(request, response) { // let's enjoy the amazing CSP setting - response.setHeader("Content-Security-Policy", "frame-ancestors 'self'", false); + response.setHeader( + "Content-Security-Policy", + "frame-ancestors 'self'", + false + ); // let's avoid caching issues response.setHeader("Pragma", "no-cache"); @@ -18,8 +22,8 @@ let cookie = request.hasHeader("Cookie") ? request.getHeader("Cookie") : "" + - "

No same site strict cookie header

" + - ""; + "

No same site strict cookie header

" + + ""; response.write(cookie); if (!request.hasHeader("Cookie")) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/about/print_postdata.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/about/print_postdata.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/about/print_postdata.sjs 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/about/print_postdata.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -1,7 +1,9 @@ const CC = Components.Constructor; -const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", - "setInputStream"); +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); function handleRequest(request, response) { response.setHeader("Content-Type", "text/plain", false); @@ -13,8 +15,9 @@ var avail; var bytes = []; - while ((avail = body.available()) > 0) + while ((avail = body.available()) > 0) { Array.prototype.push.apply(bytes, body.readByteArray(avail)); + } var data = String.fromCharCode.apply(null, bytes); response.bodyOutputStream.write(data, data.length); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/about/xfo_iframe.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/about/xfo_iframe.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/about/xfo_iframe.sjs 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/about/xfo_iframe.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -13,7 +13,8 @@ response.setStatusLine(request.httpVersion, 200); response.setHeader("Content-Type", "text/html", false); - let txt = "XFO page" + + let txt = + "XFO page" + "

" + "XFO blocked page opened in new window!" + "

"; @@ -22,8 +23,8 @@ let cookie = request.hasHeader("Cookie") ? request.getHeader("Cookie") : "" + - "

No same site strict cookie header

" + - ""; + "

No same site strict cookie header

" + + ""; response.write(cookie); if (!request.hasHeader("Cookie")) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/favicons/cookie_favicon.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/favicons/cookie_favicon.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/favicons/cookie_favicon.sjs 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/favicons/cookie_favicon.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -17,6 +17,9 @@ response.setStatusLine(request.httpVersion, 302, "Moved Temporarily"); response.setHeader("Set-Cookie", `faviconCookie=${++state}`); - response.setHeader("Location", "http://example.com/browser/browser/base/content/test/favicons/moz.png"); + response.setHeader( + "Location", + "http://example.com/browser/browser/base/content/test/favicons/moz.png" + ); setState("cache_cookie", `${state}`); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/general/browser_star_hsts.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/general/browser_star_hsts.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/general/browser_star_hsts.sjs 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/general/browser_star_hsts.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -2,8 +2,7 @@ * 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/. */ -function handleRequest(request, response) -{ +function handleRequest(request, response) { let page = "

HSTS page

"; response.setStatusLine(request.httpVersion, "200", "OK"); response.setHeader("Strict-Transport-Security", "max-age=60"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/general/bug792517.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/general/bug792517.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/general/bug792517.sjs 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/general/bug792517.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -3,11 +3,11 @@ */ function handleRequest(aRequest, aResponse) { - aResponse.setStatusLine(aRequest.httpVersion, 200); - if (aRequest.hasHeader('Cookie')) { - aResponse.write("cookie-present"); - } else { - aResponse.setHeader("Set-Cookie", "foopy=1"); - aResponse.write("cookie-not-present"); - } + aResponse.setStatusLine(aRequest.httpVersion, 200); + if (aRequest.hasHeader("Cookie")) { + aResponse.write("cookie-present"); + } else { + aResponse.setHeader("Set-Cookie", "foopy=1"); + aResponse.write("cookie-not-present"); + } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/general/download_with_content_disposition_header.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/general/download_with_content_disposition_header.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/general/download_with_content_disposition_header.sjs 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/general/download_with_content_disposition_header.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -2,20 +2,17 @@ * 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/. */ -function handleRequest(request, response) -{ +function handleRequest(request, response) { let page = "download"; response.setStatusLine(request.httpVersion, "200", "OK"); - let [first, second] = request.queryString.split('='); + let [first, second] = request.queryString.split("="); let headerStr = first; if (second !== "none") { headerStr += "; filename=" + second; } - response.setHeader( - "Content-Disposition", - headerStr); + response.setHeader("Content-Disposition", headerStr); response.setHeader("Content-Type", "text/plain", false); response.setHeader("Content-Length", page.length + "", false); response.write(page); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/general/print_postdata.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/general/print_postdata.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/general/print_postdata.sjs 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/general/print_postdata.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -1,7 +1,9 @@ const CC = Components.Constructor; -const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", - "setInputStream"); +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); function handleRequest(request, response) { response.setHeader("Content-Type", "text/plain", false); @@ -13,8 +15,9 @@ var avail; var bytes = []; - while ((avail = body.available()) > 0) + while ((avail = body.available()) > 0) { Array.prototype.push.apply(bytes, body.readByteArray(avail)); + } var data = String.fromCharCode.apply(null, bytes); response.bodyOutputStream.write(data, data.length); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/general/redirect_download.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/general/redirect_download.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/general/redirect_download.sjs 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/general/redirect_download.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -5,5 +5,7 @@ response.setStatusLine("1.1", 302, "Found"); response.setHeader( "Location", - `download_with_content_disposition_header.sjs?${queryStr}`, false); + `download_with_content_disposition_header.sjs?${queryStr}`, + false + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/general/refresh_header.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/general/refresh_header.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/general/refresh_header.sjs 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/general/refresh_header.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -21,4 +21,4 @@ response.setStatusLine(request.httpVersion, "200", "Found"); response.setHeader("refresh", `${delay}; url=${page}`); response.write("OK"); -} \ No newline at end of file +} diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/general/refresh_meta.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/general/refresh_meta.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/general/refresh_meta.sjs 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/general/refresh_meta.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -33,4 +33,4 @@ response.setStatusLine(request.httpVersion, "200", "Found"); response.setHeader("Cache-Control", "no-cache", false); response.write(html); -} \ No newline at end of file +} diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/permissions/browser_autoplay_blocked_slow.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/permissions/browser_autoplay_blocked_slow.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/permissions/browser_autoplay_blocked_slow.sjs 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/permissions/browser_autoplay_blocked_slow.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -25,8 +25,12 @@ let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); resp.write(AUTOPLAY_HTML); - timer.init(() => { - resp.write(""); - resp.finish(); - }, DELAY_MS, Ci.nsITimer.TYPE_ONE_SHOT); + timer.init( + () => { + resp.write(""); + resp.finish(); + }, + DELAY_MS, + Ci.nsITimer.TYPE_ONE_SHOT + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/protectionsUI/cookieServer.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/protectionsUI/cookieServer.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/protectionsUI/cookieServer.sjs 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/protectionsUI/cookieServer.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -4,13 +4,17 @@ // A 1x1 PNG image. // Source: https://commons.wikimedia.org/wiki/File:1x1.png (Public Domain) -const IMAGE = atob("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAA" + - "ACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII="); +const IMAGE = atob( + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAA" + + "ACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=" +); function handleRequest(request, response) { response.setStatusLine(request.httpVersion, 200); - if (request.queryString && - request.queryString.includes("type=image-no-cookie")) { + if ( + request.queryString && + request.queryString.includes("type=image-no-cookie") + ) { response.setHeader("Content-Type", "image/png", false); response.write(IMAGE); } else { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/referrer/file_referrer_policyserver_attr.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/referrer/file_referrer_policyserver_attr.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/referrer/file_referrer_policyserver_attr.sjs 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/referrer/file_referrer_policyserver_attr.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -3,8 +3,7 @@ * Used in browser_referrer_*.js, bug 1113431. * Arguments: ?scheme=http://&policy=origin&rel=noreferrer */ -function handleRequest(request, response) -{ +function handleRequest(request, response) { Components.utils.importGlobalProperties(["URLSearchParams"]); let query = new URLSearchParams(request.queryString); @@ -14,12 +13,13 @@ let cross = query.get("cross"); let host = cross ? "example.com" : "test1.example.com"; - let linkUrl = scheme + host + - "/browser/browser/base/content/test/referrer/" + - "file_referrer_testserver.sjs"; + let linkUrl = + scheme + + host + + "/browser/browser/base/content/test/referrer/" + + "file_referrer_testserver.sjs"; - let referrerPolicy = - policy ? `referrerpolicy="${policy}"` : ""; + let referrerPolicy = policy ? `referrerpolicy="${policy}"` : ""; let html = ` @@ -28,7 +28,9 @@ Test referrer - + referrer test link `; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/referrer/file_referrer_policyserver.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/referrer/file_referrer_policyserver.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/referrer/file_referrer_policyserver.sjs 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/referrer/file_referrer_policyserver.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -3,8 +3,7 @@ * Used in browser_referrer_*.js, bug 1113431. * Arguments: ?scheme=http://&policy=origin&rel=noreferrer */ -function handleRequest(request, response) -{ +function handleRequest(request, response) { Components.utils.importGlobalProperties(["URLSearchParams"]); let query = new URLSearchParams(request.queryString); @@ -14,11 +13,14 @@ let cross = query.get("cross"); let host = cross ? "example.com" : "test1.example.com"; - let linkUrl = scheme + host + - "/browser/browser/base/content/test/referrer/" + - "file_referrer_testserver.sjs"; - let metaReferrerTag = - policy ? `` : ""; + let linkUrl = + scheme + + host + + "/browser/browser/base/content/test/referrer/" + + "file_referrer_testserver.sjs"; + let metaReferrerTag = policy + ? `` + : ""; let html = ` diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/referrer/file_referrer_testserver.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/referrer/file_referrer_testserver.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/referrer/file_referrer_testserver.sjs 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/referrer/file_referrer_testserver.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -2,8 +2,7 @@ * Renders the HTTP Referer header up to the second path slash. * Used in browser_referrer_*.js, bug 1113431. */ -function handleRequest(request, response) -{ +function handleRequest(request, response) { let referrer = ""; try { referrer = request.getHeader("referer"); @@ -12,7 +11,10 @@ } // Strip it past the first path slash. Makes tests easier to read. - referrer = referrer.split("/").slice(0, 4).join("/"); + referrer = referrer + .split("/") + .slice(0, 4) + .join("/"); let html = ` diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/siteIdentity/file_bug906190.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/siteIdentity/file_bug906190.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/siteIdentity/file_bug906190.sjs 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/siteIdentity/file_bug906190.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -1,9 +1,10 @@ function handleRequest(request, response) { var page = "bug 906190"; - var path = "https://test1.example.com/browser/browser/base/content/test/siteIdentity/"; + var path = + "https://test1.example.com/browser/browser/base/content/test/siteIdentity/"; var url; - if (request.queryString.includes('bad-redirection=1')) { + if (request.queryString.includes("bad-redirection=1")) { url = path + "this_page_does_not_exist.html"; } else { url = path + "file_bug906190_redirected.html"; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/siteIdentity/test_mcb_redirect.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/siteIdentity/test_mcb_redirect.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/base/content/test/siteIdentity/test_mcb_redirect.sjs 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/base/content/test/siteIdentity/test_mcb_redirect.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -1,14 +1,17 @@ function handleRequest(request, response) { - var page = "bug 418354 and bug 1082837"; + var page = + "bug 418354 and bug 1082837"; - if (request.queryString === "script") { - var redirect = "http://example.com/browser/browser/base/content/test/siteIdentity/test_mcb_redirect.js"; + if (request.queryString === "script") { + var redirect = + "http://example.com/browser/browser/base/content/test/siteIdentity/test_mcb_redirect.js"; response.setHeader("Cache-Control", "no-cache", false); } else if (request.queryString === "image_http") { var redirect = "http://example.com/tests/image/test/mochitest/blue.png"; response.setHeader("Cache-Control", "max-age=3600", false); } else if (request.queryString === "image_redirect_http_sjs") { - var redirect = "http://example.com/browser/browser/base/content/test/siteIdentity/test_mcb_redirect.sjs?image_redirect_https"; + var redirect = + "http://example.com/browser/browser/base/content/test/siteIdentity/test_mcb_redirect.sjs?image_redirect_https"; response.setHeader("Cache-Control", "max-age=3600", false); } else if (request.queryString === "image_redirect_https") { var redirect = "https://example.com/tests/image/test/mochitest/blue.png"; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/about/AboutRedirector.cpp firefox-trunk-95.0~a1~hg20211020r596404/browser/components/about/AboutRedirector.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/about/AboutRedirector.cpp 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/about/AboutRedirector.cpp 2021-10-20 19:28:14.000000000 +0000 @@ -60,6 +60,7 @@ nsIAboutModule::ALLOW_SCRIPT}, {"framecrashed", "chrome://browser/content/aboutFrameCrashed.html", nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | + nsIAboutModule::URI_CAN_LOAD_IN_CHILD | nsIAboutModule::HIDE_FROM_ABOUTABOUT}, {"logins", "chrome://browser/content/aboutlogins/aboutLogins.html", nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::URI_MUST_LOAD_IN_CHILD | diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/BrowserGlue.jsm firefox-trunk-95.0~a1~hg20211020r596404/browser/components/BrowserGlue.jsm --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/BrowserGlue.jsm 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/BrowserGlue.jsm 2021-10-20 19:28:14.000000000 +0000 @@ -89,7 +89,6 @@ TelemetryUtils: "resource://gre/modules/TelemetryUtils.jsm", TRRRacer: "resource:///modules/TRRPerformance.jsm", UIState: "resource://services-sync/UIState.jsm", - UITour: "resource:///modules/UITour.jsm", UrlbarQuickSuggest: "resource:///modules/UrlbarQuickSuggest.jsm", UrlbarPrefs: "resource:///modules/UrlbarPrefs.jsm", WebChannel: "resource://gre/modules/WebChannel.jsm", @@ -2189,6 +2188,39 @@ Services.wm.addListener(windowListener); }, + _monitorGPCPref() { + const FEATURE_PREF_ENABLED = "privacy.globalprivacycontrol.enabled"; + const FUNCTIONALITY_PREF_ENABLED = + "privacy.globalprivacycontrol.functionality.enabled"; + const PREF_WAS_ENABLED = "privacy.globalprivacycontrol.was_ever_enabled"; + const _checkGPCPref = async () => { + const feature_enabled = Services.prefs.getBoolPref( + FEATURE_PREF_ENABLED, + false + ); + const functionality_enabled = Services.prefs.getBoolPref( + FUNCTIONALITY_PREF_ENABLED, + false + ); + const was_enabled = Services.prefs.getBoolPref(PREF_WAS_ENABLED, false); + let value = 0; + if (feature_enabled && functionality_enabled) { + value = 1; + Services.prefs.setBoolPref(PREF_WAS_ENABLED, true); + } else if (was_enabled) { + value = 2; + } + Services.telemetry.scalarSet( + "security.global_privacy_control_enabled", + value + ); + }; + + Services.prefs.addObserver(FEATURE_PREF_ENABLED, _checkGPCPref); + Services.prefs.addObserver(FUNCTIONALITY_PREF_ENABLED, _checkGPCPref); + _checkGPCPref(); + }, + // All initial windows have opened. _onWindowsRestored: function BG__onWindowsRestored() { if (this._windowsWereRestored) { @@ -2266,6 +2298,7 @@ if (AppConstants.NIGHTLY_BUILD) { this._monitorTranslationsPref(); } + this._monitorGPCPref(); }, /** @@ -4071,11 +4104,10 @@ { "l10n-id": "restore-session-startup-suggestion-button", callback: () => { - UITour.getTarget(win, "history").then(historyMenu => { - UITour.showHighlight(win, historyMenu, "focus-outline", { - autohide: true, - }); - }); + win.PanelUI.selectAndMarkItem([ + "appMenu-history-button", + "appMenu-restoreSession", + ]); }, }, ]; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/contextualidentity/test/browser/saveLink.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/components/contextualidentity/test/browser/saveLink.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/contextualidentity/test/browser/saveLink.sjs 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/contextualidentity/test/browser/saveLink.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -1,6 +1,7 @@ const HTTP_ORIGIN = "http://example.com"; const HTTPS_ORIGIN = "https://example.com"; -const URI_PATH = "/browser/browser/components/contextualidentity/test/browser/saveLink.sjs"; +const URI_PATH = + "/browser/browser/components/contextualidentity/test/browser/saveLink.sjs"; Components.utils.importGlobalProperties(["URLSearchParams"]); @@ -11,7 +12,12 @@ if (params.has("UCI")) { aResponse.setStatusLine(aRequest.httpVersion, 200); aResponse.setHeader("Set-Cookie", "UCI=" + params.get("UCI")); - aResponse.write("this is a link"); + aResponse.write( + "this is a link" + ); return; } @@ -19,7 +25,11 @@ // if we are able to follow it. if (params.has("redirect")) { aResponse.setStatusLine(aRequest.httpVersion, 302, "Found"); - aResponse.setHeader("Location", HTTP_ORIGIN + URI_PATH + "?download=1", false); + aResponse.setHeader( + "Location", + HTTP_ORIGIN + URI_PATH + "?download=1", + false + ); aResponse.write("Redirect!"); return; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/customizableui/content/panelUI.js firefox-trunk-95.0~a1~hg20211020r596404/browser/components/customizableui/content/panelUI.js --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/customizableui/content/panelUI.js 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/customizableui/content/panelUI.js 2021-10-20 19:28:14.000000000 +0000 @@ -717,6 +717,87 @@ } }, + /** + * Selects and marks an item by id from the main view. The ids are an array, + * the first in the main view and the later ids in subsequent subviews that + * become marked when the user opens the subview. The subview marking is + * cancelled if a different subview is opened. + */ + async selectAndMarkItem(itemIds) { + // This shouldn't really occur, but return early just in case. + if (document.documentElement.hasAttribute("customizing")) { + return; + } + + // This function was triggered from a button while the menu was + // already open, so the panel should be in the process of hiding. + // Wait for the panel to hide first, then reopen it. + if (this.panel.state == "hiding") { + await new Promise(resolve => { + this.panel.addEventListener("popuphidden", resolve, { once: true }); + }); + } + + if (this.panel.state != "open") { + await new Promise(resolve => { + this.panel.addEventListener("ViewShown", resolve, { once: true }); + this.show(); + }); + } + + let currentView; + + let viewShownCB = event => { + viewHidingCB(); + + if (itemIds.length) { + let subItem = window.document.getElementById(itemIds[0]); + if (event.target.id == subItem?.closest("panelview")?.id) { + Services.tm.dispatchToMainThread(() => { + markItem(event.target); + }); + } else { + itemIds = []; + } + } + }; + + let viewHidingCB = () => { + if (currentView) { + currentView.ignoreMouseMove = false; + } + currentView = null; + }; + + let popupHiddenCB = () => { + viewHidingCB(); + this.panel.removeEventListener("ViewShown", viewShownCB); + }; + + let markItem = viewNode => { + let id = itemIds.shift(); + let item = window.document.getElementById(id); + item.setAttribute("tabindex", "-1"); + + currentView = PanelView.forNode(viewNode); + currentView.selectedElement = item; + currentView.focusSelectedElement(true); + + // Prevent the mouse from changing the highlight temporarily. + // This flag gets removed when the view is hidden or a key + // is pressed. + currentView.ignoreMouseMove = true; + + if (itemIds.length) { + this.panel.addEventListener("ViewShown", viewShownCB, { once: true }); + } + this.panel.addEventListener("ViewHiding", viewHidingCB, { once: true }); + }; + + this.panel.addEventListener("popuphidden", popupHiddenCB, { once: true }); + markItem(this.mainView); + }, + updateNotifications(notificationsChanged) { let notifications = this._notifications; if (!notifications || !notifications.length) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/customizableui/PanelMultiView.jsm firefox-trunk-95.0~a1~hg20211020r596404/browser/components/customizableui/PanelMultiView.jsm --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/customizableui/PanelMultiView.jsm 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/customizableui/PanelMultiView.jsm 2021-10-20 19:28:14.000000000 +0000 @@ -1216,7 +1216,11 @@ currentView.keyNavigation(aEvent); break; case "mousemove": - this.openViews.forEach(panelView => panelView.clearNavigation()); + this.openViews.forEach(panelView => { + if (!panelView.ignoreMouseMove) { + panelView.clearNavigation(); + } + }); break; case "popupshowing": { this._viewContainer.setAttribute("panelopen", "true"); @@ -1807,6 +1811,8 @@ return popup && popup.state == "open"; }; + this.ignoreMouseMove = false; + let keyCode = event.code; switch (keyCode) { case "ArrowDown": diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/downloads/content/downloadsContextMenu.inc.xhtml firefox-trunk-95.0~a1~hg20211020r596404/browser/components/downloads/content/downloadsContextMenu.inc.xhtml --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/downloads/content/downloadsContextMenu.inc.xhtml 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/downloads/content/downloadsContextMenu.inc.xhtml 2021-10-20 19:28:14.000000000 +0000 @@ -22,12 +22,7 @@ data-l10n-id="downloads-cmd-always-use-system-default"/> + data-l10n-id="downloads-cmd-show-menuitem-2"/> diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/downloads/content/downloadsPanel.inc.xhtml firefox-trunk-95.0~a1~hg20211020r596404/browser/components/downloads/content/downloadsPanel.inc.xhtml --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/downloads/content/downloadsPanel.inc.xhtml 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/downloads/content/downloadsPanel.inc.xhtml 2021-10-20 19:28:14.000000000 +0000 @@ -85,12 +85,7 @@ data-l10n-id="downloads-cmd-always-use-system-default"/> + data-l10n-id="downloads-cmd-show-menuitem-2"/> diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/downloads/DownloadsViewUI.jsm firefox-trunk-95.0~a1~hg20211020r596404/browser/components/downloads/DownloadsViewUI.jsm --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/downloads/DownloadsViewUI.jsm 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/downloads/DownloadsViewUI.jsm 2021-10-20 19:28:14.000000000 +0000 @@ -63,9 +63,9 @@ }, show: { commandName: "downloadsCmd_show", - l10nId: "downloads-cmd-show-button", - descriptionL10nId: "downloads-cmd-show-description", - panelL10nId: "downloads-cmd-show-panel", + l10nId: "downloads-cmd-show-button-2", + descriptionL10nId: "downloads-cmd-show-description-2", + panelL10nId: "downloads-cmd-show-panel-2", iconClass: "downloadIconShow", }, subviewOpenOrRemoveFile: { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/enterprisepolicies/tests/browser/404.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/components/enterprisepolicies/tests/browser/404.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/enterprisepolicies/tests/browser/404.sjs 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/enterprisepolicies/tests/browser/404.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -1,3 +1,3 @@ -function handleRequest(request, response) { - response.setStatusLine(request.httpVersion, 404, "Not Found"); -} +function handleRequest(request, response) { + response.setStatusLine(request.httpVersion, 404, "Not Found"); +} diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/extensions/test/browser/authenticate.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/components/extensions/test/browser/authenticate.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/extensions/test/browser/authenticate.sjs 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/extensions/test/browser/authenticate.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -1,5 +1,4 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { var match; var requestAuth = true; @@ -9,23 +8,27 @@ // at the beginning of the query string. var query = "?" + request.queryString; - var expected_user = "test", expected_pass = "testpass", realm = "mochitest"; + var expected_user = "test", + expected_pass = "testpass", + realm = "mochitest"; // user=xxx match = /[^_]user=([^&]*)/.exec(query); - if (match) + if (match) { expected_user = match[1]; + } // pass=xxx match = /[^_]pass=([^&]*)/.exec(query); - if (match) + if (match) { expected_pass = match[1]; + } // realm=xxx match = /[^_]realm=([^&]*)/.exec(query); - if (match) + if (match) { realm = match[1]; - + } // Look for an authentication header, if any, in the request. // @@ -34,51 +37,59 @@ // This test only supports Basic auth. The value sent by the client is // "username:password", obscured with base64 encoding. - var actual_user = "", actual_pass = "", authHeader, authPresent = false; + var actual_user = "", + actual_pass = "", + authHeader, + authPresent = false; if (request.hasHeader("Authorization")) { authPresent = true; authHeader = request.getHeader("Authorization"); match = /Basic (.+)/.exec(authHeader); - if (match.length != 2) - throw new Error("Couldn't parse auth header: " + authHeader); + if (match.length != 2) { + throw new Error("Couldn't parse auth header: " + authHeader); + } var userpass = base64ToString(match[1]); // no atob() :-( match = /(.*):(.*)/.exec(userpass); - if (match.length != 3) - throw new Error("Couldn't decode auth header: " + userpass); + if (match.length != 3) { + throw new Error("Couldn't decode auth header: " + userpass); + } actual_user = match[1]; actual_pass = match[2]; } // Don't request authentication if the credentials we got were what we // expected. - if (expected_user == actual_user && - expected_pass == actual_pass) { + if (expected_user == actual_user && expected_pass == actual_pass) { requestAuth = false; } if (requestAuth) { response.setStatusLine("1.0", 401, "Authentication required"); - response.setHeader("WWW-Authenticate", "basic realm=\"" + realm + "\"", true); + response.setHeader("WWW-Authenticate", 'basic realm="' + realm + '"', true); } else { response.setStatusLine("1.0", 200, "OK"); } response.setHeader("Content-Type", "application/xhtml+xml", false); response.write(""); - response.write("

Login: " + (requestAuth ? "FAIL" : "PASS") + "

\n"); + response.write( + "

Login: " + + (requestAuth ? "FAIL" : "PASS") + + "

\n" + ); response.write("

Auth: " + authHeader + "

\n"); response.write("

User: " + actual_user + "

\n"); response.write("

Pass: " + actual_pass + "

\n"); response.write(""); } - // base64 decoder // // Yoinked from extensions/xml-rpc/src/nsXmlRpcClient.js because btoa() // doesn't seem to exist. :-( /* Convert Base64 data to a string */ +/* eslint-disable prettier/prettier */ const toBinaryTable = [ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, @@ -89,38 +100,42 @@ -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 ]; -const base64Pad = '='; +/* eslint-enable prettier/prettier */ +const base64Pad = "="; function base64ToString(data) { + var result = ""; + var leftbits = 0; // number of bits decoded, but yet to be appended + var leftdata = 0; // bits decoded, but yet to be appended + + // Convert one by one. + for (var i = 0; i < data.length; i++) { + var c = toBinaryTable[data.charCodeAt(i) & 0x7f]; + var padding = data[i] == base64Pad; + // Skip illegal characters and whitespace + if (c == -1) { + continue; + } - var result = ''; - var leftbits = 0; // number of bits decoded, but yet to be appended - var leftdata = 0; // bits decoded, but yet to be appended - - // Convert one by one. - for (var i = 0; i < data.length; i++) { - var c = toBinaryTable[data.charCodeAt(i) & 0x7f]; - var padding = (data[i] == base64Pad); - // Skip illegal characters and whitespace - if (c == -1) continue; - - // Collect data into leftdata, update bitcount - leftdata = (leftdata << 6) | c; - leftbits += 6; - - // If we have 8 or more bits, append 8 bits to the result - if (leftbits >= 8) { - leftbits -= 8; - // Append if not padding. - if (!padding) - result += String.fromCharCode((leftdata >> leftbits) & 0xff); - leftdata &= (1 << leftbits) - 1; - } + // Collect data into leftdata, update bitcount + leftdata = (leftdata << 6) | c; + leftbits += 6; + + // If we have 8 or more bits, append 8 bits to the result + if (leftbits >= 8) { + leftbits -= 8; + // Append if not padding. + if (!padding) { + result += String.fromCharCode((leftdata >> leftbits) & 0xff); + } + leftdata &= (1 << leftbits) - 1; } + } - // If there are any bits left, the base64 string was corrupted - if (leftbits) - throw Components.Exception('Corrupted base64 string'); + // If there are any bits left, the base64 string was corrupted + if (leftbits) { + throw Components.Exception("Corrupted base64 string"); + } - return result; + return result; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/extensions/test/browser/browser_ext_contentscript_sender_url.js firefox-trunk-95.0~a1~hg20211020r596404/browser/components/extensions/test/browser/browser_ext_contentscript_sender_url.js --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/extensions/test/browser/browser_ext_contentscript_sender_url.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/extensions/test/browser/browser_ext_contentscript_sender_url.js 2021-10-20 19:28:14.000000000 +0000 @@ -0,0 +1,67 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +"use strict"; + +add_task(async function test_sender_url() { + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + content_scripts: [ + { + matches: ["http://mochi.test/*"], + run_at: "document_start", + js: ["script.js"], + }, + ], + }, + + background() { + browser.runtime.onMessage.addListener((msg, sender) => { + browser.test.log("Message received."); + browser.test.sendMessage("sender.url", sender.url); + }); + }, + + files: { + "script.js"() { + browser.test.log("Content script loaded."); + browser.runtime.sendMessage(0); + }, + }, + }); + + const image = + "http://mochi.test:8888/browser/browser/components/extensions/test/browser/ctxmenu-image.png"; + + // Bug is only visible and test only works without Fission, + // or with Fission but without BFcache in parent. + await SpecialPowers.pushPrefEnv({ + set: [["fission.bfcacheInParent", false]], + }); + + function awaitNewTab() { + return BrowserTestUtils.waitForLocationChange(gBrowser, "about:newtab"); + } + + await extension.startup(); + + await BrowserTestUtils.withNewTab({ gBrowser }, async browser => { + let newTab = awaitNewTab(); + BrowserTestUtils.loadURI(browser, "about:newtab"); + await newTab; + + BrowserTestUtils.loadURI(browser, image); + let url = await extension.awaitMessage("sender.url"); + is(url, image, `Correct sender.url: ${url}`); + + let wentBack = awaitNewTab(); + await browser.goBack(); + await wentBack; + + await browser.goForward(); + url = await extension.awaitMessage("sender.url"); + is(url, image, `Correct sender.url: ${url}`); + }); + + await extension.unload(); + await SpecialPowers.popPrefEnv(); +}); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/extensions/test/browser/browser_ext_incognito_popup.js firefox-trunk-95.0~a1~hg20211020r596404/browser/components/extensions/test/browser/browser_ext_incognito_popup.js --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/extensions/test/browser/browser_ext_incognito_popup.js 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/extensions/test/browser/browser_ext_incognito_popup.js 2021-10-20 19:28:14.000000000 +0000 @@ -23,7 +23,7 @@ } }); - let awaitPopup = windowId => { + const awaitPopup = windowId => { return new Promise(resolve => { resolveMessage = resolve; }).then(msg => { @@ -36,8 +36,8 @@ }); }; - let testWindow = async window => { - let [tab] = await browser.tabs.query({ + const testWindow = async window => { + const [tab] = await browser.tabs.query({ active: true, windowId: window.id, }); @@ -62,44 +62,53 @@ ); }; - const URL = "https://example.com/incognito"; - let windowReady = new Promise(resolve => { - browser.tabs.onUpdated.addListener(function listener( - tabId, - changed, - tab - ) { - if (changed.status == "complete" && tab.url == URL) { - browser.tabs.onUpdated.removeListener(listener); - resolve(); - } - }); - }); - - try { - { - let window = await browser.windows.getCurrent(); - - await testWindow(window); - } + const testNonPrivateWindow = async () => { + const window = await browser.windows.getCurrent(); + await testWindow(window); + }; - { - let window = await browser.windows.create({ - incognito: true, - url: URL, + const testPrivateWindow = async () => { + const URL = "https://example.com/incognito"; + const windowReady = new Promise(resolve => { + browser.tabs.onUpdated.addListener(function listener( + tabId, + changed, + tab + ) { + if (changed.status == "complete" && tab.url == URL) { + browser.tabs.onUpdated.removeListener(listener); + resolve(); + } }); - await windowReady; + }); + + const window = await browser.windows.create({ + incognito: true, + url: URL, + }); + await windowReady; - await testWindow(window); + await testWindow(window); + }; - await browser.windows.remove(window.id); + browser.test.onMessage.addListener(async msg => { + switch (msg) { + case "test-nonprivate-window": + await testNonPrivateWindow(); + break; + case "test-private-window": + await testPrivateWindow(); + break; + default: + browser.test.fail( + `Unexpected test message: ${JSON.stringify(msg)}` + ); } - browser.test.notifyPass("incognito"); - } catch (error) { - browser.test.fail(`Error: ${error} :: ${error.stack}`); - browser.test.notifyFail("incognito"); - } + browser.test.sendMessage(`${msg}:done`); + }); + + browser.test.sendMessage("bgscript:ready"); }, files: { @@ -118,22 +127,43 @@ }, }); - extension.onMessage("click-browserAction", () => { - clickBrowserAction( - extension, - Services.wm.getMostRecentWindow("navigator:browser") - ); - }); + await extension.startup(); + await extension.awaitMessage("bgscript:ready"); - extension.onMessage("click-pageAction", () => { - clickPageAction( - extension, - Services.wm.getMostRecentWindow("navigator:browser") - ); - }); + info("Run test on non private window"); + extension.sendMessage("test-nonprivate-window"); + await extension.awaitMessage("click-pageAction"); + const win = Services.wm.getMostRecentWindow("navigator:browser"); + ok(!PrivateBrowsingUtils.isWindowPrivate(win), "Got a nonprivate window"); + await clickPageAction(extension, win); + + await extension.awaitMessage("click-browserAction"); + await clickBrowserAction(extension, win); + + await extension.awaitMessage("test-nonprivate-window:done"); + await closeBrowserAction(extension, win); + await closePageAction(extension, win); + + info("Run test on private window"); + extension.sendMessage("test-private-window"); + await extension.awaitMessage("click-pageAction"); + const privateWin = Services.wm.getMostRecentWindow("navigator:browser"); + ok(PrivateBrowsingUtils.isWindowPrivate(privateWin), "Got a private window"); + await clickPageAction(extension, privateWin); + + await extension.awaitMessage("click-browserAction"); + await clickBrowserAction(extension, privateWin); + + await extension.awaitMessage("test-private-window:done"); + // Wait for the private window chrome document to be flushed before + // closing the browserACtion, pageAction and the entire private window, + // to prevent intermittent failures. + await privateWin.promiseDocumentFlushed(() => {}); + + await closeBrowserAction(extension, privateWin); + await closePageAction(extension, privateWin); + await BrowserTestUtils.closeWindow(privateWin); - await extension.startup(); - await extension.awaitFinish("incognito"); await extension.unload(); }); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/extensions/test/browser/browser_ext_persistent_storage_permission_indication.js firefox-trunk-95.0~a1~hg20211020r596404/browser/components/extensions/test/browser/browser_ext_persistent_storage_permission_indication.js --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/extensions/test/browser/browser_ext_persistent_storage_permission_indication.js 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/extensions/test/browser/browser_ext_persistent_storage_permission_indication.js 2021-10-20 19:28:14.000000000 +0000 @@ -14,6 +14,7 @@ event => event.target == gPermissionPanel._permissionPopup ); gPermissionPanel._identityPermissionBox.click(); + info("Wait permission popup to be shown"); return promise; } @@ -23,6 +24,7 @@ "popuphidden" ); gPermissionPanel._permissionPopup.hidePopup(); + info("Wait permission popup to be hidden"); return promise; } @@ -67,21 +69,25 @@ add_task(async function testPersistentStoragePermissionHidden() { let extension = ExtensionTestUtils.loadExtension({ background() { - browser.test.sendMessage("url", browser.runtime.getURL("icon.png")); + browser.test.sendMessage("url", browser.runtime.getURL("testpage.html")); }, manifest: { name: "Test Extension", permissions: ["unlimitedStorage"], }, files: { - "icon.png": "", + "testpage.html": "

Extension Test Page

", }, }); await extension.startup(); let url = await extension.awaitMessage("url"); - await BrowserTestUtils.withNewTab({ gBrowser, url }, async function() { + await BrowserTestUtils.withNewTab("about:blank", async browser => { + // Wait the tab to be fully loade, then run the test on the permission prompt. + let loaded = BrowserTestUtils.browserLoaded(browser, false, url); + BrowserTestUtils.loadURI(browser, url); + await loaded; await testPermissionPopup({ expectPermissionHidden: true }); }); @@ -91,13 +97,13 @@ add_task(async function testPersistentStoragePermissionVisible() { let extension = ExtensionTestUtils.loadExtension({ background() { - browser.test.sendMessage("url", browser.runtime.getURL("icon.png")); + browser.test.sendMessage("url", browser.runtime.getURL("testpage.html")); }, manifest: { name: "Test Extension", }, files: { - "icon.png": "", + "testpage.html": "

Extension Test Page

", }, }); @@ -113,7 +119,11 @@ Services.perms.ALLOW_ACTION ); - await BrowserTestUtils.withNewTab({ gBrowser, url }, async function() { + await BrowserTestUtils.withNewTab("about:blank", async browser => { + // Wait the tab to be fully loade, then run the test on the permission prompt. + let loaded = BrowserTestUtils.browserLoaded(browser, false, url); + BrowserTestUtils.loadURI(browser, url); + await loaded; await testPermissionPopup({ expectPermissionHidden: false }); }); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/extensions/test/browser/browser_ext_sidebarAction_click.js firefox-trunk-95.0~a1~hg20211020r596404/browser/components/extensions/test/browser/browser_ext_sidebarAction_click.js --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/extensions/test/browser/browser_ext_sidebarAction_click.js 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/extensions/test/browser/browser_ext_sidebarAction_click.js 2021-10-20 19:28:14.000000000 +0000 @@ -55,6 +55,14 @@ // This test fails if docShell.isAppTab has not been set to true. let content = SidebarUI.browser.contentWindow; + + // Wait for the layout to be flushed, otherwise this test may + // fail intermittently if synthesizeMouseAtCenter is being called + // while the sidebar is still opening and the browser window layout + // being recomputed. + await content.promiseDocumentFlushed(() => {}); + + info("Clicking link in extension sidebar"); await BrowserTestUtils.synthesizeMouseAtCenter( "#testlink", {}, diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/extensions/test/browser/browser.ini firefox-trunk-95.0~a1~hg20211020r596404/browser/components/extensions/test/browser/browser.ini --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/extensions/test/browser/browser.ini 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/extensions/test/browser/browser.ini 2021-10-20 19:28:14.000000000 +0000 @@ -98,6 +98,8 @@ [browser_ext_contentscript_in_parent.js] [browser_ext_contentscript_incognito.js] [browser_ext_contentscript_nontab_connect.js] +[browser_ext_contentscript_sender_url.js] +skip-if = debug # The nature of the reduced STR test triggers an unrelated debug assertion in DOM IPC code, see bug 1736590. [browser_ext_contextMenus.js] support-files = !/browser/components/places/tests/browser/head.js [browser_ext_contextMenus_checkboxes.js] diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/extensions/test/browser/file_bypass_cache.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/components/extensions/test/browser/file_bypass_cache.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/extensions/test/browser/file_bypass_cache.sjs 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/extensions/test/browser/file_bypass_cache.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -6,6 +6,8 @@ response.setHeader("Content-Type", "text/plain; charset=UTF-8", false); if (request.hasHeader("pragma") && request.hasHeader("cache-control")) { - response.write(`${request.getHeader("pragma")}:${request.getHeader("cache-control")}`); + response.write( + `${request.getHeader("pragma")}:${request.getHeader("cache-control")}` + ); } -} \ No newline at end of file +} diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/extensions/test/browser/file_slowed_document.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/components/extensions/test/browser/file_slowed_document.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/extensions/test/browser/file_slowed_document.sjs 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/extensions/test/browser/file_slowed_document.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -11,7 +11,11 @@ const DELAY = 2 * 1000; // Delay two seconds before completing the request. -let nsTimer = Components.Constructor("@mozilla.org/timer;1", "nsITimer", "initWithCallback"); +let nsTimer = Components.Constructor( + "@mozilla.org/timer;1", + "nsITimer", + "initWithCallback" +); let timer; @@ -31,11 +35,15 @@ // Note: We need to store a reference to the timer to prevent it from being // canceled when it's GCed. - timer = new nsTimer(() => { - if (request.queryString.includes("with-iframe")) { - response.write(``); - } - response.write(``); - response.finish(); - }, DELAY, Ci.nsITimer.TYPE_ONE_SHOT); + timer = new nsTimer( + () => { + if (request.queryString.includes("with-iframe")) { + response.write(``); + } + response.write(``); + response.finish(); + }, + DELAY, + Ci.nsITimer.TYPE_ONE_SHOT + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/extensions/test/browser/head.js firefox-trunk-95.0~a1~hg20211020r596404/browser/components/extensions/test/browser/head.js --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/extensions/test/browser/head.js 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/extensions/test/browser/head.js 2021-10-20 19:28:14.000000000 +0000 @@ -419,6 +419,14 @@ contentAreaContextMenu, "popupshown" ); + + // Wait for the layout to be flushed, otherwise this test may + // fail intermittently if synthesizeMouseAtCenter is being called + // while the sidebar is still opening and the browser window layout + // being recomputed. + await SidebarUI.browser.contentWindow.promiseDocumentFlushed(() => {}); + + info("Opening context menu in sidebarAction panel"); await BrowserTestUtils.synthesizeMouseAtCenter( selector, { type: "mousedown", button: 2 }, diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/extensions/test/xpcshell/data/test/manifest.json firefox-trunk-95.0~a1~hg20211020r596404/browser/components/extensions/test/xpcshell/data/test/manifest.json --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/extensions/test/xpcshell/data/test/manifest.json 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/extensions/test/xpcshell/data/test/manifest.json 2021-10-20 19:28:14.000000000 +0000 @@ -63,6 +63,16 @@ "name": "prefval", "condition": "pref", "pref": "code" + }, + { + "name": "experimenter-1", + "condition": "pref", + "pref": "nimbus-key-1" + }, + { + "name": "experimenter-2", + "condition": "pref", + "pref": "nimbus-key-2" } ] } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/extensions/test/xpcshell/test_ext_settings_overrides_search_mozParam.js firefox-trunk-95.0~a1~hg20211020r596404/browser/components/extensions/test/xpcshell/test_ext_settings_overrides_search_mozParam.js --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/extensions/test/xpcshell/test_ext_settings_overrides_search_mozParam.js 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/extensions/test/xpcshell/test_ext_settings_overrides_search_mozParam.js 2021-10-20 19:28:14.000000000 +0000 @@ -6,10 +6,13 @@ const { AddonTestUtils } = ChromeUtils.import( "resource://testing-common/AddonTestUtils.jsm" ); - const { SearchTestUtils } = ChromeUtils.import( "resource://testing-common/SearchTestUtils.jsm" ); +const { NimbusFeatures } = ChromeUtils.import( + "resource://nimbus/ExperimentAPI.jsm" +); +const { sinon } = ChromeUtils.import("resource://testing-common/Sinon.jsm"); AddonTestUtils.init(this); AddonTestUtils.overrideCertDB(); @@ -50,6 +53,8 @@ ]; add_task(async function setup() { + let readyStub = sinon.stub(NimbusFeatures.search, "ready").resolves(); + let updateStub = sinon.stub(NimbusFeatures.search, "onUpdate"); await promiseStartupManager(); await SearchTestUtils.useTestEngines("data", null, [ { @@ -67,6 +72,8 @@ await Services.search.init(); registerCleanupFunction(async () => { await promiseShutdownManager(); + readyStub.restore(); + updateStub.restore(); }); }); @@ -105,6 +112,81 @@ "search url is expected" ); } + + defaultBranch.setCharPref("param.code", ""); +}); + +add_task(async function test_nimbus_params() { + let sandbox = sinon.createSandbox(); + let stub = sandbox.stub(NimbusFeatures.search, "getVariable"); + // These values should match the nimbusParams below and the data/test/manifest.json + // search engine configuration + stub.withArgs("extraParams").returns([ + { + key: "nimbus-key-1", + value: "nimbus-value-1", + }, + { + key: "nimbus-key-2", + value: "nimbus-value-2", + }, + ]); + + Assert.ok( + NimbusFeatures.search.onUpdate.called, + "Called to initialize the cache" + ); + + // Populate the cache with the `getVariable` mock values + NimbusFeatures.search.onUpdate.firstCall.args[0](); + + let engine = Services.search.getEngineByName("MozParamsTest"); + + // Note: these lists should be kept in sync with the lists in + // browser/components/extensions/test/xpcshell/data/test/manifest.json + // These params are conditional based on how search is initiated. + const nimbusParams = [ + { name: "experimenter-1", condition: "pref", pref: "nimbus-key-1" }, + { name: "experimenter-2", condition: "pref", pref: "nimbus-key-2" }, + ]; + const experimentCache = { + "nimbus-key-1": "nimbus-value-1", + "nimbus-key-2": "nimbus-value-2", + }; + + let extraParams = []; + for (let p of params) { + if (p.value == "{searchTerms}") { + extraParams.push(`${p.name}=test`); + } else if (p.value == "{language}") { + extraParams.push(`${p.name}=${Services.locale.requestedLocale || "*"}`); + } else if (p.value == "{moz:locale}") { + extraParams.push(`${p.name}=${Services.locale.requestedLocale}`); + } else if (p.condition !== "pref") { + // Ignoring pref parameters + extraParams.push(`${p.name}=${p.value}`); + } + } + for (let p of nimbusParams) { + if (p.condition == "pref") { + extraParams.push(`${p.name}=${experimentCache[p.pref]}`); + } + } + let paramStr = extraParams.join("&"); + for (let p of mozParams) { + let expectedURL = engine.getSubmission( + "test", + null, + p.condition == "purpose" ? p.purpose : null + ).uri.spec; + equal( + expectedURL, + `https://example.com/?q=test&${p.name}=${p.value}&${paramStr}`, + "search url is expected" + ); + } + + sandbox.restore(); }); add_task(async function test_extension_setting_moz_params_fail() { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/originattributes/test/browser/file_saveAs.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/components/originattributes/test/browser/file_saveAs.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/originattributes/test/browser/file_saveAs.sjs 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/originattributes/test/browser/file_saveAs.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -6,7 +6,7 @@ const VIDEO_PATH = `${URI_PATH}file_thirdPartyChild.video.ogv`; // Reusing existing png file for testing. const IMAGE_PATH = `${URI_PATH}file_favicon.png`; -const FRAME_PATH = `${SECOND_ORIGIN}${URI_PATH}file_saveAs.sjs?image=1` +const FRAME_PATH = `${SECOND_ORIGIN}${URI_PATH}file_saveAs.sjs?image=1`; Components.utils.importGlobalProperties(["URLSearchParams"]); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/places/content/editBookmark.js firefox-trunk-95.0~a1~hg20211020r596404/browser/components/places/content/editBookmark.js --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/places/content/editBookmark.js 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/places/content/editBookmark.js 2021-10-20 19:28:14.000000000 +0000 @@ -343,7 +343,12 @@ PlacesUtils.bookmarks.addObserver(this); this.handlePlacesEvents = this.handlePlacesEvents.bind(this); PlacesUtils.observers.addListener( - ["bookmark-moved", "bookmark-title-changed", "bookmark-url-changed"], + [ + "bookmark-moved", + "bookmark-tags-changed", + "bookmark-title-changed", + "bookmark-url-changed", + ], this.handlePlacesEvents ); window.addEventListener("unload", this); @@ -561,7 +566,12 @@ if (this._observersAdded) { PlacesUtils.bookmarks.removeObserver(this); PlacesUtils.observers.removeListener( - ["bookmark-moved", "bookmark-title-changed", "bookmark-url-changed"], + [ + "bookmark-moved", + "bookmark-tags-changed", + "bookmark-title-changed", + "bookmark-url-changed", + ], this.handlePlacesEvents ); window.removeEventListener("unload", this); @@ -1154,6 +1164,11 @@ bm.title ); break; + case "bookmark-tags-changed": + if (this._paneInfo.visibleRows.has("tagsRow")) { + this._onTagsChange(event.guid).catch(Cu.reportError); + } + break; case "bookmark-title-changed": if (this._paneInfo.isItem || this._paneInfo.isTag) { // This also updates titles of folders in the folder menu list. @@ -1291,11 +1306,6 @@ aParentId, aGuid ) { - if (aProperty == "tags" && this._paneInfo.visibleRows.has("tagsRow")) { - this._onTagsChange(aGuid).catch(Cu.reportError); - return; - } - if (!this._paneInfo.isItem || this._paneInfo.itemId != aItemId) { return; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/places/tests/browser/browser_bookmark_add_tags.js firefox-trunk-95.0~a1~hg20211020r596404/browser/components/places/tests/browser/browser_bookmark_add_tags.js --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/places/tests/browser/browser_bookmark_add_tags.js 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/places/tests/browser/browser_bookmark_add_tags.js 2021-10-20 19:28:14.000000000 +0000 @@ -103,8 +103,9 @@ // Click the bookmark star again, add more tags. await clickBookmarkStar(); promiseNotification = PlacesTestUtils.waitForNotification( - "onItemChanged", - (id, property) => property == "tags" + "bookmark-tags-changed", + () => true, + "places" ); await fillBookmarkTextField( "editBMPanel_tagsField", diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/places/tests/browser/browser_bookmarkProperties_editTagContainer.js firefox-trunk-95.0~a1~hg20211020r596404/browser/components/places/tests/browser/browser_bookmarkProperties_editTagContainer.js --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/places/tests/browser/browser_bookmarkProperties_editTagContainer.js 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/places/tests/browser/browser_bookmarkProperties_editTagContainer.js 2021-10-20 19:28:14.000000000 +0000 @@ -34,11 +34,10 @@ ); let promiseTagResetNotification = PlacesTestUtils.waitForNotification( - "onItemChanged", - (itemId, prop) => { - let tags = PlacesUtils.tagging.getTagsForURI(uri); - return prop == "tags" && tags.length == 1 && tags[0] == "tag1"; - } + "bookmark-tags-changed", + events => + events.some(({ tags }) => tags.length === 1 && tags[0] === "tag1"), + "places" ); await withBookmarksDialog( @@ -60,11 +59,10 @@ Assert.ok(!namepicker.readOnly, "Name field should not be read-only"); Assert.equal(namepicker.value, "tag1", "Node title is correct"); let promiseTagChangeNotification = PlacesTestUtils.waitForNotification( - "onItemChanged", - (itemId, prop) => { - let tags = PlacesUtils.tagging.getTagsForURI(uri); - return prop == "tags" && tags.length == 1 && tags[0] == "tag2"; - } + "bookmark-tags-changed", + events => + events.some(({ tags }) => tags.length === 1 && tags[0] === "tag2"), + "places" ); let promiseTagRemoveNotification = PlacesTestUtils.waitForNotification( diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/places/tests/browser/browser_bookmark_remove_tags.js firefox-trunk-95.0~a1~hg20211020r596404/browser/components/places/tests/browser/browser_bookmark_remove_tags.js --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/places/tests/browser/browser_bookmark_remove_tags.js 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/places/tests/browser/browser_bookmark_remove_tags.js 2021-10-20 19:28:14.000000000 +0000 @@ -56,8 +56,9 @@ ); let promiseTagsChange = PlacesTestUtils.waitForNotification( - "onItemChanged", - (id, property) => property === "tags" + "bookmark-tags-changed", + () => true, + "places" ); // Update the "tags" field. @@ -119,8 +120,9 @@ ); let promiseTagsChange = PlacesTestUtils.waitForNotification( - "onItemChanged", - (id, property) => property === "tags" + "bookmark-tags-changed", + () => true, + "places" ); // Update the "tags" field. @@ -174,8 +176,9 @@ ); let promiseTagsChange = PlacesTestUtils.waitForNotification( - "onItemChanged", - (id, property) => property === "tags" + "bookmark-tags-changed", + () => true, + "places" ); // Update the "tags" field. diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/places/tests/browser/browser_bug485100-change-case-loses-tag.js firefox-trunk-95.0~a1~hg20211020r596404/browser/components/places/tests/browser/browser_bug485100-change-case-loses-tag.js --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/places/tests/browser/browser_bug485100-change-case-loses-tag.js 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/places/tests/browser/browser_bug485100-change-case-loses-tag.js 2021-10-20 19:28:14.000000000 +0000 @@ -23,8 +23,9 @@ // add a tag document.getElementById("editBMPanel_tagsField").value = testTag; let promiseNotification = PlacesTestUtils.waitForNotification( - "onItemChanged", - (id, property) => property == "tags" + "bookmark-tags-changed", + () => true, + "places" ); gEditItemOverlay.onTagsFieldChange(); await promiseNotification; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/places/tests/browser/browser_library_delete_bookmarks_in_tags.js firefox-trunk-95.0~a1~hg20211020r596404/browser/components/places/tests/browser/browser_library_delete_bookmarks_in_tags.js --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/places/tests/browser/browser_library_delete_bookmarks_in_tags.js 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/places/tests/browser/browser_library_delete_bookmarks_in_tags.js 2021-10-20 19:28:14.000000000 +0000 @@ -73,8 +73,9 @@ ); let promiseNotification = PlacesTestUtils.waitForNotification( - "onItemChanged", - (id, property) => property == "tags" + "bookmark-tags-changed", + () => true, + "places" ); ContentTree.view.controller.doCommand("cmd_delete"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/privatebrowsing/test/browser/title.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/components/privatebrowsing/test/browser/title.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/privatebrowsing/test/browser/title.sjs 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/privatebrowsing/test/browser/title.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -11,10 +11,11 @@ var cookie = "name=value"; var title = "No Cookie"; - if (request.hasHeader("Cookie") && request.getHeader("Cookie") == cookie) + if (request.hasHeader("Cookie") && request.getHeader("Cookie") == cookie) { title = "Cookie"; - else + } else { response.setHeader("Set-Cookie", cookie, false); + } response.write(""); response.write(title); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/resistfingerprinting/test/browser/browser_navigator_header.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/components/resistfingerprinting/test/browser/browser_navigator_header.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/resistfingerprinting/test/browser/browser_navigator_header.sjs 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/resistfingerprinting/test/browser/browser_navigator_header.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -8,4 +8,4 @@ } else { response.write("no user agent header"); } -} \ No newline at end of file +} diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/resistfingerprinting/test/browser/coop_header.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/components/resistfingerprinting/test/browser/coop_header.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/resistfingerprinting/test/browser/coop_header.sjs 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/resistfingerprinting/test/browser/coop_header.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -55,8 +55,7 @@ }; `; -function handleRequest(request, response) -{ +function handleRequest(request, response) { Components.utils.importGlobalProperties(["URLSearchParams"]); let query = new URLSearchParams(request.queryString); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/search/test/browser/browser_search_nimbus_reload.js firefox-trunk-95.0~a1~hg20211020r596404/browser/components/search/test/browser/browser_search_nimbus_reload.js --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/search/test/browser/browser_search_nimbus_reload.js 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/search/test/browser/browser_search_nimbus_reload.js 2021-10-20 19:28:15.000000000 +0000 @@ -16,7 +16,7 @@ add_task(async function test_engines_reloaded_nimbus() { let reloadSpy = sinon.spy(SearchService.prototype, "_maybeReloadEngines"); - let variableSpy = sinon.spy(NimbusFeatures.search, "getVariable"); + let getVariableSpy = sinon.spy(NimbusFeatures.search, "getVariable"); let doExperimentCleanup = await ExperimentFakes.enrollWithFeatureConfig({ featureId: "search", @@ -25,18 +25,21 @@ Assert.equal(reloadSpy.callCount, 1, "Called by experiment enrollment"); await BrowserTestUtils.waitForCondition( - () => variableSpy.called, + () => getVariableSpy.calledWith("experiment"), "Wait for SearchService update to run" ); Assert.equal( - variableSpy.callCount, - 1, - "Called by update function to fetch engines" + getVariableSpy.callCount, + 2, + "Called by update function to fetch engines and by ParamPreferenceCache" ); - Assert.equal( - variableSpy.firstCall.args[0], - "experiment", - "Got `experiment` variable value" + Assert.ok( + getVariableSpy.calledWith("extraParams"), + "Called by ParamPreferenceCache listener" + ); + Assert.ok( + getVariableSpy.calledWith("experiment"), + "Called by search service observer" ); Assert.equal( NimbusFeatures.search.getVariable("experiment"), @@ -49,5 +52,5 @@ Assert.equal(reloadSpy.callCount, 2, "Called by experiment unenrollment"); reloadSpy.restore(); - variableSpy.restore(); + getVariableSpy.restore(); }); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/sessionstore/test/browser_637020_slow.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/components/sessionstore/test/browser_637020_slow.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/sessionstore/test/browser_637020_slow.sjs 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/sessionstore/test/browser_637020_slow.sjs 2021-10-20 19:28:15.000000000 +0000 @@ -11,8 +11,12 @@ resp.setHeader("Content-Type", "text/html;charset=utf-8", false); timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - timer.init(() => { - resp.write("hi"); - resp.finish(); - }, DELAY_MS, Ci.nsITimer.TYPE_ONE_SHOT); + timer.init( + () => { + resp.write("hi"); + resp.finish(); + }, + DELAY_MS, + Ci.nsITimer.TYPE_ONE_SHOT + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/sessionstore/test/browser_sessionHistory_slow.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/components/sessionstore/test/browser_sessionHistory_slow.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/sessionstore/test/browser_sessionHistory_slow.sjs 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/sessionstore/test/browser_sessionHistory_slow.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -11,8 +11,12 @@ resp.setHeader("Content-Type", "text/html;charset=utf-8", false); timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - timer.init(() => { - resp.write("hi"); - resp.finish(); - }, DELAY_MS, Ci.nsITimer.TYPE_ONE_SHOT); + timer.init( + () => { + resp.write("hi"); + resp.finish(); + }, + DELAY_MS, + Ci.nsITimer.TYPE_ONE_SHOT + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/sessionstore/test/coopHeaderCommon.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/components/sessionstore/test/coopHeaderCommon.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/sessionstore/test/coopHeaderCommon.sjs 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/sessionstore/test/coopHeaderCommon.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -1,6 +1,6 @@ function handleRequest(request, response) { Components.utils.importGlobalProperties(["URLSearchParams"]); - Components.utils.import("resource://gre/modules/NetUtil.jsm"); + let { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm"); let query = new URLSearchParams(request.queryString); response.setHeader("Cross-Origin-Opener-Policy", "same-origin", false); @@ -15,8 +15,9 @@ }); // Set up the file streams to read in the file as UTF-8 - let fstream = Components.classes["@mozilla.org/network/file-input-stream;1"]. - createInstance(Components.interfaces.nsIFileInputStream); + let fstream = Components.classes[ + "@mozilla.org/network/file-input-stream;1" + ].createInstance(Components.interfaces.nsIFileInputStream); fstream.init(file, -1, 0, 0); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/translation/test/bing.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/components/translation/test/bing.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/translation/test/bing.sjs 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/translation/test/bing.sjs 2021-10-20 19:28:15.000000000 +0000 @@ -5,9 +5,11 @@ "use strict"; const CC = Components.Constructor; -const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", - "setInputStream"); +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); Cu.importGlobalProperties(["DOMParser"]); function handleRequest(req, res) { @@ -33,7 +35,7 @@ 405: "Method Not Allowed", 500: "Internal Server Error", 501: "Not Implemented", - 503: "Service Unavailable" + 503: "Service Unavailable", }; function HTTPError(code = 500, message) { @@ -46,8 +48,10 @@ function sendError(res, err) { if (!(err instanceof HTTPError)) { - err = new HTTPError(typeof err == "number" ? err : 500, - err.message || typeof err == "string" ? err : ""); + err = new HTTPError( + typeof err == "number" ? err : 500, + err.message || typeof err == "string" ? err : "" + ); } res.setStatusLine("1.1", err.code, err.name); res.write(err.message); @@ -57,8 +61,9 @@ let ret = {}; for (let param of query.replace(/^[?&]/, "").split("&")) { param = param.split("="); - if (!param[0]) + if (!param[0]) { continue; + } ret[unescape(param[0])] = unescape(param[1]); } return ret; @@ -69,22 +74,23 @@ let bytes = []; let body = new BinaryInputStream(req.bodyInputStream); - while ((avail = body.available()) > 0) + while ((avail = body.available()) > 0) { Array.prototype.push.apply(bytes, body.readByteArray(avail)); + } return String.fromCharCode.apply(null, bytes); } function sha1(str) { - let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"] - .createInstance(Ci.nsIScriptableUnicodeConverter); + let converter = Cc[ + "@mozilla.org/intl/scriptableunicodeconverter" + ].createInstance(Ci.nsIScriptableUnicodeConverter); converter.charset = "UTF-8"; // `result` is an out parameter, `result.value` will contain the array length. let result = {}; // `data` is an array of bytes. let data = converter.convertToByteArray(str, result); - let ch = Cc["@mozilla.org/security/hash;1"] - .createInstance(Ci.nsICryptoHash); + let ch = Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash); ch.init(ch.SHA1); ch.update(data, data.length); let hash = ch.finish(false); @@ -101,19 +107,22 @@ function parseXml(body) { let parser = new DOMParser(); let xml = parser.parseFromString(body, "text/xml"); - if (xml.documentElement.localName == "parsererror") + if (xml.documentElement.localName == "parsererror") { throw new Error("Invalid XML"); + } return xml; } function getInputStream(path) { let file = Cc["@mozilla.org/file/directory_service;1"] - .getService(Ci.nsIProperties) - .get("CurWorkD", Ci.nsIFile); - for (let part of path.split("/")) + .getService(Ci.nsIProperties) + .get("CurWorkD", Ci.nsIFile); + for (let part of path.split("/")) { file.append(part); - let fileStream = Cc["@mozilla.org/network/file-input-stream;1"] - .createInstance(Ci.nsIFileInputStream); + } + let fileStream = Cc[ + "@mozilla.org/network/file-input-stream;1" + ].createInstance(Ci.nsIFileInputStream); fileStream.init(file, 1, 0, false); return fileStream; } @@ -122,25 +131,33 @@ let err = new Error("Authorization failed"); err.code = 401; - if (!req.hasHeader("Authorization")) + if (!req.hasHeader("Authorization")) { throw new HTTPError(401, "No Authorization header provided."); + } let auth = req.getHeader("Authorization"); - if (!auth.startsWith("Bearer ")) - throw new HTTPError(401, "Invalid Authorization header content: '" + auth + "'"); + if (!auth.startsWith("Bearer ")) { + throw new HTTPError( + 401, + "Invalid Authorization header content: '" + auth + "'" + ); + } // Rejecting inactive subscriptions. if (auth.includes("inactive")) { - const INACTIVE_STATE_RESPONSE = "<html><body><h1>TranslateApiException</h1><p>Method: TranslateArray()</p><p>Message: The Azure Market Place Translator Subscription associated with the request credentials is not in an active state.</p><code></code><p>message id=5641.V2_Rest.TranslateArray.48CC6470</p></body></html>"; + const INACTIVE_STATE_RESPONSE = + "<html><body><h1>TranslateApiException</h1><p>Method: TranslateArray()</p><p>Message: The Azure Market Place Translator Subscription associated with the request credentials is not in an active state.</p><code></code><p>message id=5641.V2_Rest.TranslateArray.48CC6470</p></body></html>"; throw new HTTPError(401, INACTIVE_STATE_RESPONSE); } - } function reallyHandleRequest(req, res) { log("method: " + req.method); if (req.method != "POST") { - sendError(res, "Bing only deals with POST requests, not '" + req.method + "'."); + sendError( + res, + "Bing only deals with POST requests, not '" + req.method + "'." + ); return; } @@ -148,7 +165,9 @@ log("body: " + body); // First, we'll see if we're dealing with an XML body: - let contentType = req.hasHeader("Content-Type") ? req.getHeader("Content-Type") : null; + let contentType = req.hasHeader("Content-Type") + ? req.getHeader("Content-Type") + : null; log("contentType: " + contentType); if (contentType.startsWith("text/xml")) { @@ -161,10 +180,11 @@ let method = xml.documentElement.localName; log("invoking method: " + method); // If the requested method is supported, delegate it to its handler. - if (methodHandlers[method]) + if (methodHandlers[method]) { methodHandlers[method](res, xml); - else + } else { throw new HTTPError(501); + } } catch (ex) { sendError(res, ex, ex.code); } @@ -173,15 +193,16 @@ let params = parseQuery(body); // Delegate an authentication request to the correct handler. - if ("grant_type" in params && params.grant_type == "client_credentials") + if ("grant_type" in params && params.grant_type == "client_credentials") { methodHandlers.authenticate(res, params); - else + } else { sendError(res, 501); + } } } const methodHandlers = { - authenticate: function(res, params) { + authenticate(res, params) { // Validate a few required parameters. if (params.scope != "http://api.microsofttranslator.com") { sendError(res, "Invalid scope."); @@ -198,16 +219,16 @@ // Defines the tokens for certain client ids. const TOKEN_MAP = { - 'testInactive' : 'inactive', - 'testClient' : 'test' + testInactive: "inactive", + testClient: "test", }; - let token = 'test'; // Default token. - if((params.client_id in TOKEN_MAP)){ + let token = "test"; // Default token. + if (params.client_id in TOKEN_MAP) { token = TOKEN_MAP[params.client_id]; } let content = JSON.stringify({ access_token: token, - expires_in: 600 + expires_in: 600, }); res.setStatusLine("1.1", 200, "OK"); @@ -216,9 +237,9 @@ res.write(content); }, - TranslateArrayRequest: function(res, xml, body) { + TranslateArrayRequest(res, xml, body) { let from = xml.querySelector("From").firstChild.nodeValue; - let to = xml.querySelector("To").firstChild.nodeValue + let to = xml.querySelector("To").firstChild.nodeValue; log("translating from '" + from + "' to '" + to + "'"); res.setStatusLine("1.1", 200, "OK"); @@ -227,8 +248,11 @@ let hash = sha1(body).substr(0, 10); log("SHA1 hash of content: " + hash); let inputStream = getInputStream( - "browser/browser/components/translation/test/fixtures/result-" + hash + ".txt"); + "browser/browser/components/translation/test/fixtures/result-" + + hash + + ".txt" + ); res.bodyOutputStream.writeFrom(inputStream, inputStream.available()); inputStream.close(); - } + }, }; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/translation/test/yandex.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/components/translation/test/yandex.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/translation/test/yandex.sjs 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/translation/test/yandex.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -5,9 +5,11 @@ "use strict"; const CC = Components.Constructor; -const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", - "setInputStream"); +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); function handleRequest(req, res) { try { @@ -34,7 +36,7 @@ 422: "The text could not be translated", 500: "Internal Server Error", 501: "The specified translation direction is not supported", - 503: "Service Unavailable" + 503: "Service Unavailable", }; function HTTPError(code = 500, message) { @@ -47,8 +49,10 @@ function sendError(res, err) { if (!(err instanceof HTTPError)) { - err = new HTTPError(typeof err == "number" ? err : 500, - err.message || typeof err == "string" ? err : ""); + err = new HTTPError( + typeof err == "number" ? err : 500, + err.message || typeof err == "string" ? err : "" + ); } res.setStatusLine("1.1", err.code, err.name); res.write(err.message); @@ -58,19 +62,22 @@ // http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript function parseQuery(query) { let match, - params = {}, - pl = /\+/g, - search = /([^&=]+)=?([^&]*)/g, - decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); }; + params = {}, + pl = /\+/g, + search = /([^&=]+)=?([^&]*)/g, + decode = function(s) { + return decodeURIComponent(s.replace(pl, " ")); + }; - while (match = search.exec(query)) { + while ((match = search.exec(query))) { let k = decode(match[1]), - v = decode(match[2]); + v = decode(match[2]); if (k in params) { - if(params[k] instanceof Array) + if (params[k] instanceof Array) { params[k].push(v); - else + } else { params[k] = [params[k], v]; + } } else { params[k] = v; } @@ -80,15 +87,15 @@ } function sha1(str) { - let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"] - .createInstance(Ci.nsIScriptableUnicodeConverter); + let converter = Cc[ + "@mozilla.org/intl/scriptableunicodeconverter" + ].createInstance(Ci.nsIScriptableUnicodeConverter); converter.charset = "UTF-8"; // `result` is an out parameter, `result.value` will contain the array length. let result = {}; // `data` is an array of bytes. let data = converter.convertToByteArray(str, result); - let ch = Cc["@mozilla.org/security/hash;1"] - .createInstance(Ci.nsICryptoHash); + let ch = Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash); ch.init(ch.SHA1); ch.update(data, data.length); let hash = ch.finish(false); @@ -107,20 +114,23 @@ let bytes = []; let body = new BinaryInputStream(req.bodyInputStream); - while ((avail = body.available()) > 0) + while ((avail = body.available()) > 0) { Array.prototype.push.apply(bytes, body.readByteArray(avail)); + } return String.fromCharCode.apply(null, bytes); } function getInputStream(path) { let file = Cc["@mozilla.org/file/directory_service;1"] - .getService(Ci.nsIProperties) - .get("CurWorkD", Ci.nsIFile); - for (let part of path.split("/")) + .getService(Ci.nsIProperties) + .get("CurWorkD", Ci.nsIFile); + for (let part of path.split("/")) { file.append(part); - let fileStream = Cc["@mozilla.org/network/file-input-stream;1"] - .createInstance(Ci.nsIFileInputStream); + } + let fileStream = Cc[ + "@mozilla.org/network/file-input-stream;1" + ].createInstance(Ci.nsIFileInputStream); fileStream.init(file, 1, 0, false); return fileStream; } @@ -138,33 +148,34 @@ * If any other key is used the server reponds with 401 error code. */ function checkAuth(params) { - if(!("key" in params)) + if (!("key" in params)) { throw new HTTPError(400); + } let key = params.key; - if(key === "yandexValidKey") + if (key === "yandexValidKey") { return true; + } let invalidKeys = { - "yandexInvalidKey" : 401, - "yandexBlockedKey" : 402, - "yandexOutOfRequestsKey" : 403, - "yandexOutOfCharsKey" : 404, + yandexInvalidKey: 401, + yandexBlockedKey: 402, + yandexOutOfRequestsKey: 403, + yandexOutOfCharsKey: 404, }; - if(key in invalidKeys) + if (key in invalidKeys) { throw new HTTPError(invalidKeys[key]); + } throw new HTTPError(401); } function reallyHandleRequest(req, res) { - try { - // Preparing the query parameters. let params = {}; - if(req.method == 'POST') { + if (req.method == "POST") { params = parseQuery(getRequestBody(req)); } @@ -172,28 +183,28 @@ log(JSON.stringify(params)); checkAuth(params); - methodHandlers['translate'](res, params); - + methodHandlers.translate(res, params); } catch (ex) { sendError(res, ex, ex.code); } - } const methodHandlers = { - translate: function(res, params) { + translate(res, params) { res.setStatusLine("1.1", 200, "OK"); res.setHeader("Content-Type", "application/json"); let hash = sha1(JSON.stringify(params)).substr(0, 10); log("SHA1 hash of content: " + hash); - let fixture = "browser/browser/components/translation/test/fixtures/result-yandex-" + hash + ".json"; + let fixture = + "browser/browser/components/translation/test/fixtures/result-yandex-" + + hash + + ".json"; log("PATH: " + fixture); let inputStream = getInputStream(fixture); res.bodyOutputStream.writeFrom(inputStream, inputStream.available()); inputStream.close(); - } - + }, }; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/uitour/test/browser_UITour_availableTargets.js firefox-trunk-95.0~a1~hg20211020r596404/browser/components/uitour/test/browser_UITour_availableTargets.js --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/uitour/test/browser_UITour_availableTargets.js 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/uitour/test/browser_UITour_availableTargets.js 2021-10-20 19:28:14.000000000 +0000 @@ -15,14 +15,12 @@ "appMenu", "backForward", "help", - "history", "logins", "pageAction-bookmark", ...(hasPocket ? ["pocket"] : []), "privateWindow", ...(hasQuit ? ["quit"] : []), "readerMode-urlBar", - "restorePreviousSession", "urlbar", ]; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/uitour/UITour.jsm firefox-trunk-95.0~a1~hg20211020r596404/browser/components/uitour/UITour.jsm --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/uitour/UITour.jsm 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/uitour/UITour.jsm 2021-10-20 19:28:15.000000000 +0000 @@ -151,14 +151,6 @@ }, ], ["help", { query: "#appMenu-help-button2" }], - [ - "history", - { - query: "#appMenu-history-button", - subItem: "restorePreviousSession", - level: "top", - }, - ], ["home", { query: "#home-button" }], [ "logins", @@ -186,7 +178,6 @@ }, ], ["readerMode-urlBar", { query: "#reader-mode-button" }], - ["restorePreviousSession", { query: "#appMenu-restoreSession" }], [ "search", { @@ -836,7 +827,7 @@ this.hideHighlight(aWindow); this.hideInfo(aWindow); - await this.removePanelListeners(aWindow, true); + await this.removePanelListeners(aWindow); this.noautohideMenus.clear(); @@ -849,7 +840,7 @@ /** * Remove the listeners to a panel when tearing the tour down. */ - async removePanelListeners(aWindow, aHidePanels = false) { + async removePanelListeners(aWindow) { let panels = [ { name: "appMenu", @@ -858,14 +849,12 @@ ["popuphidden", this.onPanelHidden], ["popuphiding", this.onAppMenuHiding], ["ViewShowing", this.onAppMenuSubviewShowing], - ["ViewShown", this.onAppMenuSubviewShown], - ["ViewHiding", this.onAppMenuSubviewHiding], ], }, ]; for (let panel of panels) { // Ensure the menu panel is hidden and clean up panel listeners after calling hideMenu. - if (aHidePanels && panel.node.state != "closed") { + if (panel.node.state != "closed") { await new Promise(resolve => { panel.node.addEventListener("popuphidden", resolve, { once: true }); this.hideMenu(aWindow, panel.name); @@ -978,8 +967,6 @@ targetName: aTargetName, widgetName: targetObject.widgetName, allowAdd: targetObject.allowAdd, - level: targetObject.level, - subItem: targetObject.subItem, }); }) .catch(log.error); @@ -1137,16 +1124,6 @@ highlighter.parentElement.setAttribute("targetName", aTarget.targetName); highlighter.parentElement.hidden = false; - if (aTarget.subItem) { - // This is a subitem in the app menu, so mark it as one not to hide. - this.noautohideMenus.add("appMenu"); - highlighter.parentElement.setAttribute("subitem", aTarget.subItem); - } - - if (aTarget.level) { - highlighter.parentElement.setAttribute("level", aTarget.level); - } - let highlightAnchor = aAnchorEl; let targetRect = highlightAnchor.getBoundingClientRect(); let highlightHeight = targetRect.height; @@ -1442,8 +1419,6 @@ menu.node = aWindow.PanelUI.panel; menu.onPopupHiding = this.onAppMenuHiding; menu.onViewShowing = this.onAppMenuSubviewShowing; - menu.onViewShown = this.onAppMenuSubviewShown; - menu.onViewHiding = this.onAppMenuSubviewHiding; menu.show = () => aWindow.PanelUI.show(); if (!aOptions.autohide) { @@ -1459,8 +1434,6 @@ menu.node.addEventListener("popuphidden", menu.onPanelHidden); menu.node.addEventListener("popuphiding", menu.onPopupHiding); menu.node.addEventListener("ViewShowing", menu.onViewShowing); - menu.node.addEventListener("ViewShown", menu.onViewShown); - menu.node.addEventListener("ViewHiding", menu.onViewHiding); menu.show(); } else if (aMenuName == "bookmarks") { let menuBtn = aWindow.document.getElementById("bookmarks-menu-button"); @@ -1589,80 +1562,12 @@ UITour._hideAnnotationsForPanel(aEvent, false, UITour.targetIsInAppMenu); }, - onAppMenuSubviewShown(aEvent) { - let win = aEvent.target.ownerGlobal; - let subItemName = UITour.getSubItem(win); - if ( - subItemName && - UITour.isSubItemToHighlight(win, subItemName, aEvent.target.id) - ) { - let highlighter = UITour.getHighlightAndMaybeCreate(win.document); - UITour.recreatePopup(highlighter.parentElement); - - UITour.getTarget(win, subItemName).then(subItem => { - if (!subItem.node.hidden) { - UITour.showHighlight(win, subItem, "focus-outline", { - autohide: true, - }); - } - }); - } else if (subItemName && aEvent.target.id != "appMenu-protonMainView") { - UITour.stopSubViewHandling(win); - } - }, - - onAppMenuSubviewHiding(aEvent) { - let win = aEvent.target.ownerGlobal; - - let subItem = UITour.getSubItem(win); - if (subItem) { - if (UITour.isSubItemToHighlight(win, subItem, aEvent.target.id)) { - UITour.hideHighlight(win); - UITour.stopSubViewHandling(win); - } - } - }, - onPanelHidden(aEvent) { - if (UITour.getSubItem(aEvent.target.ownerGlobal)) { - UITour.stopSubViewHandling(aEvent.target.ownerGlobal); - } - aEvent.target.removeAttribute("noautohide"); UITour.recreatePopup(aEvent.target); UITour.clearAvailableTargetsCache(); }, - getSubItem(aWindow) { - // Get the subitem that should be highlighted in the app menu's subview. - let highlighter = UITour.getHighlightContainerAndMaybeCreate( - aWindow.document - ); - return highlighter.getAttribute("subitem"); - }, - - isSubItemToHighlight(aWindow, aSubItem, aExpectedId) { - let targetObject = UITour.targets.get(aSubItem); - if (targetObject) { - let node = UITour.getNodeFromDocument( - aWindow.document, - targetObject.query - ); - return aExpectedId == node?.closest("panelview")?.id; - } - - return false; - }, - - stopSubViewHandling(aWindow) { - UITour.removePanelListeners(aWindow); - UITour.noautohideMenus.delete("appMenu"); - - let highlighter = UITour.getHighlightAndMaybeCreate(aWindow.document); - highlighter.parentElement.removeAttribute("level"); - highlighter.parentElement.removeAttribute("subitem"); - }, - recreatePopup(aPanel) { // After changing popup attributes that relate to how the native widget is created // (e.g. @noautohide) we need to re-create the frame/widget for it to take effect. diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/urlbar/tests/browser/authenticate.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/components/urlbar/tests/browser/authenticate.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/urlbar/tests/browser/authenticate.sjs 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/urlbar/tests/browser/authenticate.sjs 2021-10-20 19:28:15.000000000 +0000 @@ -1,5 +1,4 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { try { reallyHandleRequest(request, response); } catch (e) { @@ -8,10 +7,10 @@ } } - function reallyHandleRequest(request, response) { var match; - var requestAuth = true, requestProxyAuth = true; + var requestAuth = true, + requestProxyAuth = true; // Allow the caller to drive how authentication is processed via the query. // Eg, http://localhost:8888/authenticate.sjs?user=foo&realm=bar @@ -19,133 +18,178 @@ // at the beginning of the query string. var query = "?" + request.queryString; - var expected_user = "", expected_pass = "", realm = "mochitest"; - var proxy_expected_user = "", proxy_expected_pass = "", proxy_realm = "mochi-proxy"; - var huge = false, plugin = false, anonymous = false; + var expected_user = "", + expected_pass = "", + realm = "mochitest"; + var proxy_expected_user = "", + proxy_expected_pass = "", + proxy_realm = "mochi-proxy"; + var huge = false, + plugin = false, + anonymous = false; var authHeaderCount = 1; // user=xxx match = /[^_]user=([^&]*)/.exec(query); - if (match) + if (match) { expected_user = match[1]; + } // pass=xxx match = /[^_]pass=([^&]*)/.exec(query); - if (match) + if (match) { expected_pass = match[1]; + } // realm=xxx match = /[^_]realm=([^&]*)/.exec(query); - if (match) + if (match) { realm = match[1]; + } // proxy_user=xxx match = /proxy_user=([^&]*)/.exec(query); - if (match) + if (match) { proxy_expected_user = match[1]; + } // proxy_pass=xxx match = /proxy_pass=([^&]*)/.exec(query); - if (match) + if (match) { proxy_expected_pass = match[1]; + } // proxy_realm=xxx match = /proxy_realm=([^&]*)/.exec(query); - if (match) + if (match) { proxy_realm = match[1]; + } // huge=1 match = /huge=1/.exec(query); - if (match) + if (match) { huge = true; + } // plugin=1 match = /plugin=1/.exec(query); - if (match) + if (match) { plugin = true; + } // multiple=1 match = /multiple=([^&]*)/.exec(query); - if (match) - authHeaderCount = match[1]+0; + if (match) { + authHeaderCount = match[1] + 0; + } // anonymous=1 match = /anonymous=1/.exec(query); - if (match) + if (match) { anonymous = true; + } // Look for an authentication header, if any, in the request. // // EG: Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== - // + // // This test only supports Basic auth. The value sent by the client is // "username:password", obscured with base64 encoding. - var actual_user = "", actual_pass = "", authHeader, authPresent = false; + var actual_user = "", + actual_pass = "", + authHeader, + authPresent = false; if (request.hasHeader("Authorization")) { authPresent = true; authHeader = request.getHeader("Authorization"); match = /Basic (.+)/.exec(authHeader); - if (match.length != 2) - throw "Couldn't parse auth header: " + authHeader; + if (match.length != 2) { + throw "Couldn't parse auth header: " + authHeader; + } var userpass = base64ToString(match[1]); // no atob() :-( match = /(.*):(.*)/.exec(userpass); - if (match.length != 3) - throw "Couldn't decode auth header: " + userpass; + if (match.length != 3) { + throw "Couldn't decode auth header: " + userpass; + } actual_user = match[1]; actual_pass = match[2]; - } + } - var proxy_actual_user = "", proxy_actual_pass = ""; + var proxy_actual_user = "", + proxy_actual_pass = ""; if (request.hasHeader("Proxy-Authorization")) { authHeader = request.getHeader("Proxy-Authorization"); match = /Basic (.+)/.exec(authHeader); - if (match.length != 2) - throw "Couldn't parse auth header: " + authHeader; + if (match.length != 2) { + throw "Couldn't parse auth header: " + authHeader; + } var userpass = base64ToString(match[1]); // no atob() :-( match = /(.*):(.*)/.exec(userpass); - if (match.length != 3) - throw "Couldn't decode auth header: " + userpass; + if (match.length != 3) { + throw "Couldn't decode auth header: " + userpass; + } proxy_actual_user = match[1]; proxy_actual_pass = match[2]; } // Don't request authentication if the credentials we got were what we // expected. - if (expected_user == actual_user && - expected_pass == actual_pass) { + if (expected_user == actual_user && expected_pass == actual_pass) { requestAuth = false; } - if (proxy_expected_user == proxy_actual_user && - proxy_expected_pass == proxy_actual_pass) { + if ( + proxy_expected_user == proxy_actual_user && + proxy_expected_pass == proxy_actual_pass + ) { requestProxyAuth = false; } if (anonymous) { if (authPresent) { - response.setStatusLine("1.0", 400, "Unexpected authorization header found"); + response.setStatusLine( + "1.0", + 400, + "Unexpected authorization header found" + ); } else { response.setStatusLine("1.0", 200, "Authorization header not found"); } - } else { - if (requestProxyAuth) { - response.setStatusLine("1.0", 407, "Proxy authentication required"); - for (i = 0; i < authHeaderCount; ++i) - response.setHeader("Proxy-Authenticate", "basic realm=\"" + proxy_realm + "\"", true); - } else if (requestAuth) { - response.setStatusLine("1.0", 401, "Authentication required"); - for (i = 0; i < authHeaderCount; ++i) - response.setHeader("WWW-Authenticate", "basic realm=\"" + realm + "\"", true); - } else { - response.setStatusLine("1.0", 200, "OK"); + } else if (requestProxyAuth) { + response.setStatusLine("1.0", 407, "Proxy authentication required"); + for (i = 0; i < authHeaderCount; ++i) { + response.setHeader( + "Proxy-Authenticate", + 'basic realm="' + proxy_realm + '"', + true + ); + } + } else if (requestAuth) { + response.setStatusLine("1.0", 401, "Authentication required"); + for (i = 0; i < authHeaderCount; ++i) { + response.setHeader( + "WWW-Authenticate", + 'basic realm="' + realm + '"', + true + ); } + } else { + response.setStatusLine("1.0", 200, "OK"); } response.setHeader("Content-Type", "application/xhtml+xml", false); response.write("<html xmlns='http://www.w3.org/1999/xhtml'>"); - response.write("<p>Login: <span id='ok'>" + (requestAuth ? "FAIL" : "PASS") + "</span></p>\n"); - response.write("<p>Proxy: <span id='proxy'>" + (requestProxyAuth ? "FAIL" : "PASS") + "</span></p>\n"); + response.write( + "<p>Login: <span id='ok'>" + + (requestAuth ? "FAIL" : "PASS") + + "</span></p>\n" + ); + response.write( + "<p>Proxy: <span id='proxy'>" + + (requestProxyAuth ? "FAIL" : "PASS") + + "</span></p>\n" + ); response.write("<p>Auth: <span id='auth'>" + authHeader + "</span></p>\n"); response.write("<p>User: <span id='user'>" + actual_user + "</span></p>\n"); response.write("<p>Pass: <span id='pass'>" + actual_pass + "</span></p>\n"); @@ -156,23 +200,27 @@ response.write("123456789\n"); } response.write("</div>"); - response.write("<span id='footnote'>This is a footnote after the huge content fill</span>"); + response.write( + "<span id='footnote'>This is a footnote after the huge content fill</span>" + ); } if (plugin) { - response.write("<embed id='embedtest' style='width: 400px; height: 100px;' " + - "type='application/x-test'></embed>\n"); + response.write( + "<embed id='embedtest' style='width: 400px; height: 100px;' " + + "type='application/x-test'></embed>\n" + ); } response.write("</html>"); } - // base64 decoder // // Yoinked from extensions/xml-rpc/src/nsXmlRpcClient.js because btoa() // doesn't seem to exist. :-( /* Convert Base64 data to a string */ +/* eslint-disable prettier/prettier */ const toBinaryTable = [ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, @@ -183,38 +231,42 @@ -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 ]; -const base64Pad = '='; +/* eslint-enable prettier/prettier */ +const base64Pad = "="; function base64ToString(data) { + var result = ""; + var leftbits = 0; // number of bits decoded, but yet to be appended + var leftdata = 0; // bits decoded, but yet to be appended + + // Convert one by one. + for (var i = 0; i < data.length; i++) { + var c = toBinaryTable[data.charCodeAt(i) & 0x7f]; + var padding = data[i] == base64Pad; + // Skip illegal characters and whitespace + if (c == -1) { + continue; + } + + // Collect data into leftdata, update bitcount + leftdata = (leftdata << 6) | c; + leftbits += 6; + + // If we have 8 or more bits, append 8 bits to the result + if (leftbits >= 8) { + leftbits -= 8; + // Append if not padding. + if (!padding) { + result += String.fromCharCode((leftdata >> leftbits) & 0xff); + } + leftdata &= (1 << leftbits) - 1; + } + } - var result = ''; - var leftbits = 0; // number of bits decoded, but yet to be appended - var leftdata = 0; // bits decoded, but yet to be appended - - // Convert one by one. - for (var i = 0; i < data.length; i++) { - var c = toBinaryTable[data.charCodeAt(i) & 0x7f]; - var padding = (data[i] == base64Pad); - // Skip illegal characters and whitespace - if (c == -1) continue; - - // Collect data into leftdata, update bitcount - leftdata = (leftdata << 6) | c; - leftbits += 6; - - // If we have 8 or more bits, append 8 bits to the result - if (leftbits >= 8) { - leftbits -= 8; - // Append if not padding. - if (!padding) - result += String.fromCharCode((leftdata >> leftbits) & 0xff); - leftdata &= (1 << leftbits) - 1; - } - } - - // If there are any bits left, the base64 string was corrupted - if (leftbits) - throw Components.Exception('Corrupted base64 string'); + // If there are any bits left, the base64 string was corrupted + if (leftbits) { + throw Components.Exception("Corrupted base64 string"); + } - return result; + return result; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/urlbar/tests/browser/print_postdata.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/components/urlbar/tests/browser/print_postdata.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/urlbar/tests/browser/print_postdata.sjs 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/urlbar/tests/browser/print_postdata.sjs 2021-10-20 19:28:15.000000000 +0000 @@ -1,7 +1,9 @@ const CC = Components.Constructor; -const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", - "setInputStream"); +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); function handleRequest(request, response) { response.setHeader("Content-Type", "text/plain", false); @@ -13,8 +15,9 @@ var avail; var bytes = []; - while ((avail = body.available()) > 0) + while ((avail = body.available()) > 0) { Array.prototype.push.apply(bytes, body.readByteArray(avail)); + } var data = String.fromCharCode.apply(null, bytes); response.bodyOutputStream.write(data, data.length); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/urlbar/tests/browser/redirect_error.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/components/urlbar/tests/browser/redirect_error.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/urlbar/tests/browser/redirect_error.sjs 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/urlbar/tests/browser/redirect_error.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -5,12 +5,12 @@ const REDIRECT_TO = "https://www.bank1.com/"; // Bad-cert host. function handleRequest(aRequest, aResponse) { - // Set HTTP Status - aResponse.setStatusLine(aRequest.httpVersion, 301, "Moved Permanently"); + // Set HTTP Status + aResponse.setStatusLine(aRequest.httpVersion, 301, "Moved Permanently"); - // Set redirect URI, mirroring the hash value. - let hash = (/\#.+/.test(aRequest.path))? - "#" + aRequest.path.split("#")[1]: - ""; - aResponse.setHeader("Location", REDIRECT_TO + hash); + // Set redirect URI, mirroring the hash value. + let hash = /\#.+/.test(aRequest.path) + ? "#" + aRequest.path.split("#")[1] + : ""; + aResponse.setHeader("Location", REDIRECT_TO + hash); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/urlbar/tests/browser/searchSuggestionEngine.sjs firefox-trunk-95.0~a1~hg20211020r596404/browser/components/urlbar/tests/browser/searchSuggestionEngine.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/urlbar/tests/browser/searchSuggestionEngine.sjs 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/urlbar/tests/browser/searchSuggestionEngine.sjs 2021-10-20 19:28:14.000000000 +0000 @@ -18,15 +18,19 @@ return memo; }, {}); - let timeout = parseInt(params["timeout"]); + let timeout = parseInt(params.timeout); if (timeout) { // Write the response after a timeout. resp.processAsync(); gTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - gTimer.init(() => { - writeResponse(params, resp); - resp.finish(); - }, timeout, Ci.nsITimer.TYPE_ONE_SHOT); + gTimer.init( + () => { + writeResponse(params, resp); + resp.finish(); + }, + timeout, + Ci.nsITimer.TYPE_ONE_SHOT + ); return; } @@ -36,14 +40,14 @@ function writeResponse(params, resp) { // Echo back the search string with "foo" and "bar" appended. let suffixes = ["foo", "bar"]; - if (params["count"]) { + if (params.count) { // Add more suffixes. let serial = 0; - while (suffixes.length < params["count"]) { + while (suffixes.length < params.count) { suffixes.push(++serial); } } - let data = [params["query"], suffixes.map(s => params["query"] + s)]; + let data = [params.query, suffixes.map(s => params.query + s)]; resp.setHeader("Content-Type", "application/json", false); resp.write(JSON.stringify(data)); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/urlbar/UrlbarMuxerUnifiedComplete.jsm firefox-trunk-95.0~a1~hg20211020r596404/browser/components/urlbar/UrlbarMuxerUnifiedComplete.jsm --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/urlbar/UrlbarMuxerUnifiedComplete.jsm 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/urlbar/UrlbarMuxerUnifiedComplete.jsm 2021-10-20 19:28:15.000000000 +0000 @@ -482,7 +482,7 @@ while (summedFillableLimit != fillableLimit) { if (!fractionalDataArray.length) { // This shouldn't happen, but don't let it break us. - Cu.reportError("fractionalDataArray is empty!"); + logger.error("fractionalDataArray is empty!"); break; } let data = flexDataArray[fractionalDataArray.shift().index]; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/urlbar/UrlbarProviderExtension.jsm firefox-trunk-95.0~a1~hg20211020r596404/browser/components/urlbar/UrlbarProviderExtension.jsm --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/urlbar/UrlbarProviderExtension.jsm 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/urlbar/UrlbarProviderExtension.jsm 2021-10-20 19:28:14.000000000 +0000 @@ -203,9 +203,10 @@ let extResults = await this._notifyListener("resultsRequested", context); if (extResults) { for (let extResult of extResults) { - let result = await this._makeUrlbarResult(context, extResult).catch( - Cu.reportError - ); + let result = await this._makeUrlbarResult( + context, + extResult + ).catch(ex => this.logger.error(ex)); if (result) { addCallback(this, result); } @@ -282,7 +283,7 @@ try { result = listener(...args); } catch (error) { - Cu.reportError(error); + this.logger.error(error); return undefined; } if (result.catch) { @@ -296,7 +297,7 @@ }); result = await Promise.race([ timer.promise, - result.catch(Cu.reportError), + result.catch(ex => this.logger.error(ex)), ]); timer.cancel(); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/urlbar/UrlbarProviderOmnibox.jsm firefox-trunk-95.0~a1~hg20211020r596404/browser/components/urlbar/UrlbarProviderOmnibox.jsm --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/urlbar/UrlbarProviderOmnibox.jsm 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/urlbar/UrlbarProviderOmnibox.jsm 2021-10-20 19:28:15.000000000 +0000 @@ -169,8 +169,8 @@ time: MAXIMUM_ALLOWED_EXTENSION_TIME_MS, logger: this.logger, }).promise; - await Promise.race([timeoutPromise, this._resultsPromise]).catch( - Cu.reportError + await Promise.race([timeoutPromise, this._resultsPromise]).catch(ex => + this.logger.error(ex) ); } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/urlbar/UrlbarProviderPlaces.jsm firefox-trunk-95.0~a1~hg20211020r596404/browser/components/urlbar/UrlbarProviderPlaces.jsm --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/urlbar/UrlbarProviderPlaces.jsm 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/urlbar/UrlbarProviderPlaces.jsm 2021-10-20 19:28:14.000000000 +0000 @@ -1408,7 +1408,7 @@ return conn; })().catch(ex => { dump("Couldn't get database handle: " + ex + "\n"); - Cu.reportError(ex); + this.logger.error(ex); }); } return this._promiseDatabase; @@ -1529,7 +1529,7 @@ .then(conn => search.execute(conn)) .catch(ex => { dump(`Query failed: ${ex}\n`); - Cu.reportError(ex); + this.logger.error(ex); }) .then(() => { if (search == this._currentSearch) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/urlbar/UrlbarProviderPreloadedSites.jsm firefox-trunk-95.0~a1~hg20211020r596404/browser/components/urlbar/UrlbarProviderPreloadedSites.jsm --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/urlbar/UrlbarProviderPreloadedSites.jsm 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/urlbar/UrlbarProviderPreloadedSites.jsm 2021-10-20 19:28:14.000000000 +0000 @@ -78,7 +78,7 @@ fetch("chrome://browser/content/urlbar/preloaded-top-urls.json") .then(response => response.json()) .then(sites => PreloadedSiteStorage.populate(sites)) - .catch(ex => Cu.reportError(ex)); + .catch(ex => this.logger.error(ex)); } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/urlbar/UrlbarProviderQuickSuggest.jsm firefox-trunk-95.0~a1~hg20211020r596404/browser/components/urlbar/UrlbarProviderQuickSuggest.jsm --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/urlbar/UrlbarProviderQuickSuggest.jsm 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/urlbar/UrlbarProviderQuickSuggest.jsm 2021-10-20 19:28:15.000000000 +0000 @@ -227,7 +227,7 @@ r => r.providerName == this.name ); if (resultIndex < 0) { - Cu.reportError(`Could not find quick suggest result`); + this.logger.error(`Could not find quick suggest result`); return; } result = queryContext.results[resultIndex]; @@ -346,7 +346,7 @@ try { this._merinoFetchController?.abort(); } catch (error) { - Cu.reportError(error); + this.logger.error(error); } this._merinoFetchController = null; } @@ -382,7 +382,7 @@ } catch (error) { TelemetryStopwatch.cancel(TELEMETRY_MERINO_LATENCY, queryContext); if (error.name != "AbortError") { - Cu.reportError(error); + this.logger.error(error); } } finally { if (controller == this._merinoFetchController) { @@ -402,7 +402,7 @@ return null; } } catch (error) { - Cu.reportError(error); + this.logger.error(error); } if (!body?.suggestions?.length) { @@ -411,7 +411,7 @@ let { suggestions } = body; if (!Array.isArray(suggestions)) { - Cu.reportError("Unexpected Merino response: " + JSON.stringify(body)); + this.logger.error("Unexpected Merino response: " + JSON.stringify(body)); return null; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/urlbar/UrlbarProviderSearchSuggestions.jsm firefox-trunk-95.0~a1~hg20211020r596404/browser/components/urlbar/UrlbarProviderSearchSuggestions.jsm --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/urlbar/UrlbarProviderSearchSuggestions.jsm 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/urlbar/UrlbarProviderSearchSuggestions.jsm 2021-10-20 19:28:15.000000000 +0000 @@ -378,7 +378,7 @@ } if (entry.tail && entry.tailOffsetIndex < 0) { - Cu.reportError( + this.logger.error( `Error in tail suggestion parsing. Value: ${entry.value}, tail: ${entry.tail}.` ); continue; @@ -393,7 +393,7 @@ } if (!tail) { - await tailTimer.fire().catch(Cu.reportError); + await tailTimer.fire().catch(ex => this.logger.error(ex)); } try { @@ -415,7 +415,7 @@ ) ); } catch (err) { - Cu.reportError(err); + this.logger.error(err); continue; } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/urlbar/UrlbarProviderSearchTips.jsm firefox-trunk-95.0~a1~hg20211020r596404/browser/components/urlbar/UrlbarProviderSearchTips.jsm --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/urlbar/UrlbarProviderSearchTips.jsm 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/urlbar/UrlbarProviderSearchTips.jsm 2021-10-20 19:28:15.000000000 +0000 @@ -323,7 +323,7 @@ return; } - this._maybeShowTipForUrl(uri.spec).catch(Cu.reportError); + this._maybeShowTipForUrl(uri.spec).catch(ex => this.logger.error(ex)); } /** diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/urlbar/UrlbarProvidersManager.jsm firefox-trunk-95.0~a1~hg20211020r596404/browser/components/urlbar/UrlbarProvidersManager.jsm --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/urlbar/UrlbarProvidersManager.jsm 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/urlbar/UrlbarProvidersManager.jsm 2021-10-20 19:28:15.000000000 +0000 @@ -405,7 +405,7 @@ } } }) - .catch(Cu.reportError) + .catch(ex => logger.error(ex)) ); } @@ -492,13 +492,13 @@ provider.tryMethod("cancelQuery", this.context); } if (this._heuristicProviderTimer) { - this._heuristicProviderTimer.cancel().catch(Cu.reportError); + this._heuristicProviderTimer.cancel().catch(ex => logger.error(ex)); } if (this._chunkTimer) { - this._chunkTimer.cancel().catch(Cu.reportError); + this._chunkTimer.cancel().catch(ex => logger.error(ex)); } if (this._sleepTimer) { - this._sleepTimer.fire().catch(Cu.reportError); + this._sleepTimer.fire().catch(ex => logger.error(ex)); } } @@ -600,7 +600,7 @@ this._heuristicProviderTimer && !this.context.pendingHeuristicProviders.size ) { - this._heuristicProviderTimer.fire().catch(Cu.reportError); + this._heuristicProviderTimer.fire().catch(ex => logger.error(ex)); } } @@ -608,12 +608,12 @@ this.muxer.sort(this.context); if (this._heuristicProviderTimer) { - this._heuristicProviderTimer.cancel().catch(Cu.reportError); + this._heuristicProviderTimer.cancel().catch(ex => logger.error(ex)); this._heuristicProviderTimer = null; } if (this._chunkTimer) { - this._chunkTimer.cancel().catch(Cu.reportError); + this._chunkTimer.cancel().catch(ex => logger.error(ex)); this._chunkTimer = null; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/urlbar/UrlbarProviderTabToSearch.jsm firefox-trunk-95.0~a1~hg20211020r596404/browser/components/urlbar/UrlbarProviderTabToSearch.jsm --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/urlbar/UrlbarProviderTabToSearch.jsm 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/urlbar/UrlbarProviderTabToSearch.jsm 2021-10-20 19:28:15.000000000 +0000 @@ -309,7 +309,9 @@ // is likely because your test showed a tab-to-search result but did not // start and end the engagement in which it was shown. Be sure to fire an // input event to start an engagement and blur the Urlbar to end it. - Cu.reportError(`Exception while recording TabToSearch telemetry: ${ex})`); + this.logger.error( + `Exception while recording TabToSearch telemetry: ${ex})` + ); } finally { // Even if there's an exception, we want to clear these Sets. Otherwise, // we might get into a state where we repeatedly run the same engines diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/urlbar/UrlbarProviderTopSites.jsm firefox-trunk-95.0~a1~hg20211020r596404/browser/components/urlbar/UrlbarProviderTopSites.jsm --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/urlbar/UrlbarProviderTopSites.jsm 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/urlbar/UrlbarProviderTopSites.jsm 2021-10-20 19:28:14.000000000 +0000 @@ -282,7 +282,7 @@ break; } default: - Cu.reportError(`Unknown Top Site type: ${site.type}`); + this.logger.error(`Unknown Top Site type: ${site.type}`); break; } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/components/urlbar/UrlbarResult.jsm firefox-trunk-95.0~a1~hg20211020r596404/browser/components/urlbar/UrlbarResult.jsm --- firefox-trunk-95.0~a1~hg20211017r596111/browser/components/urlbar/UrlbarResult.jsm 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/components/urlbar/UrlbarResult.jsm 2021-10-20 19:28:15.000000000 +0000 @@ -296,7 +296,7 @@ */ static addDynamicResultType(name, type = {}) { if (/[^a-z0-9_-]/i.test(name)) { - Cu.reportError(`Illegal dynamic type name: ${name}`); + this.logger.error(`Illegal dynamic type name: ${name}`); return; } this._dynamicResultTypesByName.set(name, type); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/extensions/webcompat/data/shims.js firefox-trunk-95.0~a1~hg20211020r596404/browser/extensions/webcompat/data/shims.js --- firefox-trunk-95.0~a1~hg20211017r596111/browser/extensions/webcompat/data/shims.js 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/extensions/webcompat/data/shims.js 2021-10-20 19:28:15.000000000 +0000 @@ -486,7 +486,7 @@ }, { id: "Kinja", - platform: "all", + platform: "desktop", name: "Kinja", bug: "1656171", contentScripts: [ diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/extensions/webcompat/manifest.json firefox-trunk-95.0~a1~hg20211020r596404/browser/extensions/webcompat/manifest.json --- firefox-trunk-95.0~a1~hg20211017r596111/browser/extensions/webcompat/manifest.json 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/extensions/webcompat/manifest.json 2021-10-20 19:28:15.000000000 +0000 @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "Web Compatibility Interventions", "description": "Urgent post-release fixes for web compatibility.", - "version": "27.2.0", + "version": "27.3.0", "applications": { "gecko": { "id": "webcompat@mozilla.org", diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/locales/en-US/browser/downloads.ftl firefox-trunk-95.0~a1~hg20211020r596404/browser/locales/en-US/browser/downloads.ftl --- firefox-trunk-95.0~a1~hg20211017r596111/browser/locales/en-US/browser/downloads.ftl 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/locales/en-US/browser/downloads.ftl 2021-10-20 19:28:15.000000000 +0000 @@ -29,14 +29,11 @@ downloads-cmd-cancel-panel = .aria-label = Cancel -# This message is only displayed on Windows and Linux devices -downloads-cmd-show-menuitem = - .label = Open Containing Folder - .accesskey = F - -# This message is only displayed on macOS devices -downloads-cmd-show-menuitem-mac = - .label = Show In Finder +downloads-cmd-show-menuitem-2 = + .label = { PLATFORM() -> + [macos] Show in Finder + *[other] Show in Folder + } .accesskey = F downloads-cmd-use-system-default = @@ -47,21 +44,21 @@ .label = Always Open In System Viewer .accesskey = w -downloads-cmd-show-button = +downloads-cmd-show-button-2 = .tooltiptext = { PLATFORM() -> - [macos] Show In Finder - *[other] Open Containing Folder + [macos] Show in Finder + *[other] Show in Folder } -downloads-cmd-show-panel = +downloads-cmd-show-panel-2 = .aria-label = { PLATFORM() -> - [macos] Show In Finder - *[other] Open Containing Folder + [macos] Show in Finder + *[other] Show in Folder } -downloads-cmd-show-description = +downloads-cmd-show-description-2 = .value = { PLATFORM() -> - [macos] Show In Finder - *[other] Open Containing Folder + [macos] Show in Finder + *[other] Show in Folder } downloads-cmd-show-downloads = diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/locales/en-US/chrome/browser/downloads/downloads.properties firefox-trunk-95.0~a1~hg20211020r596404/browser/locales/en-US/chrome/browser/downloads/downloads.properties --- firefox-trunk-95.0~a1~hg20211017r596111/browser/locales/en-US/chrome/browser/downloads/downloads.properties 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/locales/en-US/chrome/browser/downloads/downloads.properties 2021-10-20 19:28:15.000000000 +0000 @@ -82,17 +82,3 @@ # semi-colon list of plural forms. # See: http://developer.mozilla.org/en/Localization_and_Plurals otherDownloads3=%1$S file downloading;%1$S files downloading - -# LOCALIZATION NOTE (showLabel, showMacLabel): -# This is displayed when you hover a download item in the Library widget view. -# showMacLabel is only shown on Mac OSX. -showLabel=Open Containing Folder -showMacLabel=Open In Finder -# LOCALIZATION NOTE (openFileLabel): -# Displayed when hovering a complete download, indicates that it's possible to -# open the file using an app available in the system. -openFileLabel=Open File -# LOCALIZATION NOTE (retryLabel): -# Displayed when hovering a download which is able to be retried by users, -# indicates that it's possible to download this file again. -retryLabel=Retry Download diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/modules/PingCentre.jsm firefox-trunk-95.0~a1~hg20211020r596404/browser/modules/PingCentre.jsm --- firefox-trunk-95.0~a1~hg20211017r596111/browser/modules/PingCentre.jsm 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/modules/PingCentre.jsm 2021-10-20 19:28:15.000000000 +0000 @@ -21,7 +21,7 @@ ); ChromeUtils.defineModuleGetter( this, - "TelemetrySend", + "sendStandalonePing", "resource://gre/modules/TelemetrySend.jsm" ); @@ -109,7 +109,7 @@ // We route through this helper because it gets hooked in testing. static _sendStandalonePing(endpoint, payload) { - return TelemetrySend.sendStandalonePing(endpoint, payload); + return sendStandalonePing(endpoint, payload); } /** diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/browser/themes/linux/browser.css firefox-trunk-95.0~a1~hg20211020r596404/browser/themes/linux/browser.css --- firefox-trunk-95.0~a1~hg20211017r596111/browser/themes/linux/browser.css 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/browser/themes/linux/browser.css 2021-10-20 19:28:15.000000000 +0000 @@ -333,16 +333,8 @@ /* We draw to titlebar when Gkt+ CSD is available */ @media (-moz-gtk-csd-available) { - /* Some Gtk+ themes use non-rectangular toplevel windows. To fully support - * such themes we need to make toplevel window transparent. - * It may cause performance issues so let's put it under a preference - * and enable it for desktop environment which do that by default. - * See nsWindow::TopLevelWindowUseARGBVisual() for details. */ - @media (-moz-gtk-csd-transparent-background) { - :root[tabsintitlebar][sizemode="normal"]:not(:-moz-lwtheme) { - background-color: transparent; - appearance: none; - } + :root[tabsintitlebar][sizemode="normal"]:not(:-moz-lwtheme) { + background-color: transparent; } :root[tabsintitlebar] #titlebar { @@ -404,46 +396,40 @@ /* Render titlebar command buttons according to system config. * Use full scale icons here as the Gtk+ does. */ - @media (-moz-gtk-csd-minimize-button) { - .titlebar-min { - appearance: auto; - -moz-default-appearance: -moz-window-button-minimize; - } + .titlebar-min { + appearance: auto; + -moz-default-appearance: -moz-window-button-minimize; } - @media (-moz-gtk-csd-minimize-button: 0) { - .titlebar-min { - display: none; - } + .titlebar-max { + appearance: auto; + -moz-default-appearance: -moz-window-button-maximize; + } + .titlebar-restore { + appearance: auto; + -moz-default-appearance: -moz-window-button-restore; + } + .titlebar-close { + appearance: auto; + -moz-default-appearance: -moz-window-button-close; } - @media (-moz-gtk-csd-maximize-button) { - .titlebar-max { - appearance: auto; - -moz-default-appearance: -moz-window-button-maximize; - } - .titlebar-restore { - appearance: auto; - -moz-default-appearance: -moz-window-button-restore; + @media not (-moz-gtk-csd-minimize-button) { + .titlebar-min { + display: none; } } - @media (-moz-gtk-csd-maximize-button: 0) { + @media not (-moz-gtk-csd-maximize-button) { .titlebar-restore, .titlebar-max { display: none; } } - - @media (-moz-gtk-csd-close-button) { - .titlebar-close { - appearance: auto; - -moz-default-appearance: -moz-window-button-close; - } - } - @media (-moz-gtk-csd-close-button: 0) { + @media not (-moz-gtk-csd-close-button) { .titlebar-close { display: none; } } + @media (-moz-gtk-csd-reversed-placement) { .titlebar-buttonbox-container, .titlebar-close { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/build/mach_virtualenv_packages.txt firefox-trunk-95.0~a1~hg20211020r596404/build/mach_virtualenv_packages.txt --- firefox-trunk-95.0~a1~hg20211017r596111/build/mach_virtualenv_packages.txt 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/build/mach_virtualenv_packages.txt 2021-10-20 19:28:15.000000000 +0000 @@ -6,4 +6,4 @@ # We aren't (yet) able to pin packages in automation, so we have to # support down to the oldest locally-installed version (5.4.2). pypi-optional:psutil>=5.4.2,<=5.8.0:telemetry will be missing some data -pypi-optional:zstandard>=0.11.1,<=0.15.2:zstd archives will not be possible to extract +pypi-optional:zstandard>=0.11.1,<=0.16.0:zstd archives will not be possible to extract diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/build/moz.configure/nss.configure firefox-trunk-95.0~a1~hg20211020r596404/build/moz.configure/nss.configure --- firefox-trunk-95.0~a1~hg20211017r596111/build/moz.configure/nss.configure 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/build/moz.configure/nss.configure 2021-10-20 19:28:15.000000000 +0000 @@ -9,7 +9,7 @@ imply_option("--with-system-nspr", True, when="--with-system-nss") nss_pkg = pkg_check_modules( - "NSS", "nss >= 3.71", when="--with-system-nss", config=False + "NSS", "nss >= 3.72", when="--with-system-nss", config=False ) set_config("MOZ_SYSTEM_NSS", True, when="--with-system-nss") diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/build/win32/orderfile.txt firefox-trunk-95.0~a1~hg20211020r596404/build/win32/orderfile.txt --- firefox-trunk-95.0~a1~hg20211017r596111/build/win32/orderfile.txt 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/build/win32/orderfile.txt 2021-10-20 19:28:15.000000000 +0000 @@ -12026,7 +12026,6 @@ ?HasMultipleChildren@ContainerLayer@layers@mozilla@@QAE_NXZ ?GetEffectiveOpacity@Layer@layers@mozilla@@QAEMXZ ?ComputeEffectiveTransformsForChildren@ContainerLayer@layers@mozilla@@IAEXABV?$Matrix4x4Typed@UUnknownUnits@gfx@mozilla@@U123@M@gfx@3@@Z -??_GReadbackLayer@layers@mozilla@@UAEPAXI@Z ?JS_GetFunctionDisplayId@@YAPAVJSString@@PAVJSFunction@@@Z ??1ReadbackProcessor@layers@mozilla@@QAE@XZ ?GetClipExtents@gfxContext@@QBE?AU?$RectTyped@UUnknownUnits@gfx@mozilla@@N@gfx@mozilla@@W4ClipExtentsSpace@1@@Z @@ -17447,7 +17446,6 @@ ?Unlink@cycleCollection@DOMEventTargetHelper@mozilla@@UAGXPAX@Z ?nsCycleCollector_doDeferredDeletion@@YA_NXZ ?WriteToFile@nsINIParser_internal@@QAE?AW4nsresult@@PAVnsIFile@@@Z -?GetType@ReadbackLayer@layers@mozilla@@UBE?AW4LayerType@Layer@23@XZ ?functionBodyString@ScriptSource@js@@QAEPAVJSLinearString@@PAUJSContext@@@Z ??0Compressor@js@@QAE@PBEI@Z ?init@Compressor@js@@QAE_NXZ diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/build/win64/orderfile.txt firefox-trunk-95.0~a1~hg20211020r596404/build/win64/orderfile.txt --- firefox-trunk-95.0~a1~hg20211017r596111/build/win64/orderfile.txt 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/build/win64/orderfile.txt 2021-10-20 19:28:15.000000000 +0000 @@ -13261,7 +13261,6 @@ ?GetEffectiveOperator@layers@mozilla@@YA?AW4CompositionOp@gfx@2@PEAVLayer@12@@Z ?HasMultipleChildren@ContainerLayer@layers@mozilla@@QEAA_NXZ ?ComputeEffectiveTransformsForChildren@ContainerLayer@layers@mozilla@@IEAAXAEBV?$Matrix4x4Typed@UUnknownUnits@gfx@mozilla@@U123@M@gfx@3@@Z -??_GReadbackLayer@layers@mozilla@@UEAAPEAXI@Z ?GetClipExtents@gfxContext@@QEBA?AU?$RectTyped@UUnknownUnits@gfx@mozilla@@N@gfx@mozilla@@W4ClipExtentsSpace@1@@Z ?GetRect@DrawTarget@gfx@mozilla@@UEBA?AU?$IntRectTyped@UUnknownUnits@gfx@mozilla@@@23@XZ ?ToOutsideIntRect@layers@mozilla@@YA?AU?$IntRectTyped@UUnknownUnits@gfx@mozilla@@@gfx@2@AEBU?$RectTyped@UUnknownUnits@gfx@mozilla@@N@42@@Z diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/build/zstandard_requirements.in firefox-trunk-95.0~a1~hg20211020r596404/build/zstandard_requirements.in --- firefox-trunk-95.0~a1~hg20211017r596111/build/zstandard_requirements.in 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/build/zstandard_requirements.in 2021-10-20 19:28:15.000000000 +0000 @@ -1,2 +1,2 @@ -zstandard==0.15.2 +zstandard==0.16.0 diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/build/zstandard_requirements.txt firefox-trunk-95.0~a1~hg20211020r596404/build/zstandard_requirements.txt --- firefox-trunk-95.0~a1~hg20211017r596111/build/zstandard_requirements.txt 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/build/zstandard_requirements.txt 2021-10-20 19:28:15.000000000 +0000 @@ -1,56 +1,52 @@ # -# This file is autogenerated by pip-compile +# This file is autogenerated by pip-compile with python 3.9 # To update, run: # # pip-compile --generate-hashes --output-file=build/zstandard_requirements.txt build/zstandard_requirements.in # -zstandard==0.15.2 \ - --hash=sha256:1c5ef399f81204fbd9f0df3debf80389fd8aa9660fe1746d37c80b0d45f809e9 \ - --hash=sha256:1faefe33e3d6870a4dce637bcb41f7abb46a1872a595ecc7b034016081c37543 \ - --hash=sha256:1fb23b1754ce834a3a1a1e148cc2faad76eeadf9d889efe5e8199d3fb839d3c6 \ - --hash=sha256:22f127ff5da052ffba73af146d7d61db874f5edb468b36c9cb0b857316a21b3d \ - --hash=sha256:2353b61f249a5fc243aae3caa1207c80c7e6919a58b1f9992758fa496f61f839 \ - --hash=sha256:24cdcc6f297f7c978a40fb7706877ad33d8e28acc1786992a52199502d6da2a4 \ - --hash=sha256:31e35790434da54c106f05fa93ab4d0fab2798a6350e8a73928ec602e8505836 \ - --hash=sha256:3547ff4eee7175d944a865bbdf5529b0969c253e8a148c287f0668fe4eb9c935 \ - --hash=sha256:378ac053c0cfc74d115cbb6ee181540f3e793c7cca8ed8cd3893e338af9e942c \ - --hash=sha256:3e1cd2db25117c5b7c7e86a17cde6104a93719a9df7cb099d7498e4c1d13ee5c \ - --hash=sha256:3fe469a887f6142cc108e44c7f42c036e43620ebaf500747be2317c9f4615d4f \ - --hash=sha256:4800ab8ec94cbf1ed09c2b4686288750cab0642cb4d6fba2a56db66b923aeb92 \ - --hash=sha256:52de08355fd5cfb3ef4533891092bb96229d43c2069703d4aff04fdbedf9c92f \ - --hash=sha256:5752f44795b943c99be367fee5edf3122a1690b0d1ecd1bd5ec94c7fd2c39c94 \ - --hash=sha256:5d53f02aeb8fdd48b88bc80bece82542d084fb1a7ba03bf241fd53b63aee4f22 \ - --hash=sha256:69b7a5720b8dfab9005a43c7ddb2e3ccacbb9a2442908ae4ed49dd51ab19698a \ - --hash=sha256:6cc162b5b6e3c40b223163a9ea86cd332bd352ddadb5fd142fc0706e5e4eaaff \ - --hash=sha256:6f5d0330bc992b1e267a1b69fbdbb5ebe8c3a6af107d67e14c7a5b1ede2c5945 \ - --hash=sha256:6ffadd48e6fe85f27ca3ca10cfd3ef3d0f933bef7316870285ffeb58d791ca9c \ - --hash=sha256:72a011678c654df8323aa7b687e3147749034fdbe994d346f139ab9702b59cea \ - --hash=sha256:77d26452676f471223571efd73131fd4a626622c7960458aab2763e025836fc5 \ - --hash=sha256:7a88cc773ffe55992ff7259a8df5fb3570168d7138c69aadba40142d0e5ce39a \ - --hash=sha256:7b16bd74ae7bfbaca407a127e11058b287a4267caad13bd41305a5e630472549 \ - --hash=sha256:855d95ec78b6f0ff66e076d5461bf12d09d8e8f7e2b3fc9de7236d1464fd730e \ - --hash=sha256:8baf7991547441458325ca8fafeae79ef1501cb4354022724f3edd62279c5b2b \ - --hash=sha256:8fb77dd152054c6685639d855693579a92f276b38b8003be5942de31d241ebfb \ - --hash=sha256:92d49cc3b49372cfea2d42f43a2c16a98a32a6bc2f42abcde121132dbfc2f023 \ - --hash=sha256:94d0de65e37f5677165725f1fc7fb1616b9542d42a9832a9a0bdcba0ed68b63b \ - --hash=sha256:9867206093d7283d7de01bd2bf60389eb4d19b67306a0a763d1a8a4dbe2fb7c3 \ - --hash=sha256:9ee3c992b93e26c2ae827404a626138588e30bdabaaf7aa3aa25082a4e718790 \ - --hash=sha256:a4f8af277bb527fa3d56b216bda4da931b36b2d3fe416b6fc1744072b2c1dbd9 \ - --hash=sha256:ab9f19460dfa4c5dd25431b75bee28b5f018bf43476858d64b1aa1046196a2a0 \ - --hash=sha256:ac43c1821ba81e9344d818c5feed574a17f51fca27976ff7d022645c378fbbf5 \ - --hash=sha256:af5a011609206e390b44847da32463437505bf55fd8985e7a91c52d9da338d4b \ - --hash=sha256:b0975748bb6ec55b6d0f6665313c2cf7af6f536221dccd5879b967d76f6e7899 \ - --hash=sha256:b4963dad6cf28bfe0b61c3265d1c74a26a7605df3445bfcd3ba25de012330b2d \ - --hash=sha256:b7d3a484ace91ed827aa2ef3b44895e2ec106031012f14d28bd11a55f24fa734 \ - --hash=sha256:bd3c478a4a574f412efc58ba7e09ab4cd83484c545746a01601636e87e3dbf23 \ - --hash=sha256:c9e2dcb7f851f020232b991c226c5678dc07090256e929e45a89538d82f71d2e \ - --hash=sha256:d25c8eeb4720da41e7afbc404891e3a945b8bb6d5230e4c53d23ac4f4f9fc52c \ - --hash=sha256:dc8c03d0c5c10c200441ffb4cce46d869d9e5c4ef007f55856751dc288a2dffd \ - --hash=sha256:ec58e84d625553d191a23d5988a19c3ebfed519fff2a8b844223e3f074152163 \ - --hash=sha256:eda0719b29792f0fea04a853377cfff934660cb6cd72a0a0eeba7a1f0df4a16e \ - --hash=sha256:edde82ce3007a64e8434ccaf1b53271da4f255224d77b880b59e7d6d73df90c8 \ - --hash=sha256:f36722144bc0a5068934e51dca5a38a5b4daac1be84f4423244277e4baf24e7a \ - --hash=sha256:f8bb00ced04a8feff05989996db47906673ed45b11d86ad5ce892b5741e5f9dd \ - --hash=sha256:f98fc5750aac2d63d482909184aac72a979bfd123b112ec53fd365104ea15b1c \ - --hash=sha256:ff5b75f94101beaa373f1511319580a010f6e03458ee51b1a386d7de5331440a +zstandard==0.16.0 \ + --hash=sha256:066488e721ec882485a500c216302b443f2eaef39356f7c65130e76c671e3ce2 \ + --hash=sha256:08a728715858f1477239887ba3c692bc462b2c86e7a8e467dc5affa7bba9093f \ + --hash=sha256:11216b47c62e9fc71a25f4b42f525a81da268071bdb434bc1e642ffc38a24a02 \ + --hash=sha256:127c4c93f578d9b509732c74ed9b44b23e94041ba11b13827be0a7d2e3869b39 \ + --hash=sha256:12dddee2574b00c262270cfb46bd0c048e92208b95fdd39ad2a9eac1cef30498 \ + --hash=sha256:1bdda52224043e13ed20f847e3b308de1c9372d1563824fad776b1cf1f847ef0 \ + --hash=sha256:2e31680d1bcf85e7a58a45df7365af894402ae77a9868c751dc991dd13099a5f \ + --hash=sha256:42992e89b250fe6878c175119af529775d4be7967cd9de86990145d615d6a444 \ + --hash=sha256:453e42af96923582ddbf3acf843f55d2dc534a3f7b345003852dd522aa51eae6 \ + --hash=sha256:4d8a296dab7f8f5d53acc693a6785751f43ca39b51c8eabc672f978306fb40e6 \ + --hash=sha256:5251ac352d8350869c404a0ca94457da018b726f692f6456ec82bbf907fbc956 \ + --hash=sha256:57a6cfc34d906d514358769ed6d510b312be1cf033aafb5db44865a6717579bd \ + --hash=sha256:6ed51162e270b9b8097dcae6f2c239ada05ec112194633193ec3241498988924 \ + --hash=sha256:74cbea966462afed5a89eb99e4577538d10d425e05bf6240a75c086d59ccaf89 \ + --hash=sha256:87bea44ad24c15cd872263c0d5f912186a4be3db361eab3b25f1a61dcb5ca014 \ + --hash=sha256:8a745862ed525eee4e28bdbd58bf3ea952bf9da3c31bb4e4ce11ef15aea5c625 \ + --hash=sha256:8b760fc8118b1a0aa1d8f4e2012622e8f5f178d4b8cb94f8c6d2948b6a49a485 \ + --hash=sha256:8c8c0e813b67de1c9d7f2760768c4ae53f011c75ace18d5cff4fb40d2173763f \ + --hash=sha256:8d5fe983e23b05f0e924fe8d0dd3935f0c9fd3266e4c6ff8621c12c350da299d \ + --hash=sha256:8f5785c0b9b71d49d789240ae16a636728596631cf100f32b963a6f9857af5a4 \ + --hash=sha256:91efd5ea5fb3c347e7ebb6d5622bfa37d72594a2dec37c5dde70b691edb6cc03 \ + --hash=sha256:92e6c1a656390176d51125847f2f422f9d8ed468c24b63958f6ee50d9aa98c83 \ + --hash=sha256:9bcbfe1ec89789239f63daeea8778488cb5ba9034a374d7753815935f83dad65 \ + --hash=sha256:a92aa26789f17ca3b1f45cc7e728597165e2b166b99d1204bb397a672edee761 \ + --hash=sha256:a9ec6de2c058e611e9dfe88d9809a5676bc1d2a53543c1273a90a60e41b8f43c \ + --hash=sha256:ac5d97f9dece91a1162f651da79b735c5cde4d5863477785962aad648b592446 \ + --hash=sha256:ae19628886d994ac1f3d2fc7f9ed5bb551d81000f7b4e0c57a0e88301aea2766 \ + --hash=sha256:b2ea1937eff0ed5621876dc377933fe76624abfb2ab5b418995f43af6bac50de \ + --hash=sha256:b46220bef7bf9271a2a05512e86acbabc86cca08bebde8447bdbb4acb3179447 \ + --hash=sha256:b61586b0ff55c4137e512f1e9df4e4d7a6e1e9df782b4b87652df27737c90cc1 \ + --hash=sha256:be68fbac1e88f0dbe033a2d2e3aaaf9c8307730b905f3cd3c698ca4b904f0702 \ + --hash=sha256:c75557d53bb2d064521ff20cce9b8a51ee8301e031b1d6bcedb6458dda3bc85d \ + --hash=sha256:c7e6b6ad58ae6f77872da9376ef0ecbf8c1ae7a0c8fc29a2473abc90f79a9a1b \ + --hash=sha256:c8828f4e78774a6c0b8d21e59677f8f48d2e17fe2ef72793c94c10abc032c41c \ + --hash=sha256:cae9bfcb9148152f8bfb9163b4b779326ca39fe9889e45e0572c56d25d5021be \ + --hash=sha256:ce61492764d0442ca1e81d38d7bf7847d7df5003bce28089bab64c0519749351 \ + --hash=sha256:d40447f4a44b442fa6715779ff49a1e319729d829198279927d18bca0d7ac32d \ + --hash=sha256:d9946cfe54bf3365f14a5aa233eb2425de3b77eac6a4c7d03dda7dbb6acd3267 \ + --hash=sha256:dd5a2287893e52204e4ce9d0e1bcea6240661dbb412efb53d5446b881d3c10a2 \ + --hash=sha256:e9456492eb13249841e53221e742bef93f4868122bfc26bafa12a07677619732 \ + --hash=sha256:eaae2d3e8fdf8bfe269628385087e4b648beef85bb0c187644e7df4fb0fe9046 \ + --hash=sha256:eba125d3899f2003debf97019cd6f46f841a405df067da23d11443ad17952a40 \ + --hash=sha256:ef759c1dfe78aa5a01747d3465d2585de14e08fc2b0195ce3f31f45477fc5a72 \ + --hash=sha256:ffe1d24c5e11e98e4c5f96f846cdd19619d8c7e5e8e5082bed62d39baa30cecb # via -r build/zstandard_requirements.in diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/BUILDID firefox-trunk-95.0~a1~hg20211020r596404/BUILDID --- firefox-trunk-95.0~a1~hg20211017r596111/BUILDID 2021-10-17 14:49:53.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/BUILDID 2021-10-20 19:39:50.000000000 +0000 @@ -1 +1 @@ -20211017163927 \ No newline at end of file +20211020210652 \ No newline at end of file diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/caps/ContentPrincipalInfoHashKey.h firefox-trunk-95.0~a1~hg20211020r596404/caps/ContentPrincipalInfoHashKey.h --- firefox-trunk-95.0~a1~hg20211017r596111/caps/ContentPrincipalInfoHashKey.h 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/caps/ContentPrincipalInfoHashKey.h 2021-10-20 19:28:15.000000000 +0000 @@ -0,0 +1,56 @@ +/* -*- 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/. */ + +#ifndef CAPS_PRINCIPALHASHKEY_H_ +#define CAPS_PRINCIPALHASHKEY_H_ + +#include "mozilla/ipc/PBackgroundSharedTypes.h" +#include "PLDHashTable.h" +#include "nsHashKeys.h" +#include "nsUnicharUtils.h" + +namespace mozilla { + +class ContentPrincipalInfoHashKey : public PLDHashEntryHdr { + public: + using KeyType = const ipc::ContentPrincipalInfo&; + using KeyTypePointer = const ipc::ContentPrincipalInfo*; + + explicit ContentPrincipalInfoHashKey(KeyTypePointer aKey) + : mPrincipalInfo(*aKey) { + MOZ_COUNT_CTOR(ContentPrincipalInfoHashKey); + } + ContentPrincipalInfoHashKey(ContentPrincipalInfoHashKey&& aOther) noexcept + : mPrincipalInfo(aOther.mPrincipalInfo) { + MOZ_COUNT_CTOR(ContentPrincipalInfoHashKey); + } + + MOZ_COUNTED_DTOR(ContentPrincipalInfoHashKey) + + KeyType GetKey() const { return mPrincipalInfo; } + + bool KeyEquals(KeyTypePointer aKey) const { + // Mocks BasePrincipal::FastEquals() + return mPrincipalInfo.originNoSuffix() == aKey->originNoSuffix() && + mPrincipalInfo.attrs() == aKey->attrs(); + } + + static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; } + static PLDHashNumber HashKey(KeyTypePointer aKey) { + nsAutoCString suffix; + aKey->attrs().CreateSuffix(suffix); + return HashGeneric(HashString(aKey->originNoSuffix()), HashString(suffix)); + } + + enum { ALLOW_MEMMOVE = true }; + + protected: + const ipc::ContentPrincipalInfo mPrincipalInfo; +}; + +} // namespace mozilla + +#endif // CAPS_PRINCIPALHASHKEY_H_ diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/caps/moz.build firefox-trunk-95.0~a1~hg20211020r596404/caps/moz.build --- firefox-trunk-95.0~a1~hg20211017r596111/caps/moz.build 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/caps/moz.build 2021-10-20 19:28:15.000000000 +0000 @@ -31,6 +31,7 @@ EXPORTS.mozilla = [ "BasePrincipal.h", "ContentPrincipal.h", + "ContentPrincipalInfoHashKey.h", "ExpandedPrincipal.h", "NullPrincipal.h", "OriginAttributes.h", diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/caps/tests/mochitest/file_bug1367586-redirect.sjs firefox-trunk-95.0~a1~hg20211020r596404/caps/tests/mochitest/file_bug1367586-redirect.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/caps/tests/mochitest/file_bug1367586-redirect.sjs 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/caps/tests/mochitest/file_bug1367586-redirect.sjs 2021-10-20 19:28:15.000000000 +0000 @@ -1,5 +1,8 @@ function handleRequest(aRequest, aResponse) { - aResponse.setStatusLine(aRequest.httpVersion, 302, "Moved"); - aResponse.setHeader("Location", "http://mochi.test:8888/tests/caps/tests/mochitest/file_bug1367586-target.html"); - aResponse.write("To be redirected to target"); + aResponse.setStatusLine(aRequest.httpVersion, 302, "Moved"); + aResponse.setHeader( + "Location", + "http://mochi.test:8888/tests/caps/tests/mochitest/file_bug1367586-target.html" + ); + aResponse.write("To be redirected to target"); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/.clang-format-ignore firefox-trunk-95.0~a1~hg20211020r596404/.clang-format-ignore --- firefox-trunk-95.0~a1~hg20211017r596111/.clang-format-ignore 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/.clang-format-ignore 2021-10-20 19:28:14.000000000 +0000 @@ -4,7 +4,7 @@ config/gcc-stl-wrapper.template.h config/msvc-stl-wrapper.template.h # Generated code -js/src/builtin/intl/LanguageTagGenerated.cpp +intl/components/src/LocaleGenerated.cpp js/src/builtin/intl/TimeZoneDataGenerated.h # Don't want to reformat irregexp (third-party code) diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/debian/changelog firefox-trunk-95.0~a1~hg20211020r596404/debian/changelog --- firefox-trunk-95.0~a1~hg20211017r596111/debian/changelog 2021-10-17 14:56:18.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/debian/changelog 2021-10-20 20:34:09.000000000 +0000 @@ -1,9 +1,9 @@ -firefox-trunk (95.0~a1~hg20211017r596111-0ubuntu0.21.04.1~umd1) hirsute; urgency=emergency +firefox-trunk (95.0~a1~hg20211020r596404-0ubuntu0.21.04.1~umd1) hirsute; urgency=emergency * * New upstream snapshot - -- Rico Tzschichholz <ricotz@ubuntu.com> Sun, 17 Oct 2021 16:56:18 +0200 + -- Rico Tzschichholz <ricotz@ubuntu.com> Wed, 20 Oct 2021 22:34:09 +0200 firefox-trunk (94.0~a1~hg20211004r594164-0ubuntu1) impish; urgency=medium diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/debugger/panel.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/debugger/panel.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/debugger/panel.js 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/debugger/panel.js 2021-10-20 19:28:15.000000000 +0000 @@ -185,6 +185,19 @@ return this._actions.getMappedExpression(expression); } + /** + * Return the source-mapped variables for the current scope. + * @returns {{[String]: String} | null} A dictionary mapping original variable names to generated + * variable names if map scopes is enabled, otherwise null. + */ + getMappedVariables() { + if (!this._selectors.isMapScopesEnabled(this._getState())) { + return null; + } + const thread = this._selectors.getCurrentThread(this._getState()); + return this._selectors.getSelectedScopeMappings(this._getState(), thread); + } + isPaused() { const thread = this._selectors.getCurrentThread(this._getState()); return this._selectors.getIsPaused(this._getState(), thread); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/debugger/test/mochitest/browser.ini firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/debugger/test/mochitest/browser.ini --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/debugger/test/mochitest/browser.ini 2021-10-17 14:42:32.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/debugger/test/mochitest/browser.ini 2021-10-20 19:28:15.000000000 +0000 @@ -211,6 +211,7 @@ !serviceworker_e10s # parent intercept mode is needed bug 1588154. Bug 1613543, the test consistently timeouts on Linux coverage builds and WR debug builds. (os == 'linux' && (ccov || (webrender && !swgl && debug))) win10_2004 && ccov # Bug 1727943 + os == "linux" && bits == 64 && debug # Bug 1732486 [browser_dbg-worker-scopes.js] skip-if = os == 'linux' && debug # Bug 1456013 diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/framework/devtools.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/framework/devtools.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/framework/devtools.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/framework/devtools.js 2021-10-20 19:28:15.000000000 +0000 @@ -371,7 +371,7 @@ !isCoreTheme && theme.id == currTheme ) { - setTheme("auto"); + setTheme("light"); this.emit("theme-unregistered", theme); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/framework/test/browser.ini firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/framework/test/browser.ini --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/framework/test/browser.ini 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/framework/test/browser.ini 2021-10-20 19:28:15.000000000 +0000 @@ -174,6 +174,8 @@ [browser_two_tabs.js] [browser_webextension_descriptor.js] [browser_webextension_dropdown.js] +skip-if = + os == "linux" && bits == 64 && !debug # Bug 1714106 # We want these tests to run for mochitest-dt as well, so we include them here: [../../../../browser/base/content/test/static/browser_parsable_css.js] skip-if = debug || asan || (os == 'linux' && bits == 32) # no point in running on both opt and debug, and will likely intermittently timeout on debug diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/framework/test/browser_target_parents.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/framework/test/browser_target_parents.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/framework/test/browser_target_parents.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/framework/test/browser_target_parents.js 2021-10-20 19:28:15.000000000 +0000 @@ -20,8 +20,9 @@ const tabDescriptors = await mainRoot.listTabs(); await testGetTargetWithConcurrentCalls(tabDescriptors, tabTarget => { - // Tab Target is attached when it has a console front. - return !!tabTarget.getCachedFront("console"); + // We only call BrowsingContextTargetFront.attach and not TargetMixin.attachAndInitThread. + // So very few things are done. + return !!tabTarget.traits; }); await client.close(); @@ -40,8 +41,9 @@ // happens between the instantiation of ContentProcessTarget and its call to attach() from getTarget // function. await testGetTargetWithConcurrentCalls(processes, processTarget => { - // Content Process Target is attached when it has a console front. - return !!processTarget.getCachedFront("console"); + // We only call ContentProcessTargetFront.attach and not TargetMixin.attachAndInitThread. + // So nothing is done for content process targets. + return true; }); await client.close(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/framework/test/browser_toolbox_options_disable_cache.css.sjs firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/framework/test/browser_toolbox_options_disable_cache.css.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/framework/test/browser_toolbox_options_disable_cache.css.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/framework/test/browser_toolbox_options_disable_cache.css.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,9 +1,9 @@ - /* Any copyright is dedicated to the Public Domain. +/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ function handleRequest(request, response) { // This returns always new and different CSS content. - const page = `body::before { content: "${ Date.now() }"; }`; + const page = `body::before { content: "${Date.now()}"; }`; response.setHeader("Content-Type", "text/css; charset=utf-8", false); response.setHeader("Content-Length", page.length + "", false); response.write(page); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/framework/test/browser_toolbox_options_disable_cache.sjs firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/framework/test/browser_toolbox_options_disable_cache.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/framework/test/browser_toolbox_options_disable_cache.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/framework/test/browser_toolbox_options_disable_cache.sjs 2021-10-20 19:28:15.000000000 +0000 @@ -1,20 +1,22 @@ - /* Any copyright is dedicated to the Public Domain. +/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ function handleRequest(request, response) { - let Etag = '"4d881ab-b03-435f0a0f9ef00"'; - let IfNoneMatch = request.hasHeader("If-None-Match") - ? request.getHeader("If-None-Match") - : ""; - - let guid = 'xxxxxxxx-xxxx-xxxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { - let r = Math.random() * 16 | 0; - let v = c === "x" ? r : (r & 0x3 | 0x8); + const Etag = '"4d881ab-b03-435f0a0f9ef00"'; + const IfNoneMatch = request.hasHeader("If-None-Match") + ? request.getHeader("If-None-Match") + : ""; + + const guid = "xxxxxxxx-xxxx-xxxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function( + c + ) { + const r = (Math.random() * 16) | 0; + const v = c === "x" ? r : (r & 0x3) | 0x8; return v.toString(16); }); - let page = "<!DOCTYPE html><html><body><h1>" + guid + "</h1></body></html>"; + const page = "<!DOCTYPE html><html><body><h1>" + guid + "</h1></body></html>"; response.setHeader("Etag", Etag, false); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/framework/test/browser_toolbox_theme_registration.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/framework/test/browser_toolbox_theme_registration.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/framework/test/browser_toolbox_theme_registration.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/framework/test/browser_toolbox_theme_registration.js 2021-10-20 19:28:15.000000000 +0000 @@ -135,12 +135,12 @@ is( gDevTools.getTheme(), - gDevTools.getAutoTheme(), + LIGHT_THEME_NAME, "getTheme returns the expected theme" ); is( eventsRecorded.pop(), - gDevTools.getAutoTheme(), + LIGHT_THEME_NAME, "theme-changed fired with the expected theme" ); ok( @@ -151,10 +151,12 @@ const doc = panelWin.frameElement.contentDocument; const themeBox = doc.getElementById("devtools-theme-box"); - // The default theme must be selected now. - ok( - themeBox.querySelector(`#devtools-theme-box [value=auto]`).checked, - `auto theme must be selected` + // The default light theme must be selected now. + is( + themeBox.querySelector(`#devtools-theme-box [value=${LIGHT_THEME_NAME}]`) + .checked, + true, + `${LIGHT_THEME_NAME} theme must be selected` ); gDevTools.off("theme-changed", onThemeChanged); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/framework/test/metrics/browser_metrics.ini firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/framework/test/metrics/browser_metrics.ini --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/framework/test/metrics/browser_metrics.ini 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/framework/test/metrics/browser_metrics.ini 2021-10-20 19:28:15.000000000 +0000 @@ -11,4 +11,4 @@ # the number of loaded modules. # This ini file is for all the _other_ tests, where such setup isn't relevant. [browser_metrics_pool.js] -skip-if = debug || asan +skip-if = false | true diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/framework/test/sjs_code_bundle_reload_map.sjs firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/framework/test/sjs_code_bundle_reload_map.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/framework/test/sjs_code_bundle_reload_map.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/framework/test/sjs_code_bundle_reload_map.sjs 2021-10-20 19:28:15.000000000 +0000 @@ -13,12 +13,15 @@ response.setHeader("Access-Control-Allow-Origin", "*", false); // Redirect to a different file each time. - let counter = 1 + (+getState("counter") % 2); + const counter = 1 + (+getState("counter") % 2); - let index = request.path.lastIndexOf("/"); - let newPath = request.path.substr(0, index + 1) + - "code_bundle_reload_" + counter + ".js.map"; - let newUrl = request.scheme + "://" + request.host + newPath; + const index = request.path.lastIndexOf("/"); + const newPath = + request.path.substr(0, index + 1) + + "code_bundle_reload_" + + counter + + ".js.map"; + const newUrl = request.scheme + "://" + request.host + newPath; response.setStatusLine(request.httpVersion, 302, "Found"); response.setHeader("Location", newUrl); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/framework/test/sjs_code_reload.sjs firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/framework/test/sjs_code_reload.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/framework/test/sjs_code_reload.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/framework/test/sjs_code_reload.sjs 2021-10-20 19:28:15.000000000 +0000 @@ -13,12 +13,12 @@ response.setHeader("Access-Control-Allow-Origin", "*", false); // Redirect to a different file each time. - let counter = 1 + (+getState("counter") % 2); + const counter = 1 + (+getState("counter") % 2); - let index = request.path.lastIndexOf("/"); - let newPath = request.path.substr(0, index + 1) + - "code_bundle_reload_" + counter + ".js"; - let newUrl = request.scheme + "://" + request.host + newPath; + const index = request.path.lastIndexOf("/"); + const newPath = + request.path.substr(0, index + 1) + "code_bundle_reload_" + counter + ".js"; + const newUrl = request.scheme + "://" + request.host + newPath; response.setStatusLine(request.httpVersion, 302, "Found"); response.setHeader("Location", newUrl); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/framework/toolbox.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/framework/toolbox.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/framework/toolbox.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/framework/toolbox.js 2021-10-20 19:28:15.000000000 +0000 @@ -691,7 +691,8 @@ // Attach to a new top-level target. // For now, register these event listeners only on the top level target targetFront.on("frame-update", this._updateFrames); - targetFront.on("inspect-object", this._onInspectObject); + const consoleFront = await targetFront.getFront("console"); + consoleFront.on("inspectObject", this._onInspectObject); } // Walker listeners allow to monitor DOM Mutation breakpoint updates. @@ -718,8 +719,9 @@ _onTargetDestroyed({ targetFront }) { if (targetFront.isTopLevel) { - this.target.off("inspect-object", this._onInspectObject); - this.target.off("frame-update", this._updateFrames); + const consoleFront = targetFront.getCachedFront("console"); + consoleFront.off("inspectObject", this._onInspectObject); + targetFront.off("frame-update", this._updateFrames); } else if (this.selection) { this.selection.onTargetDestroyed(targetFront); } @@ -3611,10 +3613,6 @@ } }, - _onInspectObject: function(packet) { - this.inspectObjectActor(packet.objectActor, packet.inspectFromAnnotation); - }, - _onToolSelected: function() { this._refreshHostTitle(); @@ -3626,6 +3624,13 @@ this.component.setToolboxButtons(this.toolbarButtons); }, + /** + * Listener for "inspectObject" event on console top level target actor. + */ + _onInspectObject(packet) { + this.inspectObjectActor(packet.objectActor, packet.inspectFromAnnotation); + }, + inspectObjectActor: async function(objectActor, inspectFromAnnotation) { const objectGrip = objectActor?.getGrip ? objectActor.getGrip() diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/fronts/descriptors/worker.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/fronts/descriptors/worker.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/fronts/descriptors/worker.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/fronts/descriptors/worker.js 2021-10-20 19:28:15.000000000 +0000 @@ -95,8 +95,7 @@ const workerTargetForm = await super.getTarget(); - // Set the console actor ID on the form to expose it to Target.attachConsole - // Set the ThreadActor on the target form so it is accessible by getFront + // Set the console and thread actor IDs on the form so it is accessible by TargetMixin.getFront this.targetForm.consoleActor = workerTargetForm.consoleActor; this.targetForm.threadActor = workerTargetForm.threadActor; @@ -104,7 +103,6 @@ return this; } - await this.attachConsole(); return this; })(); return this._attach; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/fronts/targets/content-process.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/fronts/targets/content-process.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/fronts/targets/content-process.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/fronts/targets/content-process.js 2021-10-20 19:28:16.000000000 +0000 @@ -44,11 +44,6 @@ } attach() { - // All target actors have a console actor to attach. - // All but xpcshell test actors... which is using a ContentProcessTargetActor - if (this.targetForm.consoleActor) { - return this.attachConsole(); - } return Promise.resolve(); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/fronts/targets/target-mixin.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/fronts/targets/target-mixin.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/fronts/targets/target-mixin.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/fronts/targets/target-mixin.js 2021-10-20 19:28:15.000000000 +0000 @@ -468,25 +468,6 @@ } } - // Attach the console actor - async attachConsole() { - const consoleFront = await this.getFront("console"); - - if (this.isDestroyedOrBeingDestroyed()) { - return; - } - - // Calling startListeners will populate the traits as it's the first request we - // make to the front. - await consoleFront.startListeners([]); - - this._onInspectObject = packet => this.emit("inspect-object", packet); - this.removeOnInspectObjectListener = consoleFront.on( - "inspectObject", - this._onInspectObject - ); - } - /** * This method attaches the target and then attaches its related thread, sending it * the options it needs (e.g. breakpoints, pause on exception setting, …). @@ -649,12 +630,6 @@ } this.fronts.clear(); - // Remove listeners set in attachConsole - if (this.removeOnInspectObjectListener) { - this.removeOnInspectObjectListener(); - this.removeOnInspectObjectListener = null; - } - this.threadFront = null; this._offResourceEvent = null; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/fronts/targets/window-global.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/fronts/targets/window-global.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/fronts/targets/window-global.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/fronts/targets/window-global.js 2021-10-20 19:28:15.000000000 +0000 @@ -125,12 +125,6 @@ // @backward-compat { version 93 } Remove this. All the traits are on form and can be accessed // using getTraits. this.traits = response.traits || {}; - - // xpcshell tests from devtools/server/tests/xpcshell/ are implementing - // fake WindowGlobalTargetActor which do not expose any console actor. - if (this.targetForm.consoleActor) { - await this.attachConsole(); - } })(); return this._attach; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/fronts/webconsole.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/fronts/webconsole.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/fronts/webconsole.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/fronts/webconsole.js 2021-10-20 19:28:15.000000000 +0000 @@ -24,7 +24,6 @@ constructor(client, targetFront, parentFront) { super(client, targetFront, parentFront); this._client = client; - this.traits = {}; this.events = []; // Attribute name from which to retrieve the actorID out of the target actor's form @@ -306,7 +305,6 @@ async startListeners(listeners) { const response = await super.startListeners(listeners); this.hasNativeConsoleAPI = response.nativeConsoleAPI; - this.traits = response.traits; return response; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/jsonview/test/chunked_json.sjs firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/jsonview/test/chunked_json.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/jsonview/test/chunked_json.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/jsonview/test/chunked_json.sjs 2021-10-20 19:28:15.000000000 +0000 @@ -7,12 +7,14 @@ } function getResponse() { let response; - getObjectState(key, v => { response = v }); + getObjectState(key, v => { + response = v; + }); return response; } function handleRequest(request, response) { - let {queryString} = request; + const { queryString } = request; if (!queryString) { response.processAsync(); setResponse(response); @@ -21,7 +23,7 @@ response.write(" "); return; } - let [command, value] = queryString.split('='); + const [command, value] = queryString.split("="); switch (command) { case "write": getResponse().write(value); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/src/connector/index.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/src/connector/index.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/src/connector/index.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/src/connector/index.js 2021-10-20 19:28:16.000000000 +0000 @@ -432,9 +432,6 @@ if (this.hasResourceCommandSupport && this.networkFront) { return this.networkFront.getBlockedUrls(); } - if (!this.webConsoleFront.traits.blockedUrls) { - return []; - } return this.webConsoleFront.getBlockedUrls(); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/src/har/test/browser_net_har_copy_all_as_har.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/src/har/test/browser_net_har_copy_all_as_har.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/src/har/test/browser_net_har_copy_all_as_har.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/src/har/test/browser_net_har_copy_all_as_har.js 2021-10-20 19:28:15.000000000 +0000 @@ -7,6 +7,11 @@ * Basic tests for exporting Network panel content into HAR format. */ add_task(async function() { + // Using https-first for this test is blocked on Bug 1733420. + // Otherwise in https we receive the wrong headers length (12 instead of 9) + // and the wrong statusText (Connected instead of OK). + await pushPref("dom.security.https_first", false); + // Disable tcp fast open, because it is setting a response header indicator // (bug 1352274). TCP Fast Open is not present on all platforms therefore the // number of response headers will vary depending on the platform. diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/src/har/test/browser_net_har_import.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/src/har/test/browser_net_har_import.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/src/har/test/browser_net_har_import.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/src/har/test/browser_net_har_import.js 2021-10-20 19:28:15.000000000 +0000 @@ -7,6 +7,7 @@ * Tests for importing HAR data. */ add_task(async () => { + // Using https-first for this test is blocked on Bug 1733420. await pushPref("dom.security.https_first", false); const { tab, monitor } = await initNetMonitor( diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/src/har/test/sjs_cache-test-server.sjs firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/src/har/test/sjs_cache-test-server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/src/har/test/sjs_cache-test-server.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/src/har/test/sjs_cache-test-server.sjs 2021-10-20 19:28:15.000000000 +0000 @@ -3,7 +3,10 @@ function handleRequest(request, response) { response.setStatusLine(request.httpVersion, 304, "Not Modified"); - response.setHeader("Cache-Control", "no-transform,public,max-age=300,s-maxage=900"); + response.setHeader( + "Cache-Control", + "no-transform,public,max-age=300,s-maxage=900" + ); response.setHeader("Expires", "Thu, 01 Dec 2100 20:00:00 GMT"); response.setHeader("Content-Type", "text/plain; charset=utf-8", false); response.write("Hello from cache!"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_block.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_block.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_block.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_block.js 2021-10-20 19:28:16.000000000 +0000 @@ -8,7 +8,7 @@ */ add_task(async function() { - const { monitor, tab } = await initNetMonitor(SIMPLE_URL, { + const { monitor, tab } = await initNetMonitor(HTTPS_SIMPLE_URL, { requestCount: 1, }); info("Starting test... "); @@ -22,7 +22,7 @@ // Reload to have one request in the list let waitForEvents = waitForNetworkEvents(monitor, 1); - await navigateTo(SIMPLE_URL); + await navigateTo(HTTPS_SIMPLE_URL); await waitForEvents; // Capture normal request diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_block-pattern.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_block-pattern.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_block-pattern.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_block-pattern.js 2021-10-20 19:28:15.000000000 +0000 @@ -12,7 +12,7 @@ add_task(async function() { await pushPref("devtools.netmonitor.features.requestBlocking", true); - const { tab, monitor } = await initNetMonitor(CUSTOM_GET_URL, { + const { tab, monitor } = await initNetMonitor(HTTPS_CUSTOM_GET_URL, { requestCount: 1, }); info("Starting test... "); @@ -50,10 +50,10 @@ store.dispatch(Actions.toggleRequestBlockingPanel()); // Execute two XHRs (the same URL) and wait till they're finished - const TEST_URL_1 = SEARCH_SJS + "?value=test1"; - const TEST_URL_2 = SEARCH_SJS + "?value=test2"; - const TEST_URL_3 = SEARCH_SJS + "test/something/test3"; - const TEST_URL_4 = SEARCH_SJS + "test/something/test4"; + const TEST_URL_1 = HTTPS_SEARCH_SJS + "?value=test1"; + const TEST_URL_2 = HTTPS_SEARCH_SJS + "?value=test2"; + const TEST_URL_3 = HTTPS_SEARCH_SJS + "test/something/test3"; + const TEST_URL_4 = HTTPS_SEARCH_SJS + "test/something/test4"; let wait = waitForNetworkEvents(monitor, 4); await ContentTask.spawn(tab.linkedBrowser, TEST_URL_1, async function(url) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_cached-status.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_cached-status.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_cached-status.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_cached-status.js 2021-10-20 19:28:16.000000000 +0000 @@ -8,6 +8,9 @@ */ add_task(async function() { + // Using https-first for this test is blocked on Bug 1733420. + await pushPref("dom.security.https_first", false); + // Disable rcwn to make cache behavior deterministic. await pushPref("network.http.rcwn.enabled", false); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_cause_redirect.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_cause_redirect.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_cause_redirect.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_cause_redirect.js 2021-10-20 19:28:16.000000000 +0000 @@ -9,6 +9,9 @@ */ add_task(async function() { + // This test explicitly checks http->https redirects and should not force https. + await pushPref("dom.security.https_first", false); + const EXPECTED_REQUESTS = [ // Request to HTTP URL, redirects to HTTPS { status: 302 }, diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_cause_source_map.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_cause_source_map.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_cause_source_map.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_cause_source_map.js 2021-10-20 19:28:16.000000000 +0000 @@ -8,7 +8,7 @@ */ const CAUSE_FILE_NAME = "html_maps-test-page.html"; -const CAUSE_URL = EXAMPLE_URL + CAUSE_FILE_NAME; +const CAUSE_URL = HTTPS_EXAMPLE_URL + CAUSE_FILE_NAME; const N_EXPECTED_REQUESTS = 4; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_charts-01.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_charts-01.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_charts-01.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_charts-01.js 2021-10-20 19:28:15.000000000 +0000 @@ -8,7 +8,9 @@ */ add_task(async function() { - const { monitor } = await initNetMonitor(SIMPLE_URL, { requestCount: 1 }); + const { monitor } = await initNetMonitor(HTTPS_SIMPLE_URL, { + requestCount: 1, + }); info("Starting test... "); @@ -16,7 +18,7 @@ const { Chart } = windowRequire("devtools/client/shared/widgets/Chart"); const wait = waitForNetworkEvents(monitor, 1); - await navigateTo(SIMPLE_URL); + await navigateTo(HTTPS_SIMPLE_URL); await wait; const pie = Chart.Pie(document, { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_charts-02.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_charts-02.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_charts-02.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_charts-02.js 2021-10-20 19:28:15.000000000 +0000 @@ -11,14 +11,16 @@ add_task(async function() { const { L10N } = require("devtools/client/netmonitor/src/utils/l10n"); - const { monitor } = await initNetMonitor(SIMPLE_URL, { requestCount: 1 }); + const { monitor } = await initNetMonitor(HTTPS_SIMPLE_URL, { + requestCount: 1, + }); info("Starting test... "); const { document, windowRequire } = monitor.panelWin; const { Chart } = windowRequire("devtools/client/shared/widgets/Chart"); const wait = waitForNetworkEvents(monitor, 1); - await navigateTo(SIMPLE_URL); + await navigateTo(HTTPS_SIMPLE_URL); await wait; const pie = Chart.Pie(document, { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_charts-03.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_charts-03.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_charts-03.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_charts-03.js 2021-10-20 19:28:15.000000000 +0000 @@ -10,14 +10,16 @@ add_task(async function() { const { L10N } = require("devtools/client/netmonitor/src/utils/l10n"); - const { monitor } = await initNetMonitor(SIMPLE_URL, { requestCount: 1 }); + const { monitor } = await initNetMonitor(HTTPS_SIMPLE_URL, { + requestCount: 1, + }); info("Starting test... "); const { document, windowRequire } = monitor.panelWin; const { Chart } = windowRequire("devtools/client/shared/widgets/Chart"); const wait = waitForNetworkEvents(monitor, 1); - await navigateTo(SIMPLE_URL); + await navigateTo(HTTPS_SIMPLE_URL); await wait; const table = Chart.Table(document, { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_charts-04.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_charts-04.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_charts-04.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_charts-04.js 2021-10-20 19:28:16.000000000 +0000 @@ -11,14 +11,16 @@ add_task(async function() { const { L10N } = require("devtools/client/netmonitor/src/utils/l10n"); - const { monitor } = await initNetMonitor(SIMPLE_URL, { requestCount: 1 }); + const { monitor } = await initNetMonitor(HTTPS_SIMPLE_URL, { + requestCount: 1, + }); info("Starting test... "); const { document, windowRequire } = monitor.panelWin; const { Chart } = windowRequire("devtools/client/shared/widgets/Chart"); const wait = waitForNetworkEvents(monitor, 1); - await navigateTo(SIMPLE_URL); + await navigateTo(HTTPS_SIMPLE_URL); await wait; const table = Chart.Table(document, { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_charts-05.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_charts-05.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_charts-05.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_charts-05.js 2021-10-20 19:28:15.000000000 +0000 @@ -10,14 +10,16 @@ add_task(async function() { const { L10N } = require("devtools/client/netmonitor/src/utils/l10n"); - const { monitor } = await initNetMonitor(SIMPLE_URL, { requestCount: 1 }); + const { monitor } = await initNetMonitor(HTTPS_SIMPLE_URL, { + requestCount: 1, + }); info("Starting test... "); const { document, windowRequire } = monitor.panelWin; const { Chart } = windowRequire("devtools/client/shared/widgets/Chart"); const wait = waitForNetworkEvents(monitor, 1); - await navigateTo(SIMPLE_URL); + await navigateTo(HTTPS_SIMPLE_URL); await wait; const chart = Chart.PieTable(document, { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_charts-06.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_charts-06.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_charts-06.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_charts-06.js 2021-10-20 19:28:16.000000000 +0000 @@ -10,14 +10,16 @@ add_task(async function() { const { L10N } = require("devtools/client/netmonitor/src/utils/l10n"); - const { monitor } = await initNetMonitor(SIMPLE_URL, { requestCount: 1 }); + const { monitor } = await initNetMonitor(HTTPS_SIMPLE_URL, { + requestCount: 1, + }); info("Starting test... "); const { document, windowRequire } = monitor.panelWin; const { Chart } = windowRequire("devtools/client/shared/widgets/Chart"); const wait = waitForNetworkEvents(monitor, 1); - await navigateTo(SIMPLE_URL); + await navigateTo(HTTPS_SIMPLE_URL); await wait; const pie = Chart.Pie(document, { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_charts-07.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_charts-07.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_charts-07.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_charts-07.js 2021-10-20 19:28:16.000000000 +0000 @@ -10,14 +10,16 @@ add_task(async function() { const { L10N } = require("devtools/client/netmonitor/src/utils/l10n"); - const { monitor } = await initNetMonitor(SIMPLE_URL, { requestCount: 1 }); + const { monitor } = await initNetMonitor(HTTPS_SIMPLE_URL, { + requestCount: 1, + }); info("Starting test... "); const { document, windowRequire } = monitor.panelWin; const { Chart } = windowRequire("devtools/client/shared/widgets/Chart"); const wait = waitForNetworkEvents(monitor, 1); - await navigateTo(SIMPLE_URL); + await navigateTo(HTTPS_SIMPLE_URL); await wait; const table = Chart.Table(document, { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_column_slow-request-indicator.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_column_slow-request-indicator.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_column_slow-request-indicator.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_column_slow-request-indicator.js 2021-10-20 19:28:15.000000000 +0000 @@ -8,6 +8,11 @@ */ add_task(async function() { + // Using https-first for this test is blocked on Bug 1733420. + // We always get a waiting time of 0ms for requests coming from httpd.js in + // https. + await pushPref("dom.security.https_first", false); + // The script sjs_slow-script-server.sjs takes about 2s which is // definately above the slow threshold set here. const SLOW_THRESHOLD = 450; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_columns_showhide.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_columns_showhide.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_columns_showhide.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_columns_showhide.js 2021-10-20 19:28:16.000000000 +0000 @@ -7,7 +7,9 @@ * Test showing/hiding columns. */ add_task(async function() { - const { monitor } = await initNetMonitor(SIMPLE_URL, { requestCount: 1 }); + const { monitor } = await initNetMonitor(HTTPS_SIMPLE_URL, { + requestCount: 1, + }); info("Starting test... "); const { document, store, connector, windowRequire } = monitor.panelWin; @@ -17,7 +19,7 @@ ); const wait = waitForNetworkEvents(monitor, 1); - await navigateTo(SIMPLE_URL); + await navigateTo(HTTPS_SIMPLE_URL); await wait; const item = getSortedRequests(store.getState())[0]; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_columns_time.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_columns_time.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_columns_time.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_columns_time.js 2021-10-20 19:28:16.000000000 +0000 @@ -8,6 +8,11 @@ * header is visible only if there are requests in the list. */ add_task(async function() { + // Using https-first for this test is blocked on Bug 1733420. + // We expect a > 0 latency, but we always get -1 for HTTPS requests using + // httpd.js. + await pushPref("dom.security.https_first", false); + const { monitor } = await initNetMonitor(SIMPLE_URL, { requestCount: 1, }); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_content-type.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_content-type.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_content-type.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_content-type.js 2021-10-20 19:28:15.000000000 +0000 @@ -8,6 +8,9 @@ */ add_task(async function() { + // Using https-first for this test is blocked on Bug 1733420. + await pushPref("dom.security.https_first", false); + const { tab, monitor } = await initNetMonitor( CONTENT_TYPE_WITHOUT_CACHE_URL, { requestCount: 1 } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_copy_as_curl.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_copy_as_curl.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_copy_as_curl.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_copy_as_curl.js 2021-10-20 19:28:16.000000000 +0000 @@ -10,7 +10,9 @@ const POST_PAYLOAD = "Plaintext value as a payload"; add_task(async function() { - const { tab, monitor } = await initNetMonitor(CURL_URL, { requestCount: 1 }); + const { tab, monitor } = await initNetMonitor(HTTPS_CURL_URL, { + requestCount: 1, + }); info("Starting test... "); // Different quote chars are used for Windows and POSIX @@ -53,8 +55,8 @@ } // Construct the expected command - const SIMPLE_BASE = ["curl " + quote(SIMPLE_SJS)]; - const SLOW_BASE = ["curl " + quote(SLOW_SJS)]; + const SIMPLE_BASE = ["curl " + quote(HTTPS_SIMPLE_SJS)]; + const SLOW_BASE = ["curl " + quote(HTTPS_SLOW_SJS)]; const BASE_RESULT = [ "--compressed", header("User-Agent: " + navigator.userAgent), @@ -63,10 +65,13 @@ header("X-Custom-Header-1: Custom value"), header("X-Custom-Header-2: 8.8.8.8"), header("X-Custom-Header-3: Mon, 3 Mar 2014 11:11:11 GMT"), - header("Referer: " + CURL_URL), + header("Referer: " + HTTPS_CURL_URL), header("Connection: keep-alive"), header("Pragma: no-cache"), header("Cache-Control: no-cache"), + header("Sec-Fetch-Dest: empty"), + header("Sec-Fetch-Mode: cors"), + header("Sec-Fetch-Site: same-origin"), ]; const COOKIE_PARTIAL_RESULT = [header("Cookie: bob=true; tom=cool")]; @@ -77,7 +82,7 @@ "--data-raw " + quote(POST_PAYLOAD), header("Content-Type: text/plain;charset=UTF-8"), ]; - const ORIGIN_RESULT = [header("Origin: http://example.com")]; + const ORIGIN_RESULT = [header("Origin: https://example.com")]; const HEAD_PARTIAL_RESULT = ["-I"]; @@ -123,7 +128,9 @@ // Unfinished request (bug#1378464, bug#1420513) const waitSlow = waitForNetworkEvents(monitor, 0); - await SpecialPowers.spawn(tab.linkedBrowser, [SLOW_SJS], async function(url) { + await SpecialPowers.spawn(tab.linkedBrowser, [HTTPS_SLOW_SJS], async function( + url + ) { content.wrappedJSObject.performRequest(url, "GET", null); }); await waitSlow; @@ -164,7 +171,7 @@ tab.linkedBrowser, [ { - url: SIMPLE_SJS, + url: HTTPS_SIMPLE_SJS, method_: method, payload_: payload, }, diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_copy_as_fetch.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_copy_as_fetch.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_copy_as_fetch.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_copy_as_fetch.js 2021-10-20 19:28:15.000000000 +0000 @@ -8,12 +8,14 @@ */ add_task(async function() { - const { tab, monitor } = await initNetMonitor(CURL_URL, { requestCount: 1 }); + const { tab, monitor } = await initNetMonitor(HTTPS_CURL_URL, { + requestCount: 1, + }); info("Starting test... "); // GET request, no cookies (first request) await performRequest("GET"); - await testClipboardContent(`await fetch("http://example.com/browser/devtools/client/netmonitor/test/sjs_simple-test-server.sjs", { + await testClipboardContent(`await fetch("https://example.com/browser/devtools/client/netmonitor/test/sjs_simple-test-server.sjs", { "credentials": "omit", "headers": { "User-Agent": "${navigator.userAgent}", @@ -22,10 +24,13 @@ "X-Custom-Header-1": "Custom value", "X-Custom-Header-2": "8.8.8.8", "X-Custom-Header-3": "Mon, 3 Mar 2014 11:11:11 GMT", + "Sec-Fetch-Dest": "empty", + "Sec-Fetch-Mode": "cors", + "Sec-Fetch-Site": "same-origin", "Pragma": "no-cache", "Cache-Control": "no-cache" }, - "referrer": "http://example.com/browser/devtools/client/netmonitor/test/html_copy-as-curl.html", + "referrer": "https://example.com/browser/devtools/client/netmonitor/test/html_copy-as-curl.html", "method": "GET", "mode": "cors" });`); @@ -38,7 +43,7 @@ tab.linkedBrowser, [ { - url: SIMPLE_SJS, + url: HTTPS_SIMPLE_SJS, method_: method, payload_: payload, }, diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_copy_headers.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_copy_headers.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_copy_headers.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_copy_headers.js 2021-10-20 19:28:16.000000000 +0000 @@ -8,6 +8,9 @@ */ add_task(async function() { + // Using https-first for this test is blocked on Bug 1733420. + await pushPref("dom.security.https_first", false); + const { monitor } = await initNetMonitor(SIMPLE_URL, { requestCount: 1, }); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_copy_svg_image_as_data_uri.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_copy_svg_image_as_data_uri.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_copy_svg_image_as_data_uri.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_copy_svg_image_as_data_uri.js 2021-10-20 19:28:15.000000000 +0000 @@ -7,10 +7,12 @@ * Tests if copying an image as data uri works. */ -const SVG_URL = EXAMPLE_URL + "dropmarker.svg"; +const SVG_URL = HTTPS_EXAMPLE_URL + "dropmarker.svg"; add_task(async function() { - const { tab, monitor } = await initNetMonitor(CURL_URL, { requestCount: 1 }); + const { tab, monitor } = await initNetMonitor(HTTPS_CURL_URL, { + requestCount: 1, + }); info("Starting test... "); const { document } = monitor.panelWin; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_cors_requests.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_cors_requests.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_cors_requests.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_cors_requests.js 2021-10-20 19:28:16.000000000 +0000 @@ -8,7 +8,9 @@ */ add_task(async function() { - const { tab, monitor } = await initNetMonitor(CORS_URL, { requestCount: 1 }); + const { tab, monitor } = await initNetMonitor(HTTPS_CORS_URL, { + requestCount: 1, + }); const { document, store, windowRequire } = monitor.panelWin; const Actions = windowRequire("devtools/client/netmonitor/src/actions/index"); @@ -21,7 +23,7 @@ const wait = waitForNetworkEvents(monitor, 2); info("Performing a CORS request"); - const requestUrl = "http://test1.example.com" + CORS_SJS_PATH; + const requestUrl = "https://test1.example.com" + CORS_SJS_PATH; await SpecialPowers.spawn(tab.linkedBrowser, [requestUrl], async function( url ) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_curl-utils.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_curl-utils.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_curl-utils.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_curl-utils.js 2021-10-20 19:28:15.000000000 +0000 @@ -10,7 +10,7 @@ const { Curl, CurlUtils } = require("devtools/client/shared/curl"); add_task(async function() { - const { tab, monitor } = await initNetMonitor(CURL_UTILS_URL, { + const { tab, monitor } = await initNetMonitor(HTTPS_CURL_UTILS_URL, { requestCount: 1, }); info("Starting test... "); @@ -25,11 +25,13 @@ store.dispatch(Actions.batchEnable(false)); const wait = waitForNetworkEvents(monitor, 6); - await SpecialPowers.spawn(tab.linkedBrowser, [SIMPLE_SJS], async function( - url - ) { - content.wrappedJSObject.performRequests(url); - }); + await SpecialPowers.spawn( + tab.linkedBrowser, + [HTTPS_SIMPLE_SJS], + async function(url) { + content.wrappedJSObject.performRequests(url); + } + ); await wait; const requests = { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_cyrillic-01.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_cyrillic-01.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_cyrillic-01.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_cyrillic-01.js 2021-10-20 19:28:16.000000000 +0000 @@ -8,6 +8,9 @@ */ add_task(async function() { + // Using https-first for this test is blocked on Bug 1733420. + await pushPref("dom.security.https_first", false); + const { tab, monitor } = await initNetMonitor(CYRILLIC_URL, { requestCount: 1, }); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_cyrillic-02.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_cyrillic-02.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_cyrillic-02.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_cyrillic-02.js 2021-10-20 19:28:16.000000000 +0000 @@ -9,6 +9,9 @@ */ add_task(async function() { + // Using https-first for this test is blocked on Bug 1733420. + await pushPref("dom.security.https_first", false); + const { monitor } = await initNetMonitor(CYRILLIC_URL, { requestCount: 1, }); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_details_copy.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_details_copy.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_details_copy.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_details_copy.js 2021-10-20 19:28:15.000000000 +0000 @@ -6,6 +6,9 @@ * Test that the URL Preview can be copied */ add_task(async function() { + // Using https-first for this test is blocked on Bug 1733420. + await pushPref("dom.security.https_first", false); + const { monitor } = await initNetMonitor(SIMPLE_URL, { requestCount: 1, }); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_edit_resend_cancel.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_edit_resend_cancel.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_edit_resend_cancel.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_edit_resend_cancel.js 2021-10-20 19:28:15.000000000 +0000 @@ -8,7 +8,9 @@ */ add_task(async function() { - const { monitor } = await initNetMonitor(SIMPLE_URL, { requestCount: 1 }); + const { monitor } = await initNetMonitor(HTTPS_SIMPLE_URL, { + requestCount: 1, + }); info("Starting test... "); const { document, store, windowRequire } = monitor.panelWin; @@ -20,7 +22,7 @@ // Reload to have one request in the list const waitForEvents = waitForNetworkEvents(monitor, 1); - await navigateTo(SIMPLE_URL); + await navigateTo(HTTPS_SIMPLE_URL); await waitForEvents; // Context Menu > "Edit & Resend" diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_edit_resend_caret.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_edit_resend_caret.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_edit_resend_caret.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_edit_resend_caret.js 2021-10-20 19:28:16.000000000 +0000 @@ -10,7 +10,9 @@ */ add_task(async function() { - const { monitor } = await initNetMonitor(SIMPLE_URL, { requestCount: 1 }); + const { monitor } = await initNetMonitor(HTTPS_SIMPLE_URL, { + requestCount: 1, + }); info("Starting test... "); const { document, store, windowRequire } = monitor.panelWin; @@ -19,7 +21,7 @@ // Reload to have one request in the list. const waitForEvents = waitForNetworkEvents(monitor, 1); - await navigateTo(SIMPLE_URL); + await navigateTo(HTTPS_SIMPLE_URL); await waitForEvents; // Open context menu and execute "Edit & Resend". diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_filter-01.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_filter-01.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_filter-01.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_filter-01.js 2021-10-20 19:28:16.000000000 +0000 @@ -207,6 +207,11 @@ ]; add_task(async function() { + // Using https-first for this test is blocked on Bug 1733420. + // In the meantime, we cannot expect correct status text from https requests + // to httpd.js. + await pushPref("dom.security.https_first", false); + const { monitor } = await initNetMonitor(FILTERING_URL, { requestCount: 1 }); const { document, store, windowRequire } = monitor.panelWin; const Actions = windowRequire("devtools/client/netmonitor/src/actions/index"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_filter-02.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_filter-02.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_filter-02.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_filter-02.js 2021-10-20 19:28:15.000000000 +0000 @@ -132,6 +132,11 @@ ]; add_task(async function() { + // Using https-first for this test is blocked on Bug 1733420. + // In the meantime, we cannot expect correct status text from https requests + // to httpd.js. + await pushPref("dom.security.https_first", false); + const { monitor } = await initNetMonitor(FILTERING_URL, { requestCount: 1 }); info("Starting test... "); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_filter-autocomplete.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_filter-autocomplete.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_filter-autocomplete.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_filter-autocomplete.js 2021-10-20 19:28:16.000000000 +0000 @@ -38,6 +38,10 @@ } add_task(async function() { + // Using https-first for this test is blocked on Bug 1733420. + // We cannot assert status code 304 with HTTPS requests to httpd.js + await pushPref("dom.security.https_first", false); + const { monitor } = await initNetMonitor(FILTERING_URL, { requestCount: 1 }); const { document, store, windowRequire } = monitor.panelWin; const Actions = windowRequire("devtools/client/netmonitor/src/actions/index"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_filter-flags.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_filter-flags.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_filter-flags.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_filter-flags.js 2021-10-20 19:28:15.000000000 +0000 @@ -142,6 +142,10 @@ ]; add_task(async function() { + // Using https-first for this test is blocked on Bug 1733420. + // We cannot assert status code 304 with HTTPS requests to httpd.js + await pushPref("dom.security.https_first", false); + const { monitor } = await initNetMonitor(FILTERING_URL, { requestCount: 1 }); const { document, store, windowRequire } = monitor.panelWin; const Actions = windowRequire("devtools/client/netmonitor/src/actions/index"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_filter-sts-search.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_filter-sts-search.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_filter-sts-search.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_filter-sts-search.js 2021-10-20 19:28:16.000000000 +0000 @@ -13,6 +13,10 @@ ]; add_task(async function() { + // Using https-first for this test is blocked on Bug 1733420. + // We cannot assert status code 304 with HTTPS requests to httpd.js + await pushPref("dom.security.https_first", false); + const { monitor } = await initNetMonitor(FILTERING_URL, { requestCount: 1 }); const { document, store, windowRequire } = monitor.panelWin; const Actions = windowRequire("devtools/client/netmonitor/src/actions/index"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_fission_switch_target.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_fission_switch_target.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_fission_switch_target.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_fission_switch_target.js 2021-10-20 19:28:16.000000000 +0000 @@ -5,9 +5,9 @@ // Test switching for the top-level target. -const EXAMPLE_COM_URL = "http://example.com/document-builder.sjs?html=testcom"; -const EXAMPLE_NET_URL = "http://example.net/document-builder.sjs?html=testnet"; -const REQUEST_URL = SEARCH_SJS + "?value=test"; +const EXAMPLE_COM_URL = "https://example.com/document-builder.sjs?html=testcom"; +const EXAMPLE_NET_URL = "https://example.net/document-builder.sjs?html=testnet"; +const REQUEST_URL = HTTPS_SEARCH_SJS + "?value=test"; const PARENT_PROCESS_URL = "about:blank"; add_task(async function() { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_headers_sorted.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_headers_sorted.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_headers_sorted.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_headers_sorted.js 2021-10-20 19:28:15.000000000 +0000 @@ -12,7 +12,7 @@ * order and not sorted. */ add_task(async function() { - const { monitor } = await initNetMonitor(SIMPLE_SJS, { + const { monitor } = await initNetMonitor(HTTPS_SIMPLE_SJS, { requestCount: 1, }); info("Starting test... "); @@ -71,6 +71,9 @@ "Cookie", "Host", "Pragma", + "Sec-Fetch-Dest", + "Sec-Fetch-Mode", + "Sec-Fetch-Site", "Upgrade-Insecure-Requests", "User-Agent", ]; @@ -138,6 +141,9 @@ "Connection", "Cookie", "Upgrade-Insecure-Requests", + "Sec-Fetch-Dest", + "Sec-Fetch-Mode", + "Sec-Fetch-Site", "Pragma", "Cache-Control", ]; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_image_cache.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_image_cache.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_image_cache.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_image_cache.js 2021-10-20 19:28:15.000000000 +0000 @@ -40,7 +40,7 @@ is(requests.length, 4, "There should be 4 requests"); const requestData = { - uri: EXAMPLE_URL + "test-image.png", + uri: HTTPS_EXAMPLE_URL + "test-image.png", details: { status: 200, statusText: "OK (cached)", diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_initiator.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_initiator.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_initiator.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_initiator.js 2021-10-20 19:28:16.000000000 +0000 @@ -10,7 +10,7 @@ */ const INITIATOR_FILE_NAME = "html_cause-test-page.html"; -const INITIATOR_URL = EXAMPLE_URL + INITIATOR_FILE_NAME; +const INITIATOR_URL = HTTPS_EXAMPLE_URL + INITIATOR_FILE_NAME; const EXPECTED_REQUESTS = [ { @@ -22,42 +22,42 @@ }, { method: "GET", - url: EXAMPLE_URL + "stylesheet_request", + url: HTTPS_EXAMPLE_URL + "stylesheet_request", causeType: "stylesheet", causeUri: INITIATOR_URL, stack: false, }, { method: "GET", - url: EXAMPLE_URL + "img_request", + url: HTTPS_EXAMPLE_URL + "img_request", causeType: "img", causeUri: INITIATOR_URL, stack: false, }, { method: "GET", - url: EXAMPLE_URL + "img_srcset_request", + url: HTTPS_EXAMPLE_URL + "img_srcset_request", causeType: "imageset", causeUri: INITIATOR_URL, stack: false, }, { method: "GET", - url: EXAMPLE_URL + "xhr_request", + url: HTTPS_EXAMPLE_URL + "xhr_request", causeType: "xhr", causeUri: INITIATOR_URL, stack: [{ fn: "performXhrRequestCallback", file: INITIATOR_URL, line: 32 }], }, { method: "GET", - url: EXAMPLE_URL + "fetch_request", + url: HTTPS_EXAMPLE_URL + "fetch_request", causeType: "fetch", causeUri: INITIATOR_URL, stack: [{ fn: "performFetchRequest", file: INITIATOR_URL, line: 37 }], }, { method: "GET", - url: EXAMPLE_URL + "promise_fetch_request", + url: HTTPS_EXAMPLE_URL + "promise_fetch_request", causeType: "fetch", causeUri: INITIATOR_URL, stack: [ @@ -76,7 +76,7 @@ }, { method: "GET", - url: EXAMPLE_URL + "timeout_fetch_request", + url: HTTPS_EXAMPLE_URL + "timeout_fetch_request", causeType: "fetch", causeUri: INITIATOR_URL, stack: [ @@ -95,7 +95,7 @@ }, { method: "GET", - url: EXAMPLE_URL + "favicon_request", + url: HTTPS_EXAMPLE_URL + "favicon_request", causeType: "img", causeUri: INITIATOR_URL, // the favicon request is triggered in FaviconLoader.jsm module, it should @@ -111,21 +111,21 @@ }, { method: "GET", - url: EXAMPLE_URL + "lazy_img_request", + url: HTTPS_EXAMPLE_URL + "lazy_img_request", causeType: "lazy-img", causeUri: INITIATOR_URL, stack: false, }, { method: "GET", - url: EXAMPLE_URL + "lazy_img_srcset_request", + url: HTTPS_EXAMPLE_URL + "lazy_img_srcset_request", causeType: "lazy-imageset", causeUri: INITIATOR_URL, stack: false, }, { method: "POST", - url: EXAMPLE_URL + "beacon_request", + url: HTTPS_EXAMPLE_URL + "beacon_request", causeType: "beacon", causeUri: INITIATOR_URL, stack: [{ fn: "performBeaconRequest", file: INITIATOR_URL, line: 82 }], diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_json_custom_mime.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_json_custom_mime.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_json_custom_mime.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_json_custom_mime.js 2021-10-20 19:28:16.000000000 +0000 @@ -8,6 +8,11 @@ */ add_task(async function() { + // Using https-first for this test is blocked on Bug 1733420. + // We cannot assert status text "OK" with HTTPS requests to httpd.js, instead + // we get "Connected" + await pushPref("dom.security.https_first", false); + const { tab, monitor } = await initNetMonitor(JSON_CUSTOM_MIME_URL, { requestCount: 1, }); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_json-long.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_json-long.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_json-long.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_json-long.js 2021-10-20 19:28:16.000000000 +0000 @@ -8,6 +8,11 @@ */ add_task(async function() { + // Using https-first for this test is blocked on Bug 1733420. + // We cannot assert status text "OK" with HTTPS requests to httpd.js, instead + // we get "Connected" + await pushPref("dom.security.https_first", false); + const { L10N } = require("devtools/client/netmonitor/src/utils/l10n"); const { tab, monitor } = await initNetMonitor(JSON_LONG_URL, { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_json-malformed.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_json-malformed.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_json-malformed.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_json-malformed.js 2021-10-20 19:28:15.000000000 +0000 @@ -8,6 +8,11 @@ */ add_task(async function() { + // Using https-first for this test is blocked on Bug 1733420. + // We cannot assert status text "OK" with HTTPS requests to httpd.js, instead + // we get "Connected" + await pushPref("dom.security.https_first", false); + const { L10N } = require("devtools/client/netmonitor/src/utils/l10n"); const { tab, monitor } = await initNetMonitor(JSON_MALFORMED_URL, { requestCount: 1, diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_jsonp.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_jsonp.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_jsonp.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_jsonp.js 2021-10-20 19:28:16.000000000 +0000 @@ -8,6 +8,11 @@ */ add_task(async function() { + // Using https-first for this test is blocked on Bug 1733420. + // We cannot assert status text "OK" with HTTPS requests to httpd.js, instead + // we get "Connected" + await pushPref("dom.security.https_first", false); + const { L10N } = require("devtools/client/netmonitor/src/utils/l10n"); const { tab, monitor } = await initNetMonitor(JSONP_URL, { requestCount: 1 }); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_json_text_mime.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_json_text_mime.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_json_text_mime.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_json_text_mime.js 2021-10-20 19:28:15.000000000 +0000 @@ -8,6 +8,11 @@ */ add_task(async function() { + // Using https-first for this test is blocked on Bug 1733420. + // We cannot assert status text "OK" with HTTPS requests to httpd.js, instead + // we get "Connected" + await pushPref("dom.security.https_first", false); + const { L10N } = require("devtools/client/netmonitor/src/utils/l10n"); const { tab, monitor } = await initNetMonitor(JSON_TEXT_MIME_URL, { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_large-response.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_large-response.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_large-response.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_large-response.js 2021-10-20 19:28:16.000000000 +0000 @@ -10,6 +10,11 @@ const HTML_LONG_URL = CONTENT_TYPE_SJS + "?fmt=html-long"; add_task(async function() { + // Using https-first for this test is blocked on Bug 1733420. + // We cannot assert status text "OK" with HTTPS requests to httpd.js, instead + // we get "Connected" + await pushPref("dom.security.https_first", false); + const { tab, monitor } = await initNetMonitor(CUSTOM_GET_URL, { requestCount: 1, }); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_post-data-01.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_post-data-01.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_post-data-01.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_post-data-01.js 2021-10-20 19:28:15.000000000 +0000 @@ -7,6 +7,11 @@ * Tests if the POST requests display the correct information in the UI. */ add_task(async function() { + // Using https-first for this test is blocked on Bug 1733420. + // We cannot assert status text "Och Aye" with HTTPS requests to httpd.js, instead + // we get "Connected" + await pushPref("dom.security.https_first", false); + const { L10N } = require("devtools/client/netmonitor/src/utils/l10n"); // Set a higher panel height in order to get full CodeMirror content diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_req-resp-bodies.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_req-resp-bodies.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_req-resp-bodies.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_req-resp-bodies.js 2021-10-20 19:28:15.000000000 +0000 @@ -8,6 +8,11 @@ */ add_task(async function() { + // Using https-first for this test is blocked on Bug 1733420. + // We cannot assert status text "OK" with HTTPS requests to httpd.js, instead + // we get "Connected" + await pushPref("dom.security.https_first", false); + const { L10N } = require("devtools/client/netmonitor/src/utils/l10n"); const { tab, monitor } = await initNetMonitor(JSON_LONG_URL, { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_resend_cors.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_resend_cors.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_resend_cors.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_resend_cors.js 2021-10-20 19:28:15.000000000 +0000 @@ -9,7 +9,9 @@ */ add_task(async function() { - const { tab, monitor } = await initNetMonitor(CORS_URL, { requestCount: 1 }); + const { tab, monitor } = await initNetMonitor(HTTPS_CORS_URL, { + requestCount: 1, + }); info("Starting test... "); const { store, windowRequire, connector } = monitor.panelWin; @@ -20,7 +22,7 @@ store.dispatch(Actions.batchEnable(false)); - const requestUrl = "http://test1.example.com" + CORS_SJS_PATH; + const requestUrl = "https://test1.example.com" + CORS_SJS_PATH; info("Waiting for OPTIONS, then POST"); const wait = waitForNetworkEvents(monitor, 2); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_resend_headers.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_resend_headers.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_resend_headers.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_resend_headers.js 2021-10-20 19:28:16.000000000 +0000 @@ -8,7 +8,9 @@ */ add_task(async function() { - const { monitor } = await initNetMonitor(SIMPLE_SJS, { requestCount: 1 }); + const { monitor } = await initNetMonitor(HTTPS_SIMPLE_SJS, { + requestCount: 1, + }); info("Starting test... "); const { store, windowRequire, connector } = monitor.panelWin; @@ -20,7 +22,7 @@ store.dispatch(Actions.batchEnable(false)); - const requestUrl = SIMPLE_SJS; + const requestUrl = HTTPS_SIMPLE_SJS; const requestHeaders = [ { name: "Host", value: "fakehost.example.com" }, { name: "User-Agent", value: "Testzilla" }, diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_resend_hidden_headers.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_resend_hidden_headers.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_resend_hidden_headers.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_resend_hidden_headers.js 2021-10-20 19:28:16.000000000 +0000 @@ -8,7 +8,9 @@ */ add_task(async function() { - const { monitor } = await initNetMonitor(SIMPLE_URL, { requestCount: 1 }); + const { monitor } = await initNetMonitor(HTTPS_SIMPLE_URL, { + requestCount: 1, + }); info("Starting test... "); const { store, windowRequire, connector } = monitor.panelWin; @@ -21,7 +23,7 @@ store.dispatch(Actions.batchEnable(false)); - const requestUrl = SIMPLE_SJS; + const requestUrl = HTTPS_SIMPLE_SJS; const requestHeaders = [ { name: "Accept", value: "application/vnd.example+json" }, ]; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_response_CORS_blocked.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_response_CORS_blocked.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_response_CORS_blocked.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_response_CORS_blocked.js 2021-10-20 19:28:15.000000000 +0000 @@ -11,7 +11,7 @@ add_task(async function testCORSNotificationPresent() { info("Test that CORS notification is present"); - const { tab, monitor } = await initNetMonitor(CORS_URL, { + const { tab, monitor } = await initNetMonitor(HTTPS_CORS_URL, { requestCount: 1, }); @@ -23,7 +23,7 @@ const wait = waitForNetworkEvents(monitor, 1); info("making request to a origin that doesn't allow cross origin"); - const requestUrl = EXAMPLE_ORG_URL + "sjs_simple-test-server.sjs"; + const requestUrl = HTTPS_EXAMPLE_ORG_URL + "sjs_simple-test-server.sjs"; await SpecialPowers.spawn(tab.linkedBrowser, [requestUrl], async function( url ) { @@ -79,7 +79,7 @@ const wait = waitForNetworkEvents(monitor, 1); info("Making request to a origin that allows cross origin"); - const requestUrl = "http://test1.example.com" + CORS_SJS_PATH; + const requestUrl = "https://test1.example.com" + CORS_SJS_PATH; await SpecialPowers.spawn(tab.linkedBrowser, [requestUrl], async function( url ) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_search-results.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_search-results.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_search-results.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_search-results.js 2021-10-20 19:28:15.000000000 +0000 @@ -11,7 +11,7 @@ add_task(async function() { await pushPref("devtools.netmonitor.features.search", true); - const { tab, monitor } = await initNetMonitor(CUSTOM_GET_URL, { + const { tab, monitor } = await initNetMonitor(HTTPS_CUSTOM_GET_URL, { requestCount: 1, }); info("Starting test... "); @@ -24,7 +24,10 @@ const SEARCH_STRING = "test"; // Execute two XHRs and wait until they are finished. - const URLS = [SEARCH_SJS + "?value=test1", SEARCH_SJS + "?value=test2"]; + const URLS = [ + HTTPS_SEARCH_SJS + "?value=test1", + HTTPS_SEARCH_SJS + "?value=test2", + ]; const wait = waitForNetworkEvents(monitor, 2); await SpecialPowers.spawn(tab.linkedBrowser, [URLS], makeRequests); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_security-redirect.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_security-redirect.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_security-redirect.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_security-redirect.js 2021-10-20 19:28:16.000000000 +0000 @@ -9,6 +9,9 @@ */ add_task(async function() { + // This test explicitly asserts http -> https redirects. + await pushPref("dom.security.https_first", false); + const { tab, monitor } = await initNetMonitor(CUSTOM_GET_URL, { requestCount: 1, }); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_security-state.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_security-state.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_security-state.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_security-state.js 2021-10-20 19:28:16.000000000 +0000 @@ -9,6 +9,9 @@ */ add_task(async function() { + // This test explicitly asserts some insecure domains. + await pushPref("dom.security.https_first", false); + const EXPECTED_SECURITY_STATES = { "test1.example.com": "security-state-insecure", "example.com": "security-state-secure", diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_security-tab-deselect.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_security-tab-deselect.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_security-tab-deselect.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_security-tab-deselect.js 2021-10-20 19:28:15.000000000 +0000 @@ -9,6 +9,10 @@ */ add_task(async function() { + // This test needs to trigger http and https requests. + // Disable https-first to avoid blocking the HTTP request due to mixed content. + await pushPref("dom.security.https_first", false); + const { tab, monitor } = await initNetMonitor(CUSTOM_GET_URL, { requestCount: 1, }); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_security-tab-visibility.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_security-tab-visibility.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_security-tab-visibility.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_security-tab-visibility.js 2021-10-20 19:28:15.000000000 +0000 @@ -8,6 +8,9 @@ */ add_task(async function() { + // This test explicitly asserts some insecure domains. + await pushPref("dom.security.https_first", false); + const TEST_DATA = [ { desc: "http request", diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_send-beacon.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_send-beacon.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_send-beacon.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_send-beacon.js 2021-10-20 19:28:15.000000000 +0000 @@ -8,6 +8,9 @@ */ add_task(async function() { + // Using https-first for this test is blocked on Bug 1733420. + await pushPref("dom.security.https_first", false); + const { tab, monitor } = await initNetMonitor(SEND_BEACON_URL, { requestCount: 1, }); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_simple-request-details.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_simple-request-details.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_simple-request-details.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_simple-request-details.js 2021-10-20 19:28:15.000000000 +0000 @@ -8,6 +8,9 @@ */ add_task(async function() { + // Using https-first for this test is blocked on Bug 1733420. + await pushPref("dom.security.https_first", false); + const { L10N } = require("devtools/client/netmonitor/src/utils/l10n"); const { monitor } = await initNetMonitor(SIMPLE_SJS, { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_sort-01.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_sort-01.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_sort-01.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_sort-01.js 2021-10-20 19:28:16.000000000 +0000 @@ -8,6 +8,9 @@ */ add_task(async function() { + // Using https-first for this test is blocked on Bug 1733420. + await pushPref("dom.security.https_first", false); + const { L10N } = require("devtools/client/netmonitor/src/utils/l10n"); const { monitor } = await initNetMonitor(SORTING_URL, { requestCount: 1 }); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_sort-02.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_sort-02.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_sort-02.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_sort-02.js 2021-10-20 19:28:15.000000000 +0000 @@ -8,6 +8,9 @@ */ add_task(async function() { + // Using https-first for this test is blocked on Bug 1733420. + await pushPref("dom.security.https_first", false); + const { L10N } = require("devtools/client/netmonitor/src/utils/l10n"); const { monitor } = await initNetMonitor(SORTING_URL, { requestCount: 1 }); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_sort-reset.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_sort-reset.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_sort-reset.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_sort-reset.js 2021-10-20 19:28:16.000000000 +0000 @@ -8,6 +8,9 @@ */ add_task(async function() { + // Using https-first for this test is blocked on Bug 1733420. + await pushPref("dom.security.https_first", false); + const { L10N } = require("devtools/client/netmonitor/src/utils/l10n"); const { monitor } = await initNetMonitor(SORTING_URL, { requestCount: 1 }); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_stacktraces-visibility.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_stacktraces-visibility.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_stacktraces-visibility.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_stacktraces-visibility.js 2021-10-20 19:28:16.000000000 +0000 @@ -9,6 +9,11 @@ */ add_task(async function() { + // Using https-first for this test is blocked on Bug 1733420. + // Behind the scenes this test relies on one of the requests creating a 404 + // which is not possible when using HTTPS with httpd.js + await pushPref("dom.security.https_first", false); + const URL = EXAMPLE_URL + "html_single-get-page.html"; const REQUEST = "http://example.com/browser/devtools/client/netmonitor/test/request_0"; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_status-codes.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_status-codes.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_status-codes.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_status-codes.js 2021-10-20 19:28:15.000000000 +0000 @@ -8,6 +8,10 @@ */ add_task(async function() { + // Using https-first for this test is blocked on Bug 1733420. + // We cannot assert status text with HTTPS requests to httpd.js, or 404... + await pushPref("dom.security.https_first", false); + const { L10N } = require("devtools/client/netmonitor/src/utils/l10n"); const { tab, monitor } = await initNetMonitor(STATUS_CODES_URL, { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_streaming-response.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_streaming-response.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_streaming-response.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_streaming-response.js 2021-10-20 19:28:16.000000000 +0000 @@ -9,6 +9,11 @@ */ add_task(async function() { + // Using https-first for this test is blocked on Bug 1733420. + // We cannot assert status text "OK" with HTTPS requests to httpd.js, instead + // we get "Connected" + await pushPref("dom.security.https_first", false); + const { tab, monitor } = await initNetMonitor(CUSTOM_GET_URL, { requestCount: 1, }); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_telemetry_edit_resend.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_telemetry_edit_resend.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_telemetry_edit_resend.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_telemetry_edit_resend.js 2021-10-20 19:28:16.000000000 +0000 @@ -9,7 +9,9 @@ * Test the edit_resend telemetry event. */ add_task(async function() { - const { monitor } = await initNetMonitor(SIMPLE_URL, { requestCount: 1 }); + const { monitor } = await initNetMonitor(HTTPS_SIMPLE_URL, { + requestCount: 1, + }); info("Starting test... "); const { document, store, windowRequire } = monitor.panelWin; @@ -25,7 +27,7 @@ // Reload to have one request in the list. const waitForEvents = waitForNetworkEvents(monitor, 1); - await navigateTo(SIMPLE_URL); + await navigateTo(HTTPS_SIMPLE_URL); await waitForEvents; // Open context menu and execute "Edit & Resend". diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_telemetry_filters_changed.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_telemetry_filters_changed.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_telemetry_filters_changed.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_telemetry_filters_changed.js 2021-10-20 19:28:16.000000000 +0000 @@ -9,7 +9,9 @@ * Test the filters_changed telemetry event. */ add_task(async function() { - const { monitor } = await initNetMonitor(SIMPLE_URL, { requestCount: 1 }); + const { monitor } = await initNetMonitor(HTTPS_SIMPLE_URL, { + requestCount: 1, + }); info("Starting test... "); const { document, store, windowRequire } = monitor.panelWin; @@ -29,7 +31,7 @@ // Reload to have one request in the list. const wait = waitForNetworkEvents(monitor, 1); - await navigateTo(SIMPLE_URL); + await navigateTo(HTTPS_SIMPLE_URL); await wait; info("Click on the 'HTML' filter"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_telemetry_sidepanel_changed.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_telemetry_sidepanel_changed.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_telemetry_sidepanel_changed.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_telemetry_sidepanel_changed.js 2021-10-20 19:28:15.000000000 +0000 @@ -9,7 +9,9 @@ * Test the sidepanel_changed telemetry event. */ add_task(async function() { - const { monitor } = await initNetMonitor(SIMPLE_URL, { requestCount: 1 }); + const { monitor } = await initNetMonitor(HTTPS_SIMPLE_URL, { + requestCount: 1, + }); info("Starting test... "); const { document, store, windowRequire } = monitor.panelWin; @@ -25,7 +27,7 @@ // Reload to have one request in the list. const waitForEvents = waitForNetworkEvents(monitor, 1); - await navigateTo(SIMPLE_URL); + await navigateTo(HTTPS_SIMPLE_URL); await waitForEvents; // Click on a request and wait till the default "Headers" side panel is opened. diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_timeline_ticks.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_timeline_ticks.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_timeline_ticks.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_timeline_ticks.js 2021-10-20 19:28:16.000000000 +0000 @@ -10,7 +10,7 @@ add_task(async function() { const { L10N } = require("devtools/client/netmonitor/src/utils/l10n"); - const { monitor } = await initNetMonitor(SIMPLE_URL, { + const { monitor } = await initNetMonitor(HTTPS_SIMPLE_URL, { requestCount: 1, }); info("Starting test... "); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_tracking-resources.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_tracking-resources.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_tracking-resources.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_tracking-resources.js 2021-10-20 19:28:16.000000000 +0000 @@ -8,7 +8,7 @@ ); const TEST_URI = - "http://example.com/browser/devtools/client/" + + "https://example.com/browser/devtools/client/" + "netmonitor/test/html_tracking-protection.html"; registerCleanupFunction(function() { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_truncate-post-data.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_truncate-post-data.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_truncate-post-data.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_truncate-post-data.js 2021-10-20 19:28:15.000000000 +0000 @@ -8,6 +8,9 @@ * Verifies that requests with large post data are truncated and error is displayed. */ add_task(async function() { + // Using https-first for this test is blocked on Bug 1733420. + await pushPref("dom.security.https_first", false); + const { monitor, tab } = await initNetMonitor(POST_JSON_URL, { requestCount: 1, }); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_use_as_fetch.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_use_as_fetch.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_use_as_fetch.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_use_as_fetch.js 2021-10-20 19:28:16.000000000 +0000 @@ -8,6 +8,9 @@ */ add_task(async function() { + // Using https-first for this test is blocked on Bug 1733420. + await pushPref("dom.security.https_first", false); + const { tab, monitor, toolbox } = await initNetMonitor(CURL_URL, { requestCount: 1, }); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_websocket_stacks.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_websocket_stacks.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_websocket_stacks.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_websocket_stacks.js 2021-10-20 19:28:16.000000000 +0000 @@ -7,7 +7,7 @@ // web sockets on the main or worker threads. const TOP_FILE_NAME = "html_websocket-test-page.html"; -const TOP_URL = EXAMPLE_URL + TOP_FILE_NAME; +const TOP_URL = HTTPS_EXAMPLE_URL + TOP_FILE_NAME; const WORKER_FILE_NAME = "js_websocket-worker-test.js"; const EXPECTED_REQUESTS = { @@ -28,9 +28,9 @@ { file: TOP_FILE_NAME, line: 3 }, ], }, - [EXAMPLE_URL + WORKER_FILE_NAME]: { + [HTTPS_EXAMPLE_URL + WORKER_FILE_NAME]: { method: "GET", - url: EXAMPLE_URL + WORKER_FILE_NAME, + url: HTTPS_EXAMPLE_URL + WORKER_FILE_NAME, causeType: "script", causeUri: TOP_URL, stack: [{ file: TOP_URL, line: 9 }], @@ -41,7 +41,11 @@ causeType: "websocket", causeUri: TOP_URL, stack: [ - { fn: "openWorkerSocket", file: EXAMPLE_URL + WORKER_FILE_NAME, line: 5 }, + { + fn: "openWorkerSocket", + file: HTTPS_EXAMPLE_URL + WORKER_FILE_NAME, + line: 5, + }, { file: WORKER_FILE_NAME, line: 2 }, ], }, diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_worker_stacks.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_worker_stacks.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/browser_net_worker_stacks.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/browser_net_worker_stacks.js 2021-10-20 19:28:16.000000000 +0000 @@ -7,9 +7,9 @@ // running worker threads. const TOP_FILE_NAME = "html_worker-test-page.html"; -const TOP_URL = EXAMPLE_URL + TOP_FILE_NAME; +const TOP_URL = HTTPS_EXAMPLE_URL + TOP_FILE_NAME; const WORKER_FILE_NAME = "js_worker-test.js"; -const WORKER_URL = EXAMPLE_URL + WORKER_FILE_NAME; +const WORKER_URL = HTTPS_EXAMPLE_URL + WORKER_FILE_NAME; const EXPECTED_REQUESTS = [ { @@ -32,7 +32,7 @@ }, { method: "GET", - url: EXAMPLE_URL + "missing1.js", + url: HTTPS_EXAMPLE_URL + "missing1.js", causeType: "script", causeUri: TOP_URL, stack: [ @@ -42,7 +42,7 @@ }, { method: "GET", - url: EXAMPLE_URL + "missing2.js", + url: HTTPS_EXAMPLE_URL + "missing2.js", causeType: "script", causeUri: TOP_URL, stack: [ @@ -52,7 +52,7 @@ }, { method: "GET", - url: EXAMPLE_URL + "js_worker-test2.js", + url: HTTPS_EXAMPLE_URL + "js_worker-test2.js", causeType: "script", causeUri: TOP_URL, stack: [ @@ -62,7 +62,7 @@ }, { method: "GET", - url: EXAMPLE_URL + "missing.json", + url: HTTPS_EXAMPLE_URL + "missing.json", causeType: "xhr", causeUri: TOP_URL, stack: [ @@ -72,7 +72,7 @@ }, { method: "GET", - url: EXAMPLE_URL + "missing.txt", + url: HTTPS_EXAMPLE_URL + "missing.txt", causeType: "fetch", causeUri: TOP_URL, stack: [ diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/head.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/head.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/head.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/head.js 2021-10-20 19:28:15.000000000 +0000 @@ -81,6 +81,8 @@ const CONTENT_TYPE_WITHOUT_CACHE_REQUESTS = 8; const CYRILLIC_URL = EXAMPLE_URL + "html_cyrillic-test-page.html"; const STATUS_CODES_URL = EXAMPLE_URL + "html_status-codes-test-page.html"; +const HTTPS_STATUS_CODES_URL = + HTTPS_EXAMPLE_URL + "html_status-codes-test-page.html"; const POST_DATA_URL = EXAMPLE_URL + "html_post-data-test-page.html"; const POST_ARRAY_DATA_URL = EXAMPLE_URL + "html_post-array-data-test-page.html"; const POST_JSON_URL = EXAMPLE_URL + "html_post-json-test-page.html"; @@ -101,6 +103,7 @@ const FONTS_URL = EXAMPLE_URL + "html_fonts-test-page.html"; const SORTING_URL = EXAMPLE_URL + "html_sorting-test-page.html"; const FILTERING_URL = EXAMPLE_URL + "html_filter-test-page.html"; +const HTTPS_FILTERING_URL = HTTPS_EXAMPLE_URL + "html_filter-test-page.html"; const INFINITE_GET_URL = EXAMPLE_URL + "html_infinite-get-page.html"; const CUSTOM_GET_URL = EXAMPLE_URL + "html_custom-get-page.html"; const HTTPS_CUSTOM_GET_URL = HTTPS_EXAMPLE_URL + "html_custom-get-page.html"; @@ -108,17 +111,20 @@ const HTTPS_SINGLE_GET_URL = HTTPS_EXAMPLE_URL + "html_single-get-page.html"; const STATISTICS_URL = EXAMPLE_URL + "html_statistics-test-page.html"; const CURL_URL = EXAMPLE_URL + "html_copy-as-curl.html"; -const CURL_UTILS_URL = EXAMPLE_URL + "html_curl-utils.html"; +const HTTPS_CURL_URL = HTTPS_EXAMPLE_URL + "html_copy-as-curl.html"; +const HTTPS_CURL_UTILS_URL = HTTPS_EXAMPLE_URL + "html_curl-utils.html"; const SEND_BEACON_URL = EXAMPLE_URL + "html_send-beacon.html"; const CORS_URL = EXAMPLE_URL + "html_cors-test-page.html"; +const HTTPS_CORS_URL = HTTPS_EXAMPLE_URL + "html_cors-test-page.html"; const PAUSE_URL = EXAMPLE_URL + "html_pause-test-page.html"; const OPEN_REQUEST_IN_TAB_URL = EXAMPLE_URL + "html_open-request-in-tab.html"; const CSP_URL = EXAMPLE_URL + "html_csp-test-page.html"; const CSP_RESEND_URL = EXAMPLE_URL + "html_csp-resend-test-page.html"; -const IMAGE_CACHE_URL = EXAMPLE_URL + "html_image-cache.html"; +const IMAGE_CACHE_URL = HTTPS_EXAMPLE_URL + "html_image-cache.html"; const SLOW_REQUESTS_URL = EXAMPLE_URL + "html_slow-requests-test-page.html"; const SIMPLE_SJS = EXAMPLE_URL + "sjs_simple-test-server.sjs"; +const HTTPS_SIMPLE_SJS = HTTPS_EXAMPLE_URL + "sjs_simple-test-server.sjs"; const SIMPLE_UNSORTED_COOKIES_SJS = EXAMPLE_URL + "sjs_simple-unsorted-cookies-test-server.sjs"; const CONTENT_TYPE_SJS = EXAMPLE_URL + "sjs_content-type-test-server.sjs"; @@ -136,6 +142,7 @@ const HSTS_SJS = EXAMPLE_URL + "sjs_hsts-test-server.sjs"; const METHOD_SJS = EXAMPLE_URL + "sjs_method-test-server.sjs"; const SLOW_SJS = EXAMPLE_URL + "sjs_slow-test-server.sjs"; +const HTTPS_SLOW_SJS = HTTPS_EXAMPLE_URL + "sjs_slow-test-server.sjs"; const SET_COOKIE_SAME_SITE_SJS = EXAMPLE_URL + "sjs_set-cookie-same-site.sjs"; const SEARCH_SJS = EXAMPLE_URL + "sjs_search-test-server.sjs"; const HTTPS_SEARCH_SJS = HTTPS_EXAMPLE_URL + "sjs_search-test-server.sjs"; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/html_tracking-protection.html firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/html_tracking-protection.html --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/html_tracking-protection.html 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/html_tracking-protection.html 2021-10-20 19:28:15.000000000 +0000 @@ -13,7 +13,7 @@ function performRequests() { const xhr = new XMLHttpRequest(); - xhr.open("GET", "http://tracking.example.org/", true); + xhr.open("GET", "https://tracking.example.org/", true); xhr.send(null); } </script> diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/sjs_content-type-test-server.sjs firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/sjs_content-type-test-server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/sjs_content-type-test-server.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/sjs_content-type-test-server.sjs 2021-10-20 19:28:15.000000000 +0000 @@ -4,16 +4,22 @@ Cu.importGlobalProperties(["TextEncoder"]); function gzipCompressString(string, obs) { - - let scs = Cc["@mozilla.org/streamConverters;1"] - .getService(Ci.nsIStreamConverterService); - let listener = Cc["@mozilla.org/network/stream-loader;1"] - .createInstance(Ci.nsIStreamLoader); + const scs = Cc["@mozilla.org/streamConverters;1"].getService( + Ci.nsIStreamConverterService + ); + const listener = Cc["@mozilla.org/network/stream-loader;1"].createInstance( + Ci.nsIStreamLoader + ); listener.init(obs); - let converter = scs.asyncConvertData("uncompressed", "gzip", - listener, null); - let stringStream = Cc["@mozilla.org/io/string-input-stream;1"] - .createInstance(Ci.nsIStringInputStream); + const converter = scs.asyncConvertData( + "uncompressed", + "gzip", + listener, + null + ); + const stringStream = Cc[ + "@mozilla.org/io/string-input-stream;1" + ].createInstance(Ci.nsIStringInputStream); stringStream.data = string; converter.onStartRequest(null); converter.onDataAvailable(null, stringStream, 0, string.length); @@ -21,11 +27,11 @@ } function doubleGzipCompressString(string, observer) { - let observer2 = { + const observer2 = { onStreamComplete: function(loader, context, status, length, result) { - let buffer = String.fromCharCode.apply(this, result); + const buffer = String.fromCharCode.apply(this, result); gzipCompressString(buffer, observer); - } + }, }; gzipCompressString(string, observer2); } @@ -33,18 +39,25 @@ function handleRequest(request, response) { response.processAsync(); - let params = request.queryString.split("&"); - let format = (params.filter((s) => s.includes("fmt="))[0] || "").split("=")[1]; - let status = (params.filter((s) => s.includes("sts="))[0] || "").split("=")[1] || 200; - let cookies = (params.filter((s) => s.includes("cookies="))[0] || "").split("=")[1] || 0; - let cors = request.queryString.includes("cors=1"); + const params = request.queryString.split("&"); + const format = (params.filter(s => s.includes("fmt="))[0] || "").split( + "=" + )[1]; + const status = + (params.filter(s => s.includes("sts="))[0] || "").split("=")[1] || 200; + const cookies = + (params.filter(s => s.includes("cookies="))[0] || "").split("=")[1] || 0; + const cors = request.queryString.includes("cors=1"); let cachedCount = 0; - let cacheExpire = 60; // seconds + const cacheExpire = 60; // seconds function setCacheHeaders() { if (status != 304) { - response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + response.setHeader( + "Cache-Control", + "no-cache, no-store, must-revalidate" + ); response.setHeader("Pragma", "no-cache"); response.setHeader("Expires", "0"); return; @@ -53,15 +66,27 @@ if (cachedCount % 2) { response.setHeader("Cache-Control", "max-age=" + cacheExpire, false); } else { - response.setHeader("Expires", Date(Date.now() + cacheExpire * 1000), false); + response.setHeader( + "Expires", + Date(Date.now() + cacheExpire * 1000), + false + ); } cachedCount++; } function setCookieHeaders() { if (cookies) { - response.setHeader("Set-Cookie", "name1=value1; Domain=.foo.example.com", true); - response.setHeader("Set-Cookie", "name2=value2; Domain=.example.com", true); + response.setHeader( + "Set-Cookie", + "name1=value1; Domain=.foo.example.com", + true + ); + response.setHeader( + "Set-Cookie", + "name2=value2; Domain=.example.com", + true + ); } } @@ -72,248 +97,296 @@ } let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - timer.initWithCallback(() => { - // to avoid garbage collection - timer = null; - switch (format) { - case "txt": { - response.setStatusLine(request.httpVersion, status, "DA DA DA"); - response.setHeader("Content-Type", "text/plain", false); - setCacheHeaders(); - - function convertToUtf8(str) { - return String.fromCharCode(...new TextEncoder().encode(str)); - } - - // This script must be evaluated as UTF-8 for this to write out the - // bytes of the string in UTF-8. If it's evaluated as Latin-1, the - // written bytes will be the result of UTF-8-encoding this string - // *twice*. - let data = "Братан, ты вообще качаешься?"; - let stringOfUtf8Bytes = convertToUtf8(data); - response.write(stringOfUtf8Bytes); + timer.initWithCallback( + () => { + // to avoid garbage collection + timer = null; + switch (format) { + case "txt": { + response.setStatusLine(request.httpVersion, status, "DA DA DA"); + response.setHeader("Content-Type", "text/plain", false); + setCacheHeaders(); - response.finish(); - break; - } - case "xml": { - response.setStatusLine(request.httpVersion, status, "OK"); - response.setHeader("Content-Type", "text/xml; charset=utf-8", false); - setCacheHeaders(); - response.write("<label value='greeting'>Hello XML!</label>"); - response.finish(); - break; - } - case "html": { - let content = (params.filter((s) => s.includes("res="))[0] || "").split("=")[1]; - response.setStatusLine(request.httpVersion, status, "OK"); - response.setHeader("Content-Type", "text/html; charset=utf-8", false); - setAllowOriginHeaders(); - setCacheHeaders(); - setCookieHeaders(); - response.write(content || "<p>Hello HTML!</p>"); - response.finish(); - break; - } - case "html-long": { - let str = new Array(102400 /* 100 KB in bytes */).join("."); - response.setStatusLine(request.httpVersion, status, "OK"); - response.setHeader("Content-Type", "text/html; charset=utf-8", false); - setCacheHeaders(); - response.write("<p>" + str + "</p>"); - response.finish(); - break; - } - case "css": { - response.setStatusLine(request.httpVersion, status, "OK"); - response.setHeader("Content-Type", "text/css; charset=utf-8", false); - setCacheHeaders(); - response.write("body:pre { content: 'Hello CSS!' }"); - response.finish(); - break; - } - case "js": { - response.setStatusLine(request.httpVersion, status, "OK"); - response.setHeader("Content-Type", "application/javascript; charset=utf-8", false); - setCacheHeaders(); - response.write("function() { return 'Hello JS!'; }"); - response.finish(); - break; - } - case "json": { - response.setStatusLine(request.httpVersion, status, "OK"); - response.setHeader("Content-Type", "application/json; charset=utf-8", false); - setCacheHeaders(); - response.write("{ \"greeting\": \"Hello JSON!\" }"); - response.finish(); - break; - } - case "jsonp": { - let fun = (params.filter((s) => s.includes("jsonp="))[0] || "").split("=")[1]; - response.setStatusLine(request.httpVersion, status, "OK"); - response.setHeader("Content-Type", "text/json; charset=utf-8", false); - setCacheHeaders(); - response.write(fun + "({ \"greeting\": \"Hello JSONP!\" })"); - response.finish(); - break; - } - case "jsonp2": { - let fun = (params.filter((s) => s.includes("jsonp="))[0] || "").split("=")[1]; - response.setStatusLine(request.httpVersion, status, "OK"); - response.setHeader("Content-Type", "text/json; charset=utf-8", false); - setCacheHeaders(); - response.write(" " + fun + " ( { \"greeting\": \"Hello weird JSONP!\" } ) ; "); - response.finish(); - break; - } - case "json-b64": { - response.setStatusLine(request.httpVersion, status, "OK"); - response.setHeader("Content-Type", "text/json; charset=utf-8", false); - setCacheHeaders(); - response.write(btoa("{ \"greeting\": \"This is a base 64 string.\" }")); - response.finish(); - break; - } - case "json-long": { - let str = "{ \"greeting\": \"Hello long string JSON!\" },"; - response.setStatusLine(request.httpVersion, status, "OK"); - response.setHeader("Content-Type", "text/json; charset=utf-8", false); - setCacheHeaders(); - response.write("[" + new Array(2048).join(str).slice(0, -1) + "]"); - response.finish(); - break; - } - case "json-malformed": { - response.setStatusLine(request.httpVersion, status, "OK"); - response.setHeader("Content-Type", "text/json; charset=utf-8", false); - setCacheHeaders(); - response.write("{ \"greeting\": \"Hello malformed JSON!\" },"); - response.finish(); - break; - } - case "json-text-mime": { - response.setStatusLine(request.httpVersion, status, "OK"); - response.setHeader("Content-Type", "text/plain; charset=utf-8", false); - setCacheHeaders(); - response.write("{ \"greeting\": \"Hello third-party JSON!\" }"); - response.finish(); - break; - } - case "json-custom-mime": { - response.setStatusLine(request.httpVersion, status, "OK"); - response.setHeader("Content-Type", "text/x-bigcorp-json; charset=utf-8", false); - setCacheHeaders(); - response.write("{ \"greeting\": \"Hello oddly-named JSON!\" }"); - response.finish(); - break; - } - case "font": { - response.setStatusLine(request.httpVersion, status, "OK"); - response.setHeader("Content-Type", "font/woff", false); - setAllowOriginHeaders(); - setCacheHeaders(); - response.finish(); - break; - } - case "image": { - response.setStatusLine(request.httpVersion, status, "OK"); - response.setHeader("Content-Type", "image/png", false); - setCacheHeaders(); - response.finish(); - break; - } - case "audio": { - response.setStatusLine(request.httpVersion, status, "OK"); - response.setHeader("Content-Type", "audio/ogg", false); - setCacheHeaders(); - response.finish(); - break; - } - case "video": { - response.setStatusLine(request.httpVersion, status, "OK"); - response.setHeader("Content-Type", "video/webm", false); - setCacheHeaders(); - response.finish(); - break; - } - case "flash": { - response.setStatusLine(request.httpVersion, status, "OK"); - response.setHeader("Content-Type", "application/x-shockwave-flash", false); - setCacheHeaders(); - response.finish(); - break; - } - case "ws": { - response.setStatusLine(request.httpVersion, 101, "Switching Protocols"); - response.setHeader("Connection", "upgrade", false); - response.setHeader("Upgrade", "websocket", false); - setCacheHeaders(); - response.finish(); - break; - } - case "gzip": { - // Note: we're doing a double gzip encoding to test multiple - // converters in network monitor. - response.setStatusLine(request.httpVersion, status, "OK"); - response.setHeader("Content-Type", "text/plain", false); - response.setHeader("Content-Encoding", "gzip\t ,gzip", false); - setCacheHeaders(); - let observer = { - onStreamComplete: function(loader, context, status, length, result) { - let buffer = String.fromCharCode.apply(this, result); - response.setHeader("Content-Length", "" + buffer.length, false); - response.write(buffer); - response.finish(); + function convertToUtf8(str) { + return String.fromCharCode(...new TextEncoder().encode(str)); } - }; - let data = new Array(1000).join("Hello gzip!"); - doubleGzipCompressString(data, observer); - break; - } - case "br": { - response.setStatusLine(request.httpVersion, status, "Connected"); - response.setHeader("Content-Type", "text/plain", false); - response.setHeader("Content-Encoding", "br", false); - setCacheHeaders(); - response.setHeader("Content-Length", "10", false); - // Use static data since we cannot encode brotli. - response.write("\x1b\x3f\x00\x00\x24\xb0\xe2\x99\x80\x12"); - response.finish(); - break; - } - case "hls-m3u8": { - response.setStatusLine(request.httpVersion, status, "OK"); - response.setHeader("Content-Type", "application/x-mpegurl", false); - setCacheHeaders(); - response.write("#EXTM3U\n"); - response.finish(); - break; - } - case "hls-m3u8-alt-mime-type": { - response.setStatusLine(request.httpVersion, status, "OK"); - response.setHeader("Content-Type", "application/vnd.apple.mpegurl", false); - setCacheHeaders(); - response.write("#EXTM3U\n"); - response.finish(); - break; - } - case "mpeg-dash": { - response.setStatusLine(request.httpVersion, status, "OK"); - response.setHeader("Content-Type", "video/vnd.mpeg.dash.mpd", false); - setCacheHeaders(); - response.write('<?xml version="1.0" encoding="UTF-8"?>\n'); - response.write('<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></MPD>\n'); - response.finish(); - break; - } - default: { - response.setStatusLine(request.httpVersion, 404, "Not Found"); - response.setHeader("Content-Type", "text/html; charset=utf-8", false); - setCacheHeaders(); - response.write("<blink>Not Found</blink>"); - response.finish(); - break; + + // This script must be evaluated as UTF-8 for this to write out the + // bytes of the string in UTF-8. If it's evaluated as Latin-1, the + // written bytes will be the result of UTF-8-encoding this string + // *twice*. + const data = "Братан, ты вообще качаешься?"; + const stringOfUtf8Bytes = convertToUtf8(data); + response.write(stringOfUtf8Bytes); + + response.finish(); + break; + } + case "xml": { + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "text/xml; charset=utf-8", false); + setCacheHeaders(); + response.write("<label value='greeting'>Hello XML!</label>"); + response.finish(); + break; + } + case "html": { + const content = ( + params.filter(s => s.includes("res="))[0] || "" + ).split("=")[1]; + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "text/html; charset=utf-8", false); + setAllowOriginHeaders(); + setCacheHeaders(); + setCookieHeaders(); + response.write(content || "<p>Hello HTML!</p>"); + response.finish(); + break; + } + case "html-long": { + const str = new Array(102400 /* 100 KB in bytes */).join("."); + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "text/html; charset=utf-8", false); + setCacheHeaders(); + response.write("<p>" + str + "</p>"); + response.finish(); + break; + } + case "css": { + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "text/css; charset=utf-8", false); + setCacheHeaders(); + response.write("body:pre { content: 'Hello CSS!' }"); + response.finish(); + break; + } + case "js": { + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader( + "Content-Type", + "application/javascript; charset=utf-8", + false + ); + setCacheHeaders(); + response.write("function() { return 'Hello JS!'; }"); + response.finish(); + break; + } + case "json": { + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader( + "Content-Type", + "application/json; charset=utf-8", + false + ); + setCacheHeaders(); + response.write('{ "greeting": "Hello JSON!" }'); + response.finish(); + break; + } + case "jsonp": { + const fun = (params.filter(s => s.includes("jsonp="))[0] || "").split( + "=" + )[1]; + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "text/json; charset=utf-8", false); + setCacheHeaders(); + response.write(fun + '({ "greeting": "Hello JSONP!" })'); + response.finish(); + break; + } + case "jsonp2": { + const fun = (params.filter(s => s.includes("jsonp="))[0] || "").split( + "=" + )[1]; + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "text/json; charset=utf-8", false); + setCacheHeaders(); + response.write( + " " + fun + ' ( { "greeting": "Hello weird JSONP!" } ) ; ' + ); + response.finish(); + break; + } + case "json-b64": { + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "text/json; charset=utf-8", false); + setCacheHeaders(); + response.write(btoa('{ "greeting": "This is a base 64 string." }')); + response.finish(); + break; + } + case "json-long": { + const str = '{ "greeting": "Hello long string JSON!" },'; + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "text/json; charset=utf-8", false); + setCacheHeaders(); + response.write("[" + new Array(2048).join(str).slice(0, -1) + "]"); + response.finish(); + break; + } + case "json-malformed": { + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "text/json; charset=utf-8", false); + setCacheHeaders(); + response.write('{ "greeting": "Hello malformed JSON!" },'); + response.finish(); + break; + } + case "json-text-mime": { + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader( + "Content-Type", + "text/plain; charset=utf-8", + false + ); + setCacheHeaders(); + response.write('{ "greeting": "Hello third-party JSON!" }'); + response.finish(); + break; + } + case "json-custom-mime": { + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader( + "Content-Type", + "text/x-bigcorp-json; charset=utf-8", + false + ); + setCacheHeaders(); + response.write('{ "greeting": "Hello oddly-named JSON!" }'); + response.finish(); + break; + } + case "font": { + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "font/woff", false); + setAllowOriginHeaders(); + setCacheHeaders(); + response.finish(); + break; + } + case "image": { + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "image/png", false); + setCacheHeaders(); + response.finish(); + break; + } + case "audio": { + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "audio/ogg", false); + setCacheHeaders(); + response.finish(); + break; + } + case "video": { + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "video/webm", false); + setCacheHeaders(); + response.finish(); + break; + } + case "flash": { + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader( + "Content-Type", + "application/x-shockwave-flash", + false + ); + setCacheHeaders(); + response.finish(); + break; + } + case "ws": { + response.setStatusLine( + request.httpVersion, + 101, + "Switching Protocols" + ); + response.setHeader("Connection", "upgrade", false); + response.setHeader("Upgrade", "websocket", false); + setCacheHeaders(); + response.finish(); + break; + } + case "gzip": { + // Note: we're doing a double gzip encoding to test multiple + // converters in network monitor. + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "text/plain", false); + response.setHeader("Content-Encoding", "gzip\t ,gzip", false); + setCacheHeaders(); + const observer = { + onStreamComplete: function( + loader, + context, + status, + length, + result + ) { + const buffer = String.fromCharCode.apply(this, result); + response.setHeader("Content-Length", "" + buffer.length, false); + response.write(buffer); + response.finish(); + }, + }; + const data = new Array(1000).join("Hello gzip!"); + doubleGzipCompressString(data, observer); + break; + } + case "br": { + response.setStatusLine(request.httpVersion, status, "Connected"); + response.setHeader("Content-Type", "text/plain", false); + response.setHeader("Content-Encoding", "br", false); + setCacheHeaders(); + response.setHeader("Content-Length", "10", false); + // Use static data since we cannot encode brotli. + response.write("\x1b\x3f\x00\x00\x24\xb0\xe2\x99\x80\x12"); + response.finish(); + break; + } + case "hls-m3u8": { + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "application/x-mpegurl", false); + setCacheHeaders(); + response.write("#EXTM3U\n"); + response.finish(); + break; + } + case "hls-m3u8-alt-mime-type": { + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader( + "Content-Type", + "application/vnd.apple.mpegurl", + false + ); + setCacheHeaders(); + response.write("#EXTM3U\n"); + response.finish(); + break; + } + case "mpeg-dash": { + response.setStatusLine(request.httpVersion, status, "OK"); + response.setHeader("Content-Type", "video/vnd.mpeg.dash.mpd", false); + setCacheHeaders(); + response.write('<?xml version="1.0" encoding="UTF-8"?>\n'); + response.write( + '<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></MPD>\n' + ); + response.finish(); + break; + } + default: { + response.setStatusLine(request.httpVersion, 404, "Not Found"); + response.setHeader("Content-Type", "text/html; charset=utf-8", false); + setCacheHeaders(); + response.write("<blink>Not Found</blink>"); + response.finish(); + break; + } } - } - }, 10, Ci.nsITimer.TYPE_ONE_SHOT); // Make sure this request takes a few ms. + }, + 10, + Ci.nsITimer.TYPE_ONE_SHOT + ); // Make sure this request takes a few ms. } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/sjs_https-redirect-test-server.sjs firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/sjs_https-redirect-test-server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/sjs_https-redirect-test-server.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/sjs_https-redirect-test-server.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -15,5 +15,4 @@ response.setStatusLine(request.httpVersion, 200, "OK"); response.write("Page was accessed over HTTPS!"); } - } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/sjs_json-test-server.sjs firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/sjs_json-test-server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/sjs_json-test-server.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/sjs_json-test-server.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -2,7 +2,6 @@ http://creativecommons.org/publicdomain/zero/1.0/ */ function handleRequest(request, response) { - response.setStatusLine(request.httpVersion, 200, "OK"); response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); response.setHeader("Pragma", "no-cache"); @@ -12,14 +11,14 @@ // This server checks the name parameter from the request to decide which JSON object to // return. - let params = request.queryString.split("&"); - let name = (params.filter((s) => s.includes("name="))[0] || "").split("=")[1]; + const params = request.queryString.split("&"); + const name = (params.filter(s => s.includes("name="))[0] || "").split("=")[1]; switch (name) { case "null": - response.write("{ \"greeting\": null }"); + response.write('{ "greeting": null }'); break; case "nogrip": - response.write("{\"obj\": {\"type\": \"string\" }}"); + response.write('{"obj": {"type": "string" }}'); break; case "empty": response.write("{}"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/sjs_method-test-server.sjs firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/sjs_method-test-server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/sjs_method-test-server.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/sjs_method-test-server.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -3,9 +3,11 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ const CC = Components.Constructor; -const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", "nsIBinaryInputStream", - "setInputStream"); + "setInputStream" +); function handleRequest(request, response) { response.setStatusLine(request.httpVersion, 200, "Och Aye"); @@ -14,12 +16,18 @@ var body = ""; if (request.method == "POST") { var bodyStream = new BinaryInputStream(request.bodyInputStream); - var bytes = [], avail = 0; + var bytes = [], + avail = 0; while ((avail = bodyStream.available()) > 0) { - body += String.fromCharCode.apply(String, bodyStream.readByteArray(avail)); + body += String.fromCharCode.apply( + String, + bodyStream.readByteArray(avail) + ); } } - var contentType = request.hasHeader("content-type") ? request.getHeader("content-type") : "" + var contentType = request.hasHeader("content-type") + ? request.getHeader("content-type") + : ""; var bodyOutput = [request.method, contentType, body].join("\n"); response.bodyOutputStream.write(bodyOutput, bodyOutput.length); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/sjs_slow-script-server.sjs firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/sjs_slow-script-server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/sjs_slow-script-server.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/sjs_slow-script-server.sjs 2021-10-20 19:28:15.000000000 +0000 @@ -9,9 +9,13 @@ function handleRequest(request, response) { response.processAsync(); timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - timer.init(() => { - response.setHeader("Content-Type", "text/javascript", false); - response.write("console.log('script loaded')\n"); - response.finish(); - }, DELAY_MS, Ci.nsITimer.TYPE_ONE_SHOT); + timer.init( + () => { + response.setHeader("Content-Type", "text/javascript", false); + response.write("console.log('script loaded')\n"); + response.finish(); + }, + DELAY_MS, + Ci.nsITimer.TYPE_ONE_SHOT + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/sjs_slow-test-server.sjs firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/sjs_slow-test-server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/sjs_slow-test-server.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/sjs_slow-test-server.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -9,9 +9,15 @@ function handleRequest(request, response) { response.processAsync(); timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - timer.init(() => { - response.setHeader("Content-Type", "text/html", false); - response.write("<body>Slow loading page for netmonitor test. You should never see this.</body>"); - response.finish(); - }, DELAY_MS, Ci.nsITimer.TYPE_ONE_SHOT); + timer.init( + () => { + response.setHeader("Content-Type", "text/html", false); + response.write( + "<body>Slow loading page for netmonitor test. You should never see this.</body>" + ); + response.finish(); + }, + DELAY_MS, + Ci.nsITimer.TYPE_ONE_SHOT + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/sjs_sorting-test-server.sjs firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/sjs_sorting-test-server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/sjs_sorting-test-server.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/sjs_sorting-test-server.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -4,21 +4,32 @@ function handleRequest(request, response) { response.processAsync(); - let params = request.queryString.split("&"); - let index = params.filter((s) => s.includes("index="))[0].split("=")[1]; + const params = request.queryString.split("&"); + const index = params.filter(s => s.includes("index="))[0].split("=")[1]; let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - timer.initWithCallback(() => { - // to avoid garbage collection - timer = null; - response.setStatusLine(request.httpVersion, index == 1 ? 101 : index * 100, "Meh"); + timer.initWithCallback( + () => { + // to avoid garbage collection + timer = null; + response.setStatusLine( + request.httpVersion, + index == 1 ? 101 : index * 100, + "Meh" + ); - response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); - response.setHeader("Pragma", "no-cache"); - response.setHeader("Expires", "0"); + response.setHeader( + "Cache-Control", + "no-cache, no-store, must-revalidate" + ); + response.setHeader("Pragma", "no-cache"); + response.setHeader("Expires", "0"); - response.setHeader("Content-Type", "text/" + index, false); - response.write(new Array(index * 10).join(index)); // + 0.01 KB - response.finish(); - }, 10, Ci.nsITimer.TYPE_ONE_SHOT); // Make sure this request takes a few ms. + response.setHeader("Content-Type", "text/" + index, false); + response.write(new Array(index * 10).join(index)); // + 0.01 KB + response.finish(); + }, + 10, + Ci.nsITimer.TYPE_ONE_SHOT + ); // Make sure this request takes a few ms. } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/sjs_sse-test-server.sjs firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/sjs_sse-test-server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/sjs_sse-test-server.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/sjs_sse-test-server.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,6 +1,6 @@ -function handleRequest(request, response) { - response.processAsync(); - response.setHeader("Content-Type", "text/event-stream"); - response.write("data: Why so serious?\n\n"); - response.finish(); -} +function handleRequest(request, response) { + response.processAsync(); + response.setHeader("Content-Type", "text/event-stream"); + response.write("data: Why so serious?\n\n"); + response.finish(); +} diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/sjs_status-codes-test-server.sjs firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/sjs_status-codes-test-server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/sjs_status-codes-test-server.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/sjs_status-codes-test-server.sjs 2021-10-20 19:28:15.000000000 +0000 @@ -4,54 +4,67 @@ function handleRequest(request, response) { response.processAsync(); - let params = request.queryString.split("&"); - let status = params.filter(s => s.includes("sts="))[0].split("=")[1]; - let cached = params.filter(s => s === 'cached').length !== 0; + const params = request.queryString.split("&"); + const status = params.filter(s => s.includes("sts="))[0].split("=")[1]; + const cached = params.filter(s => s === "cached").length !== 0; let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - timer.initWithCallback(() => { - // to avoid garbage collection - timer = null; - switch (status) { - case "100": - response.setStatusLine(request.httpVersion, 101, "Switching Protocols"); - break; - case "200": - response.setStatusLine(request.httpVersion, 202, "Created"); - break; - case "300": - response.setStatusLine(request.httpVersion, 303, "See Other"); - break; - case "304": - response.setStatusLine(request.httpVersion, 304, "Not Modified"); - break; - case "400": - response.setStatusLine(request.httpVersion, 404, "Not Found"); - break; - case "500": - response.setStatusLine(request.httpVersion, 501, "Not Implemented"); - break; - case "ok": - response.setStatusLine(request.httpVersion, 200, "OK"); - break; - case "redirect": - response.setStatusLine(request.httpVersion, 301, "Moved Permanently"); - response.setHeader("Location", "http://example.com/redirected"); - break; - } + timer.initWithCallback( + () => { + // to avoid garbage collection + timer = null; + switch (status) { + case "100": + response.setStatusLine( + request.httpVersion, + 101, + "Switching Protocols" + ); + break; + case "200": + response.setStatusLine(request.httpVersion, 202, "Created"); + break; + case "300": + response.setStatusLine(request.httpVersion, 303, "See Other"); + break; + case "304": + response.setStatusLine(request.httpVersion, 304, "Not Modified"); + break; + case "400": + response.setStatusLine(request.httpVersion, 404, "Not Found"); + break; + case "500": + response.setStatusLine(request.httpVersion, 501, "Not Implemented"); + break; + case "ok": + response.setStatusLine(request.httpVersion, 200, "OK"); + break; + case "redirect": + response.setStatusLine(request.httpVersion, 301, "Moved Permanently"); + response.setHeader("Location", "http://example.com/redirected"); + break; + } - if(!cached) { - response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); - response.setHeader("Pragma", "no-cache"); - response.setHeader("Expires", "0"); - } - else { - response.setHeader("Cache-Control", "no-transform,public,max-age=300,s-maxage=900"); - response.setHeader("Expires", "Thu, 01 Dec 2100 20:00:00 GMT"); - } + if (!cached) { + response.setHeader( + "Cache-Control", + "no-cache, no-store, must-revalidate" + ); + response.setHeader("Pragma", "no-cache"); + response.setHeader("Expires", "0"); + } else { + response.setHeader( + "Cache-Control", + "no-transform,public,max-age=300,s-maxage=900" + ); + response.setHeader("Expires", "Thu, 01 Dec 2100 20:00:00 GMT"); + } - response.setHeader("Content-Type", "text/plain; charset=utf-8", false); - response.write("Hello status code " + status + "!"); - response.finish(); - }, 10, Ci.nsITimer.TYPE_ONE_SHOT); // Make sure this request takes a few ms. + response.setHeader("Content-Type", "text/plain; charset=utf-8", false); + response.write("Hello status code " + status + "!"); + response.finish(); + }, + 10, + Ci.nsITimer.TYPE_ONE_SHOT + ); // Make sure this request takes a few ms. } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/sjs_timings-test-server.sjs firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/sjs_timings-test-server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/sjs_timings-test-server.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/sjs_timings-test-server.sjs 2021-10-20 19:28:15.000000000 +0000 @@ -2,13 +2,13 @@ http://creativecommons.org/publicdomain/zero/1.0/ */ var trailerServerTiming = [ - {metric:"metric3", duration:"99789.11", description:"time3"}, - {metric:"metric4", duration:"1112.13", description:"time4"} + { metric: "metric3", duration: "99789.11", description: "time3" }, + { metric: "metric4", duration: "1112.13", description: "time4" }, ]; var responseServerTiming = [ - {metric:"metric1", duration:"123.4", description:"time1"}, - {metric:"metric2", duration:"0", description:"time2"} + { metric: "metric1", duration: "123.4", description: "time1" }, + { metric: "metric2", duration: "0", description: "time2" }, ]; function handleRequest(request, response) { @@ -29,9 +29,16 @@ function createServerTimingHeader(headerData) { var header = ""; for (var i = 0; i < headerData.length; i++) { - header += "Server-Timing: " + headerData[i].metric + ";" + - "dur=" + headerData[i].duration + ";" + - "desc=" + headerData[i].description + "\r\n"; + header += + "Server-Timing: " + + headerData[i].metric + + ";" + + "dur=" + + headerData[i].duration + + ";" + + "desc=" + + headerData[i].description + + "\r\n"; } return header; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/sjs_truncate-test-server.sjs firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/sjs_truncate-test-server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/netmonitor/test/sjs_truncate-test-server.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/netmonitor/test/sjs_truncate-test-server.sjs 2021-10-20 19:28:15.000000000 +0000 @@ -2,8 +2,10 @@ http://creativecommons.org/publicdomain/zero/1.0/ */ function handleRequest(request, response) { - let params = request.queryString.split("&"); - let limit = (params.filter((s) => s.includes("limit="))[0] || "").split("=")[1]; + const params = request.queryString.split("&"); + const limit = (params.filter(s => s.includes("limit="))[0] || "").split( + "=" + )[1]; response.setStatusLine(request.httpVersion, 200, "Och Aye"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/shared/test/browser_dbg_listtabs-01.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/shared/test/browser_dbg_listtabs-01.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/shared/test/browser_dbg_listtabs-01.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/shared/test/browser_dbg_listtabs-01.js 2021-10-20 19:28:15.000000000 +0000 @@ -13,98 +13,68 @@ const TAB1_URL = EXAMPLE_URL + "doc_empty-tab-01.html"; const TAB2_URL = EXAMPLE_URL + "doc_empty-tab-02.html"; -var gTab1, gTab1Front, gTab2, gTab2Front, gClient; - -function test() { +add_task(async function test() { DevToolsServer.init(); DevToolsServer.registerAllActors(); const transport = DevToolsServer.connectPipe(); - gClient = new DevToolsClient(transport); - gClient.connect().then(([aType, aTraits]) => { - is(aType, "browser", "Root actor should identify itself as a browser."); - - Promise.resolve(null) - .then(testFirstTab) - .then(testSecondTab) - .then(testRemoveTab) - .then(testAttachRemovedTab) - .then(() => gClient.close()) - .then(finish) - .catch(error => { - ok(false, "Got an error: " + error.message + "\n" + error.stack); - }); - }); -} - -function testFirstTab() { - return addTab(TAB1_URL).then(tab => { - gTab1 = tab; - - return getTargetActorForUrl(gClient, TAB1_URL).then(front => { - ok(front, "Should find a target actor for the first tab."); - gTab1Front = front; - }); - }); -} - -function testSecondTab() { - return addTab(TAB2_URL).then(tab => { - gTab2 = tab; - - return getTargetActorForUrl(gClient, TAB1_URL).then(firstFront => { - return getTargetActorForUrl(gClient, TAB2_URL).then(secondFront => { - is(firstFront, gTab1Front, "First tab's actor shouldn't have changed."); - ok(secondFront, "Should find a target actor for the second tab."); - gTab2Front = secondFront; - }); - }); - }); -} + const client = new DevToolsClient(transport); + const [aType] = await client.connect(); + is(aType, "browser", "Root actor should identify itself as a browser."); + + const firstTab = await testFirstTab(client); + const secondTab = await testSecondTab(client, firstTab.front); + await testRemoveTab(client, firstTab.tab); + await testAttachRemovedTab(secondTab.tab, secondTab.front); + await client.close(); +}); -function testRemoveTab() { - return removeTab(gTab1).then(() => { - return getTargetActorForUrl(gClient, TAB1_URL).then(front => { - ok(!front, "Shouldn't find a target actor for the first tab anymore."); - }); - }); -} +async function testFirstTab(client) { + const tab = await addTab(TAB1_URL); -function testAttachRemovedTab() { - return removeTab(gTab2).then(() => { - return new Promise((resolve, reject) => { - gClient.on("paused", () => { - ok( - false, - "Attaching to an exited target actor shouldn't generate a pause." - ); - reject(); - }); - - const { actorID } = gTab2Front; - gTab2Front.reconfigure({}).then(null, error => { - ok( - error.message.includes( - `Connection closed, pending request to ${actorID}, type reconfigure failed` - ), - "Actor is gone since the tab was removed." - ); - resolve(); - }); - }); - }); + const front = await getDescriptorActorForUrl(client, TAB1_URL); + ok(front, "Should find a target actor for the first tab."); + return { tab, front }; +} + +async function testSecondTab(client, firstTabFront) { + const tab = await addTab(TAB2_URL); + + const firstFront = await getDescriptorActorForUrl(client, TAB1_URL); + const secondFront = await getDescriptorActorForUrl(client, TAB2_URL); + is(firstFront, firstTabFront, "First tab's actor shouldn't have changed."); + ok(secondFront, "Should find a target actor for the second tab."); + return { tab, front: secondFront }; +} + +async function testRemoveTab(client, firstTab) { + await removeTab(firstTab); + const front = await getDescriptorActorForUrl(client, TAB1_URL); + ok(!front, "Shouldn't find a target actor for the first tab anymore."); +} + +async function testAttachRemovedTab(secondTab, secondTabFront) { + await removeTab(secondTab); + + const { actorID } = secondTabFront; + try { + await secondTabFront.getFavicon({}); + ok( + false, + "any request made to the descriptor for a closed tab should have failed" + ); + } catch (error) { + ok( + error.message.includes( + `Connection closed, pending request to ${actorID}, type getFavicon failed` + ), + "Actor is gone since the tab was removed." + ); + } } -registerCleanupFunction(function() { - gTab1 = null; - gTab1Front = null; - gTab2 = null; - gTab2Front = null; - gClient = null; -}); - -async function getTargetActorForUrl(client, url) { +async function getDescriptorActorForUrl(client, url) { const tabDescriptors = await client.mainRoot.listTabs(); const tabDescriptor = tabDescriptors.find(front => front.url == url); - return tabDescriptor?.getTarget(); + return tabDescriptor; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/shared/theme.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/shared/theme.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/shared/theme.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/shared/theme.js 2021-10-20 19:28:16.000000000 +0000 @@ -91,7 +91,6 @@ */ const addThemeObserver = (exports.addThemeObserver = observer => { Services.obs.addObserver(observer, "look-and-feel-changed"); - Services.prefs.addObserver("browser.theme.toolbar-theme", observer); Services.prefs.addObserver(THEME_PREF, observer); }); @@ -100,7 +99,6 @@ */ const removeThemeObserver = (exports.removeThemeObserver = observer => { Services.obs.removeObserver(observer, "look-and-feel-changed"); - Services.prefs.removeObserver("browser.theme.toolbar-theme", observer); Services.prefs.removeObserver(THEME_PREF, observer); }); /* eslint-enable */ diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/shared/theme-switching.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/shared/theme-switching.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/shared/theme-switching.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/shared/theme-switching.js 2021-10-20 19:28:16.000000000 +0000 @@ -15,7 +15,6 @@ const { getTheme, - getAutoTheme, addThemeObserver, removeThemeObserver, } = require("devtools/client/shared/theme"); @@ -67,7 +66,7 @@ // The theme might not be available anymore (e.g. uninstalled) // Use the default one. if (!newThemeDef) { - newThemeDef = gDevTools.getThemeDefinition(getAutoTheme()); + newThemeDef = gDevTools.getThemeDefinition("light"); } // Store the sheets in a WeakMap for access later when the theme gets diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/webconsole/actions/autocomplete.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/webconsole/actions/autocomplete.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/webconsole/actions/autocomplete.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/webconsole/actions/autocomplete.js 2021-10-20 19:28:16.000000000 +0000 @@ -11,6 +11,11 @@ AUTOCOMPLETE_RETRIEVE_FROM_CACHE, } = require("devtools/client/webconsole/constants"); +const { + analyzeInputString, + shouldInputBeAutocompleted, +} = require("devtools/shared/webconsole/analyze-input-string"); + /** * Update the data used for the autocomplete popup in the console input (JsTerm). * @@ -27,6 +32,8 @@ } const inputValue = hud.getInputValue(); + const mappedVars = hud.getMappedVariables() ?? {}; + const allVars = (expressionVars ?? []).concat(Object.keys(mappedVars)); const frameActorId = await webConsoleUI.getFrameActor(); const webconsoleFront = await webConsoleUI.getWebconsoleFront({ frameActorId, @@ -43,39 +50,31 @@ return dispatch(autocompleteClear()); } - const input = inputValue.substring(0, cursor); + const rawInput = inputValue.substring(0, cursor); const retrieveFromCache = !force && cache && cache.input && - input.startsWith(cache.input) && - /[a-zA-Z0-9]$/.test(input) && + rawInput.startsWith(cache.input) && + /[a-zA-Z0-9]$/.test(rawInput) && frameActorId === cache.frameActorId; if (retrieveFromCache) { - return dispatch(autoCompleteDataRetrieveFromCache(input)); + return dispatch(autoCompleteDataRetrieveFromCache(rawInput)); } - let authorizedEvaluations = - Array.isArray(state.authorizedEvaluations) && - state.authorizedEvaluations.length > 0 - ? state.authorizedEvaluations - : []; - - if (Array.isArray(getterPath) && getterPath.length > 0) { - // We need to check for any previous authorizations. For example, here if getterPath - // is ["a", "b", "c", "d"], we want to see if there was any other path that was - // authorized in a previous request. For that, we only add the previous - // authorizations if the last auth is contained in getterPath. (for the example, we - // would keep if it is [["a", "b"]], not if [["b"]] nor [["f", "g"]]) - const last = authorizedEvaluations[authorizedEvaluations.length - 1]; - const concat = !last || last.every((x, index) => x === getterPath[index]); - if (concat) { - authorizedEvaluations.push(getterPath); - } else { - authorizedEvaluations = [getterPath]; - } - } + const authorizedEvaluations = updateAuthorizedEvaluations( + state.authorizedEvaluations, + getterPath, + mappedVars + ); + + const { input, originalExpression } = await getMappedInput( + rawInput, + mappedVars, + hud, + webconsoleFront + ); return dispatch( autocompleteDataFetch({ @@ -84,13 +83,117 @@ webconsoleFront, authorizedEvaluations, force, - expressionVars, + allVars, + mappedVars, + originalExpression, }) ); }; } /** + * Combine or replace authorizedEvaluations with the newly authorized getter path, if any. + * @param {Array<Array<String>>} authorizedEvaluations Existing authorized evaluations (may + * be updated in place) + * @param {Array<String>} getterPath The new getter path + * @param {{[String]: String}} mappedVars Map of original to generated variable names. + * @returns {Array<Array<String>>} The updated authorized evaluations (the original array, + * if it was updated in place) */ +function updateAuthorizedEvaluations( + authorizedEvaluations, + getterPath, + mappedVars +) { + if ( + !Array.isArray(authorizedEvaluations) || + authorizedEvaluations.length == 0 + ) { + authorizedEvaluations = []; + } + + if (Array.isArray(getterPath) && getterPath.length > 0) { + // We need to check for any previous authorizations. For example, here if getterPath + // is ["a", "b", "c", "d"], we want to see if there was any other path that was + // authorized in a previous request. For that, we only add the previous + // authorizations if the last auth is contained in getterPath. (for the example, we + // would keep if it is [["a", "b"]], not if [["b"]] nor [["f", "g"]]) + const last = authorizedEvaluations[authorizedEvaluations.length - 1]; + + const generatedPath = mappedVars[getterPath[0]]?.split("."); + if (generatedPath) { + getterPath = generatedPath.concat(getterPath.slice(1)); + } + + const isMappedVariable = + generatedPath && getterPath.length === generatedPath.length; + const concat = !last || last.every((x, index) => x === getterPath[index]); + if (isMappedVariable) { + // If the path consists only of an original variable, add all the prefixes of its + // mapping. For example, for myVar => a.b.c, authorize a, a.b, and a.b.c. This + // ensures we'll only show a prompt for myVar once even if a.b and a.b.c are both + // unsafe getters. + authorizedEvaluations = generatedPath.map((_, i) => + generatedPath.slice(0, i + 1) + ); + } else if (concat) { + authorizedEvaluations.push(getterPath); + } else { + authorizedEvaluations = [getterPath]; + } + } + return authorizedEvaluations; +} + +/** + * Apply source mapping to the autocomplete input. + * @param {String} rawInput The input to map. + * @param {{[String]: String}} mappedVars Map of original to generated variable names. + * @param {WebConsole} hud A reference to the webconsole hud. + * @param {WebConsoleFront} webconsoleFront The webconsole front. + * @returns {String} The source-mapped expression to autocomplete. + */ +async function getMappedInput(rawInput, mappedVars, hud, webconsoleFront) { + if (!mappedVars || Object.keys(mappedVars).length == 0) { + return { input: rawInput, originalExpression: undefined }; + } + + const inputAnalysis = analyzeInputString(rawInput, 500); + if (!shouldInputBeAutocompleted(inputAnalysis)) { + return { input: rawInput, originalExpression: undefined }; + } + + const { + mainExpression: originalExpression, + isPropertyAccess, + isElementAccess, + lastStatement, + } = inputAnalysis; + + // If we're autocompleting a variable name, pass it through unchanged so that we + // show original variable names rather than generated ones. + // For example, if we have the mapping `myVariable` => `x`, show variables starting + // with myVariable rather than x. + if (!isPropertyAccess && !isElementAccess) { + return { input: lastStatement, originalExpression }; + } + + let generated = + (await hud.getMappedExpression(originalExpression))?.expression ?? + originalExpression; + // Strip off the semicolon if the expression was converted to a statement + const trailingSemicolon = /;\s*$/; + if ( + trailingSemicolon.test(generated) && + !trailingSemicolon.test(originalExpression) + ) { + generated = generated.slice(0, generated.lastIndexOf(";")); + } + + const suffix = lastStatement.slice(originalExpression.length); + return { input: generated + suffix, originalExpression }; +} + +/** * Called when the autocompletion data should be cleared. */ function autocompleteClear() { @@ -136,7 +239,9 @@ force, webconsoleFront, authorizedEvaluations, - expressionVars, + allVars, + mappedVars, + originalExpression, }) { return async ({ dispatch, webConsoleUI }) => { const selectedNodeActor = webConsoleUI.getSelectedNodeActorID(); @@ -150,10 +255,17 @@ frameActorId, selectedNodeActor, authorizedEvaluations, - expressionVars + allVars ) .then(data => { - dispatch( + if (data.isUnsafeGetter && originalExpression !== undefined) { + data.getterPath = unmapGetterPath( + data.getterPath, + originalExpression, + mappedVars + ); + } + return dispatch( autocompleteDataReceive({ id, input, @@ -161,7 +273,6 @@ frameActorId, data, authorizedEvaluations, - expressionVars, }) ); }) @@ -173,6 +284,38 @@ } /** + * Replace generated variable names in an unsafe getter path with their original + * counterparts. + * @param {Array<String>} getterPath Array of properties leading up to and including the + * unsafe getter. + * @param {String} originalExpression The expression that was evaluated, before mapping. + * @param {{[String]: String}} mappedVars Map of original to generated variable names. + * @returns {Array<String>} An updated getter path containing original variables. + */ +function unmapGetterPath(getterPath, originalExpression, mappedVars) { + // We know that the original expression is a sequence of property accesses, that only + // the first part can be a mapped variable, and that the getter path must start with + // its generated path or be a prefix of it. + + // Suppose we have the expression `foo.bar`, which maps to `a.b.c.bar`. + // Get the first part of the expression ("foo") + const originalVariable = /^[^.[?]*/s.exec(originalExpression)[0].trim(); + const generatedVariable = mappedVars[originalVariable]; + if (generatedVariable) { + // Get number of properties in "a.b.c" + const generatedVariableParts = generatedVariable.split("."); + // Replace ["a", "b", "c"] with "foo" in the getter path. + // Note that this will also work if the getter path ends inside of the mapped + // variable, like ["a", "b"]. + return [ + originalVariable, + ...getterPath.slice(generatedVariableParts.length), + ]; + } + return getterPath; +} + +/** * Called when we receive the autocompletion data from the server. * * @param {Object} Object of the following shape: diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/webconsole/test/browser/browser_jsterm_autocomplete_mapped_variables.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/webconsole/test/browser/browser_jsterm_autocomplete_mapped_variables.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/webconsole/test/browser/browser_jsterm_autocomplete_mapped_variables.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/webconsole/test/browser/browser_jsterm_autocomplete_mapped_variables.js 2021-10-20 19:28:16.000000000 +0000 @@ -0,0 +1,130 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that makes sure source mapped variables appear in autocompletion +// on an equal footing with variables from the generated source. + +"use strict"; +/* import-globals-from head.js*/ + +const TEST_URI = + "http://example.com/browser/devtools/client/webconsole/" + + "test/browser/test-autocomplete-mapped.html"; + +add_task(async function() { + const hud = await openNewTabAndConsole(TEST_URI); + const { jsterm } = hud; + const { autocompletePopup: popup } = jsterm; + const toolbox = await gDevTools.getToolboxForTab(gBrowser.selectedTab); + + info("Opening Debugger and enabling map scopes"); + await openDebugger(); + const dbg = createDebuggerContext(toolbox); + dbg.actions.toggleMapScopes(); + + info("Waiting for pause"); + // This calls firstCall() on the content page and waits for pause. (firstCall + // has a debugger statement) + await pauseDebugger(dbg); + + await toolbox.selectTool("webconsole"); + await setInputValueForAutocompletion(hud, "valu"); + ok( + hasExactPopupLabels(popup, ["value", "valueOf", "values"]), + "Autocomplete popup displays original variable name" + ); + + await setInputValueForAutocompletion(hud, "temp"); + ok( + hasExactPopupLabels(popup, ["temp", "temp2"]), + "Autocomplete popup displays original variable name when entering a complete variable name" + ); + + await setInputValueForAutocompletion(hud, "t"); + ok( + hasPopupLabel(popup, "t"), + "Autocomplete popup displays generated variable name" + ); + + await setInputValueForAutocompletion(hud, "value.to"); + ok( + hasPopupLabel(popup, "toString"), + "Autocomplete popup displays properties of original variable" + ); + + await setInputValueForAutocompletion(hud, "imported.imp"); + ok( + hasPopupLabel(popup, "importResult"), + "Autocomplete popup displays properties of multi-part variable" + ); + + let tooltip = await setInputValueForGetterConfirmDialog( + toolbox, + hud, + "getter." + ); + let labelEl = tooltip.querySelector(".confirm-label"); + is( + labelEl.textContent, + "Invoke getter getter to retrieve the property list?", + "Dialog has expected text content" + ); + + info( + "Check that getter confirmation on a variable that maps to two getters invokes both getters" + ); + let onPopUpOpen = popup.once("popup-opened"); + EventUtils.synthesizeKey("KEY_Tab"); + await onPopUpOpen; + ok(popup.isOpen, "popup is open after Tab"); + ok(hasPopupLabel(popup, "getterResult"), "popup has expected items"); + + info( + "Check that the getter confirmation dialog shows the original variable name" + ); + tooltip = await setInputValueForGetterConfirmDialog( + toolbox, + hud, + "localWithGetter.value." + ); + labelEl = tooltip.querySelector(".confirm-label"); + is( + labelEl.textContent, + "Invoke getter localWithGetter.value to retrieve the property list?", + "Dialog has expected text content" + ); + + info( + "Check that hitting Tab does invoke the getter and return its properties" + ); + onPopUpOpen = popup.once("popup-opened"); + EventUtils.synthesizeKey("KEY_Tab"); + await onPopUpOpen; + ok(popup.isOpen, "popup is open after Tab"); + ok(hasPopupLabel(popup, "then"), "popup has expected items"); + info("got popup items: " + JSON.stringify(getAutocompletePopupLabels(popup))); + + info( + "Check that authorizing an original getter applies to the generated getter" + ); + await setInputValueForAutocompletion(hud, "o.value."); + ok(hasPopupLabel(popup, "then"), "popup has expected items"); + + await setInputValueForAutocompletion(hud, "(temp + temp2)."); + ok( + hasPopupLabel(popup, "toFixed"), + "Autocomplete popup displays properties of eagerly evaluated value" + ); + info("got popup items: " + JSON.stringify(getAutocompletePopupLabels(popup))); + + info("Disabling map scopes"); + dbg.actions.toggleMapScopes(); + await setInputValueForAutocompletion(hud, "tem"); + const autocompleteLabels = getAutocompletePopupLabels(popup); + ok( + !autocompleteLabels.includes("temp"), + "Autocomplete popup does not display mapped variables when mapping is disabled" + ); + + await resume(dbg); +}); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/webconsole/test/browser/_jsterm.ini firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/webconsole/test/browser/_jsterm.ini --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/webconsole/test/browser/_jsterm.ini 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/webconsole/test/browser/_jsterm.ini 2021-10-20 19:28:16.000000000 +0000 @@ -4,6 +4,10 @@ support-files = head.js test-autocomplete-in-stackframe.html + test-autocomplete-mapped.html + test-autocomplete-mapped.js + test-autocomplete-mapped.js.map + test-autocomplete-mapped.src.js test-block-action.html test-block-action-style.css test-console-evaluation-context-selector-child.html @@ -54,6 +58,7 @@ skip-if = (os == "win" && os_version == "6.1") # Bug 1620521 [browser_jsterm_autocomplete_inside_text.js] skip-if = (os == "win" && os_version == "6.1") # Bug 1620521 +[browser_jsterm_autocomplete_mapped_variables.js] [browser_jsterm_autocomplete_native_getters.js] [browser_jsterm_autocomplete_nav_and_tab_key.js] [browser_jsterm_autocomplete_null.js] diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/webconsole/test/browser/test-autocomplete-mapped.html firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/webconsole/test/browser/test-autocomplete-mapped.html --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/webconsole/test/browser/test-autocomplete-mapped.html 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/webconsole/test/browser/test-autocomplete-mapped.html 2021-10-20 19:28:16.000000000 +0000 @@ -0,0 +1,15 @@ +<!DOCTYPE HTML> +<html dir="ltr" lang="en"> + <head> + <meta charset="utf8"> + <!-- + - Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ + --> + <title>Test for autocomplete displaying mapped variable names + + + +

Hello world!

+ + diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/webconsole/test/browser/test-autocomplete-mapped.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/webconsole/test/browser/test-autocomplete-mapped.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/webconsole/test/browser/test-autocomplete-mapped.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/webconsole/test/browser/test-autocomplete-mapped.js 2021-10-20 19:28:16.000000000 +0000 @@ -0,0 +1,18 @@ +"use strict"; +const i = { x: { y: { importResult: true } } }; +const j = { get x() { return blackbox({ get y() { return blackbox({ getterResult: 1 }); } }); } }; + +const blackbox = x=>[x].pop(); + +function firstCall() { + const t = 42; + const u = i.x.y; + const v = j.x.y.getterResult; + const o = { + get value() { + return blackbox(Promise.resolve()); + } + }; + debugger; +} +//# sourceMappingURL=test-autocomplete-mapped.js.map diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/webconsole/test/browser/test-autocomplete-mapped.js.map firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/webconsole/test/browser/test-autocomplete-mapped.js.map --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/webconsole/test/browser/test-autocomplete-mapped.js.map 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/webconsole/test/browser/test-autocomplete-mapped.js.map 2021-10-20 19:28:16.000000000 +0000 @@ -0,0 +1 @@ +{"version":3,"sources":["test-autocomplete-mapped.src.js"],"names":["blackbox","x","pop","firstCall","value","imported","getter","localWithGetter","Promise","resolve"],"mappings":"AAAA;AACA,MAAS,CAAQ;AACjB,MAAS,CAAM;;AAEf,MAAMA,WAAWC,GAAK,CAACA,GAAGC;;AAE1B,SAASC;EACP,MAAMC,IAAQ;EACd,MAAMC,IAAO,KAAQ;EACrB,MAAMC,IAAQ,kBAAM;EACpB,MAAMC,IAAkB;IACtBH;MAAc,OAAOJ,SAASQ,QAAQC;;;EAGxC","file":"test-autocomplete-mapped.js"} \ No newline at end of file diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/webconsole/test/browser/test-autocomplete-mapped.src.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/webconsole/test/browser/test-autocomplete-mapped.src.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/webconsole/test/browser/test-autocomplete-mapped.src.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/webconsole/test/browser/test-autocomplete-mapped.src.js 2021-10-20 19:28:16.000000000 +0000 @@ -0,0 +1,16 @@ +"use strict"; +import { imported } from "somewhere"; +import { getter } from "somewhere-else"; + +const blackbox = x => [x].pop(); + +function firstCall() { + const value = 42; + const temp = imported; + const temp2 = getter; + const localWithGetter = { + get value() { return blackbox(Promise.resolve()); } + }; + const unmapped = 100; + debugger; +} diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/webconsole/test/browser/test_hsts-invalid-headers.sjs firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/webconsole/test/browser/test_hsts-invalid-headers.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/webconsole/test/browser/test_hsts-invalid-headers.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/webconsole/test/browser/test_hsts-invalid-headers.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,14 +1,13 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setHeader("Content-Type", "text/plain; charset=utf-8", false); let issue; switch (request.queryString) { case "badSyntax": - response.setHeader("Strict-Transport-Security", "\""); + response.setHeader("Strict-Transport-Security", '"'); issue = "is not syntactically correct."; break; case "noMaxAge": @@ -24,13 +23,17 @@ issue = "includes an invalid max-age directive."; break; case "multipleIncludeSubDomains": - response.setHeader("Strict-Transport-Security", - "includeSubDomains; includeSubDomains"); + response.setHeader( + "Strict-Transport-Security", + "includeSubDomains; includeSubDomains" + ); issue = "includes multiple includeSubDomains directives."; break; case "multipleMaxAge": - response.setHeader("Strict-Transport-Security", - "max-age=444; max-age=999"); + response.setHeader( + "Strict-Transport-Security", + "max-age=444; max-age=999" + ); issue = "includes multiple max-age directives."; break; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/webconsole/webconsole.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/webconsole/webconsole.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/client/webconsole/webconsole.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/client/webconsole/webconsole.js 2021-10-20 19:28:16.000000000 +0000 @@ -322,6 +322,11 @@ return null; } + getMappedVariables() { + const { toolbox } = this; + return toolbox?.getPanel("jsdebugger")?.getMappedVariables(); + } + get parserService() { if (this._parserService) { return this._parserService; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/server/actors/webconsole.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/server/actors/webconsole.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/server/actors/webconsole.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/server/actors/webconsole.js 2021-10-20 19:28:16.000000000 +0000 @@ -198,11 +198,6 @@ "last-pb-context-exited" ); } - - this.traits = { - // Supports retrieving blocked urls - blockedUrls: true, - }; }, /** * Debugger instance. @@ -240,12 +235,6 @@ conn: null, /** - * List of supported features by the console actor. - * @type object - */ - traits: null, - - /** * The global we work with (this can be a Window, a Worker global or even a Sandbox * for processes and addons). * @@ -794,7 +783,6 @@ return { startedListeners: startedListeners, nativeConsoleAPI: this.hasNativeConsoleAPI(this.global), - traits: this.traits, }; }, diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/server/tests/chrome/inspector-delay-image-response.sjs firefox-trunk-95.0~a1~hg20211020r596404/devtools/server/tests/chrome/inspector-delay-image-response.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/server/tests/chrome/inspector-delay-image-response.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/server/tests/chrome/inspector-delay-image-response.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -5,16 +5,18 @@ // A 1x1 PNG image. // Source: https://commons.wikimedia.org/wiki/File:1x1.png (Public Domain) -const IMAGE = atob("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAA" + - "ACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII="); +const IMAGE = atob( + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAA" + + "ACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=" +); // To avoid GC. let timer = null; function handleRequest(request, response) { - let query = {}; + const query = {}; request.queryString.split("&").forEach(function(val) { - let [name, value] = val.split("="); + const [name, value] = val.split("="); query[name] = unescape(value); }); @@ -33,8 +35,12 @@ const nsITimer = Components.interfaces.nsITimer; timer = Components.classes["@mozilla.org/timer;1"].createInstance(nsITimer); - timer.initWithCallback(function() { - response.write(IMAGE); - response.finish(); - }, query.delay, nsITimer.TYPE_ONE_SHOT); + timer.initWithCallback( + function() { + response.write(IMAGE); + response.finish(); + }, + query.delay, + nsITimer.TYPE_ONE_SHOT + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/shared/commands/inspected-window/tests/inspectedwindow-reload-target.sjs firefox-trunk-95.0~a1~hg20211020r596404/devtools/shared/commands/inspected-window/tests/inspectedwindow-reload-target.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/shared/commands/inspected-window/tests/inspectedwindow-reload-target.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/shared/commands/inspected-window/tests/inspectedwindow-reload-target.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,9 +1,9 @@ Components.utils.importGlobalProperties(["URLSearchParams"]); function handleRequest(request, response) { - let params = new URLSearchParams(request.queryString); + const params = new URLSearchParams(request.queryString); - switch(params.get("test")) { + switch (params.get("test")) { case "cache": handleCacheTestRequest(request, response); break; @@ -22,7 +22,9 @@ response.setHeader("Content-Type", "text/plain; charset=UTF-8", false); if (request.hasHeader("pragma") && request.hasHeader("cache-control")) { - response.write(`${request.getHeader("pragma")}:${request.getHeader("cache-control")}`); + response.write( + `${request.getHeader("pragma")}:${request.getHeader("cache-control")}` + ); } else { response.write("empty cache headers"); } @@ -47,11 +49,12 @@ if (frames > 0) { // Output an iframe in seamless mode, so that there is an higher chance that in case // of test failures we get a screenshot where the nested iframes are all visible. - content = ``; + content = ``; } else { // Output an about:srcdoc frame to be sure that inspectedWindow.eval is able to // evaluate js code into it. - let srcdoc = ` + const srcdoc = `
injected script NOT executed
`; @@ -80,4 +83,4 @@ `); -} \ No newline at end of file +} diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/shared/commands/resource/tests/sse_backend.sjs firefox-trunk-95.0~a1~hg20211020r596404/devtools/shared/commands/resource/tests/sse_backend.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/shared/commands/resource/tests/sse_backend.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/shared/commands/resource/tests/sse_backend.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,6 +1,6 @@ -function handleRequest(request, response) { - response.processAsync(); - response.setHeader("Content-Type", "text/event-stream"); - response.write("data: Why so serious?\n\n"); - response.finish(); -} +function handleRequest(request, response) { + response.processAsync(); + response.setHeader("Content-Type", "text/event-stream"); + response.write("data: Why so serious?\n\n"); + response.finish(); +} diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/shared/commands/target/tests/incremental-js-value-script.sjs firefox-trunk-95.0~a1~hg20211020r596404/devtools/shared/commands/target/tests/incremental-js-value-script.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/shared/commands/target/tests/incremental-js-value-script.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/shared/commands/target/tests/incremental-js-value-script.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,11 +1,11 @@ function handleRequest(request, response) { - let Etag = '"4d881ab-b03-435f0a0f9ef00"'; - let IfNoneMatch = request.hasHeader("If-None-Match") - ? request.getHeader("If-None-Match") - : ""; + const Etag = '"4d881ab-b03-435f0a0f9ef00"'; + const IfNoneMatch = request.hasHeader("If-None-Match") + ? request.getHeader("If-None-Match") + : ""; var counter = getState("cache-counter") || 1; - let page = "" + counter; + const page = "" + counter; setState("cache-counter", "" + (parseInt(counter) + 1)); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/shared/compatibility/dataset/browsers.json firefox-trunk-95.0~a1~hg20211020r596404/devtools/shared/compatibility/dataset/browsers.json --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/shared/compatibility/dataset/browsers.json 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/shared/compatibility/dataset/browsers.json 2021-10-20 19:28:16.000000000 +0000 @@ -1 +1 @@ -{"chrome":{"name":"Chrome","preview_name":"Canary","pref_url":"chrome://flags","releases":{"1":{"release_date":"2008-12-11","release_notes":"https://chromereleases.googleblog.com/2008/12/stable-release-google-chrome-is-out-of.html","status":"retired","engine":"WebKit","engine_version":"528"},"2":{"release_date":"2009-05-21","release_notes":"https://chromereleases.googleblog.com/2009/05/stable-update-google-chrome-2017228.html","status":"retired","engine":"WebKit","engine_version":"530"},"3":{"release_date":"2009-09-15","release_notes":"https://chromereleases.googleblog.com/2009/09/stable-channel-update.html","status":"retired","engine":"WebKit","engine_version":"532"},"4":{"release_date":"2010-01-25","release_notes":"https://chromereleases.googleblog.com/2010/01/stable-channel-update_25.html","status":"retired","engine":"WebKit","engine_version":"532.5"},"5":{"release_date":"2010-05-25","release_notes":"https://chromereleases.googleblog.com/2010/05/stable-channel-update.html","status":"retired","engine":"WebKit","engine_version":"533"},"6":{"release_date":"2010-09-02","release_notes":"https://chromereleases.googleblog.com/2010/09/stable-and-beta-channel-updates.html","status":"retired","engine":"WebKit","engine_version":"534.3"},"7":{"release_date":"2010-10-19","release_notes":"https://chromereleases.googleblog.com/2010/10/stable-channel-update.html","status":"retired","engine":"WebKit","engine_version":"534.7"},"8":{"release_date":"2010-12-02","release_notes":"https://chromereleases.googleblog.com/2010/12/stable-beta-channel-updates.html","status":"retired","engine":"WebKit","engine_version":"534.10"},"9":{"release_date":"2011-02-03","release_notes":"https://chromereleases.googleblog.com/2011/02/stable-channel-update.html","status":"retired","engine":"WebKit","engine_version":"534.13"},"10":{"release_date":"2011-03-08","release_notes":"https://chromereleases.googleblog.com/2011/03/chrome-stable-release.html","status":"retired","engine":"WebKit","engine_version":"534.16"},"11":{"release_date":"2011-04-27","release_notes":"https://chromereleases.googleblog.com/2011/04/chrome-stable-update.html","status":"retired","engine":"WebKit","engine_version":"534.24"},"12":{"release_date":"2011-06-07","release_notes":"https://chromereleases.googleblog.com/2011/06/chrome-stable-release.html","status":"retired","engine":"WebKit","engine_version":"534.30"},"13":{"release_date":"2011-08-02","release_notes":"https://chromereleases.googleblog.com/2011/08/stable-channel-update.html","status":"retired","engine":"WebKit","engine_version":"535.1"},"14":{"release_date":"2011-09-16","release_notes":"https://chromereleases.googleblog.com/2011/09/stable-channel-update_16.html","status":"retired","engine":"WebKit","engine_version":"535.1"},"15":{"release_date":"2011-10-25","release_notes":"https://chromereleases.googleblog.com/2011/10/chrome-stable-release.html","status":"retired","engine":"WebKit","engine_version":"535.2"},"16":{"release_date":"2011-12-13","release_notes":"https://chromereleases.googleblog.com/2011/12/stable-channel-update.html","status":"retired","engine":"WebKit","engine_version":"535.7"},"17":{"release_date":"2012-02-08","release_notes":"https://chromereleases.googleblog.com/2012/02/stable-channel-update.html","status":"retired","engine":"WebKit","engine_version":"535.11"},"18":{"release_date":"2012-03-28","release_notes":"https://chromereleases.googleblog.com/2012/03/stable-channel-release-and-beta-channel.html","status":"retired","engine":"WebKit","engine_version":"535.19"},"19":{"release_date":"2012-05-15","release_notes":"https://chromereleases.googleblog.com/2012/05/stable-channel-update.html","status":"retired","engine":"WebKit","engine_version":"536.5"},"20":{"release_date":"2012-06-26","release_notes":"https://chromereleases.googleblog.com/2012/06/stable-channel-update_26.html","status":"retired","engine":"WebKit","engine_version":"536.10"},"21":{"release_date":"2012-07-31","release_notes":"https://chromereleases.googleblog.com/2012/07/stable-channel-release.html","status":"retired","engine":"WebKit","engine_version":"537.1"},"22":{"release_date":"2012-09-25","release_notes":"https://chromereleases.googleblog.com/2012/09/stable-channel-update_25.html","status":"retired","engine":"WebKit","engine_version":"537.4"},"23":{"release_date":"2012-11-06","release_notes":"https://chromereleases.googleblog.com/2012/11/stable-channel-release-and-beta-channel.html","status":"retired","engine":"WebKit","engine_version":"537.11"},"24":{"release_date":"2013-01-10","release_notes":"https://chromereleases.googleblog.com/2013/01/stable-channel-update.html","status":"retired","engine":"WebKit","engine_version":"537.17"},"25":{"release_date":"2013-02-21","release_notes":"https://chromereleases.googleblog.com/2013/02/stable-channel-update_21.html","status":"retired","engine":"WebKit","engine_version":"537.22"},"26":{"release_date":"2013-03-26","release_notes":"https://chromereleases.googleblog.com/2013/03/stable-channel-update_26.html","status":"retired","engine":"WebKit","engine_version":"537.31"},"27":{"release_date":"2013-05-21","release_notes":"https://chromereleases.googleblog.com/2013/05/stable-channel-release.html","status":"retired","engine":"WebKit","engine_version":"537.36"},"28":{"release_date":"2013-07-09","release_notes":"https://chromereleases.googleblog.com/2013/07/stable-channel-update.html","status":"retired","engine":"Blink","engine_version":"28"},"29":{"release_date":"2013-08-20","release_notes":"https://chromereleases.googleblog.com/2013/08/stable-channel-update.html","status":"retired","engine":"Blink","engine_version":"29"},"30":{"release_date":"2013-10-01","release_notes":"https://chromereleases.googleblog.com/2013/10/stable-channel-update.html","status":"retired","engine":"Blink","engine_version":"30"},"31":{"release_date":"2013-11-12","release_notes":"https://chromereleases.googleblog.com/2013/11/stable-channel-update.html","status":"retired","engine":"Blink","engine_version":"31"},"32":{"release_date":"2014-01-14","release_notes":"https://chromereleases.googleblog.com/2014/01/stable-channel-update.html","status":"retired","engine":"Blink","engine_version":"32"},"33":{"release_date":"2014-02-20","release_notes":"https://chromereleases.googleblog.com/2014/02/stable-channel-update_20.html","status":"retired","engine":"Blink","engine_version":"33"},"34":{"release_date":"2014-04-08","release_notes":"https://chromereleases.googleblog.com/2014/04/stable-channel-update.html","status":"retired","engine":"Blink","engine_version":"34"},"35":{"release_date":"2014-05-20","release_notes":"https://chromereleases.googleblog.com/2014/05/stable-channel-update_20.html","status":"retired","engine":"Blink","engine_version":"35"},"36":{"release_date":"2014-07-16","release_notes":"https://chromereleases.googleblog.com/2014/07/stable-channel-update.html","status":"retired","engine":"Blink","engine_version":"36"},"37":{"release_date":"2014-08-26","release_notes":"https://chromereleases.googleblog.com/2014/08/stable-channel-update_26.html","status":"retired","engine":"Blink","engine_version":"37"},"38":{"release_date":"2014-10-07","release_notes":"https://chromereleases.googleblog.com/2014/10/stable-channel-update.html","status":"retired","engine":"Blink","engine_version":"38"},"39":{"release_date":"2014-11-18","release_notes":"https://chromereleases.googleblog.com/2014/11/stable-channel-update_18.html","status":"retired","engine":"Blink","engine_version":"39"},"40":{"release_date":"2015-01-21","release_notes":"https://chromereleases.googleblog.com/2015/01/stable-update.html","status":"retired","engine":"Blink","engine_version":"40"},"41":{"release_date":"2015-03-03","release_notes":"https://chromereleases.googleblog.com/2015/03/stable-channel-update.html","status":"retired","engine":"Blink","engine_version":"41"},"42":{"release_date":"2015-04-14","release_notes":"https://chromereleases.googleblog.com/2015/04/stable-channel-update_14.html","status":"retired","engine":"Blink","engine_version":"42"},"43":{"release_date":"2015-05-19","release_notes":"https://chromereleases.googleblog.com/2015/05/stable-channel-update_19.html","status":"retired","engine":"Blink","engine_version":"43"},"44":{"release_date":"2015-07-21","release_notes":"https://chromereleases.googleblog.com/2015/07/stable-channel-update_21.html","status":"retired","engine":"Blink","engine_version":"44"},"45":{"release_date":"2015-09-01","release_notes":"https://chromereleases.googleblog.com/2015/09/stable-channel-update.html","status":"retired","engine":"Blink","engine_version":"45"},"46":{"release_date":"2015-10-13","release_notes":"https://chromereleases.googleblog.com/2015/10/stable-channel-update.html","status":"retired","engine":"Blink","engine_version":"46"},"47":{"release_date":"2015-12-01","release_notes":"https://chromereleases.googleblog.com/2015/12/stable-channel-update.html","status":"retired","engine":"Blink","engine_version":"47"},"48":{"release_date":"2016-01-20","release_notes":"https://chromereleases.googleblog.com/2016/01/stable-channel-update_20.html","status":"retired","engine":"Blink","engine_version":"48"},"49":{"release_date":"2016-03-02","release_notes":"https://chromereleases.googleblog.com/2016/03/stable-channel-update.html","status":"retired","engine":"Blink","engine_version":"49"},"50":{"release_date":"2016-04-13","release_notes":"https://chromereleases.googleblog.com/2016/04/stable-channel-update_13.html","status":"retired","engine":"Blink","engine_version":"50"},"51":{"release_date":"2016-05-25","release_notes":"https://chromereleases.googleblog.com/2016/05/stable-channel-update_25.html","status":"retired","engine":"Blink","engine_version":"51"},"52":{"release_date":"2016-07-20","release_notes":"https://chromereleases.googleblog.com/2016/07/stable-channel-update.html","status":"retired","engine":"Blink","engine_version":"52"},"53":{"release_date":"2016-08-31","release_notes":"https://chromereleases.googleblog.com/2016/08/stable-channel-update-for-desktop_31.html","status":"retired","engine":"Blink","engine_version":"53"},"54":{"release_date":"2016-10-12","release_notes":"https://chromereleases.googleblog.com/2016/10/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"54"},"55":{"release_date":"2016-12-01","release_notes":"https://chromereleases.googleblog.com/2016/12/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"55"},"56":{"release_date":"2017-01-25","release_notes":"https://chromereleases.googleblog.com/2017/01/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"56"},"57":{"release_date":"2017-03-09","release_notes":"https://chromereleases.googleblog.com/2017/03/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"57"},"58":{"release_date":"2017-04-19","release_notes":"https://chromereleases.googleblog.com/2017/04/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"58"},"59":{"release_date":"2017-06-05","release_notes":"https://chromereleases.googleblog.com/2017/06/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"59"},"60":{"release_date":"2017-07-25","release_notes":"https://chromereleases.googleblog.com/2017/07/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"60"},"61":{"release_date":"2017-09-05","release_notes":"https://chromereleases.googleblog.com/2017/09/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"61"},"62":{"release_date":"2017-10-17","release_notes":"https://chromereleases.googleblog.com/2017/10/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"62"},"63":{"release_date":"2017-12-06","release_notes":"https://chromereleases.googleblog.com/2017/12/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"63"},"64":{"release_date":"2018-01-23","release_notes":"https://chromereleases.googleblog.com/2018/01/stable-channel-update-for-desktop_24.html","status":"retired","engine":"Blink","engine_version":"64"},"65":{"release_date":"2018-03-06","release_notes":"https://chromereleases.googleblog.com/2018/03/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"65"},"66":{"release_date":"2018-04-17","release_notes":"https://chromereleases.googleblog.com/2018/04/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"66"},"67":{"release_date":"2018-05-29","release_notes":"https://chromereleases.googleblog.com/2018/05/stable-channel-update-for-desktop_58.html","status":"retired","engine":"Blink","engine_version":"67"},"68":{"release_date":"2018-07-24","release_notes":"https://chromereleases.googleblog.com/2018/07/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"68"},"69":{"release_date":"2018-09-04","release_notes":"https://chromereleases.googleblog.com/2018/09/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"69"},"70":{"release_date":"2018-10-16","release_notes":"https://chromereleases.googleblog.com/2018/10/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"70"},"71":{"release_date":"2018-12-04","release_notes":"https://chromereleases.googleblog.com/2018/12/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"71"},"72":{"release_date":"2019-01-29","release_notes":"https://chromereleases.googleblog.com/2019/01/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"72"},"73":{"release_date":"2019-03-12","release_notes":"https://chromereleases.googleblog.com/2019/03/stable-channel-update-for-desktop_12.html","status":"retired","engine":"Blink","engine_version":"73"},"74":{"release_date":"2019-04-23","release_notes":"https://chromereleases.googleblog.com/2019/04/stable-channel-update-for-desktop_23.html","status":"retired","engine":"Blink","engine_version":"74"},"75":{"release_date":"2019-06-04","release_notes":"https://chromereleases.googleblog.com/2019/06/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"75"},"76":{"release_date":"2019-07-30","release_notes":"https://chromereleases.googleblog.com/2019/07/stable-channel-update-for-desktop_30.html","status":"retired","engine":"Blink","engine_version":"76"},"77":{"release_date":"2019-09-10","release_notes":"https://chromereleases.googleblog.com/2019/09/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"77"},"78":{"release_date":"2019-10-22","release_notes":"https://chromereleases.googleblog.com/2019/10/stable-channel-update-for-desktop_22.html","status":"retired","engine":"Blink","engine_version":"78"},"79":{"release_date":"2019-12-10","release_notes":"https://chromereleases.googleblog.com/2019/12/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"79"},"80":{"release_date":"2020-02-04","release_notes":"https://chromereleases.googleblog.com/2020/02/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"80"},"81":{"release_date":"2020-04-07","release_notes":"https://chromereleases.googleblog.com/2020/04/stable-channel-update-for-desktop_7.html","status":"retired","engine":"Blink","engine_version":"81"},"83":{"release_date":"2020-05-19","release_notes":"https://chromereleases.googleblog.com/2020/05/stable-channel-update-for-desktop_19.html","status":"retired","engine":"Blink","engine_version":"83"},"84":{"release_date":"2020-07-27","release_notes":"https://chromereleases.googleblog.com/2020/07/stable-channel-update-for-desktop_27.html","status":"retired","engine":"Blink","engine_version":"84"},"85":{"release_date":"2020-08-25","release_notes":"https://chromereleases.googleblog.com/2020/08/stable-channel-update-for-desktop_25.html","status":"retired","engine":"Blink","engine_version":"85"},"86":{"release_date":"2020-10-20","release_notes":"https://chromereleases.googleblog.com/2020/10/stable-channel-update-for-desktop_20.html","status":"retired","engine":"Blink","engine_version":"86"},"87":{"release_date":"2020-11-17","release_notes":"https://chromereleases.googleblog.com/2020/11/stable-channel-update-for-desktop_17.html","status":"retired","engine":"Blink","engine_version":"87"},"88":{"release_date":"2021-01-19","release_notes":"https://chromereleases.googleblog.com/2021/01/stable-channel-update-for-desktop_19.html","status":"retired","engine":"Blink","engine_version":"88"},"89":{"release_date":"2021-03-02","release_notes":"https://chromereleases.googleblog.com/2021/03/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"89"},"90":{"release_date":"2021-04-13","release_notes":"https://chromereleases.googleblog.com/2021/04/stable-channel-update-for-desktop_14.html","status":"retired","engine":"Blink","engine_version":"90"},"91":{"release_date":"2021-05-25","release_notes":"https://chromereleases.googleblog.com/2021/05/stable-channel-update-for-desktop_25.html","status":"retired","engine":"Blink","engine_version":"91"},"92":{"release_date":"2021-07-20","release_notes":"https://chromereleases.googleblog.com/2021/07/stable-channel-update-for-desktop_20.html","status":"current","engine":"Blink","engine_version":"92"},"93":{"status":"beta","engine":"Blink","engine_version":"93"},"94":{"status":"nightly","engine":"Blink","engine_version":"94"}}},"chrome_android":{"name":"Chrome Android","pref_url":"chrome://flags","releases":{"18":{"release_date":"2012-06-27","release_notes":"https://chromereleases.googleblog.com/2012/06/chrome-for-android-out-of-beta.html","status":"retired","engine":"WebKit","engine_version":"535.19"},"25":{"release_date":"2013-02-27","release_notes":"https://chromereleases.googleblog.com/2013/02/chrome-for-android-update.html","status":"retired","engine":"WebKit","engine_version":"537.22"},"26":{"release_date":"2013-04-03","release_notes":"https://chromereleases.googleblog.com/2013/04/chrome-for-android-stable-channel-update.html","status":"retired","engine":"WebKit","engine_version":"537.31"},"27":{"release_date":"2013-05-22","release_notes":"https://chromereleases.googleblog.com/2013/05/chrome-for-android-update.html","status":"retired","engine":"WebKit","engine_version":"537.36"},"28":{"release_date":"2013-07-10","release_notes":"https://chromereleases.googleblog.com/2013/07/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"28"},"29":{"release_date":"2013-08-21","release_notes":"https://chromereleases.googleblog.com/2013/08/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"29"},"30":{"release_date":"2013-10-02","release_notes":"https://chromereleases.googleblog.com/2013/10/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"30"},"31":{"release_date":"2013-11-14","release_notes":"https://chromereleases.googleblog.com/2013/11/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"31"},"32":{"release_date":"2014-01-15","release_notes":"https://chromereleases.googleblog.com/2014/01/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"32"},"33":{"release_date":"2014-02-26","release_notes":"https://chromereleases.googleblog.com/2014/02/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"33"},"34":{"release_date":"2014-04-02","release_notes":"https://chromereleases.googleblog.com/2014/04/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"34"},"35":{"release_date":"2014-05-20","release_notes":"https://chromereleases.googleblog.com/2014/05/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"35"},"36":{"release_date":"2014-07-16","release_notes":"https://chromereleases.googleblog.com/2014/07/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"36"},"37":{"release_date":"2014-09-03","release_notes":"https://chromereleases.googleblog.com/2014/09/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"37"},"38":{"release_date":"2014-10-08","release_notes":"https://chromereleases.googleblog.com/2014/10/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"38"},"39":{"release_date":"2014-11-12","release_notes":"https://chromereleases.googleblog.com/2014/11/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"39"},"40":{"release_date":"2015-01-21","release_notes":"https://chromereleases.googleblog.com/2015/01/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"40"},"41":{"release_date":"2015-03-11","release_notes":"https://chromereleases.googleblog.com/2015/03/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"41"},"42":{"release_date":"2015-04-15","release_notes":"https://chromereleases.googleblog.com/2015/04/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"42"},"43":{"release_date":"2015-05-27","release_notes":"https://chromereleases.googleblog.com/2015/05/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"43"},"44":{"release_date":"2015-07-29","release_notes":"https://chromereleases.googleblog.com/2015/07/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"44"},"45":{"release_date":"2015-09-01","release_notes":"https://chromereleases.googleblog.com/2015/09/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"45"},"46":{"release_date":"2015-10-14","release_notes":"https://chromereleases.googleblog.com/2015/10/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"46"},"47":{"release_date":"2015-12-02","release_notes":"https://chromereleases.googleblog.com/2015/12/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"47"},"48":{"release_date":"2016-01-26","status":"retired","engine":"Blink","engine_version":"48"},"49":{"release_date":"2016-03-09","release_notes":"https://chromereleases.googleblog.com/2016/03/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"49"},"50":{"release_date":"2016-04-13","status":"retired","engine":"Blink","engine_version":"50"},"51":{"release_date":"2016-06-08","release_notes":"https://chromereleases.googleblog.com/2016/06/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"51"},"52":{"release_date":"2016-07-27","release_notes":"https://chromereleases.googleblog.com/2016/07/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"52"},"53":{"release_date":"2016-09-07","release_notes":"https://chromereleases.googleblog.com/2016/09/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"53"},"54":{"release_date":"2016-10-19","release_notes":"https://chromereleases.googleblog.com/2016/10/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"54"},"55":{"release_date":"2016-12-06","release_notes":"https://chromereleases.googleblog.com/2016/12/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"55"},"56":{"release_date":"2017-02-01","release_notes":"https://chromereleases.googleblog.com/2017/02/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"56"},"57":{"release_date":"2017-03-16","release_notes":"https://chromereleases.googleblog.com/2017/03/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"57"},"58":{"release_date":"2017-04-25","release_notes":"https://chromereleases.googleblog.com/2017/04/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"58"},"59":{"release_date":"2017-06-06","release_notes":"https://chromereleases.googleblog.com/2017/06/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"59"},"60":{"release_date":"2017-08-01","release_notes":"https://chromereleases.googleblog.com/2017/08/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"60"},"61":{"release_date":"2017-09-05","release_notes":"https://chromereleases.googleblog.com/2017/09/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"61"},"62":{"release_date":"2017-10-24","release_notes":"https://chromereleases.googleblog.com/2017/10/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"62"},"63":{"release_date":"2017-12-05","release_notes":"https://chromereleases.googleblog.com/2017/12/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"63"},"64":{"release_date":"2018-01-23","release_notes":"https://chromereleases.googleblog.com/2018/01/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"64"},"65":{"release_date":"2018-03-06","release_notes":"https://chromereleases.googleblog.com/2018/03/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"65"},"66":{"release_date":"2018-04-17","release_notes":"https://chromereleases.googleblog.com/2018/04/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"66"},"67":{"release_date":"2018-05-31","release_notes":"https://chromereleases.googleblog.com/2018/05/chrome-for-android-update_31.html","status":"retired","engine":"Blink","engine_version":"67"},"68":{"release_date":"2018-07-24","release_notes":"https://chromereleases.googleblog.com/2018/07/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"68"},"69":{"release_date":"2018-09-04","release_notes":"https://chromereleases.googleblog.com/2018/09/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"69"},"70":{"release_date":"2018-10-17","release_notes":"https://chromereleases.googleblog.com/2018/10/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"70"},"71":{"release_date":"2018-12-04","release_notes":"https://chromereleases.googleblog.com/2018/12/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"71"},"72":{"release_date":"2019-01-29","release_notes":"https://chromereleases.googleblog.com/2019/01/chrome-for-android-update_29.html","status":"retired","engine":"Blink","engine_version":"72"},"73":{"release_date":"2019-03-12","release_notes":"https://chromereleases.googleblog.com/2019/03/chrome-for-android-update_12.html","status":"retired","engine":"Blink","engine_version":"73"},"74":{"release_date":"2019-04-24","release_notes":"https://chromereleases.googleblog.com/2019/04/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"74"},"75":{"release_date":"2019-06-04","release_notes":"https://chromereleases.googleblog.com/2019/06/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"75"},"76":{"release_date":"2019-07-30","release_notes":"https://chromereleases.googleblog.com/2019/07/chrome-for-android-update_30.html","status":"retired","engine":"Blink","engine_version":"76"},"77":{"release_date":"2019-09-10","release_notes":"https://chromereleases.googleblog.com/2019/09/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"77"},"78":{"release_date":"2019-10-22","release_notes":"https://chromereleases.googleblog.com/2019/10/chrome-for-android-update_22.html","status":"retired","engine":"Blink","engine_version":"78"},"79":{"release_date":"2019-12-17","release_notes":"https://chromereleases.googleblog.com/2019/12/chrome-for-android-update_17.html","status":"retired","engine":"Blink","engine_version":"79"},"80":{"release_date":"2020-02-04","release_notes":"https://chromereleases.googleblog.com/2020/02/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"80"},"81":{"release_date":"2020-04-07","release_notes":"https://chromereleases.googleblog.com/2020/04/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"81"},"83":{"release_date":"2020-05-19","release_notes":"https://chromereleases.googleblog.com/2020/05/chrome-for-android-update_19.html","status":"retired","engine":"Blink","engine_version":"83"},"84":{"release_date":"2020-07-27","release_notes":"https://chromereleases.googleblog.com/2020/07/chrome-for-android-update_27.html","status":"retired","engine":"Blink","engine_version":"84"},"85":{"release_date":"2020-08-25","release_notes":"https://chromereleases.googleblog.com/2020/08/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"85"},"86":{"release_date":"2020-10-20","release_notes":"https://chromereleases.googleblog.com/2020/10/chrome-for-android-update_20.html","status":"retired","engine":"Blink","engine_version":"86"},"87":{"release_date":"2020-11-17","release_notes":"https://chromereleases.googleblog.com/2020/11/chrome-for-android-update_17.html","status":"retired","engine":"Blink","engine_version":"87"},"88":{"release_date":"2021-01-19","release_notes":"https://chromereleases.googleblog.com/2021/01/chrome-for-android-update_19.html","status":"retired","engine":"Blink","engine_version":"88"},"89":{"release_date":"2021-03-02","release_notes":"https://chromereleases.googleblog.com/2021/03/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"89"},"90":{"release_date":"2021-04-13","release_notes":"https://chromereleases.googleblog.com/2021/04/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"90"},"91":{"release_date":"2021-05-25","release_notes":"https://chromereleases.googleblog.com/2021/05/chrome-for-android-update_01607414128.html","status":"retired","engine":"Blink","engine_version":"91"},"92":{"release_date":"2021-07-20","release_notes":"https://chromereleases.googleblog.com/2021/07/chrome-for-android-update_01500789893.html","status":"current","engine":"Blink","engine_version":"92"},"93":{"status":"beta","engine":"Blink","engine_version":"93"},"94":{"status":"nightly","engine":"Blink","engine_version":"94"}}},"deno":{"name":"Deno","releases":{"1.0":{"release_date":"2020-05-13","release_notes":"https://github.com/denoland/deno/releases/tag/v1.0.0","status":"retired","engine":"V8","engine_version":"8.4"},"1.1":{"release_date":"2020-06-12","release_notes":"https://github.com/denoland/deno/releases/tag/v1.1.0","status":"retired","engine":"V8","engine_version":"8.4"},"1.2":{"release_date":"2020-07-13","release_notes":"https://github.com/denoland/deno/releases/tag/v1.2.0","status":"retired","engine":"V8","engine_version":"8.5"},"1.3":{"release_date":"2020-08-13","release_notes":"https://github.com/denoland/deno/releases/tag/v1.3.0","status":"retired","engine":"V8","engine_version":"8.6"},"1.4":{"release_date":"2020-09-13","release_notes":"https://github.com/denoland/deno/releases/tag/v1.4.0","status":"retired","engine":"V8","engine_version":"8.7"},"1.5":{"release_date":"2020-10-27","release_notes":"https://github.com/denoland/deno/releases/tag/v1.5.0","status":"retired","engine":"V8","engine_version":"8.7"},"1.6":{"release_date":"2020-12-08","release_notes":"https://github.com/denoland/deno/releases/tag/v1.6.0","status":"retired","engine":"V8","engine_version":"8.8"},"1.7":{"release_date":"2021-01-19","release_notes":"https://github.com/denoland/deno/releases/tag/v1.7.0","status":"retired","engine":"V8","engine_version":"8.9"},"1.8":{"release_date":"2021-03-02","release_notes":"https://github.com/denoland/deno/releases/tag/v1.8.0","status":"retired","engine":"V8","engine_version":"9.0"},"1.9":{"release_date":"2021-04-13","release_notes":"https://github.com/denoland/deno/releases/tag/v1.9.0","status":"retired","engine":"V8","engine_version":"9.1"},"1.10":{"release_date":"2021-05-11","release_notes":"https://github.com/denoland/deno/releases/tag/v1.10.1","status":"retired","engine":"V8","engine_version":"9.1"},"1.11":{"release_date":"2021-06-08","release_notes":"https://github.com/denoland/deno/releases/tag/v1.11.0","status":"retired","engine":"V8","engine_version":"9.1"},"1.12":{"release_date":"2021-07-13","release_notes":"https://github.com/denoland/deno/releases/tag/v1.12.0","status":"current","engine":"V8","engine_version":"9.2"},"1.13":{"release_date":"2021-08-10","release_notes":"https://github.com/denoland/deno/releases/tag/v1.13.0","status":"current","engine":"V8","engine_version":"9.3"},"1.14":{"release_date":"2021-09-14","status":"nightly","engine":"V8","engine_version":"9.4"}}},"edge":{"name":"Edge","pref_url":"about:flags","releases":{"12":{"release_date":"2015-07-28","release_notes":"https://docs.microsoft.com/en-us/microsoft-edge/dev-guide/whats-new/edgehtml-12","status":"retired","engine":"EdgeHTML","engine_version":"12"},"13":{"release_date":"2015-11-12","release_notes":"https://docs.microsoft.com/en-us/microsoft-edge/dev-guide/whats-new/edgehtml-13","status":"retired","engine":"EdgeHTML","engine_version":"13"},"14":{"release_date":"2016-08-02","release_notes":"https://docs.microsoft.com/en-us/microsoft-edge/dev-guide/whats-new/edgehtml-14","status":"retired","engine":"EdgeHTML","engine_version":"14"},"15":{"release_date":"2017-04-05","release_notes":"https://docs.microsoft.com/en-us/microsoft-edge/dev-guide/whats-new/edgehtml-15","status":"retired","engine":"EdgeHTML","engine_version":"15"},"16":{"release_date":"2017-10-17","release_notes":"https://docs.microsoft.com/en-us/microsoft-edge/dev-guide/whats-new/edgehtml-16","status":"retired","engine":"EdgeHTML","engine_version":"16"},"17":{"release_date":"2018-04-30","release_notes":"https://docs.microsoft.com/en-us/microsoft-edge/dev-guide/whats-new/edgehtml-17","status":"retired","engine":"EdgeHTML","engine_version":"17"},"18":{"release_date":"2018-10-02","release_notes":"https://docs.microsoft.com/en-us/microsoft-edge/dev-guide/whats-new","status":"retired","engine":"EdgeHTML","engine_version":"18"},"79":{"release_date":"2020-01-15","release_notes":"https://blogs.windows.com/windowsexperience/2020/01/15/new-year-new-browser-the-new-microsoft-edge-is-out-of-preview-and-now-available-for-download/","status":"retired","engine":"Blink","engine_version":"79"},"80":{"release_date":"2020-02-07","release_notes":"https://docs.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel#version-80036148-february-7","status":"retired","engine":"Blink","engine_version":"80"},"81":{"release_date":"2020-04-13","release_notes":"https://docs.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel#version-81041653-april-13","status":"retired","engine":"Blink","engine_version":"81"},"83":{"release_date":"2020-05-21","release_notes":"https://docs.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel#version-83047837-may-21","status":"retired","engine":"Blink","engine_version":"83"},"84":{"release_date":"2020-07-16","release_notes":"https://docs.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel#version-84052240-july-16","status":"retired","engine":"Blink","engine_version":"84"},"85":{"release_date":"2020-08-27","release_notes":"https://docs.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel#version-85056441-august-27","status":"retired","engine":"Blink","engine_version":"85"},"86":{"release_date":"2020-10-09","release_notes":"https://docs.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel#version-86062238-october-9","status":"retired","engine":"Blink","engine_version":"86"},"87":{"release_date":"2020-11-19","release_notes":"https://docs.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel#version-87066441-november-19","status":"retired","engine":"Blink","engine_version":"87"},"88":{"release_date":"2021-01-21","release_notes":"https://docs.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel#version-88070550-january-21","status":"retired","engine":"Blink","engine_version":"88"},"89":{"release_date":"2021-03-04","release_notes":"https://docs.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel#version-89077445-march-4","status":"retired","engine":"Blink","engine_version":"89"},"90":{"release_date":"2021-04-15","release_notes":"https://docs.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel#version-90081839-april-15","status":"retired","engine":"Blink","engine_version":"90"},"91":{"release_date":"2021-05-27","release_notes":"https://docs.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel#version-91086437-may-27","status":"retired","engine":"Blink","engine_version":"91"},"92":{"release_date":"2021-07-22","release_notes":"https://docs.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel#version-92090255-july-22","status":"current","engine":"Blink","engine_version":"92"},"93":{"status":"beta","engine":"Blink","engine_version":"93"},"94":{"status":"nightly","engine":"Blink","engine_version":"94"}}},"firefox":{"name":"Firefox","preview_name":"Nightly","pref_url":"about:config","releases":{"1":{"release_date":"2004-11-09","release_notes":"http://website-archive.mozilla.org/www.mozilla.org/firefox_releasenotes/en-US/firefox/releases/1.0.html","status":"retired","engine":"Gecko","engine_version":"1.7"},"2":{"release_date":"2006-10-24","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/2","status":"retired","engine":"Gecko","engine_version":"1.8.1"},"3":{"release_date":"2008-06-17","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/3","status":"retired","engine":"Gecko","engine_version":"1.9"},"4":{"release_date":"2011-03-22","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/4","status":"retired","engine":"Gecko","engine_version":"2"},"5":{"release_date":"2011-06-21","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/5","status":"retired","engine":"Gecko","engine_version":"5"},"6":{"release_date":"2011-08-16","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/6","status":"retired","engine":"Gecko","engine_version":"6"},"7":{"release_date":"2011-09-27","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/7","status":"retired","engine":"Gecko","engine_version":"7"},"8":{"release_date":"2011-11-08","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/8","status":"retired","engine":"Gecko","engine_version":"8"},"9":{"release_date":"2011-12-20","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/9","status":"retired","engine":"Gecko","engine_version":"9"},"10":{"release_date":"2012-01-31","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/10","status":"retired","engine":"Gecko","engine_version":"10"},"11":{"release_date":"2012-03-13","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/11","status":"retired","engine":"Gecko","engine_version":"11"},"12":{"release_date":"2012-04-24","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/12","status":"retired","engine":"Gecko","engine_version":"12"},"13":{"release_date":"2012-06-05","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/13","status":"retired","engine":"Gecko","engine_version":"13"},"14":{"release_date":"2012-07-17","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/14","status":"retired","engine":"Gecko","engine_version":"14"},"15":{"release_date":"2012-08-28","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/15","status":"retired","engine":"Gecko","engine_version":"15"},"16":{"release_date":"2012-10-09","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/16","status":"retired","engine":"Gecko","engine_version":"16"},"17":{"release_date":"2012-11-20","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/17","status":"retired","engine":"Gecko","engine_version":"17"},"18":{"release_date":"2013-01-08","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/18","status":"retired","engine":"Gecko","engine_version":"18"},"19":{"release_date":"2013-02-19","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/19","status":"retired","engine":"Gecko","engine_version":"19"},"20":{"release_date":"2013-04-02","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/20","status":"retired","engine":"Gecko","engine_version":"20"},"21":{"release_date":"2013-05-14","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/21","status":"retired","engine":"Gecko","engine_version":"21"},"22":{"release_date":"2013-06-25","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/22","status":"retired","engine":"Gecko","engine_version":"22"},"23":{"release_date":"2013-08-06","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/23","status":"retired","engine":"Gecko","engine_version":"23"},"24":{"release_date":"2013-09-17","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/24","status":"retired","engine":"Gecko","engine_version":"24"},"25":{"release_date":"2013-10-29","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/25","status":"retired","engine":"Gecko","engine_version":"25"},"26":{"release_date":"2013-12-10","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/26","status":"retired","engine":"Gecko","engine_version":"26"},"27":{"release_date":"2014-02-04","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/27","status":"retired","engine":"Gecko","engine_version":"27"},"28":{"release_date":"2014-03-18","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/28","status":"retired","engine":"Gecko","engine_version":"28"},"29":{"release_date":"2014-04-29","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/29","status":"retired","engine":"Gecko","engine_version":"29"},"30":{"release_date":"2014-06-10","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/30","status":"retired","engine":"Gecko","engine_version":"30"},"31":{"release_date":"2014-07-22","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/31","status":"retired","engine":"Gecko","engine_version":"31"},"32":{"release_date":"2014-09-02","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/32","status":"retired","engine":"Gecko","engine_version":"32"},"33":{"release_date":"2014-10-14","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/33","status":"retired","engine":"Gecko","engine_version":"33"},"34":{"release_date":"2014-12-01","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/34","status":"retired","engine":"Gecko","engine_version":"34"},"35":{"release_date":"2015-01-13","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/35","status":"retired","engine":"Gecko","engine_version":"35"},"36":{"release_date":"2015-02-24","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/36","status":"retired","engine":"Gecko","engine_version":"36"},"37":{"release_date":"2015-03-31","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/37","status":"retired","engine":"Gecko","engine_version":"37"},"38":{"release_date":"2015-05-12","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/38","status":"retired","engine":"Gecko","engine_version":"38"},"39":{"release_date":"2015-07-02","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/39","status":"retired","engine":"Gecko","engine_version":"39"},"40":{"release_date":"2015-08-11","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/40","status":"retired","engine":"Gecko","engine_version":"40"},"41":{"release_date":"2015-09-22","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/41","status":"retired","engine":"Gecko","engine_version":"41"},"42":{"release_date":"2015-11-03","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/42","status":"retired","engine":"Gecko","engine_version":"42"},"43":{"release_date":"2015-12-15","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/43","status":"retired","engine":"Gecko","engine_version":"43"},"44":{"release_date":"2016-01-26","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/44","status":"retired","engine":"Gecko","engine_version":"44"},"45":{"release_date":"2016-03-08","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/45","status":"retired","engine":"Gecko","engine_version":"45"},"46":{"release_date":"2016-04-26","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/46","status":"retired","engine":"Gecko","engine_version":"46"},"47":{"release_date":"2016-06-07","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/47","status":"retired","engine":"Gecko","engine_version":"47"},"48":{"release_date":"2016-08-02","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/48","status":"retired","engine":"Gecko","engine_version":"48"},"49":{"release_date":"2016-09-20","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/49","status":"retired","engine":"Gecko","engine_version":"49"},"50":{"release_date":"2016-11-15","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/50","status":"retired","engine":"Gecko","engine_version":"50"},"51":{"release_date":"2017-01-24","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/51","status":"retired","engine":"Gecko","engine_version":"51"},"52":{"release_date":"2017-03-07","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/52","status":"retired","engine":"Gecko","engine_version":"52"},"53":{"release_date":"2017-04-19","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/53","status":"retired","engine":"Gecko","engine_version":"53"},"54":{"release_date":"2017-06-13","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/54","status":"retired","engine":"Gecko","engine_version":"54"},"55":{"release_date":"2017-08-08","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/55","status":"retired","engine":"Gecko","engine_version":"55"},"56":{"release_date":"2017-09-28","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/56","status":"retired","engine":"Gecko","engine_version":"56"},"57":{"release_date":"2017-11-14","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/57","status":"retired","engine":"Gecko","engine_version":"57"},"58":{"release_date":"2018-01-23","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/58","status":"retired","engine":"Gecko","engine_version":"58"},"59":{"release_date":"2018-03-13","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/59","status":"retired","engine":"Gecko","engine_version":"59"},"60":{"release_date":"2018-05-09","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/60","status":"esr","engine":"Gecko","engine_version":"60"},"61":{"release_date":"2018-06-26","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/61","status":"retired","engine":"Gecko","engine_version":"61"},"62":{"release_date":"2018-09-05","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/62","status":"retired","engine":"Gecko","engine_version":"62"},"63":{"release_date":"2018-10-23","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/63","status":"retired","engine":"Gecko","engine_version":"63"},"64":{"release_date":"2018-12-11","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/64","status":"retired","engine":"Gecko","engine_version":"64"},"65":{"release_date":"2019-01-29","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/65","status":"retired","engine":"Gecko","engine_version":"65"},"66":{"release_date":"2019-03-19","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/66","status":"retired","engine":"Gecko","engine_version":"66"},"67":{"release_date":"2019-05-21","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/67","status":"retired","engine":"Gecko","engine_version":"67"},"68":{"release_date":"2019-07-09","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/68","status":"retired","engine":"Gecko","engine_version":"68"},"69":{"release_date":"2019-09-03","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/69","status":"retired","engine":"Gecko","engine_version":"69"},"70":{"release_date":"2019-10-22","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/70","status":"retired","engine":"Gecko","engine_version":"70"},"71":{"release_date":"2019-12-10","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/71","status":"retired","engine":"Gecko","engine_version":"71"},"72":{"release_date":"2020-01-07","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/72","status":"retired","engine":"Gecko","engine_version":"72"},"73":{"release_date":"2020-02-11","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/73","status":"retired","engine":"Gecko","engine_version":"73"},"74":{"release_date":"2020-03-10","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/74","status":"retired","engine":"Gecko","engine_version":"74"},"75":{"release_date":"2020-04-07","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/75","status":"retired","engine":"Gecko","engine_version":"75"},"76":{"release_date":"2020-05-05","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/76","status":"retired","engine":"Gecko","engine_version":"76"},"77":{"release_date":"2020-06-02","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/77","status":"retired","engine":"Gecko","engine_version":"77"},"78":{"release_date":"2020-06-30","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/78","status":"retired","engine":"Gecko","engine_version":"78"},"79":{"release_date":"2020-07-28","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/79","status":"retired","engine":"Gecko","engine_version":"79"},"80":{"release_date":"2020-08-25","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/80","status":"retired","engine":"Gecko","engine_version":"80"},"81":{"release_date":"2020-09-22","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/81","status":"retired","engine":"Gecko","engine_version":"81"},"82":{"release_date":"2020-10-20","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/82","status":"retired","engine":"Gecko","engine_version":"82"},"83":{"release_date":"2020-11-17","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/83","status":"retired","engine":"Gecko","engine_version":"83"},"84":{"release_date":"2020-12-15","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/84","status":"retired","engine":"Gecko","engine_version":"84"},"85":{"release_date":"2021-01-26","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/85","status":"retired","engine":"Gecko","engine_version":"85"},"86":{"release_date":"2021-02-23","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/86","status":"retired","engine":"Gecko","engine_version":"86"},"87":{"release_date":"2021-03-23","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/87","status":"retired","engine":"Gecko","engine_version":"87"},"88":{"release_date":"2021-04-19","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/88","status":"retired","engine":"Gecko","engine_version":"88"},"89":{"release_date":"2021-06-01","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/89","status":"retired","engine":"Gecko","engine_version":"89"},"90":{"release_date":"2021-07-13","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/90","status":"retired","engine":"Gecko","engine_version":"90"},"91":{"release_date":"2021-08-10","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/91","status":"current","engine":"Gecko","engine_version":"91"},"92":{"release_date":"2021-09-07","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/92","status":"beta","engine":"Gecko","engine_version":"92"},"93":{"release_date":"2021-10-05","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/93","status":"nightly","engine":"Gecko","engine_version":"93"},"94":{"release_date":"2021-11-02","status":"planned","engine":"Gecko","engine_version":"94"},"95":{"release_date":"2021-12-07","status":"planned","engine":"Gecko","engine_version":"95"},"96":{"release_date":"2022-01-11","status":"planned","engine":"Gecko","engine_version":"96"},"97":{"release_date":"2022-02-08","status":"planned","engine":"Gecko","engine_version":"97"},"98":{"release_date":"2022-03-08","status":"planned","engine":"Gecko","engine_version":"98"},"1.5":{"release_date":"2005-11-29","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/1.5","status":"retired","engine":"Gecko","engine_version":"1.8"},"3.5":{"release_date":"2009-06-30","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/3.5","status":"retired","engine":"Gecko","engine_version":"1.9.1"},"3.6":{"release_date":"2010-01-21","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/3.6","status":"retired","engine":"Gecko","engine_version":"1.9.2"}}},"firefox_android":{"name":"Firefox for Android","pref_url":"about:config","releases":{"4":{"release_date":"2011-03-29","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/4","status":"retired","engine":"Gecko","engine_version":"2"},"5":{"release_date":"2011-06-21","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/5","status":"retired","engine":"Gecko","engine_version":"5"},"6":{"release_date":"2011-08-16","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/6","status":"retired","engine":"Gecko","engine_version":"6"},"7":{"release_date":"2011-09-27","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/7","status":"retired","engine":"Gecko","engine_version":"7"},"8":{"release_date":"2011-11-08","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/8","status":"retired","engine":"Gecko","engine_version":"8"},"9":{"release_date":"2011-12-21","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/9","status":"retired","engine":"Gecko","engine_version":"9"},"10":{"release_date":"2012-01-31","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/10","status":"retired","engine":"Gecko","engine_version":"10"},"14":{"release_date":"2012-06-26","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/14","status":"retired","engine":"Gecko","engine_version":"14"},"15":{"release_date":"2012-08-28","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/15","status":"retired","engine":"Gecko","engine_version":"15"},"16":{"release_date":"2012-10-09","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/16","status":"retired","engine":"Gecko","engine_version":"16"},"17":{"release_date":"2012-11-20","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/17","status":"retired","engine":"Gecko","engine_version":"17"},"18":{"release_date":"2013-01-08","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/18","status":"retired","engine":"Gecko","engine_version":"18"},"19":{"release_date":"2013-02-19","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/19","status":"retired","engine":"Gecko","engine_version":"19"},"20":{"release_date":"2013-04-02","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/20","status":"retired","engine":"Gecko","engine_version":"20"},"21":{"release_date":"2013-05-14","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/21","status":"retired","engine":"Gecko","engine_version":"21"},"22":{"release_date":"2013-06-25","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/22","status":"retired","engine":"Gecko","engine_version":"22"},"23":{"release_date":"2013-08-06","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/23","status":"retired","engine":"Gecko","engine_version":"23"},"24":{"release_date":"2013-09-17","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/24","status":"retired","engine":"Gecko","engine_version":"24"},"25":{"release_date":"2013-10-29","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/25","status":"retired","engine":"Gecko","engine_version":"25"},"26":{"release_date":"2013-12-10","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/26","status":"retired","engine":"Gecko","engine_version":"26"},"27":{"release_date":"2014-02-04","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/27","status":"retired","engine":"Gecko","engine_version":"27"},"28":{"release_date":"2014-03-18","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/28","status":"retired","engine":"Gecko","engine_version":"28"},"29":{"release_date":"2014-04-29","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/29","status":"retired","engine":"Gecko","engine_version":"29"},"30":{"release_date":"2014-06-10","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/30","status":"retired","engine":"Gecko","engine_version":"30"},"31":{"release_date":"2014-07-22","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/31","status":"retired","engine":"Gecko","engine_version":"31"},"32":{"release_date":"2014-09-02","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/32","status":"retired","engine":"Gecko","engine_version":"32"},"33":{"release_date":"2014-10-14","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/33","status":"retired","engine":"Gecko","engine_version":"33"},"34":{"release_date":"2014-12-01","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/34","status":"retired","engine":"Gecko","engine_version":"34"},"35":{"release_date":"2015-01-13","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/35","status":"retired","engine":"Gecko","engine_version":"35"},"36":{"release_date":"2015-02-27","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/36","status":"retired","engine":"Gecko","engine_version":"36"},"37":{"release_date":"2015-03-31","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/37","status":"retired","engine":"Gecko","engine_version":"37"},"38":{"release_date":"2015-05-12","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/38","status":"retired","engine":"Gecko","engine_version":"38"},"39":{"release_date":"2015-07-02","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/39","status":"retired","engine":"Gecko","engine_version":"39"},"40":{"release_date":"2015-08-11","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/40","status":"retired","engine":"Gecko","engine_version":"40"},"41":{"release_date":"2015-09-22","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/41","status":"retired","engine":"Gecko","engine_version":"41"},"42":{"release_date":"2015-11-03","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/42","status":"retired","engine":"Gecko","engine_version":"42"},"43":{"release_date":"2015-12-15","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/43","status":"retired","engine":"Gecko","engine_version":"43"},"44":{"release_date":"2016-01-26","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/44","status":"retired","engine":"Gecko","engine_version":"44"},"45":{"release_date":"2016-03-08","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/45","status":"retired","engine":"Gecko","engine_version":"45"},"46":{"release_date":"2016-04-26","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/46","status":"retired","engine":"Gecko","engine_version":"46"},"47":{"release_date":"2016-06-07","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/47","status":"retired","engine":"Gecko","engine_version":"47"},"48":{"release_date":"2016-08-02","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/48","status":"retired","engine":"Gecko","engine_version":"48"},"49":{"release_date":"2016-09-20","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/49","status":"retired","engine":"Gecko","engine_version":"49"},"50":{"release_date":"2016-11-15","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/50","status":"retired","engine":"Gecko","engine_version":"50"},"51":{"release_date":"2017-01-24","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/51","status":"retired","engine":"Gecko","engine_version":"51"},"52":{"release_date":"2017-03-07","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/52","status":"retired","engine":"Gecko","engine_version":"52"},"53":{"release_date":"2017-04-19","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/53","status":"retired","engine":"Gecko","engine_version":"53"},"54":{"release_date":"2017-06-13","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/54","status":"retired","engine":"Gecko","engine_version":"54"},"55":{"release_date":"2017-08-08","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/55","status":"retired","engine":"Gecko","engine_version":"55"},"56":{"release_date":"2017-09-28","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/56","status":"retired","engine":"Gecko","engine_version":"56"},"57":{"release_date":"2017-11-28","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/57","status":"retired","engine":"Gecko","engine_version":"57"},"58":{"release_date":"2018-01-22","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/58","status":"retired","engine":"Gecko","engine_version":"58"},"59":{"release_date":"2018-03-13","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/59","status":"retired","engine":"Gecko","engine_version":"59"},"60":{"release_date":"2018-05-09","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/60","status":"retired","engine":"Gecko","engine_version":"60"},"61":{"release_date":"2018-06-26","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/61","status":"retired","engine":"Gecko","engine_version":"61"},"62":{"release_date":"2018-09-05","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/62","status":"retired","engine":"Gecko","engine_version":"62"},"63":{"release_date":"2018-10-23","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/63","status":"retired","engine":"Gecko","engine_version":"63"},"64":{"release_date":"2018-12-11","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/64","status":"retired","engine":"Gecko","engine_version":"64"},"65":{"release_date":"2019-01-29","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/65","status":"retired","engine":"Gecko","engine_version":"65"},"66":{"release_date":"2019-03-19","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/66","status":"retired","engine":"Gecko","engine_version":"66"},"67":{"release_date":"2019-05-21","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/67","status":"retired","engine":"Gecko","engine_version":"67"},"68":{"release_date":"2019-07-09","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/68","status":"retired","engine":"Gecko","engine_version":"68"},"79":{"release_date":"2020-07-28","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/79","status":"retired","engine":"Gecko","engine_version":"79"},"80":{"release_date":"2020-08-31","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/80","status":"retired","engine":"Gecko","engine_version":"80"},"81":{"release_date":"2020-09-22","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/81","status":"retired","engine":"Gecko","engine_version":"81"},"82":{"release_date":"2020-10-20","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/82","status":"retired","engine":"Gecko","engine_version":"82"},"83":{"release_date":"2020-11-17","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/83","status":"retired","engine":"Gecko","engine_version":"83"},"84":{"release_date":"2020-12-15","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/84","status":"retired","engine":"Gecko","engine_version":"84"},"85":{"release_date":"2021-01-26","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/85","status":"retired","engine":"Gecko","engine_version":"85"},"86":{"release_date":"2021-02-23","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/86","status":"retired","engine":"Gecko","engine_version":"86"},"87":{"release_date":"2021-03-23","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/87","status":"retired","engine":"Gecko","engine_version":"87"},"88":{"release_date":"2021-04-19","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/88","status":"retired","engine":"Gecko","engine_version":"88"},"89":{"release_date":"2021-06-01","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/89","status":"retired","engine":"Gecko","engine_version":"89"},"90":{"release_date":"2021-07-13","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/90","status":"retired","engine":"Gecko","engine_version":"90"},"91":{"release_date":"2021-08-10","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/91","status":"current","engine":"Gecko","engine_version":"91"},"92":{"release_date":"2021-09-07","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/92","status":"beta","engine":"Gecko","engine_version":"92"},"93":{"release_date":"2021-10-05","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/93","status":"nightly","engine":"Gecko","engine_version":"93"},"94":{"release_date":"2021-11-02","status":"planned","engine":"Gecko","engine_version":"94"},"95":{"release_date":"2021-12-07","status":"planned","engine":"Gecko","engine_version":"95"},"96":{"release_date":"2022-01-11","status":"planned","engine":"Gecko","engine_version":"96"},"97":{"release_date":"2022-02-08","status":"planned","engine":"Gecko","engine_version":"97"},"98":{"release_date":"2022-03-08","status":"planned","engine":"Gecko","engine_version":"98"}}},"ie":{"name":"Internet Explorer","releases":{"1":{"release_date":"1995-08-16","status":"retired"},"2":{"release_date":"1995-11-22","status":"retired"},"3":{"release_date":"1996-08-13","status":"retired"},"4":{"release_date":"1997-09-30","status":"retired"},"5":{"release_date":"1999-03-18","status":"retired"},"6":{"release_date":"2001-08-27","status":"retired"},"7":{"release_date":"2006-10-18","status":"retired"},"8":{"release_date":"2009-03-19","status":"retired","engine":"Trident","engine_version":"4.0"},"9":{"release_date":"2011-03-14","status":"retired","engine":"Trident","engine_version":"5.0"},"10":{"release_date":"2012-10-26","status":"retired","engine":"Trident","engine_version":"6.0"},"11":{"release_date":"2013-10-17","status":"current","engine":"Trident","engine_version":"7.0"},"5.5":{"release_date":"2000-07-06","status":"retired"}}},"nodejs":{"name":"Node.js","releases":{"0.10.0":{"release_date":"2013-03-11","release_notes":"https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V010.md","status":"retired","engine":"V8","engine_version":"3.14"},"0.12.0":{"release_date":"2015-02-06","release_notes":"https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V012.md","status":"retired","engine":"V8","engine_version":"3.28"},"4.0.0":{"release_date":"2015-09-08","release_notes":"https://nodejs.org/en/blog/release/v4.0.0/","status":"retired","engine":"V8","engine_version":"4.5"},"5.0.0":{"release_date":"2015-10-29","release_notes":"https://nodejs.org/en/blog/release/v5.0.0/","status":"retired","engine":"V8","engine_version":"4.6"},"6.0.0":{"release_date":"2016-04-26","release_notes":"https://nodejs.org/en/blog/release/v6.0.0/","status":"retired","engine":"V8","engine_version":"5"},"6.5.0":{"release_date":"2016-08-26","release_notes":"https://nodejs.org/en/blog/release/v6.5.0/","status":"retired","engine":"V8","engine_version":"5.1"},"7.0.0":{"release_date":"2016-10-25","release_notes":"https://nodejs.org/en/blog/release/v7.0.0/","status":"retired","engine":"V8","engine_version":"5.4"},"7.5.0":{"release_date":"2017-01-31","release_notes":"https://nodejs.org/en/blog/release/v7.5.0/","status":"retired","engine":"V8","engine_version":"5.4"},"7.6.0":{"release_date":"2017-02-21","release_notes":"https://nodejs.org/en/blog/release/v7.6.0/","status":"retired","engine":"V8","engine_version":"5.5"},"7.7.0":{"release_date":"2017-02-28","release_notes":"https://nodejs.org/en/blog/release/v7.7.0/","status":"retired","engine":"V8","engine_version":"5.5"},"7.10.0":{"release_date":"2017-05-02","release_notes":"https://nodejs.org/en/blog/release/v7.10.0/","status":"retired","engine":"V8","engine_version":"5.5"},"8.0.0":{"release_date":"2017-05-30","release_notes":"https://nodejs.org/en/blog/release/v8.0.0/","status":"retired","engine":"V8","engine_version":"5.8"},"8.3.0":{"release_date":"2017-08-09","release_notes":"https://nodejs.org/en/blog/release/v8.3.0/","status":"retired","engine":"V8","engine_version":"6.0"},"8.5.0":{"release_date":"2017-09-12","release_notes":"https://nodejs.org/en/blog/release/v8.5.0/","status":"retired","engine":"V8","engine_version":"6.0"},"8.7.0":{"release_date":"2017-10-11","release_notes":"https://nodejs.org/en/blog/release/v8.7.0/","status":"retired","engine":"V8","engine_version":"6.1"},"8.10.0":{"release_date":"2018-03-06","release_notes":"https://nodejs.org/en/blog/release/v8.10.0/","status":"retired","engine":"V8","engine_version":"6.2"},"9.3.0":{"release_date":"2017-12-12","release_notes":"https://nodejs.org/en/blog/release/v9.3.0/","status":"retired","engine":"V8","engine_version":"6.2"},"10.0.0":{"release_date":"2018-04-24","release_notes":"https://nodejs.org/en/blog/release/v10.0.0/","status":"retired","engine":"V8","engine_version":"6.6"},"10.4.0":{"release_date":"2018-06-06","release_notes":"https://nodejs.org/en/blog/release/v10.4.0/","status":"retired","engine":"V8","engine_version":"6.7"},"10.5.0":{"release_date":"2018-06-20","release_notes":"https://nodejs.org/en/blog/release/v10.5.0/","status":"retired","engine":"V8","engine_version":"6.7"},"10.7.0":{"release_date":"2018-07-18","release_notes":"https://nodejs.org/en/blog/release/v10.7.0/","status":"retired","engine":"V8","engine_version":"6.7"},"10.9.0":{"release_date":"2018-08-16","release_notes":"https://nodejs.org/en/blog/release/v10.9.0/","status":"retired","engine":"V8","engine_version":"6.8"},"11.0.0":{"release_date":"2018-10-23","release_notes":"https://nodejs.org/en/blog/release/v11.0.0/","status":"retired","engine":"V8","engine_version":"7.0"},"11.7.0":{"release_date":"2019-01-18","release_notes":"https://nodejs.org/en/blog/release/v11.7.0/","status":"retired","engine":"V8","engine_version":"7.0"},"12.0.0":{"release_date":"2019-04-23","release_notes":"https://nodejs.org/en/blog/release/v12.0.0/","status":"retired","engine":"V8","engine_version":"7.4"},"12.5.0":{"release_date":"2019-06-27","release_notes":"https://nodejs.org/en/blog/release/v12.5.0/","status":"retired","engine":"V8","engine_version":"7.5"},"12.9.0":{"release_date":"2019-08-20","release_notes":"https://nodejs.org/en/blog/release/v12.9.0/","status":"retired","engine":"V8","engine_version":"7.6"},"12.11.0":{"release_date":"2019-09-25","release_notes":"https://nodejs.org/en/blog/release/v12.11.0/","status":"retired","engine":"V8","engine_version":"7.7"},"12.17.0":{"release_date":"2020-05-26","release_notes":"https://nodejs.org/en/blog/release/v12.17.0/","status":"esr","engine":"V8","engine_version":"7.8"},"13.0.0":{"release_date":"2019-10-10","release_notes":"https://nodejs.org/en/blog/release/v13.0.0/","status":"retired","engine":"V8","engine_version":"7.8"},"13.2.0":{"release_date":"2019-11-21","release_notes":"https://nodejs.org/en/blog/release/v13.2.0/","status":"retired","engine":"V8","engine_version":"7.9"},"14.0.0":{"release_date":"2020-04-21","release_notes":"https://nodejs.org/en/blog/release/v14.0.0/","status":"retired","engine":"V8","engine_version":"8.1"},"14.3.0":{"release_date":"2020-05-19","release_notes":"https://nodejs.org/en/blog/release/v14.3.0/","status":"retired","engine":"V8","engine_version":"8.3"},"14.5.0":{"release_date":"2020-06-30","release_notes":"https://nodejs.org/en/blog/release/v14.5.0/","status":"retired","engine":"V8","engine_version":"8.3"},"14.6.0":{"release_date":"2020-07-21","release_notes":"https://nodejs.org/en/blog/release/v14.6.0/","status":"retired","engine":"V8","engine_version":"8.4"},"14.8.0":{"release_date":"2020-08-11","release_notes":"https://nodejs.org/en/blog/release/v14.8.0/","status":"esr","engine":"V8","engine_version":"8.4"},"15.0.0":{"release_date":"2020-10-20","release_notes":"https://nodejs.org/en/blog/release/v15.0.0/","status":"retired","engine":"V8","engine_version":"8.6"},"16.0.0":{"release_date":"2021-04-20","release_notes":"https://nodejs.org/en/blog/release/v16.0.0/","status":"retired","engine":"V8","engine_version":"9.0"},"16.4.0":{"release_date":"2021-06-23","release_notes":"https://nodejs.org/en/blog/release/v16.4.0/","status":"retired","engine":"V8","engine_version":"9.1"},"16.6.0":{"release_date":"2021-07-29","release_notes":"https://nodejs.org/en/blog/release/v16.6.0/","status":"retired","engine":"V8","engine_version":"9.2"},"16.7.0":{"release_date":"2021-08-17","release_notes":"https://nodejs.org/en/blog/release/v16.7.0/","status":"current","engine":"V8","engine_version":"9.2"}}},"opera":{"name":"Opera","releases":{"2":{"release_date":"1996-07-14","status":"retired"},"3":{"release_date":"1997-12-01","status":"retired"},"4":{"release_date":"2000-06-28","status":"retired"},"5":{"release_date":"2000-12-06","status":"retired"},"6":{"release_date":"2001-12-18","status":"retired"},"7":{"release_date":"2003-01-28","status":"retired","engine":"Presto","engine_version":"1"},"8":{"release_date":"2005-04-19","status":"retired","engine":"Presto","engine_version":"1"},"9":{"release_date":"2006-06-20","status":"retired","engine":"Presto","engine_version":"2"},"10":{"release_date":"2009-09-01","release_notes":"https://dev.opera.com/blog/opera-10-weve-only-just-begun/","status":"retired","engine":"Presto","engine_version":"2.2"},"11":{"release_date":"2010-12-16","release_notes":"https://dev.opera.com/blog/new-html5-features-in-opera-11/","status":"retired","engine":"Presto","engine_version":"2.7"},"12":{"release_date":"2012-06-14","release_notes":"https://dev.opera.com/blog/hello-opera-12/","status":"retired","engine":"Presto","engine_version":"2.10"},"15":{"release_date":"2013-07-02","release_notes":"https://dev.opera.com/blog/introducing-opera-15-for-desktop-and-a-fast-release-cycle/","status":"retired","engine":"Blink","engine_version":"28"},"16":{"release_date":"2013-08-27","release_notes":"https://dev.opera.com/blog/opera-16-released-in-the-wild/","status":"retired","engine":"Blink","engine_version":"29"},"17":{"release_date":"2013-10-08","release_notes":"https://dev.opera.com/blog/opera-desktop-17-released/","status":"retired","engine":"Blink","engine_version":"30"},"18":{"release_date":"2013-11-19","release_notes":"https://dev.opera.com/blog/opera-desktop-18-released/","status":"retired","engine":"Blink","engine_version":"31"},"19":{"release_date":"2014-01-28","release_notes":"https://dev.opera.com/blog/opera-19/","status":"retired","engine":"Blink","engine_version":"32"},"20":{"release_date":"2014-03-04","release_notes":"https://dev.opera.com/blog/opera-20/","status":"retired","engine":"Blink","engine_version":"33"},"21":{"release_date":"2014-05-06","release_notes":"https://dev.opera.com/blog/opera-21/","status":"retired","engine":"Blink","engine_version":"34"},"22":{"release_date":"2014-06-03","release_notes":"https://dev.opera.com/blog/opera-22/","status":"retired","engine":"Blink","engine_version":"35"},"23":{"release_date":"2014-07-22","release_notes":"https://dev.opera.com/blog/opera-23/","status":"retired","engine":"Blink","engine_version":"36"},"24":{"release_date":"2014-09-02","release_notes":"https://dev.opera.com/blog/opera-24/","status":"retired","engine":"Blink","engine_version":"37"},"25":{"release_date":"2014-10-15","release_notes":"https://dev.opera.com/blog/opera-25/","status":"retired","engine":"Blink","engine_version":"38"},"26":{"release_date":"2014-12-03","release_notes":"https://dev.opera.com/blog/opera-26/","status":"retired","engine":"Blink","engine_version":"39"},"27":{"release_date":"2015-01-27","release_notes":"https://dev.opera.com/blog/opera-27/","status":"retired","engine":"Blink","engine_version":"40"},"28":{"release_date":"2015-03-10","release_notes":"https://dev.opera.com/blog/opera-28/","status":"retired","engine":"Blink","engine_version":"41"},"29":{"release_date":"2015-04-28","release_notes":"https://dev.opera.com/blog/opera-29/","status":"retired","engine":"Blink","engine_version":"42"},"30":{"release_date":"2015-06-09","release_notes":"https://dev.opera.com/blog/opera-30/","status":"retired","engine":"Blink","engine_version":"43"},"31":{"release_date":"2015-08-04","release_notes":"https://dev.opera.com/blog/opera-31/","status":"retired","engine":"Blink","engine_version":"44"},"32":{"release_date":"2015-09-15","release_notes":"https://dev.opera.com/blog/opera-32/","status":"retired","engine":"Blink","engine_version":"45"},"33":{"release_date":"2015-10-27","release_notes":"https://dev.opera.com/blog/opera-33/","status":"retired","engine":"Blink","engine_version":"46"},"34":{"release_date":"2015-12-08","release_notes":"https://dev.opera.com/blog/opera-34/","status":"retired","engine":"Blink","engine_version":"47"},"35":{"release_date":"2016-02-02","release_notes":"https://dev.opera.com/blog/opera-35/","status":"retired","engine":"Blink","engine_version":"48"},"36":{"release_date":"2016-03-15","release_notes":"https://dev.opera.com/blog/opera-36/","status":"retired","engine":"Blink","engine_version":"49"},"37":{"release_date":"2016-05-04","release_notes":"https://dev.opera.com/blog/opera-37/","status":"retired","engine":"Blink","engine_version":"50"},"38":{"release_date":"2016-06-08","release_notes":"https://dev.opera.com/blog/opera-38/","status":"retired","engine":"Blink","engine_version":"51"},"39":{"release_date":"2016-08-02","release_notes":"https://dev.opera.com/blog/opera-39/","status":"retired","engine":"Blink","engine_version":"52"},"40":{"release_date":"2016-09-20","release_notes":"https://dev.opera.com/blog/opera-40/","status":"retired","engine":"Blink","engine_version":"53"},"41":{"release_date":"2016-10-25","release_notes":"https://dev.opera.com/blog/opera-41/","status":"retired","engine":"Blink","engine_version":"54"},"42":{"release_date":"2016-12-13","release_notes":"https://dev.opera.com/blog/opera-42/","status":"retired","engine":"Blink","engine_version":"55"},"43":{"release_date":"2017-02-07","release_notes":"https://dev.opera.com/blog/opera-43/","status":"retired","engine":"Blink","engine_version":"56"},"44":{"release_date":"2017-03-21","release_notes":"https://dev.opera.com/blog/opera-44/","status":"retired","engine":"Blink","engine_version":"57"},"45":{"release_date":"2017-05-10","release_notes":"https://dev.opera.com/blog/opera-45/","status":"retired","engine":"Blink","engine_version":"58"},"46":{"release_date":"2017-06-22","release_notes":"https://dev.opera.com/blog/opera-46/","status":"retired","engine":"Blink","engine_version":"59"},"47":{"release_date":"2017-08-09","release_notes":"https://dev.opera.com/blog/opera-47/","status":"retired","engine":"Blink","engine_version":"60"},"48":{"release_date":"2017-09-27","status":"retired","engine":"Blink","engine_version":"61"},"49":{"release_date":"2017-11-08","release_notes":"https://dev.opera.com/blog/opera-49/","status":"retired","engine":"Blink","engine_version":"62"},"50":{"release_date":"2018-01-04","release_notes":"https://dev.opera.com/blog/opera-50/","status":"retired","engine":"Blink","engine_version":"63"},"51":{"release_date":"2018-02-07","release_notes":"https://dev.opera.com/blog/opera-51/","status":"retired","engine":"Blink","engine_version":"64"},"52":{"release_date":"2018-03-22","release_notes":"https://dev.opera.com/blog/opera-52/","status":"retired","engine":"Blink","engine_version":"65"},"53":{"release_date":"2018-05-10","release_notes":"https://dev.opera.com/blog/opera-53/","status":"retired","engine":"Blink","engine_version":"66"},"54":{"release_date":"2018-06-28","release_notes":"https://dev.opera.com/blog/opera-54/","status":"retired","engine":"Blink","engine_version":"67"},"55":{"release_date":"2018-08-16","release_notes":"https://blogs.opera.com/desktop/2018/08/opera-55-offers-better-control-web-pages-accessible-bookmarks/","status":"retired","engine":"Blink","engine_version":"68"},"56":{"release_date":"2018-09-25","release_notes":"https://dev.opera.com/blog/opera-56/","status":"retired","engine":"Blink","engine_version":"69"},"57":{"release_date":"2018-11-28","release_notes":"https://dev.opera.com/blog/opera-57/","status":"retired","engine":"Blink","engine_version":"70"},"58":{"release_date":"2019-01-23","release_notes":"https://dev.opera.com/blog/opera-58/","status":"retired","engine":"Blink","engine_version":"71"},"60":{"release_date":"2019-04-09","release_notes":"https://blogs.opera.com/desktop/2019/04/opera-60-reborn-3-web-3-0-vpn-ad-blocker/","status":"retired","engine":"Blink","engine_version":"73"},"62":{"release_date":"2019-06-27","release_notes":"https://blogs.opera.com/desktop/2019/06/opera-62-comes-with-design-updates-to-reborn-3/","status":"retired","engine":"Blink","engine_version":"75"},"63":{"release_date":"2019-08-20","release_notes":"https://blogs.opera.com/desktop/2019/08/opera-63-initial-release/","status":"retired","engine":"Blink","engine_version":"76"},"64":{"release_date":"2019-10-07","release_notes":"https://blogs.opera.com/desktop/2019/10/opera-64-faster-more-private-more-fun/","status":"retired","engine":"Blink","engine_version":"77"},"65":{"release_date":"2019-11-13","release_notes":"https://blogs.opera.com/desktop/2019/11/opera-65-comes-with-an-improved-tracker-blocker-and-redesigned-address-bar/","status":"retired","engine":"Blink","engine_version":"78"},"66":{"release_date":"2020-01-07","release_notes":"https://blogs.opera.com/desktop/2020/01/opera-66-initial-release-makes-it-easier-to-reopen-closed-tabs-and-to-access-extensions/","status":"retired","engine":"Blink","engine_version":"79"},"67":{"release_date":"2020-03-03","release_notes":"https://blogs.opera.com/desktop/2020/03/opera-67-3575-53-stable-update/","status":"retired","engine":"Blink","engine_version":"80"},"68":{"release_date":"2020-04-22","release_notes":"https://blogs.opera.com/desktop/2020/04/opera-68-is-here-with-built-in-instagram-in-the-sidebar/","status":"retired","engine":"Blink","engine_version":"81"},"69":{"release_date":"2020-06-24","release_notes":"https://blogs.opera.com/desktop/2020/06/opera-69-comes-with-built-in-twitter/","status":"retired","engine":"Blink","engine_version":"83"},"70":{"release_date":"2020-07-27","release_notes":"https://blogs.opera.com/desktop/2020/07/opera-70-comes-with-easier-access-to-closed-tabs-simpler-searches-and-new-workspace-icons/","status":"retired","engine":"Blink","engine_version":"84"},"71":{"release_date":"2020-09-15","release_notes":"https://blogs.opera.com/desktop/2020/09/opera-71-update/","status":"retired","engine":"Blink","engine_version":"85"},"72":{"release_date":"2020-10-21","release_notes":"https://blogs.opera.com/desktop/2020/10/opera-72-update/","status":"retired","engine":"Blink","engine_version":"86"},"73":{"release_date":"2020-12-09","release_notes":"https://blogs.opera.com/desktop/2020/12/opera-73-update/","status":"retired","engine":"Blink","engine_version":"87"},"74":{"release_date":"2021-02-02","release_notes":"https://blogs.opera.com/desktop/2021/02/opera-74-stable/","status":"retired","engine":"Blink","engine_version":"88"},"75":{"release_date":"2021-03-24","release_notes":"https://blogs.opera.com/desktop/2021/03/opera-75-brings-easier-access-to-top-features-2/","status":"retired","engine":"Blink","engine_version":"89"},"76":{"release_date":"2021-04-28","release_notes":"https://blogs.opera.com/desktop/2021/04/opera-76-stable/","status":"retired","engine":"Blink","engine_version":"90"},"77":{"release_date":"2021-06-09","release_notes":"https://blogs.opera.com/desktop/2021/06/opera-77-stable/","status":"retired","engine":"Blink","engine_version":"91"},"78":{"release_date":"2021-08-03","release_notes":"https://blogs.opera.com/desktop/2021/08/opera-78-stable/","status":"current","engine":"Blink","engine_version":"92"},"79":{"status":"beta","engine":"Blink","engine_version":"93"},"3.5":{"release_date":"1998-11-18","status":"retired"},"3.6":{"release_date":"1999-05-06","status":"retired"},"5.1":{"release_date":"2001-04-10","status":"retired"},"7.1":{"release_date":"2003-04-11","status":"retired","engine":"Presto","engine_version":"1"},"7.2":{"release_date":"2003-09-23","status":"retired","engine":"Presto","engine_version":"1"},"7.5":{"release_date":"2004-05-12","status":"retired","engine":"Presto","engine_version":"1"},"8.5":{"release_date":"2005-09-20","status":"retired","engine":"Presto","engine_version":"1"},"9.1":{"release_date":"2006-12-18","status":"retired","engine":"Presto","engine_version":"2"},"9.2":{"release_date":"2007-04-11","status":"retired","engine":"Presto","engine_version":"2"},"9.5":{"release_date":"2008-06-12","status":"retired","engine":"Presto","engine_version":"2.1"},"9.6":{"release_date":"2008-10-08","release_notes":"https://dev.opera.com/blog/a-look-under-the-hood-of-opera-9-6/","status":"retired","engine":"Presto","engine_version":"2.1"},"10.1":{"release_date":"2009-11-23","release_notes":"https://dev.opera.com/blog/opera-10-10-and-10-2-alpha/","status":"retired","engine":"Presto","engine_version":"2.2"},"10.5":{"release_date":"2010-03-02","release_notes":"https://dev.opera.com/blog/opera-10-50-final-for-windows-is-out/","status":"retired","engine":"Presto","engine_version":"2.5"},"10.6":{"release_date":"2010-07-01","release_notes":"https://dev.opera.com/blog/hello-opera-10-60/","status":"retired","engine":"Presto","engine_version":"2.6"},"11.1":{"release_date":"2011-04-12","release_notes":"https://dev.opera.com/blog/unveiling-opera-11-10-final/","status":"retired","engine":"Presto","engine_version":"2.8"},"11.5":{"release_date":"2011-06-28","release_notes":"https://dev.opera.com/blog/opera-11-50-released-speed-dial-extensions-improved-standards-support/","status":"retired","engine":"Presto","engine_version":"2.9"},"11.6":{"release_date":"2011-12-06","release_notes":"https://dev.opera.com/blog/hello-opera-11-60/","status":"retired","engine":"Presto","engine_version":"2.10"},"12.1":{"release_date":"2012-11-20","release_notes":"https://dev.opera.com/blog/opera-12-10-is-out/","status":"retired","engine":"Presto","engine_version":"2.12"}}},"opera_android":{"name":"Opera Android","releases":{"11":{"release_date":"2011-03-22","release_notes":"https://dev.opera.com/blog/opera-mobile-11-for-maemo-meego-windows/","status":"retired","engine":"Presto","engine_version":"2.7"},"12":{"release_date":"2012-02-25","release_notes":"https://dev.opera.com/blog/opera-mobile-12-and-introducing-opera-mini-next/","status":"retired","engine":"Presto","engine_version":"2.10"},"14":{"release_date":"2013-05-21","release_notes":"https://dev.opera.com/blog/opera-14-for-android-is-out/","status":"retired","engine":"Blink","engine_version":"26"},"15":{"release_date":"2013-07-08","release_notes":"https://blogs.opera.com/news/2013/07/opera-15-for-android/","status":"retired","engine":"Blink","engine_version":"28"},"16":{"release_date":"2013-09-18","status":"retired","engine":"Blink","engine_version":"29"},"18":{"release_date":"2013-11-20","release_notes":"https://blogs.opera.com/news/2013/11/opera-18-android-tablet/","status":"retired","engine":"Blink","engine_version":"31"},"19":{"release_date":"2014-01-28","release_notes":"https://dev.opera.com/blog/opera-19/","status":"retired","engine":"Blink","engine_version":"32"},"20":{"release_date":"2014-03-06","release_notes":"https://forums.opera.com/topic/1081/opera-20-final-release","status":"retired","engine":"Blink","engine_version":"33"},"21":{"release_date":"2014-04-22","release_notes":"https://forums.opera.com/topic/2211/opera-21-final-release","status":"retired","engine":"Blink","engine_version":"34"},"22":{"release_date":"2014-06-17","release_notes":"https://forums.opera.com/topic/3446/opera-22","status":"retired","engine":"Blink","engine_version":"35"},"24":{"release_date":"2014-09-10","release_notes":"https://forums.opera.com/topic/5022/opera-24-final-release","status":"retired","engine":"Blink","engine_version":"37"},"25":{"release_date":"2014-10-16","release_notes":"https://forums.opera.com/topic/5715/opera-25-final-release","status":"retired","engine":"Blink","engine_version":"38"},"26":{"release_date":"2014-12-02","release_notes":"https://forums.opera.com/topic/6800/opera-26-final-release","status":"retired","engine":"Blink","engine_version":"39"},"27":{"release_date":"2015-01-29","release_notes":"https://forums.opera.com/topic/7871/opera-27-final-release","status":"retired","engine":"Blink","engine_version":"40"},"28":{"release_date":"2015-03-10","release_notes":"https://forums.opera.com/topic/8556/synced-bookmarks-and-improved-memory-usage-in-opera-28-for-android","status":"retired","engine":"Blink","engine_version":"41"},"29":{"release_date":"2015-04-28","release_notes":"https://dev.opera.com/blog/opera-29/","status":"retired","engine":"Blink","engine_version":"42"},"30":{"release_date":"2015-06-10","release_notes":"https://blogs.opera.com/mobile/2015/06/opera-30-android-sync-speeddials/","status":"retired","engine":"Blink","engine_version":"43"},"32":{"release_date":"2015-09-23","release_notes":"https://blogs.opera.com/mobile/2015/09/opera-32-add-to-home-screen/","status":"retired","engine":"Blink","engine_version":"45"},"33":{"release_date":"2015-11-03","release_notes":"https://forums.opera.com/topic/12480/opera-33-now-featuring-video-optimization-and-brand-new-icons","status":"retired","engine":"Blink","engine_version":"46"},"34":{"release_date":"2015-12-16","release_notes":"https://forums.opera.com/topic/13085/opera-34-for-android-released","status":"retired","engine":"Blink","engine_version":"47"},"35":{"release_date":"2016-02-04","release_notes":"https://blogs.opera.com/mobile/2016/02/save-space-on-your-android-phone-with-web-apps/","status":"retired","engine":"Blink","engine_version":"48"},"36":{"release_date":"2016-03-31","release_notes":"https://forums.opera.com/topic/14514/opera-36-released","status":"retired","engine":"Blink","engine_version":"49"},"37":{"release_date":"2016-06-16","release_notes":"https://forums.opera.com/topic/15753/opera-37-released","status":"retired","engine":"Blink","engine_version":"50"},"41":{"release_date":"2016-10-25","status":"retired","engine":"Blink","engine_version":"54"},"42":{"release_date":"2017-01-21","release_notes":"https://forums.opera.com/topic/18950/opera-for-android-42","status":"retired","engine":"Blink","engine_version":"55"},"43":{"release_date":"2017-09-27","release_notes":"https://forums.opera.com/topic/22708/opera-for-android-43","status":"retired","engine":"Blink","engine_version":"59"},"44":{"release_date":"2017-12-11","release_notes":"https://forums.opera.com/topic/23860/opera-for-android-44","status":"retired","engine":"Blink","engine_version":"60"},"45":{"release_date":"2018-02-15","release_notes":"https://forums.opera.com/topic/25124/opera-for-android-45","status":"retired","engine":"Blink","engine_version":"61"},"46":{"release_date":"2018-05-14","release_notes":"https://forums.opera.com/topic/26662/opera-for-android-46","status":"retired","engine":"Blink","engine_version":"63"},"47":{"release_date":"2018-07-23","release_notes":"https://forums.opera.com/topic/27794/opera-for-android-47","status":"retired","engine":"Blink","engine_version":"66"},"48":{"release_date":"2018-11-08","release_notes":"https://forums.opera.com/topic/29525/opera-for-android-48","status":"retired","engine":"Blink","engine_version":"69"},"49":{"release_date":"2018-12-07","release_notes":"https://forums.opera.com/topic/29983/opera-for-android-49","status":"retired","engine":"Blink","engine_version":"70"},"50":{"release_date":"2019-02-18","release_notes":"https://forums.opera.com/topic/31003/opera-for-android-50","status":"retired","engine":"Blink","engine_version":"71"},"51":{"release_date":"2019-03-21","release_notes":"https://forums.opera.com/topic/31467/opera-for-android-51-built-in-vpn","status":"retired","engine":"Blink","engine_version":"72"},"52":{"release_date":"2019-05-17","release_notes":"https://forums.opera.com/topic/32516/opera-for-android-52","status":"retired","engine":"Blink","engine_version":"73"},"53":{"release_date":"2019-07-11","release_notes":"https://forums.opera.com/topic/33558/opera-for-android-53","status":"retired","engine":"Blink","engine_version":"74"},"54":{"release_date":"2019-10-18","release_notes":"https://forums.opera.com/topic/35853/opera-for-android-54","status":"retired","engine":"Blink","engine_version":"76"},"55":{"release_date":"2019-12-03","release_notes":"https://forums.opera.com/topic/36858/opera-for-android-55","status":"retired","engine":"Blink","engine_version":"77"},"56":{"release_date":"2020-02-06","release_notes":"https://blogs.opera.com/mobile/2020/02/easy-reading-in-opera-for-android/","status":"retired","engine":"Blink","engine_version":"78"},"57":{"release_date":"2020-03-30","release_notes":"https://blogs.opera.com/mobile/2020/03/introducing-new-features-in-opera-for-android-57/","status":"retired","engine":"Blink","engine_version":"80"},"58":{"release_date":"2020-05-13","release_notes":"https://blogs.opera.com/mobile/2020/05/opera-for-android-58-handle-notifications-easily-and-group-speed-dials-for-a-cleaner-appearance/","status":"retired","engine":"Blink","engine_version":"81"},"59":{"release_date":"2020-06-30","release_notes":"https://blogs.opera.com/mobile/2020/06/opera-for-android-59/","status":"retired","engine":"Blink","engine_version":"83"},"60":{"release_date":"2020-09-23","release_notes":"https://blogs.opera.com/mobile/2020/09/keep-in-sync-with-opera-for-android-60/","status":"retired","engine":"Blink","engine_version":"85"},"61":{"release_date":"2020-12-07","release_notes":"https://blogs.opera.com/mobile/2020/12/new-opera-for-android-61/","status":"retired","engine":"Blink","engine_version":"86"},"62":{"release_date":"2021-02-16","release_notes":"https://blogs.opera.com/mobile/2021/02/the-opera-browser-for-android-hit-a-new-record-of-80-million-maus/","status":"retired","engine":"Blink","engine_version":"87"},"63":{"release_date":"2021-04-16","status":"retired","engine":"Blink","engine_version":"89"},"64":{"release_date":"2021-05-25","status":"current","engine":"Blink","engine_version":"91"},"10.1":{"release_date":"2010-11-09","release_notes":"https://dev.opera.com/blog/opera-mobile-10-1-beta-for-android-is-here/","status":"retired","engine":"Presto","engine_version":"2.5"},"11.1":{"release_date":"2011-06-30","release_notes":"https://dev.opera.com/blog/opera-mobile-11-1-new-features-and-additions/","status":"retired","engine":"Presto","engine_version":"2.8"},"11.5":{"release_date":"2011-10-12","status":"retired","engine":"Presto","engine_version":"2.9"},"12.1":{"release_date":"2012-10-09","release_notes":"https://dev.opera.com/blog/opera-mobile-12-1-with-spdy-web-sockets-flexbox-and-more/","status":"retired","engine":"Presto","engine_version":"2.11"}}},"safari":{"name":"Safari","preview_name":"TP","releases":{"1":{"release_date":"2003-06-23","status":"retired","engine":"WebKit","engine_version":"85"},"2":{"release_date":"2005-04-29","status":"retired","engine":"WebKit","engine_version":"412"},"3":{"release_date":"2007-10-26","status":"retired","engine":"WebKit","engine_version":"523.10"},"4":{"release_date":"2009-06-08","release_notes":"https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_4_0.html","status":"retired","engine":"WebKit","engine_version":"530.17"},"5":{"release_date":"2010-06-07","status":"retired","engine":"WebKit","engine_version":"533.16"},"6":{"release_date":"2012-07-25","release_notes":"https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_6_0.html","status":"retired","engine":"WebKit","engine_version":"536.25"},"7":{"release_date":"2013-10-22","release_notes":"https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_7_0.html","status":"retired","engine":"WebKit","engine_version":"537.71"},"8":{"release_date":"2014-10-16","release_notes":"https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_8_0.html","status":"retired","engine":"WebKit","engine_version":"538.35"},"9":{"release_date":"2015-09-30","release_notes":"https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_9_0.html","status":"retired","engine":"WebKit","engine_version":"601.1.56"},"10":{"release_date":"2016-09-20","release_notes":"https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_10_0.html","status":"retired","engine":"WebKit","engine_version":"602.1.50"},"11":{"release_date":"2017-09-19","release_notes":"https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Safari_11_0/Safari_11_0.html","status":"retired","engine":"WebKit","engine_version":"604.2.4"},"12":{"release_date":"2018-09-24","release_notes":"https://developer.apple.com/documentation/safari_release_notes/safari_12_release_notes","status":"retired","engine":"WebKit","engine_version":"606.1.36"},"13":{"release_date":"2019-09-19","release_notes":"https://developer.apple.com/documentation/safari_release_notes/safari_13_release_notes","status":"retired","engine":"WebKit","engine_version":"608.2.11"},"14":{"release_date":"2020-09-16","release_notes":"https://developer.apple.com/documentation/safari-release-notes/safari-14-release-notes","status":"retired","engine":"WebKit","engine_version":"610.1.28"},"15":{"release_notes":"https://developer.apple.com/documentation/safari-release-notes/safari-15-beta-release-notes","status":"beta","engine":"WebKit"},"1.1":{"release_date":"2003-10-24","status":"retired","engine":"WebKit","engine_version":"100"},"1.2":{"release_date":"2004-02-02","status":"retired","engine":"WebKit","engine_version":"125"},"1.3":{"release_date":"2005-04-15","status":"retired","engine":"WebKit","engine_version":"312"},"3.1":{"release_date":"2008-03-18","status":"retired","engine":"WebKit","engine_version":"525.13"},"5.1":{"release_date":"2011-07-20","status":"retired","engine":"WebKit","engine_version":"534.48"},"6.1":{"release_date":"2013-10-22","status":"retired","engine":"WebKit","engine_version":"537.43"},"9.1":{"release_date":"2016-03-21","release_notes":"https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_9_1.html","status":"retired","engine":"WebKit","engine_version":"601.5.17"},"10.1":{"release_date":"2017-03-27","release_notes":"https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_10_1.html","status":"retired","engine":"WebKit","engine_version":"603.2.1"},"11.1":{"release_date":"2018-04-12","release_notes":"https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_11_1.html","status":"retired","engine":"WebKit","engine_version":"605.1.33"},"12.1":{"release_date":"2019-03-25","release_notes":"https://developer.apple.com/documentation/safari_release_notes/safari_12_1_release_notes","status":"retired","engine":"WebKit","engine_version":"607.1.40"},"13.1":{"release_date":"2020-03-24","release_notes":"https://developer.apple.com/documentation/safari-release-notes/safari-13_1-release_notes","status":"retired","engine":"WebKit","engine_version":"609.1.20"},"14.1":{"release_date":"2021-04-26","release_notes":"https://developer.apple.com/documentation/safari-release-notes/safari-14_1-release-notes","status":"current","engine":"WebKit","engine_version":"611.1.21.161.3"}}},"safari_ios":{"name":"Safari on iOS","releases":{"1":{"status":"retired","engine":"WebKit","engine_version":"522.11","release_date":"2007-06-29"},"2":{"status":"retired","engine":"WebKit","engine_version":"525.18","release_date":"2008-07-11"},"3":{"status":"retired","engine":"WebKit","engine_version":"528.18","release_date":"2009-06-17"},"4":{"status":"retired","engine":"WebKit","engine_version":"532.9","release_date":"2010-06-21"},"5":{"status":"retired","engine":"WebKit","engine_version":"534.46","release_date":"2011-10-12"},"6":{"status":"retired","engine":"WebKit","engine_version":"536.26","release_date":"2012-09-10"},"7":{"status":"retired","engine":"WebKit","engine_version":"537.51","release_date":"2013-09-18"},"8":{"status":"retired","engine":"WebKit","engine_version":"600.1.4","release_date":"2014-09-17"},"9":{"status":"retired","engine":"WebKit","engine_version":"601.1.56","release_date":"2015-09-16"},"10":{"status":"retired","engine":"WebKit","engine_version":"602.1.50","release_date":"2016-09-13"},"11":{"status":"retired","engine":"WebKit","engine_version":"604.2.4","release_date":"2017-09-19"},"12":{"release_date":"2018-09-17","release_notes":"https://developer.apple.com/documentation/safari_release_notes/safari_12_release_notes","status":"retired","engine":"WebKit","engine_version":"606.1.36"},"13":{"release_date":"2019-09-19","release_notes":"https://developer.apple.com/documentation/safari_release_notes/safari_13_release_notes","status":"retired","engine":"WebKit","engine_version":"608.2.11"},"14":{"release_date":"2020-09-16","release_notes":"https://developer.apple.com/documentation/safari-release-notes/safari-14-release-notes","status":"retired","engine":"WebKit","engine_version":"610.1.28"},"15":{"release_notes":"https://developer.apple.com/documentation/safari-release-notes/safari-15-beta-release-notes","status":"beta","engine":"WebKit"},"3.2":{"status":"retired","engine":"WebKit","engine_version":"531.21","release_date":"2010-04-03"},"4.2":{"status":"retired","engine":"WebKit","engine_version":"533.17","release_date":"2010-11-22"},"6.1":{"status":"retired","engine":"WebKit","engine_version":"536.26","release_date":"2013-01-28"},"9.3":{"status":"retired","engine":"WebKit","engine_version":"601.5.17","release_date":"2016-03-21"},"10.3":{"status":"retired","engine":"WebKit","engine_version":"603.2.1","release_date":"2017-03-27"},"11.3":{"status":"retired","engine":"WebKit","engine_version":"605.1.33","release_date":"2018-03-29"},"12.2":{"release_date":"2019-03-25","release_notes":"https://developer.apple.com/documentation/safari_release_notes/safari_12_1_release_notes","status":"retired","engine":"WebKit","engine_version":"607.1.40"},"13.4":{"release_date":"2020-03-24","release_notes":"https://developer.apple.com/documentation/safari-release-notes/safari-13_1-release_notes","status":"retired","engine":"WebKit","engine_version":"609.1.20"},"14.5":{"release_date":"2021-04-26","release_notes":"https://developer.apple.com/documentation/safari-release-notes/safari-14_1-release-notes","status":"current","engine":"WebKit","engine_version":"611.1.21.161.6"}}},"samsunginternet_android":{"name":"Samsung Internet","accepts_flags":false,"releases":{"1.0":{"release_date":"2013-04-27","status":"retired","engine":"WebKit","engine_version":"535.19"},"1.5":{"release_date":"2013-09-25","status":"retired","engine":"Blink","engine_version":"28"},"1.6":{"release_date":"2014-04-11","status":"retired","engine":"Blink","engine_version":"28"},"2.0":{"release_date":"2014-10-17","status":"retired","engine":"Blink","engine_version":"34"},"2.1":{"release_date":"2015-01-07","status":"retired","engine":"Blink","engine_version":"34"},"3.0":{"release_date":"2015-04-10","status":"retired","engine":"Blink","engine_version":"38"},"3.2":{"release_date":"2015-08-24","status":"retired","engine":"Blink","engine_version":"38"},"4.0":{"release_date":"2016-03-11","status":"retired","engine":"Blink","engine_version":"44"},"4.2":{"release_date":"2016-08-02","status":"retired","engine":"Blink","engine_version":"44"},"5.0":{"release_date":"2016-12-15","status":"retired","engine":"Blink","engine_version":"51"},"5.2":{"release_date":"2017-04-21","status":"retired","engine":"Blink","engine_version":"51"},"5.4":{"release_date":"2017-05-17","status":"retired","engine":"Blink","engine_version":"51"},"6.0":{"release_date":"2017-08-23","status":"retired","engine":"Blink","engine_version":"56"},"6.2":{"release_date":"2017-10-26","status":"retired","engine":"Blink","engine_version":"56"},"6.4":{"release_date":"2018-02-19","status":"retired","engine":"Blink","engine_version":"56"},"7.0":{"release_date":"2018-03-16","status":"retired","engine":"Blink","engine_version":"59"},"7.2":{"release_date":"2018-06-20","status":"retired","engine":"Blink","engine_version":"59"},"7.4":{"release_date":"2018-09-12","status":"retired","engine":"Blink","engine_version":"59"},"8.0":{"release_date":"2018-07-18","status":"retired","engine":"Blink","engine_version":"63"},"8.2":{"release_date":"2018-12-21","status":"retired","engine":"Blink","engine_version":"63"},"9.0":{"release_date":"2018-09-15","status":"retired","engine":"Blink","engine_version":"67"},"9.2":{"release_date":"2019-04-02","status":"retired","engine":"Blink","engine_version":"67"},"9.4":{"release_date":"2019-07-25","status":"retired","engine":"Blink","engine_version":"67"},"10.0":{"release_date":"2019-08-22","status":"retired","engine":"Blink","engine_version":"71"},"10.2":{"release_date":"2019-10-09","status":"retired","engine":"Blink","engine_version":"71"},"11.0":{"release_date":"2019-12-05","status":"retired","engine":"Blink","engine_version":"75"},"11.2":{"release_date":"2020-03-22","status":"retired","engine":"Blink","engine_version":"75"},"12.0":{"release_date":"2020-06-19","status":"retired","engine":"Blink","engine_version":"79"},"12.1":{"release_date":"2020-07-07","status":"retired","engine":"Blink","engine_version":"79"},"13.0":{"release_date":"2020-12-02","status":"retired","engine":"Blink","engine_version":"83"},"13.2":{"release_date":"2021-01-20","status":"retired","engine":"Blink","engine_version":"83"},"14.0":{"release_date":"2021-04-17","status":"retired","engine":"Blink","engine_version":"87"},"14.2":{"release_date":"2021-05-05","status":"current","engine":"Blink","engine_version":"87"},"15.0":{"release_date":"2021-07-15","status":"beta","engine":"Blink","engine_version":"90"}}},"webview_android":{"name":"WebView Android","accepts_flags":false,"releases":{"1":{"release_date":"2008-09-23","release_notes":"https://en.wikipedia.org/wiki/Android_version_history#Android_1.0_(API_1)","status":"retired","engine":"WebKit","engine_version":"523.12"},"2":{"release_date":"2009-10-26","release_notes":"https://en.wikipedia.org/wiki/Android_Eclair","status":"retired","engine":"WebKit","engine_version":"530.17"},"3":{"release_date":"2011-02-22","release_notes":"https://en.wikipedia.org/wiki/Android_Honeycomb","status":"retired","engine":"WebKit","engine_version":"534.13"},"4":{"release_date":"2011-10-18","release_notes":"https://en.wikipedia.org/wiki/Android_Ice_Cream_Sandwich","status":"retired","engine":"WebKit","engine_version":"534.30"},"37":{"release_date":"2014-09-03","release_notes":"https://chromereleases.googleblog.com/2014/09/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"37"},"38":{"release_date":"2014-10-08","release_notes":"https://chromereleases.googleblog.com/2014/10/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"38"},"39":{"release_date":"2014-11-12","release_notes":"https://chromereleases.googleblog.com/2014/11/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"39"},"40":{"release_date":"2015-01-21","release_notes":"https://chromereleases.googleblog.com/2015/01/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"40"},"41":{"release_date":"2015-03-11","release_notes":"https://chromereleases.googleblog.com/2015/03/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"41"},"42":{"release_date":"2015-04-15","release_notes":"https://chromereleases.googleblog.com/2015/04/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"42"},"43":{"release_date":"2015-05-27","release_notes":"https://chromereleases.googleblog.com/2015/05/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"43"},"44":{"release_date":"2015-07-29","release_notes":"https://chromereleases.googleblog.com/2015/07/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"44"},"45":{"release_date":"2015-09-01","release_notes":"https://chromereleases.googleblog.com/2015/09/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"45"},"46":{"release_date":"2015-10-14","release_notes":"https://chromereleases.googleblog.com/2015/10/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"46"},"47":{"release_date":"2015-12-02","release_notes":"https://chromereleases.googleblog.com/2015/12/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"47"},"48":{"release_date":"2016-01-26","status":"retired","engine":"Blink","engine_version":"48"},"49":{"release_date":"2016-03-09","release_notes":"https://chromereleases.googleblog.com/2016/03/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"49"},"50":{"release_date":"2016-04-13","status":"retired","engine":"Blink","engine_version":"50"},"51":{"release_date":"2016-06-08","release_notes":"https://chromereleases.googleblog.com/2016/06/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"51"},"52":{"release_date":"2016-07-27","release_notes":"https://chromereleases.googleblog.com/2016/07/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"52"},"53":{"release_date":"2016-09-07","release_notes":"https://chromereleases.googleblog.com/2016/09/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"53"},"54":{"release_date":"2016-10-19","release_notes":"https://chromereleases.googleblog.com/2016/10/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"54"},"55":{"release_date":"2016-12-06","release_notes":"https://chromereleases.googleblog.com/2016/12/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"55"},"56":{"release_date":"2017-02-01","release_notes":"https://chromereleases.googleblog.com/2017/02/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"56"},"57":{"release_date":"2017-03-16","release_notes":"https://chromereleases.googleblog.com/2017/03/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"57"},"58":{"release_date":"2017-04-25","release_notes":"https://chromereleases.googleblog.com/2017/04/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"58"},"59":{"release_date":"2017-06-06","release_notes":"https://chromereleases.googleblog.com/2017/06/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"59"},"60":{"release_date":"2017-08-01","release_notes":"https://chromereleases.googleblog.com/2017/08/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"60"},"61":{"release_date":"2017-09-05","release_notes":"https://chromereleases.googleblog.com/2017/09/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"61"},"62":{"release_date":"2017-10-24","release_notes":"https://chromereleases.googleblog.com/2017/10/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"62"},"63":{"release_date":"2017-12-05","release_notes":"https://chromereleases.googleblog.com/2017/12/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"63"},"64":{"release_date":"2018-01-23","release_notes":"https://chromereleases.googleblog.com/2018/01/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"64"},"65":{"release_date":"2017-03-06","release_notes":"https://chromereleases.googleblog.com/2018/03/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"65"},"66":{"release_date":"2017-04-17","release_notes":"https://chromereleases.googleblog.com/2018/04/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"66"},"67":{"release_date":"2018-05-31","release_notes":"https://chromereleases.googleblog.com/2018/05/chrome-for-android-update_31.html","status":"retired","engine":"Blink","engine_version":"67"},"68":{"release_date":"2018-07-24","release_notes":"https://chromereleases.googleblog.com/2018/07/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"68"},"69":{"release_date":"2018-09-04","release_notes":"https://chromereleases.googleblog.com/2018/09/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"69"},"70":{"release_date":"2018-10-17","release_notes":"https://chromereleases.googleblog.com/2018/10/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"70"},"71":{"release_date":"2018-12-04","release_notes":"https://chromereleases.googleblog.com/2018/12/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"71"},"72":{"release_date":"2019-01-29","release_notes":"https://chromereleases.googleblog.com/2019/01/chrome-for-android-update_29.html","status":"retired","engine":"Blink","engine_version":"72"},"73":{"release_date":"2019-03-12","release_notes":"https://chromereleases.googleblog.com/2019/03/chrome-for-android-update_12.html","status":"retired","engine":"Blink","engine_version":"73"},"74":{"release_date":"2019-04-24","release_notes":"https://chromereleases.googleblog.com/2019/04/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"74"},"75":{"release_date":"2019-06-04","release_notes":"https://chromereleases.googleblog.com/2019/06/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"75"},"76":{"release_date":"2019-07-30","release_notes":"https://chromereleases.googleblog.com/2019/07/chrome-for-android-update_30.html","status":"retired","engine":"Blink","engine_version":"76"},"77":{"release_date":"2019-09-10","release_notes":"https://chromereleases.googleblog.com/2019/09/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"77"},"78":{"release_date":"2019-10-22","release_notes":"https://chromereleases.googleblog.com/2019/10/chrome-for-android-update_22.html","status":"retired","engine":"Blink","engine_version":"78"},"79":{"release_date":"2019-12-17","release_notes":"https://chromereleases.googleblog.com/2019/12/chrome-for-android-update_17.html","status":"retired","engine":"Blink","engine_version":"79"},"80":{"release_date":"2020-02-04","release_notes":"https://chromereleases.googleblog.com/2020/02/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"80"},"81":{"release_date":"2020-04-07","release_notes":"https://chromereleases.googleblog.com/2020/04/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"81"},"83":{"release_date":"2020-05-19","release_notes":"https://chromereleases.googleblog.com/2020/05/chrome-for-android-update_19.html","status":"retired","engine":"Blink","engine_version":"83"},"84":{"release_date":"2020-07-27","release_notes":"https://chromereleases.googleblog.com/2020/07/chrome-for-android-update_27.html","status":"retired","engine":"Blink","engine_version":"84"},"85":{"release_date":"2020-08-25","release_notes":"https://chromereleases.googleblog.com/2020/08/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"85"},"86":{"release_date":"2020-10-20","release_notes":"https://chromereleases.googleblog.com/2020/10/chrome-for-android-update_20.html","status":"retired","engine":"Blink","engine_version":"86"},"87":{"release_date":"2020-11-17","release_notes":"https://chromereleases.googleblog.com/2020/11/chrome-for-android-update_17.html","status":"retired","engine":"Blink","engine_version":"87"},"88":{"release_date":"2021-01-19","release_notes":"https://chromereleases.googleblog.com/2021/01/chrome-for-android-update_19.html","status":"retired","engine":"Blink","engine_version":"88"},"89":{"release_date":"2021-03-02","release_notes":"https://chromereleases.googleblog.com/2021/03/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"89"},"90":{"release_date":"2021-04-13","release_notes":"https://chromereleases.googleblog.com/2021/04/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"90"},"91":{"release_date":"2021-05-25","release_notes":"https://chromereleases.googleblog.com/2021/05/chrome-for-android-update_01607414128.html","status":"retired","engine":"Blink","engine_version":"91"},"92":{"release_date":"2021-07-20","release_notes":"https://chromereleases.googleblog.com/2021/07/chrome-for-android-update_01500789893.html","status":"current","engine":"Blink","engine_version":"92"},"93":{"status":"beta","engine":"Blink","engine_version":"93"},"94":{"status":"nightly","engine":"Blink","engine_version":"94"},"1.5":{"release_date":"2009-04-27","release_notes":"https://en.wikipedia.org/wiki/Android_Cupcake","status":"retired","engine":"WebKit","engine_version":"525.20"},"2.2":{"release_date":"2010-05-20","release_notes":"https://en.wikipedia.org/wiki/Android_Froyo","status":"retired","engine":"WebKit","engine_version":"533.1"},"4.4":{"release_date":"2013-12-09","release_notes":"https://chromereleases.googleblog.com/2013/10/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"30"},"4.4.3":{"release_date":"2014-06-02","release_notes":"https://chromereleases.googleblog.com/2014/02/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"33"}}}} \ No newline at end of file +{"chrome":{"name":"Chrome","preview_name":"Canary","pref_url":"chrome://flags","releases":{"1":{"release_date":"2008-12-11","release_notes":"https://chromereleases.googleblog.com/2008/12/stable-release-google-chrome-is-out-of.html","status":"retired","engine":"WebKit","engine_version":"528"},"2":{"release_date":"2009-05-21","release_notes":"https://chromereleases.googleblog.com/2009/05/stable-update-google-chrome-2017228.html","status":"retired","engine":"WebKit","engine_version":"530"},"3":{"release_date":"2009-09-15","release_notes":"https://chromereleases.googleblog.com/2009/09/stable-channel-update.html","status":"retired","engine":"WebKit","engine_version":"532"},"4":{"release_date":"2010-01-25","release_notes":"https://chromereleases.googleblog.com/2010/01/stable-channel-update_25.html","status":"retired","engine":"WebKit","engine_version":"532.5"},"5":{"release_date":"2010-05-25","release_notes":"https://chromereleases.googleblog.com/2010/05/stable-channel-update.html","status":"retired","engine":"WebKit","engine_version":"533"},"6":{"release_date":"2010-09-02","release_notes":"https://chromereleases.googleblog.com/2010/09/stable-and-beta-channel-updates.html","status":"retired","engine":"WebKit","engine_version":"534.3"},"7":{"release_date":"2010-10-19","release_notes":"https://chromereleases.googleblog.com/2010/10/stable-channel-update.html","status":"retired","engine":"WebKit","engine_version":"534.7"},"8":{"release_date":"2010-12-02","release_notes":"https://chromereleases.googleblog.com/2010/12/stable-beta-channel-updates.html","status":"retired","engine":"WebKit","engine_version":"534.10"},"9":{"release_date":"2011-02-03","release_notes":"https://chromereleases.googleblog.com/2011/02/stable-channel-update.html","status":"retired","engine":"WebKit","engine_version":"534.13"},"10":{"release_date":"2011-03-08","release_notes":"https://chromereleases.googleblog.com/2011/03/chrome-stable-release.html","status":"retired","engine":"WebKit","engine_version":"534.16"},"11":{"release_date":"2011-04-27","release_notes":"https://chromereleases.googleblog.com/2011/04/chrome-stable-update.html","status":"retired","engine":"WebKit","engine_version":"534.24"},"12":{"release_date":"2011-06-07","release_notes":"https://chromereleases.googleblog.com/2011/06/chrome-stable-release.html","status":"retired","engine":"WebKit","engine_version":"534.30"},"13":{"release_date":"2011-08-02","release_notes":"https://chromereleases.googleblog.com/2011/08/stable-channel-update.html","status":"retired","engine":"WebKit","engine_version":"535.1"},"14":{"release_date":"2011-09-16","release_notes":"https://chromereleases.googleblog.com/2011/09/stable-channel-update_16.html","status":"retired","engine":"WebKit","engine_version":"535.1"},"15":{"release_date":"2011-10-25","release_notes":"https://chromereleases.googleblog.com/2011/10/chrome-stable-release.html","status":"retired","engine":"WebKit","engine_version":"535.2"},"16":{"release_date":"2011-12-13","release_notes":"https://chromereleases.googleblog.com/2011/12/stable-channel-update.html","status":"retired","engine":"WebKit","engine_version":"535.7"},"17":{"release_date":"2012-02-08","release_notes":"https://chromereleases.googleblog.com/2012/02/stable-channel-update.html","status":"retired","engine":"WebKit","engine_version":"535.11"},"18":{"release_date":"2012-03-28","release_notes":"https://chromereleases.googleblog.com/2012/03/stable-channel-release-and-beta-channel.html","status":"retired","engine":"WebKit","engine_version":"535.19"},"19":{"release_date":"2012-05-15","release_notes":"https://chromereleases.googleblog.com/2012/05/stable-channel-update.html","status":"retired","engine":"WebKit","engine_version":"536.5"},"20":{"release_date":"2012-06-26","release_notes":"https://chromereleases.googleblog.com/2012/06/stable-channel-update_26.html","status":"retired","engine":"WebKit","engine_version":"536.10"},"21":{"release_date":"2012-07-31","release_notes":"https://chromereleases.googleblog.com/2012/07/stable-channel-release.html","status":"retired","engine":"WebKit","engine_version":"537.1"},"22":{"release_date":"2012-09-25","release_notes":"https://chromereleases.googleblog.com/2012/09/stable-channel-update_25.html","status":"retired","engine":"WebKit","engine_version":"537.4"},"23":{"release_date":"2012-11-06","release_notes":"https://chromereleases.googleblog.com/2012/11/stable-channel-release-and-beta-channel.html","status":"retired","engine":"WebKit","engine_version":"537.11"},"24":{"release_date":"2013-01-10","release_notes":"https://chromereleases.googleblog.com/2013/01/stable-channel-update.html","status":"retired","engine":"WebKit","engine_version":"537.17"},"25":{"release_date":"2013-02-21","release_notes":"https://chromereleases.googleblog.com/2013/02/stable-channel-update_21.html","status":"retired","engine":"WebKit","engine_version":"537.22"},"26":{"release_date":"2013-03-26","release_notes":"https://chromereleases.googleblog.com/2013/03/stable-channel-update_26.html","status":"retired","engine":"WebKit","engine_version":"537.31"},"27":{"release_date":"2013-05-21","release_notes":"https://chromereleases.googleblog.com/2013/05/stable-channel-release.html","status":"retired","engine":"WebKit","engine_version":"537.36"},"28":{"release_date":"2013-07-09","release_notes":"https://chromereleases.googleblog.com/2013/07/stable-channel-update.html","status":"retired","engine":"Blink","engine_version":"28"},"29":{"release_date":"2013-08-20","release_notes":"https://chromereleases.googleblog.com/2013/08/stable-channel-update.html","status":"retired","engine":"Blink","engine_version":"29"},"30":{"release_date":"2013-10-01","release_notes":"https://chromereleases.googleblog.com/2013/10/stable-channel-update.html","status":"retired","engine":"Blink","engine_version":"30"},"31":{"release_date":"2013-11-12","release_notes":"https://chromereleases.googleblog.com/2013/11/stable-channel-update.html","status":"retired","engine":"Blink","engine_version":"31"},"32":{"release_date":"2014-01-14","release_notes":"https://chromereleases.googleblog.com/2014/01/stable-channel-update.html","status":"retired","engine":"Blink","engine_version":"32"},"33":{"release_date":"2014-02-20","release_notes":"https://chromereleases.googleblog.com/2014/02/stable-channel-update_20.html","status":"retired","engine":"Blink","engine_version":"33"},"34":{"release_date":"2014-04-08","release_notes":"https://chromereleases.googleblog.com/2014/04/stable-channel-update.html","status":"retired","engine":"Blink","engine_version":"34"},"35":{"release_date":"2014-05-20","release_notes":"https://chromereleases.googleblog.com/2014/05/stable-channel-update_20.html","status":"retired","engine":"Blink","engine_version":"35"},"36":{"release_date":"2014-07-16","release_notes":"https://chromereleases.googleblog.com/2014/07/stable-channel-update.html","status":"retired","engine":"Blink","engine_version":"36"},"37":{"release_date":"2014-08-26","release_notes":"https://chromereleases.googleblog.com/2014/08/stable-channel-update_26.html","status":"retired","engine":"Blink","engine_version":"37"},"38":{"release_date":"2014-10-07","release_notes":"https://chromereleases.googleblog.com/2014/10/stable-channel-update.html","status":"retired","engine":"Blink","engine_version":"38"},"39":{"release_date":"2014-11-18","release_notes":"https://chromereleases.googleblog.com/2014/11/stable-channel-update_18.html","status":"retired","engine":"Blink","engine_version":"39"},"40":{"release_date":"2015-01-21","release_notes":"https://chromereleases.googleblog.com/2015/01/stable-update.html","status":"retired","engine":"Blink","engine_version":"40"},"41":{"release_date":"2015-03-03","release_notes":"https://chromereleases.googleblog.com/2015/03/stable-channel-update.html","status":"retired","engine":"Blink","engine_version":"41"},"42":{"release_date":"2015-04-14","release_notes":"https://chromereleases.googleblog.com/2015/04/stable-channel-update_14.html","status":"retired","engine":"Blink","engine_version":"42"},"43":{"release_date":"2015-05-19","release_notes":"https://chromereleases.googleblog.com/2015/05/stable-channel-update_19.html","status":"retired","engine":"Blink","engine_version":"43"},"44":{"release_date":"2015-07-21","release_notes":"https://chromereleases.googleblog.com/2015/07/stable-channel-update_21.html","status":"retired","engine":"Blink","engine_version":"44"},"45":{"release_date":"2015-09-01","release_notes":"https://chromereleases.googleblog.com/2015/09/stable-channel-update.html","status":"retired","engine":"Blink","engine_version":"45"},"46":{"release_date":"2015-10-13","release_notes":"https://chromereleases.googleblog.com/2015/10/stable-channel-update.html","status":"retired","engine":"Blink","engine_version":"46"},"47":{"release_date":"2015-12-01","release_notes":"https://chromereleases.googleblog.com/2015/12/stable-channel-update.html","status":"retired","engine":"Blink","engine_version":"47"},"48":{"release_date":"2016-01-20","release_notes":"https://chromereleases.googleblog.com/2016/01/stable-channel-update_20.html","status":"retired","engine":"Blink","engine_version":"48"},"49":{"release_date":"2016-03-02","release_notes":"https://chromereleases.googleblog.com/2016/03/stable-channel-update.html","status":"retired","engine":"Blink","engine_version":"49"},"50":{"release_date":"2016-04-13","release_notes":"https://chromereleases.googleblog.com/2016/04/stable-channel-update_13.html","status":"retired","engine":"Blink","engine_version":"50"},"51":{"release_date":"2016-05-25","release_notes":"https://chromereleases.googleblog.com/2016/05/stable-channel-update_25.html","status":"retired","engine":"Blink","engine_version":"51"},"52":{"release_date":"2016-07-20","release_notes":"https://chromereleases.googleblog.com/2016/07/stable-channel-update.html","status":"retired","engine":"Blink","engine_version":"52"},"53":{"release_date":"2016-08-31","release_notes":"https://chromereleases.googleblog.com/2016/08/stable-channel-update-for-desktop_31.html","status":"retired","engine":"Blink","engine_version":"53"},"54":{"release_date":"2016-10-12","release_notes":"https://chromereleases.googleblog.com/2016/10/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"54"},"55":{"release_date":"2016-12-01","release_notes":"https://chromereleases.googleblog.com/2016/12/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"55"},"56":{"release_date":"2017-01-25","release_notes":"https://chromereleases.googleblog.com/2017/01/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"56"},"57":{"release_date":"2017-03-09","release_notes":"https://chromereleases.googleblog.com/2017/03/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"57"},"58":{"release_date":"2017-04-19","release_notes":"https://chromereleases.googleblog.com/2017/04/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"58"},"59":{"release_date":"2017-06-05","release_notes":"https://chromereleases.googleblog.com/2017/06/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"59"},"60":{"release_date":"2017-07-25","release_notes":"https://chromereleases.googleblog.com/2017/07/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"60"},"61":{"release_date":"2017-09-05","release_notes":"https://chromereleases.googleblog.com/2017/09/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"61"},"62":{"release_date":"2017-10-17","release_notes":"https://chromereleases.googleblog.com/2017/10/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"62"},"63":{"release_date":"2017-12-06","release_notes":"https://chromereleases.googleblog.com/2017/12/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"63"},"64":{"release_date":"2018-01-23","release_notes":"https://chromereleases.googleblog.com/2018/01/stable-channel-update-for-desktop_24.html","status":"retired","engine":"Blink","engine_version":"64"},"65":{"release_date":"2018-03-06","release_notes":"https://chromereleases.googleblog.com/2018/03/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"65"},"66":{"release_date":"2018-04-17","release_notes":"https://chromereleases.googleblog.com/2018/04/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"66"},"67":{"release_date":"2018-05-29","release_notes":"https://chromereleases.googleblog.com/2018/05/stable-channel-update-for-desktop_58.html","status":"retired","engine":"Blink","engine_version":"67"},"68":{"release_date":"2018-07-24","release_notes":"https://chromereleases.googleblog.com/2018/07/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"68"},"69":{"release_date":"2018-09-04","release_notes":"https://chromereleases.googleblog.com/2018/09/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"69"},"70":{"release_date":"2018-10-16","release_notes":"https://chromereleases.googleblog.com/2018/10/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"70"},"71":{"release_date":"2018-12-04","release_notes":"https://chromereleases.googleblog.com/2018/12/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"71"},"72":{"release_date":"2019-01-29","release_notes":"https://chromereleases.googleblog.com/2019/01/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"72"},"73":{"release_date":"2019-03-12","release_notes":"https://chromereleases.googleblog.com/2019/03/stable-channel-update-for-desktop_12.html","status":"retired","engine":"Blink","engine_version":"73"},"74":{"release_date":"2019-04-23","release_notes":"https://chromereleases.googleblog.com/2019/04/stable-channel-update-for-desktop_23.html","status":"retired","engine":"Blink","engine_version":"74"},"75":{"release_date":"2019-06-04","release_notes":"https://chromereleases.googleblog.com/2019/06/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"75"},"76":{"release_date":"2019-07-30","release_notes":"https://chromereleases.googleblog.com/2019/07/stable-channel-update-for-desktop_30.html","status":"retired","engine":"Blink","engine_version":"76"},"77":{"release_date":"2019-09-10","release_notes":"https://chromereleases.googleblog.com/2019/09/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"77"},"78":{"release_date":"2019-10-22","release_notes":"https://chromereleases.googleblog.com/2019/10/stable-channel-update-for-desktop_22.html","status":"retired","engine":"Blink","engine_version":"78"},"79":{"release_date":"2019-12-10","release_notes":"https://chromereleases.googleblog.com/2019/12/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"79"},"80":{"release_date":"2020-02-04","release_notes":"https://chromereleases.googleblog.com/2020/02/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"80"},"81":{"release_date":"2020-04-07","release_notes":"https://chromereleases.googleblog.com/2020/04/stable-channel-update-for-desktop_7.html","status":"retired","engine":"Blink","engine_version":"81"},"83":{"release_date":"2020-05-19","release_notes":"https://chromereleases.googleblog.com/2020/05/stable-channel-update-for-desktop_19.html","status":"retired","engine":"Blink","engine_version":"83"},"84":{"release_date":"2020-07-27","release_notes":"https://chromereleases.googleblog.com/2020/07/stable-channel-update-for-desktop_27.html","status":"retired","engine":"Blink","engine_version":"84"},"85":{"release_date":"2020-08-25","release_notes":"https://chromereleases.googleblog.com/2020/08/stable-channel-update-for-desktop_25.html","status":"retired","engine":"Blink","engine_version":"85"},"86":{"release_date":"2020-10-20","release_notes":"https://chromereleases.googleblog.com/2020/10/stable-channel-update-for-desktop_20.html","status":"retired","engine":"Blink","engine_version":"86"},"87":{"release_date":"2020-11-17","release_notes":"https://chromereleases.googleblog.com/2020/11/stable-channel-update-for-desktop_17.html","status":"retired","engine":"Blink","engine_version":"87"},"88":{"release_date":"2021-01-19","release_notes":"https://chromereleases.googleblog.com/2021/01/stable-channel-update-for-desktop_19.html","status":"retired","engine":"Blink","engine_version":"88"},"89":{"release_date":"2021-03-02","release_notes":"https://chromereleases.googleblog.com/2021/03/stable-channel-update-for-desktop.html","status":"retired","engine":"Blink","engine_version":"89"},"90":{"release_date":"2021-04-13","release_notes":"https://chromereleases.googleblog.com/2021/04/stable-channel-update-for-desktop_14.html","status":"retired","engine":"Blink","engine_version":"90"},"91":{"release_date":"2021-05-25","release_notes":"https://chromereleases.googleblog.com/2021/05/stable-channel-update-for-desktop_25.html","status":"retired","engine":"Blink","engine_version":"91"},"92":{"release_date":"2021-07-20","release_notes":"https://chromereleases.googleblog.com/2021/07/stable-channel-update-for-desktop_20.html","status":"retired","engine":"Blink","engine_version":"92"},"93":{"release_date":"2021-08-31","release_notes":"https://chromereleases.googleblog.com/2021/08/stable-channel-update-for-desktop_31.html","status":"retired","engine":"Blink","engine_version":"93"},"94":{"release_date":"2021-09-21","release_notes":"https://chromereleases.googleblog.com/2021/09/stable-channel-update-for-desktop_21.html","status":"current","engine":"Blink","engine_version":"94"},"95":{"release_date":"2021-10-19","status":"beta","engine":"Blink","engine_version":"95"},"96":{"release_date":"2021-11-16","status":"nightly","engine":"Blink","engine_version":"96"}}},"chrome_android":{"name":"Chrome Android","pref_url":"chrome://flags","releases":{"18":{"release_date":"2012-06-27","release_notes":"https://chromereleases.googleblog.com/2012/06/chrome-for-android-out-of-beta.html","status":"retired","engine":"WebKit","engine_version":"535.19"},"25":{"release_date":"2013-02-27","release_notes":"https://chromereleases.googleblog.com/2013/02/chrome-for-android-update.html","status":"retired","engine":"WebKit","engine_version":"537.22"},"26":{"release_date":"2013-04-03","release_notes":"https://chromereleases.googleblog.com/2013/04/chrome-for-android-stable-channel-update.html","status":"retired","engine":"WebKit","engine_version":"537.31"},"27":{"release_date":"2013-05-22","release_notes":"https://chromereleases.googleblog.com/2013/05/chrome-for-android-update.html","status":"retired","engine":"WebKit","engine_version":"537.36"},"28":{"release_date":"2013-07-10","release_notes":"https://chromereleases.googleblog.com/2013/07/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"28"},"29":{"release_date":"2013-08-21","release_notes":"https://chromereleases.googleblog.com/2013/08/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"29"},"30":{"release_date":"2013-10-02","release_notes":"https://chromereleases.googleblog.com/2013/10/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"30"},"31":{"release_date":"2013-11-14","release_notes":"https://chromereleases.googleblog.com/2013/11/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"31"},"32":{"release_date":"2014-01-15","release_notes":"https://chromereleases.googleblog.com/2014/01/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"32"},"33":{"release_date":"2014-02-26","release_notes":"https://chromereleases.googleblog.com/2014/02/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"33"},"34":{"release_date":"2014-04-02","release_notes":"https://chromereleases.googleblog.com/2014/04/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"34"},"35":{"release_date":"2014-05-20","release_notes":"https://chromereleases.googleblog.com/2014/05/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"35"},"36":{"release_date":"2014-07-16","release_notes":"https://chromereleases.googleblog.com/2014/07/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"36"},"37":{"release_date":"2014-09-03","release_notes":"https://chromereleases.googleblog.com/2014/09/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"37"},"38":{"release_date":"2014-10-08","release_notes":"https://chromereleases.googleblog.com/2014/10/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"38"},"39":{"release_date":"2014-11-12","release_notes":"https://chromereleases.googleblog.com/2014/11/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"39"},"40":{"release_date":"2015-01-21","release_notes":"https://chromereleases.googleblog.com/2015/01/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"40"},"41":{"release_date":"2015-03-11","release_notes":"https://chromereleases.googleblog.com/2015/03/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"41"},"42":{"release_date":"2015-04-15","release_notes":"https://chromereleases.googleblog.com/2015/04/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"42"},"43":{"release_date":"2015-05-27","release_notes":"https://chromereleases.googleblog.com/2015/05/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"43"},"44":{"release_date":"2015-07-29","release_notes":"https://chromereleases.googleblog.com/2015/07/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"44"},"45":{"release_date":"2015-09-01","release_notes":"https://chromereleases.googleblog.com/2015/09/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"45"},"46":{"release_date":"2015-10-14","release_notes":"https://chromereleases.googleblog.com/2015/10/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"46"},"47":{"release_date":"2015-12-02","release_notes":"https://chromereleases.googleblog.com/2015/12/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"47"},"48":{"release_date":"2016-01-26","status":"retired","engine":"Blink","engine_version":"48"},"49":{"release_date":"2016-03-09","release_notes":"https://chromereleases.googleblog.com/2016/03/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"49"},"50":{"release_date":"2016-04-13","status":"retired","engine":"Blink","engine_version":"50"},"51":{"release_date":"2016-06-08","release_notes":"https://chromereleases.googleblog.com/2016/06/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"51"},"52":{"release_date":"2016-07-27","release_notes":"https://chromereleases.googleblog.com/2016/07/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"52"},"53":{"release_date":"2016-09-07","release_notes":"https://chromereleases.googleblog.com/2016/09/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"53"},"54":{"release_date":"2016-10-19","release_notes":"https://chromereleases.googleblog.com/2016/10/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"54"},"55":{"release_date":"2016-12-06","release_notes":"https://chromereleases.googleblog.com/2016/12/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"55"},"56":{"release_date":"2017-02-01","release_notes":"https://chromereleases.googleblog.com/2017/02/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"56"},"57":{"release_date":"2017-03-16","release_notes":"https://chromereleases.googleblog.com/2017/03/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"57"},"58":{"release_date":"2017-04-25","release_notes":"https://chromereleases.googleblog.com/2017/04/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"58"},"59":{"release_date":"2017-06-06","release_notes":"https://chromereleases.googleblog.com/2017/06/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"59"},"60":{"release_date":"2017-08-01","release_notes":"https://chromereleases.googleblog.com/2017/08/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"60"},"61":{"release_date":"2017-09-05","release_notes":"https://chromereleases.googleblog.com/2017/09/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"61"},"62":{"release_date":"2017-10-24","release_notes":"https://chromereleases.googleblog.com/2017/10/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"62"},"63":{"release_date":"2017-12-05","release_notes":"https://chromereleases.googleblog.com/2017/12/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"63"},"64":{"release_date":"2018-01-23","release_notes":"https://chromereleases.googleblog.com/2018/01/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"64"},"65":{"release_date":"2018-03-06","release_notes":"https://chromereleases.googleblog.com/2018/03/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"65"},"66":{"release_date":"2018-04-17","release_notes":"https://chromereleases.googleblog.com/2018/04/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"66"},"67":{"release_date":"2018-05-31","release_notes":"https://chromereleases.googleblog.com/2018/05/chrome-for-android-update_31.html","status":"retired","engine":"Blink","engine_version":"67"},"68":{"release_date":"2018-07-24","release_notes":"https://chromereleases.googleblog.com/2018/07/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"68"},"69":{"release_date":"2018-09-04","release_notes":"https://chromereleases.googleblog.com/2018/09/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"69"},"70":{"release_date":"2018-10-17","release_notes":"https://chromereleases.googleblog.com/2018/10/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"70"},"71":{"release_date":"2018-12-04","release_notes":"https://chromereleases.googleblog.com/2018/12/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"71"},"72":{"release_date":"2019-01-29","release_notes":"https://chromereleases.googleblog.com/2019/01/chrome-for-android-update_29.html","status":"retired","engine":"Blink","engine_version":"72"},"73":{"release_date":"2019-03-12","release_notes":"https://chromereleases.googleblog.com/2019/03/chrome-for-android-update_12.html","status":"retired","engine":"Blink","engine_version":"73"},"74":{"release_date":"2019-04-24","release_notes":"https://chromereleases.googleblog.com/2019/04/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"74"},"75":{"release_date":"2019-06-04","release_notes":"https://chromereleases.googleblog.com/2019/06/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"75"},"76":{"release_date":"2019-07-30","release_notes":"https://chromereleases.googleblog.com/2019/07/chrome-for-android-update_30.html","status":"retired","engine":"Blink","engine_version":"76"},"77":{"release_date":"2019-09-10","release_notes":"https://chromereleases.googleblog.com/2019/09/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"77"},"78":{"release_date":"2019-10-22","release_notes":"https://chromereleases.googleblog.com/2019/10/chrome-for-android-update_22.html","status":"retired","engine":"Blink","engine_version":"78"},"79":{"release_date":"2019-12-17","release_notes":"https://chromereleases.googleblog.com/2019/12/chrome-for-android-update_17.html","status":"retired","engine":"Blink","engine_version":"79"},"80":{"release_date":"2020-02-04","release_notes":"https://chromereleases.googleblog.com/2020/02/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"80"},"81":{"release_date":"2020-04-07","release_notes":"https://chromereleases.googleblog.com/2020/04/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"81"},"83":{"release_date":"2020-05-19","release_notes":"https://chromereleases.googleblog.com/2020/05/chrome-for-android-update_19.html","status":"retired","engine":"Blink","engine_version":"83"},"84":{"release_date":"2020-07-27","release_notes":"https://chromereleases.googleblog.com/2020/07/chrome-for-android-update_27.html","status":"retired","engine":"Blink","engine_version":"84"},"85":{"release_date":"2020-08-25","release_notes":"https://chromereleases.googleblog.com/2020/08/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"85"},"86":{"release_date":"2020-10-20","release_notes":"https://chromereleases.googleblog.com/2020/10/chrome-for-android-update_20.html","status":"retired","engine":"Blink","engine_version":"86"},"87":{"release_date":"2020-11-17","release_notes":"https://chromereleases.googleblog.com/2020/11/chrome-for-android-update_17.html","status":"retired","engine":"Blink","engine_version":"87"},"88":{"release_date":"2021-01-19","release_notes":"https://chromereleases.googleblog.com/2021/01/chrome-for-android-update_19.html","status":"retired","engine":"Blink","engine_version":"88"},"89":{"release_date":"2021-03-02","release_notes":"https://chromereleases.googleblog.com/2021/03/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"89"},"90":{"release_date":"2021-04-13","release_notes":"https://chromereleases.googleblog.com/2021/04/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"90"},"91":{"release_date":"2021-05-25","release_notes":"https://chromereleases.googleblog.com/2021/05/chrome-for-android-update_01607414128.html","status":"retired","engine":"Blink","engine_version":"91"},"92":{"release_date":"2021-07-20","release_notes":"https://chromereleases.googleblog.com/2021/07/chrome-for-android-update_01500789893.html","status":"retired","engine":"Blink","engine_version":"92"},"93":{"release_date":"2021-08-31","release_notes":"https://chromereleases.googleblog.com/2021/08/chrome-for-android-update_0881967577.html","status":"retired","engine":"Blink","engine_version":"93"},"94":{"release_date":"2021-09-21","release_notes":"https://chromereleases.googleblog.com/2021/09/chrome-for-android-update_21.html","status":"current","engine":"Blink","engine_version":"94"},"95":{"release_date":"2021-10-19","status":"beta","engine":"Blink","engine_version":"95"},"96":{"release_date":"2021-11-16","status":"nightly","engine":"Blink","engine_version":"96"}}},"deno":{"name":"Deno","releases":{"1.0":{"release_date":"2020-05-13","release_notes":"https://github.com/denoland/deno/releases/tag/v1.0.0","status":"retired","engine":"V8","engine_version":"8.4"},"1.1":{"release_date":"2020-06-12","release_notes":"https://github.com/denoland/deno/releases/tag/v1.1.0","status":"retired","engine":"V8","engine_version":"8.4"},"1.2":{"release_date":"2020-07-13","release_notes":"https://github.com/denoland/deno/releases/tag/v1.2.0","status":"retired","engine":"V8","engine_version":"8.5"},"1.3":{"release_date":"2020-08-13","release_notes":"https://github.com/denoland/deno/releases/tag/v1.3.0","status":"retired","engine":"V8","engine_version":"8.6"},"1.4":{"release_date":"2020-09-13","release_notes":"https://github.com/denoland/deno/releases/tag/v1.4.0","status":"retired","engine":"V8","engine_version":"8.7"},"1.5":{"release_date":"2020-10-27","release_notes":"https://github.com/denoland/deno/releases/tag/v1.5.0","status":"retired","engine":"V8","engine_version":"8.7"},"1.6":{"release_date":"2020-12-08","release_notes":"https://github.com/denoland/deno/releases/tag/v1.6.0","status":"retired","engine":"V8","engine_version":"8.8"},"1.7":{"release_date":"2021-01-19","release_notes":"https://github.com/denoland/deno/releases/tag/v1.7.0","status":"retired","engine":"V8","engine_version":"8.9"},"1.8":{"release_date":"2021-03-02","release_notes":"https://github.com/denoland/deno/releases/tag/v1.8.0","status":"retired","engine":"V8","engine_version":"9.0"},"1.9":{"release_date":"2021-04-13","release_notes":"https://github.com/denoland/deno/releases/tag/v1.9.0","status":"retired","engine":"V8","engine_version":"9.1"},"1.10":{"release_date":"2021-05-11","release_notes":"https://github.com/denoland/deno/releases/tag/v1.10.1","status":"retired","engine":"V8","engine_version":"9.1"},"1.11":{"release_date":"2021-06-08","release_notes":"https://github.com/denoland/deno/releases/tag/v1.11.0","status":"retired","engine":"V8","engine_version":"9.1"},"1.12":{"release_date":"2021-07-13","release_notes":"https://github.com/denoland/deno/releases/tag/v1.12.0","status":"current","engine":"V8","engine_version":"9.2"},"1.13":{"release_date":"2021-08-10","release_notes":"https://github.com/denoland/deno/releases/tag/v1.13.0","status":"current","engine":"V8","engine_version":"9.3"},"1.14":{"release_date":"2021-09-14","release_notes":"https://github.com/denoland/deno/releases/tag/v1.14.0","status":"current","engine":"V8","engine_version":"9.4"},"1.15":{"release_date":"2021-10-12","release_notes":"https://github.com/denoland/deno/releases/tag/v1.15.0","status":"current","engine":"V8","engine_version":"9.5"},"1.16":{"release_date":"2021-11-16","status":"nightly","engine":"V8","engine_version":"9.6"}}},"edge":{"name":"Edge","pref_url":"about:flags","releases":{"12":{"release_date":"2015-07-28","release_notes":"https://docs.microsoft.com/en-us/microsoft-edge/dev-guide/whats-new/edgehtml-12","status":"retired","engine":"EdgeHTML","engine_version":"12"},"13":{"release_date":"2015-11-12","release_notes":"https://docs.microsoft.com/en-us/microsoft-edge/dev-guide/whats-new/edgehtml-13","status":"retired","engine":"EdgeHTML","engine_version":"13"},"14":{"release_date":"2016-08-02","release_notes":"https://docs.microsoft.com/en-us/microsoft-edge/dev-guide/whats-new/edgehtml-14","status":"retired","engine":"EdgeHTML","engine_version":"14"},"15":{"release_date":"2017-04-05","release_notes":"https://docs.microsoft.com/en-us/microsoft-edge/dev-guide/whats-new/edgehtml-15","status":"retired","engine":"EdgeHTML","engine_version":"15"},"16":{"release_date":"2017-10-17","release_notes":"https://docs.microsoft.com/en-us/microsoft-edge/dev-guide/whats-new/edgehtml-16","status":"retired","engine":"EdgeHTML","engine_version":"16"},"17":{"release_date":"2018-04-30","release_notes":"https://docs.microsoft.com/en-us/microsoft-edge/dev-guide/whats-new/edgehtml-17","status":"retired","engine":"EdgeHTML","engine_version":"17"},"18":{"release_date":"2018-10-02","release_notes":"https://docs.microsoft.com/en-us/microsoft-edge/dev-guide/whats-new","status":"retired","engine":"EdgeHTML","engine_version":"18"},"79":{"release_date":"2020-01-15","release_notes":"https://blogs.windows.com/windowsexperience/2020/01/15/new-year-new-browser-the-new-microsoft-edge-is-out-of-preview-and-now-available-for-download/","status":"retired","engine":"Blink","engine_version":"79"},"80":{"release_date":"2020-02-07","release_notes":"https://docs.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel#version-80036148-february-7","status":"retired","engine":"Blink","engine_version":"80"},"81":{"release_date":"2020-04-13","release_notes":"https://docs.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel#version-81041653-april-13","status":"retired","engine":"Blink","engine_version":"81"},"83":{"release_date":"2020-05-21","release_notes":"https://docs.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel#version-83047837-may-21","status":"retired","engine":"Blink","engine_version":"83"},"84":{"release_date":"2020-07-16","release_notes":"https://docs.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel#version-84052240-july-16","status":"retired","engine":"Blink","engine_version":"84"},"85":{"release_date":"2020-08-27","release_notes":"https://docs.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel#version-85056441-august-27","status":"retired","engine":"Blink","engine_version":"85"},"86":{"release_date":"2020-10-09","release_notes":"https://docs.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel#version-86062238-october-9","status":"retired","engine":"Blink","engine_version":"86"},"87":{"release_date":"2020-11-19","release_notes":"https://docs.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel#version-87066441-november-19","status":"retired","engine":"Blink","engine_version":"87"},"88":{"release_date":"2021-01-21","release_notes":"https://docs.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel#version-88070550-january-21","status":"retired","engine":"Blink","engine_version":"88"},"89":{"release_date":"2021-03-04","release_notes":"https://docs.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel#version-89077445-march-4","status":"retired","engine":"Blink","engine_version":"89"},"90":{"release_date":"2021-04-15","release_notes":"https://docs.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel#version-90081839-april-15","status":"retired","engine":"Blink","engine_version":"90"},"91":{"release_date":"2021-05-27","release_notes":"https://docs.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel#version-91086437-may-27","status":"retired","engine":"Blink","engine_version":"91"},"92":{"release_date":"2021-07-22","release_notes":"https://docs.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel#version-92090255-july-22","status":"retired","engine":"Blink","engine_version":"92"},"93":{"release_date":"2021-09-02","release_notes":"https://docs.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel#version-93096138-september-02","status":"retired","engine":"Blink","engine_version":"93"},"94":{"release_date":"2021-09-24","release_notes":"https://docs.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel#version-94099231-september-24","status":"current","engine":"Blink","engine_version":"94"},"95":{"status":"beta","engine":"Blink","engine_version":"95"},"96":{"status":"nightly","engine":"Blink","engine_version":"96"}}},"firefox":{"name":"Firefox","preview_name":"Nightly","pref_url":"about:config","releases":{"1":{"release_date":"2004-11-09","release_notes":"http://website-archive.mozilla.org/www.mozilla.org/firefox_releasenotes/en-US/firefox/releases/1.0.html","status":"retired","engine":"Gecko","engine_version":"1.7"},"2":{"release_date":"2006-10-24","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/2","status":"retired","engine":"Gecko","engine_version":"1.8.1"},"3":{"release_date":"2008-06-17","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/3","status":"retired","engine":"Gecko","engine_version":"1.9"},"4":{"release_date":"2011-03-22","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/4","status":"retired","engine":"Gecko","engine_version":"2"},"5":{"release_date":"2011-06-21","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/5","status":"retired","engine":"Gecko","engine_version":"5"},"6":{"release_date":"2011-08-16","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/6","status":"retired","engine":"Gecko","engine_version":"6"},"7":{"release_date":"2011-09-27","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/7","status":"retired","engine":"Gecko","engine_version":"7"},"8":{"release_date":"2011-11-08","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/8","status":"retired","engine":"Gecko","engine_version":"8"},"9":{"release_date":"2011-12-20","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/9","status":"retired","engine":"Gecko","engine_version":"9"},"10":{"release_date":"2012-01-31","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/10","status":"retired","engine":"Gecko","engine_version":"10"},"11":{"release_date":"2012-03-13","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/11","status":"retired","engine":"Gecko","engine_version":"11"},"12":{"release_date":"2012-04-24","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/12","status":"retired","engine":"Gecko","engine_version":"12"},"13":{"release_date":"2012-06-05","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/13","status":"retired","engine":"Gecko","engine_version":"13"},"14":{"release_date":"2012-07-17","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/14","status":"retired","engine":"Gecko","engine_version":"14"},"15":{"release_date":"2012-08-28","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/15","status":"retired","engine":"Gecko","engine_version":"15"},"16":{"release_date":"2012-10-09","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/16","status":"retired","engine":"Gecko","engine_version":"16"},"17":{"release_date":"2012-11-20","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/17","status":"retired","engine":"Gecko","engine_version":"17"},"18":{"release_date":"2013-01-08","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/18","status":"retired","engine":"Gecko","engine_version":"18"},"19":{"release_date":"2013-02-19","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/19","status":"retired","engine":"Gecko","engine_version":"19"},"20":{"release_date":"2013-04-02","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/20","status":"retired","engine":"Gecko","engine_version":"20"},"21":{"release_date":"2013-05-14","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/21","status":"retired","engine":"Gecko","engine_version":"21"},"22":{"release_date":"2013-06-25","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/22","status":"retired","engine":"Gecko","engine_version":"22"},"23":{"release_date":"2013-08-06","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/23","status":"retired","engine":"Gecko","engine_version":"23"},"24":{"release_date":"2013-09-17","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/24","status":"retired","engine":"Gecko","engine_version":"24"},"25":{"release_date":"2013-10-29","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/25","status":"retired","engine":"Gecko","engine_version":"25"},"26":{"release_date":"2013-12-10","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/26","status":"retired","engine":"Gecko","engine_version":"26"},"27":{"release_date":"2014-02-04","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/27","status":"retired","engine":"Gecko","engine_version":"27"},"28":{"release_date":"2014-03-18","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/28","status":"retired","engine":"Gecko","engine_version":"28"},"29":{"release_date":"2014-04-29","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/29","status":"retired","engine":"Gecko","engine_version":"29"},"30":{"release_date":"2014-06-10","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/30","status":"retired","engine":"Gecko","engine_version":"30"},"31":{"release_date":"2014-07-22","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/31","status":"retired","engine":"Gecko","engine_version":"31"},"32":{"release_date":"2014-09-02","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/32","status":"retired","engine":"Gecko","engine_version":"32"},"33":{"release_date":"2014-10-14","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/33","status":"retired","engine":"Gecko","engine_version":"33"},"34":{"release_date":"2014-12-01","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/34","status":"retired","engine":"Gecko","engine_version":"34"},"35":{"release_date":"2015-01-13","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/35","status":"retired","engine":"Gecko","engine_version":"35"},"36":{"release_date":"2015-02-24","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/36","status":"retired","engine":"Gecko","engine_version":"36"},"37":{"release_date":"2015-03-31","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/37","status":"retired","engine":"Gecko","engine_version":"37"},"38":{"release_date":"2015-05-12","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/38","status":"retired","engine":"Gecko","engine_version":"38"},"39":{"release_date":"2015-07-02","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/39","status":"retired","engine":"Gecko","engine_version":"39"},"40":{"release_date":"2015-08-11","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/40","status":"retired","engine":"Gecko","engine_version":"40"},"41":{"release_date":"2015-09-22","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/41","status":"retired","engine":"Gecko","engine_version":"41"},"42":{"release_date":"2015-11-03","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/42","status":"retired","engine":"Gecko","engine_version":"42"},"43":{"release_date":"2015-12-15","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/43","status":"retired","engine":"Gecko","engine_version":"43"},"44":{"release_date":"2016-01-26","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/44","status":"retired","engine":"Gecko","engine_version":"44"},"45":{"release_date":"2016-03-08","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/45","status":"retired","engine":"Gecko","engine_version":"45"},"46":{"release_date":"2016-04-26","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/46","status":"retired","engine":"Gecko","engine_version":"46"},"47":{"release_date":"2016-06-07","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/47","status":"retired","engine":"Gecko","engine_version":"47"},"48":{"release_date":"2016-08-02","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/48","status":"retired","engine":"Gecko","engine_version":"48"},"49":{"release_date":"2016-09-20","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/49","status":"retired","engine":"Gecko","engine_version":"49"},"50":{"release_date":"2016-11-15","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/50","status":"retired","engine":"Gecko","engine_version":"50"},"51":{"release_date":"2017-01-24","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/51","status":"retired","engine":"Gecko","engine_version":"51"},"52":{"release_date":"2017-03-07","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/52","status":"retired","engine":"Gecko","engine_version":"52"},"53":{"release_date":"2017-04-19","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/53","status":"retired","engine":"Gecko","engine_version":"53"},"54":{"release_date":"2017-06-13","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/54","status":"retired","engine":"Gecko","engine_version":"54"},"55":{"release_date":"2017-08-08","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/55","status":"retired","engine":"Gecko","engine_version":"55"},"56":{"release_date":"2017-09-28","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/56","status":"retired","engine":"Gecko","engine_version":"56"},"57":{"release_date":"2017-11-14","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/57","status":"retired","engine":"Gecko","engine_version":"57"},"58":{"release_date":"2018-01-23","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/58","status":"retired","engine":"Gecko","engine_version":"58"},"59":{"release_date":"2018-03-13","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/59","status":"retired","engine":"Gecko","engine_version":"59"},"60":{"release_date":"2018-05-09","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/60","status":"esr","engine":"Gecko","engine_version":"60"},"61":{"release_date":"2018-06-26","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/61","status":"retired","engine":"Gecko","engine_version":"61"},"62":{"release_date":"2018-09-05","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/62","status":"retired","engine":"Gecko","engine_version":"62"},"63":{"release_date":"2018-10-23","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/63","status":"retired","engine":"Gecko","engine_version":"63"},"64":{"release_date":"2018-12-11","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/64","status":"retired","engine":"Gecko","engine_version":"64"},"65":{"release_date":"2019-01-29","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/65","status":"retired","engine":"Gecko","engine_version":"65"},"66":{"release_date":"2019-03-19","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/66","status":"retired","engine":"Gecko","engine_version":"66"},"67":{"release_date":"2019-05-21","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/67","status":"retired","engine":"Gecko","engine_version":"67"},"68":{"release_date":"2019-07-09","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/68","status":"retired","engine":"Gecko","engine_version":"68"},"69":{"release_date":"2019-09-03","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/69","status":"retired","engine":"Gecko","engine_version":"69"},"70":{"release_date":"2019-10-22","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/70","status":"retired","engine":"Gecko","engine_version":"70"},"71":{"release_date":"2019-12-10","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/71","status":"retired","engine":"Gecko","engine_version":"71"},"72":{"release_date":"2020-01-07","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/72","status":"retired","engine":"Gecko","engine_version":"72"},"73":{"release_date":"2020-02-11","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/73","status":"retired","engine":"Gecko","engine_version":"73"},"74":{"release_date":"2020-03-10","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/74","status":"retired","engine":"Gecko","engine_version":"74"},"75":{"release_date":"2020-04-07","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/75","status":"retired","engine":"Gecko","engine_version":"75"},"76":{"release_date":"2020-05-05","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/76","status":"retired","engine":"Gecko","engine_version":"76"},"77":{"release_date":"2020-06-02","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/77","status":"retired","engine":"Gecko","engine_version":"77"},"78":{"release_date":"2020-06-30","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/78","status":"retired","engine":"Gecko","engine_version":"78"},"79":{"release_date":"2020-07-28","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/79","status":"retired","engine":"Gecko","engine_version":"79"},"80":{"release_date":"2020-08-25","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/80","status":"retired","engine":"Gecko","engine_version":"80"},"81":{"release_date":"2020-09-22","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/81","status":"retired","engine":"Gecko","engine_version":"81"},"82":{"release_date":"2020-10-20","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/82","status":"retired","engine":"Gecko","engine_version":"82"},"83":{"release_date":"2020-11-17","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/83","status":"retired","engine":"Gecko","engine_version":"83"},"84":{"release_date":"2020-12-15","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/84","status":"retired","engine":"Gecko","engine_version":"84"},"85":{"release_date":"2021-01-26","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/85","status":"retired","engine":"Gecko","engine_version":"85"},"86":{"release_date":"2021-02-23","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/86","status":"retired","engine":"Gecko","engine_version":"86"},"87":{"release_date":"2021-03-23","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/87","status":"retired","engine":"Gecko","engine_version":"87"},"88":{"release_date":"2021-04-19","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/88","status":"retired","engine":"Gecko","engine_version":"88"},"89":{"release_date":"2021-06-01","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/89","status":"retired","engine":"Gecko","engine_version":"89"},"90":{"release_date":"2021-07-13","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/90","status":"retired","engine":"Gecko","engine_version":"90"},"91":{"release_date":"2021-08-10","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/91","status":"retired","engine":"Gecko","engine_version":"91"},"92":{"release_date":"2021-09-07","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/92","status":"retired","engine":"Gecko","engine_version":"92"},"93":{"release_date":"2021-10-05","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/93","status":"current","engine":"Gecko","engine_version":"93"},"94":{"release_date":"2021-11-02","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/94","status":"beta","engine":"Gecko","engine_version":"94"},"95":{"release_date":"2021-12-07","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/95","status":"nightly","engine":"Gecko","engine_version":"95"},"96":{"release_date":"2022-01-11","status":"planned","engine":"Gecko","engine_version":"96"},"97":{"release_date":"2022-02-08","status":"planned","engine":"Gecko","engine_version":"97"},"98":{"release_date":"2022-03-08","status":"planned","engine":"Gecko","engine_version":"98"},"1.5":{"release_date":"2005-11-29","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/1.5","status":"retired","engine":"Gecko","engine_version":"1.8"},"3.5":{"release_date":"2009-06-30","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/3.5","status":"retired","engine":"Gecko","engine_version":"1.9.1"},"3.6":{"release_date":"2010-01-21","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/3.6","status":"retired","engine":"Gecko","engine_version":"1.9.2"}}},"firefox_android":{"name":"Firefox for Android","pref_url":"about:config","releases":{"4":{"release_date":"2011-03-29","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/4","status":"retired","engine":"Gecko","engine_version":"2"},"5":{"release_date":"2011-06-21","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/5","status":"retired","engine":"Gecko","engine_version":"5"},"6":{"release_date":"2011-08-16","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/6","status":"retired","engine":"Gecko","engine_version":"6"},"7":{"release_date":"2011-09-27","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/7","status":"retired","engine":"Gecko","engine_version":"7"},"8":{"release_date":"2011-11-08","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/8","status":"retired","engine":"Gecko","engine_version":"8"},"9":{"release_date":"2011-12-21","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/9","status":"retired","engine":"Gecko","engine_version":"9"},"10":{"release_date":"2012-01-31","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/10","status":"retired","engine":"Gecko","engine_version":"10"},"14":{"release_date":"2012-06-26","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/14","status":"retired","engine":"Gecko","engine_version":"14"},"15":{"release_date":"2012-08-28","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/15","status":"retired","engine":"Gecko","engine_version":"15"},"16":{"release_date":"2012-10-09","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/16","status":"retired","engine":"Gecko","engine_version":"16"},"17":{"release_date":"2012-11-20","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/17","status":"retired","engine":"Gecko","engine_version":"17"},"18":{"release_date":"2013-01-08","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/18","status":"retired","engine":"Gecko","engine_version":"18"},"19":{"release_date":"2013-02-19","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/19","status":"retired","engine":"Gecko","engine_version":"19"},"20":{"release_date":"2013-04-02","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/20","status":"retired","engine":"Gecko","engine_version":"20"},"21":{"release_date":"2013-05-14","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/21","status":"retired","engine":"Gecko","engine_version":"21"},"22":{"release_date":"2013-06-25","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/22","status":"retired","engine":"Gecko","engine_version":"22"},"23":{"release_date":"2013-08-06","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/23","status":"retired","engine":"Gecko","engine_version":"23"},"24":{"release_date":"2013-09-17","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/24","status":"retired","engine":"Gecko","engine_version":"24"},"25":{"release_date":"2013-10-29","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/25","status":"retired","engine":"Gecko","engine_version":"25"},"26":{"release_date":"2013-12-10","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/26","status":"retired","engine":"Gecko","engine_version":"26"},"27":{"release_date":"2014-02-04","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/27","status":"retired","engine":"Gecko","engine_version":"27"},"28":{"release_date":"2014-03-18","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/28","status":"retired","engine":"Gecko","engine_version":"28"},"29":{"release_date":"2014-04-29","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/29","status":"retired","engine":"Gecko","engine_version":"29"},"30":{"release_date":"2014-06-10","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/30","status":"retired","engine":"Gecko","engine_version":"30"},"31":{"release_date":"2014-07-22","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/31","status":"retired","engine":"Gecko","engine_version":"31"},"32":{"release_date":"2014-09-02","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/32","status":"retired","engine":"Gecko","engine_version":"32"},"33":{"release_date":"2014-10-14","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/33","status":"retired","engine":"Gecko","engine_version":"33"},"34":{"release_date":"2014-12-01","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/34","status":"retired","engine":"Gecko","engine_version":"34"},"35":{"release_date":"2015-01-13","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/35","status":"retired","engine":"Gecko","engine_version":"35"},"36":{"release_date":"2015-02-27","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/36","status":"retired","engine":"Gecko","engine_version":"36"},"37":{"release_date":"2015-03-31","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/37","status":"retired","engine":"Gecko","engine_version":"37"},"38":{"release_date":"2015-05-12","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/38","status":"retired","engine":"Gecko","engine_version":"38"},"39":{"release_date":"2015-07-02","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/39","status":"retired","engine":"Gecko","engine_version":"39"},"40":{"release_date":"2015-08-11","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/40","status":"retired","engine":"Gecko","engine_version":"40"},"41":{"release_date":"2015-09-22","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/41","status":"retired","engine":"Gecko","engine_version":"41"},"42":{"release_date":"2015-11-03","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/42","status":"retired","engine":"Gecko","engine_version":"42"},"43":{"release_date":"2015-12-15","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/43","status":"retired","engine":"Gecko","engine_version":"43"},"44":{"release_date":"2016-01-26","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/44","status":"retired","engine":"Gecko","engine_version":"44"},"45":{"release_date":"2016-03-08","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/45","status":"retired","engine":"Gecko","engine_version":"45"},"46":{"release_date":"2016-04-26","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/46","status":"retired","engine":"Gecko","engine_version":"46"},"47":{"release_date":"2016-06-07","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/47","status":"retired","engine":"Gecko","engine_version":"47"},"48":{"release_date":"2016-08-02","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/48","status":"retired","engine":"Gecko","engine_version":"48"},"49":{"release_date":"2016-09-20","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/49","status":"retired","engine":"Gecko","engine_version":"49"},"50":{"release_date":"2016-11-15","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/50","status":"retired","engine":"Gecko","engine_version":"50"},"51":{"release_date":"2017-01-24","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/51","status":"retired","engine":"Gecko","engine_version":"51"},"52":{"release_date":"2017-03-07","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/52","status":"retired","engine":"Gecko","engine_version":"52"},"53":{"release_date":"2017-04-19","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/53","status":"retired","engine":"Gecko","engine_version":"53"},"54":{"release_date":"2017-06-13","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/54","status":"retired","engine":"Gecko","engine_version":"54"},"55":{"release_date":"2017-08-08","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/55","status":"retired","engine":"Gecko","engine_version":"55"},"56":{"release_date":"2017-09-28","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/56","status":"retired","engine":"Gecko","engine_version":"56"},"57":{"release_date":"2017-11-28","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/57","status":"retired","engine":"Gecko","engine_version":"57"},"58":{"release_date":"2018-01-22","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/58","status":"retired","engine":"Gecko","engine_version":"58"},"59":{"release_date":"2018-03-13","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/59","status":"retired","engine":"Gecko","engine_version":"59"},"60":{"release_date":"2018-05-09","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/60","status":"retired","engine":"Gecko","engine_version":"60"},"61":{"release_date":"2018-06-26","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/61","status":"retired","engine":"Gecko","engine_version":"61"},"62":{"release_date":"2018-09-05","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/62","status":"retired","engine":"Gecko","engine_version":"62"},"63":{"release_date":"2018-10-23","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/63","status":"retired","engine":"Gecko","engine_version":"63"},"64":{"release_date":"2018-12-11","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/64","status":"retired","engine":"Gecko","engine_version":"64"},"65":{"release_date":"2019-01-29","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/65","status":"retired","engine":"Gecko","engine_version":"65"},"66":{"release_date":"2019-03-19","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/66","status":"retired","engine":"Gecko","engine_version":"66"},"67":{"release_date":"2019-05-21","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/67","status":"retired","engine":"Gecko","engine_version":"67"},"68":{"release_date":"2019-07-09","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/68","status":"retired","engine":"Gecko","engine_version":"68"},"79":{"release_date":"2020-07-28","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/79","status":"retired","engine":"Gecko","engine_version":"79"},"80":{"release_date":"2020-08-31","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/80","status":"retired","engine":"Gecko","engine_version":"80"},"81":{"release_date":"2020-09-22","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/81","status":"retired","engine":"Gecko","engine_version":"81"},"82":{"release_date":"2020-10-20","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/82","status":"retired","engine":"Gecko","engine_version":"82"},"83":{"release_date":"2020-11-17","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/83","status":"retired","engine":"Gecko","engine_version":"83"},"84":{"release_date":"2020-12-15","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/84","status":"retired","engine":"Gecko","engine_version":"84"},"85":{"release_date":"2021-01-26","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/85","status":"retired","engine":"Gecko","engine_version":"85"},"86":{"release_date":"2021-02-23","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/86","status":"retired","engine":"Gecko","engine_version":"86"},"87":{"release_date":"2021-03-23","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/87","status":"retired","engine":"Gecko","engine_version":"87"},"88":{"release_date":"2021-04-19","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/88","status":"retired","engine":"Gecko","engine_version":"88"},"89":{"release_date":"2021-06-01","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/89","status":"retired","engine":"Gecko","engine_version":"89"},"90":{"release_date":"2021-07-13","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/90","status":"retired","engine":"Gecko","engine_version":"90"},"91":{"release_date":"2021-08-10","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/91","status":"retired","engine":"Gecko","engine_version":"91"},"92":{"release_date":"2021-09-07","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/92","status":"retired","engine":"Gecko","engine_version":"92"},"93":{"release_date":"2021-10-05","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/93","status":"current","engine":"Gecko","engine_version":"93"},"94":{"release_date":"2021-11-02","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/94","status":"beta","engine":"Gecko","engine_version":"94"},"95":{"release_date":"2021-12-07","release_notes":"https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/95","status":"nightly","engine":"Gecko","engine_version":"95"},"96":{"release_date":"2022-01-11","status":"planned","engine":"Gecko","engine_version":"96"},"97":{"release_date":"2022-02-08","status":"planned","engine":"Gecko","engine_version":"97"},"98":{"release_date":"2022-03-08","status":"planned","engine":"Gecko","engine_version":"98"}}},"ie":{"name":"Internet Explorer","releases":{"1":{"release_date":"1995-08-16","status":"retired"},"2":{"release_date":"1995-11-22","status":"retired"},"3":{"release_date":"1996-08-13","status":"retired"},"4":{"release_date":"1997-09-30","status":"retired"},"5":{"release_date":"1999-03-18","status":"retired"},"6":{"release_date":"2001-08-27","status":"retired"},"7":{"release_date":"2006-10-18","status":"retired"},"8":{"release_date":"2009-03-19","status":"retired","engine":"Trident","engine_version":"4.0"},"9":{"release_date":"2011-03-14","status":"retired","engine":"Trident","engine_version":"5.0"},"10":{"release_date":"2012-10-26","status":"retired","engine":"Trident","engine_version":"6.0"},"11":{"release_date":"2013-10-17","status":"current","engine":"Trident","engine_version":"7.0"},"5.5":{"release_date":"2000-07-06","status":"retired"}}},"nodejs":{"name":"Node.js","releases":{"0.10.0":{"release_date":"2013-03-11","release_notes":"https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V010.md","status":"retired","engine":"V8","engine_version":"3.14"},"0.12.0":{"release_date":"2015-02-06","release_notes":"https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V012.md","status":"retired","engine":"V8","engine_version":"3.28"},"4.0.0":{"release_date":"2015-09-08","release_notes":"https://nodejs.org/en/blog/release/v4.0.0/","status":"retired","engine":"V8","engine_version":"4.5"},"5.0.0":{"release_date":"2015-10-29","release_notes":"https://nodejs.org/en/blog/release/v5.0.0/","status":"retired","engine":"V8","engine_version":"4.6"},"6.0.0":{"release_date":"2016-04-26","release_notes":"https://nodejs.org/en/blog/release/v6.0.0/","status":"retired","engine":"V8","engine_version":"5"},"6.5.0":{"release_date":"2016-08-26","release_notes":"https://nodejs.org/en/blog/release/v6.5.0/","status":"retired","engine":"V8","engine_version":"5.1"},"7.0.0":{"release_date":"2016-10-25","release_notes":"https://nodejs.org/en/blog/release/v7.0.0/","status":"retired","engine":"V8","engine_version":"5.4"},"7.5.0":{"release_date":"2017-01-31","release_notes":"https://nodejs.org/en/blog/release/v7.5.0/","status":"retired","engine":"V8","engine_version":"5.4"},"7.6.0":{"release_date":"2017-02-21","release_notes":"https://nodejs.org/en/blog/release/v7.6.0/","status":"retired","engine":"V8","engine_version":"5.5"},"7.7.0":{"release_date":"2017-02-28","release_notes":"https://nodejs.org/en/blog/release/v7.7.0/","status":"retired","engine":"V8","engine_version":"5.5"},"7.10.0":{"release_date":"2017-05-02","release_notes":"https://nodejs.org/en/blog/release/v7.10.0/","status":"retired","engine":"V8","engine_version":"5.5"},"8.0.0":{"release_date":"2017-05-30","release_notes":"https://nodejs.org/en/blog/release/v8.0.0/","status":"retired","engine":"V8","engine_version":"5.8"},"8.3.0":{"release_date":"2017-08-09","release_notes":"https://nodejs.org/en/blog/release/v8.3.0/","status":"retired","engine":"V8","engine_version":"6.0"},"8.5.0":{"release_date":"2017-09-12","release_notes":"https://nodejs.org/en/blog/release/v8.5.0/","status":"retired","engine":"V8","engine_version":"6.0"},"8.7.0":{"release_date":"2017-10-11","release_notes":"https://nodejs.org/en/blog/release/v8.7.0/","status":"retired","engine":"V8","engine_version":"6.1"},"8.10.0":{"release_date":"2018-03-06","release_notes":"https://nodejs.org/en/blog/release/v8.10.0/","status":"retired","engine":"V8","engine_version":"6.2"},"9.3.0":{"release_date":"2017-12-12","release_notes":"https://nodejs.org/en/blog/release/v9.3.0/","status":"retired","engine":"V8","engine_version":"6.2"},"10.0.0":{"release_date":"2018-04-24","release_notes":"https://nodejs.org/en/blog/release/v10.0.0/","status":"retired","engine":"V8","engine_version":"6.6"},"10.4.0":{"release_date":"2018-06-06","release_notes":"https://nodejs.org/en/blog/release/v10.4.0/","status":"retired","engine":"V8","engine_version":"6.7"},"10.5.0":{"release_date":"2018-06-20","release_notes":"https://nodejs.org/en/blog/release/v10.5.0/","status":"retired","engine":"V8","engine_version":"6.7"},"10.7.0":{"release_date":"2018-07-18","release_notes":"https://nodejs.org/en/blog/release/v10.7.0/","status":"retired","engine":"V8","engine_version":"6.7"},"10.9.0":{"release_date":"2018-08-16","release_notes":"https://nodejs.org/en/blog/release/v10.9.0/","status":"retired","engine":"V8","engine_version":"6.8"},"11.0.0":{"release_date":"2018-10-23","release_notes":"https://nodejs.org/en/blog/release/v11.0.0/","status":"retired","engine":"V8","engine_version":"7.0"},"11.7.0":{"release_date":"2019-01-18","release_notes":"https://nodejs.org/en/blog/release/v11.7.0/","status":"retired","engine":"V8","engine_version":"7.0"},"12.0.0":{"release_date":"2019-04-23","release_notes":"https://nodejs.org/en/blog/release/v12.0.0/","status":"retired","engine":"V8","engine_version":"7.4"},"12.5.0":{"release_date":"2019-06-27","release_notes":"https://nodejs.org/en/blog/release/v12.5.0/","status":"retired","engine":"V8","engine_version":"7.5"},"12.9.0":{"release_date":"2019-08-20","release_notes":"https://nodejs.org/en/blog/release/v12.9.0/","status":"retired","engine":"V8","engine_version":"7.6"},"12.11.0":{"release_date":"2019-09-25","release_notes":"https://nodejs.org/en/blog/release/v12.11.0/","status":"retired","engine":"V8","engine_version":"7.7"},"12.17.0":{"release_date":"2020-05-26","release_notes":"https://nodejs.org/en/blog/release/v12.17.0/","status":"esr","engine":"V8","engine_version":"7.8"},"13.0.0":{"release_date":"2019-10-10","release_notes":"https://nodejs.org/en/blog/release/v13.0.0/","status":"retired","engine":"V8","engine_version":"7.8"},"13.2.0":{"release_date":"2019-11-21","release_notes":"https://nodejs.org/en/blog/release/v13.2.0/","status":"retired","engine":"V8","engine_version":"7.9"},"14.0.0":{"release_date":"2020-04-21","release_notes":"https://nodejs.org/en/blog/release/v14.0.0/","status":"retired","engine":"V8","engine_version":"8.1"},"14.3.0":{"release_date":"2020-05-19","release_notes":"https://nodejs.org/en/blog/release/v14.3.0/","status":"retired","engine":"V8","engine_version":"8.3"},"14.5.0":{"release_date":"2020-06-30","release_notes":"https://nodejs.org/en/blog/release/v14.5.0/","status":"retired","engine":"V8","engine_version":"8.3"},"14.6.0":{"release_date":"2020-07-21","release_notes":"https://nodejs.org/en/blog/release/v14.6.0/","status":"retired","engine":"V8","engine_version":"8.4"},"14.8.0":{"release_date":"2020-08-11","release_notes":"https://nodejs.org/en/blog/release/v14.8.0/","status":"esr","engine":"V8","engine_version":"8.4"},"15.0.0":{"release_date":"2020-10-20","release_notes":"https://nodejs.org/en/blog/release/v15.0.0/","status":"retired","engine":"V8","engine_version":"8.6"},"16.0.0":{"release_date":"2021-04-20","release_notes":"https://nodejs.org/en/blog/release/v16.0.0/","status":"retired","engine":"V8","engine_version":"9.0"},"16.4.0":{"release_date":"2021-06-23","release_notes":"https://nodejs.org/en/blog/release/v16.4.0/","status":"retired","engine":"V8","engine_version":"9.1"},"16.6.0":{"release_date":"2021-07-29","release_notes":"https://nodejs.org/en/blog/release/v16.6.0/","status":"retired","engine":"V8","engine_version":"9.2"},"16.7.0":{"release_date":"2021-08-17","release_notes":"https://nodejs.org/en/blog/release/v16.7.0/","status":"retired","engine":"V8","engine_version":"9.2"},"16.9.0":{"release_date":"2021-09-07","release_notes":"https://nodejs.org/en/blog/release/v16.9.0/","status":"current","engine":"V8","engine_version":"9.3"}}},"opera":{"name":"Opera","releases":{"2":{"release_date":"1996-07-14","status":"retired"},"3":{"release_date":"1997-12-01","status":"retired"},"4":{"release_date":"2000-06-28","status":"retired"},"5":{"release_date":"2000-12-06","status":"retired"},"6":{"release_date":"2001-12-18","status":"retired"},"7":{"release_date":"2003-01-28","status":"retired","engine":"Presto","engine_version":"1"},"8":{"release_date":"2005-04-19","status":"retired","engine":"Presto","engine_version":"1"},"9":{"release_date":"2006-06-20","status":"retired","engine":"Presto","engine_version":"2"},"10":{"release_date":"2009-09-01","release_notes":"https://dev.opera.com/blog/opera-10-weve-only-just-begun/","status":"retired","engine":"Presto","engine_version":"2.2"},"11":{"release_date":"2010-12-16","release_notes":"https://dev.opera.com/blog/new-html5-features-in-opera-11/","status":"retired","engine":"Presto","engine_version":"2.7"},"12":{"release_date":"2012-06-14","release_notes":"https://dev.opera.com/blog/hello-opera-12/","status":"retired","engine":"Presto","engine_version":"2.10"},"15":{"release_date":"2013-07-02","release_notes":"https://dev.opera.com/blog/introducing-opera-15-for-desktop-and-a-fast-release-cycle/","status":"retired","engine":"Blink","engine_version":"28"},"16":{"release_date":"2013-08-27","release_notes":"https://dev.opera.com/blog/opera-16-released-in-the-wild/","status":"retired","engine":"Blink","engine_version":"29"},"17":{"release_date":"2013-10-08","release_notes":"https://dev.opera.com/blog/opera-desktop-17-released/","status":"retired","engine":"Blink","engine_version":"30"},"18":{"release_date":"2013-11-19","release_notes":"https://dev.opera.com/blog/opera-desktop-18-released/","status":"retired","engine":"Blink","engine_version":"31"},"19":{"release_date":"2014-01-28","release_notes":"https://dev.opera.com/blog/opera-19/","status":"retired","engine":"Blink","engine_version":"32"},"20":{"release_date":"2014-03-04","release_notes":"https://dev.opera.com/blog/opera-20/","status":"retired","engine":"Blink","engine_version":"33"},"21":{"release_date":"2014-05-06","release_notes":"https://dev.opera.com/blog/opera-21/","status":"retired","engine":"Blink","engine_version":"34"},"22":{"release_date":"2014-06-03","release_notes":"https://dev.opera.com/blog/opera-22/","status":"retired","engine":"Blink","engine_version":"35"},"23":{"release_date":"2014-07-22","release_notes":"https://dev.opera.com/blog/opera-23/","status":"retired","engine":"Blink","engine_version":"36"},"24":{"release_date":"2014-09-02","release_notes":"https://dev.opera.com/blog/opera-24/","status":"retired","engine":"Blink","engine_version":"37"},"25":{"release_date":"2014-10-15","release_notes":"https://dev.opera.com/blog/opera-25/","status":"retired","engine":"Blink","engine_version":"38"},"26":{"release_date":"2014-12-03","release_notes":"https://dev.opera.com/blog/opera-26/","status":"retired","engine":"Blink","engine_version":"39"},"27":{"release_date":"2015-01-27","release_notes":"https://dev.opera.com/blog/opera-27/","status":"retired","engine":"Blink","engine_version":"40"},"28":{"release_date":"2015-03-10","release_notes":"https://dev.opera.com/blog/opera-28/","status":"retired","engine":"Blink","engine_version":"41"},"29":{"release_date":"2015-04-28","release_notes":"https://dev.opera.com/blog/opera-29/","status":"retired","engine":"Blink","engine_version":"42"},"30":{"release_date":"2015-06-09","release_notes":"https://dev.opera.com/blog/opera-30/","status":"retired","engine":"Blink","engine_version":"43"},"31":{"release_date":"2015-08-04","release_notes":"https://dev.opera.com/blog/opera-31/","status":"retired","engine":"Blink","engine_version":"44"},"32":{"release_date":"2015-09-15","release_notes":"https://dev.opera.com/blog/opera-32/","status":"retired","engine":"Blink","engine_version":"45"},"33":{"release_date":"2015-10-27","release_notes":"https://dev.opera.com/blog/opera-33/","status":"retired","engine":"Blink","engine_version":"46"},"34":{"release_date":"2015-12-08","release_notes":"https://dev.opera.com/blog/opera-34/","status":"retired","engine":"Blink","engine_version":"47"},"35":{"release_date":"2016-02-02","release_notes":"https://dev.opera.com/blog/opera-35/","status":"retired","engine":"Blink","engine_version":"48"},"36":{"release_date":"2016-03-15","release_notes":"https://dev.opera.com/blog/opera-36/","status":"retired","engine":"Blink","engine_version":"49"},"37":{"release_date":"2016-05-04","release_notes":"https://dev.opera.com/blog/opera-37/","status":"retired","engine":"Blink","engine_version":"50"},"38":{"release_date":"2016-06-08","release_notes":"https://dev.opera.com/blog/opera-38/","status":"retired","engine":"Blink","engine_version":"51"},"39":{"release_date":"2016-08-02","release_notes":"https://dev.opera.com/blog/opera-39/","status":"retired","engine":"Blink","engine_version":"52"},"40":{"release_date":"2016-09-20","release_notes":"https://dev.opera.com/blog/opera-40/","status":"retired","engine":"Blink","engine_version":"53"},"41":{"release_date":"2016-10-25","release_notes":"https://dev.opera.com/blog/opera-41/","status":"retired","engine":"Blink","engine_version":"54"},"42":{"release_date":"2016-12-13","release_notes":"https://dev.opera.com/blog/opera-42/","status":"retired","engine":"Blink","engine_version":"55"},"43":{"release_date":"2017-02-07","release_notes":"https://dev.opera.com/blog/opera-43/","status":"retired","engine":"Blink","engine_version":"56"},"44":{"release_date":"2017-03-21","release_notes":"https://dev.opera.com/blog/opera-44/","status":"retired","engine":"Blink","engine_version":"57"},"45":{"release_date":"2017-05-10","release_notes":"https://dev.opera.com/blog/opera-45/","status":"retired","engine":"Blink","engine_version":"58"},"46":{"release_date":"2017-06-22","release_notes":"https://dev.opera.com/blog/opera-46/","status":"retired","engine":"Blink","engine_version":"59"},"47":{"release_date":"2017-08-09","release_notes":"https://dev.opera.com/blog/opera-47/","status":"retired","engine":"Blink","engine_version":"60"},"48":{"release_date":"2017-09-27","status":"retired","engine":"Blink","engine_version":"61"},"49":{"release_date":"2017-11-08","release_notes":"https://dev.opera.com/blog/opera-49/","status":"retired","engine":"Blink","engine_version":"62"},"50":{"release_date":"2018-01-04","release_notes":"https://dev.opera.com/blog/opera-50/","status":"retired","engine":"Blink","engine_version":"63"},"51":{"release_date":"2018-02-07","release_notes":"https://dev.opera.com/blog/opera-51/","status":"retired","engine":"Blink","engine_version":"64"},"52":{"release_date":"2018-03-22","release_notes":"https://dev.opera.com/blog/opera-52/","status":"retired","engine":"Blink","engine_version":"65"},"53":{"release_date":"2018-05-10","release_notes":"https://dev.opera.com/blog/opera-53/","status":"retired","engine":"Blink","engine_version":"66"},"54":{"release_date":"2018-06-28","release_notes":"https://dev.opera.com/blog/opera-54/","status":"retired","engine":"Blink","engine_version":"67"},"55":{"release_date":"2018-08-16","release_notes":"https://blogs.opera.com/desktop/2018/08/opera-55-offers-better-control-web-pages-accessible-bookmarks/","status":"retired","engine":"Blink","engine_version":"68"},"56":{"release_date":"2018-09-25","release_notes":"https://dev.opera.com/blog/opera-56/","status":"retired","engine":"Blink","engine_version":"69"},"57":{"release_date":"2018-11-28","release_notes":"https://dev.opera.com/blog/opera-57/","status":"retired","engine":"Blink","engine_version":"70"},"58":{"release_date":"2019-01-23","release_notes":"https://dev.opera.com/blog/opera-58/","status":"retired","engine":"Blink","engine_version":"71"},"60":{"release_date":"2019-04-09","release_notes":"https://blogs.opera.com/desktop/2019/04/opera-60-reborn-3-web-3-0-vpn-ad-blocker/","status":"retired","engine":"Blink","engine_version":"73"},"62":{"release_date":"2019-06-27","release_notes":"https://blogs.opera.com/desktop/2019/06/opera-62-comes-with-design-updates-to-reborn-3/","status":"retired","engine":"Blink","engine_version":"75"},"63":{"release_date":"2019-08-20","release_notes":"https://blogs.opera.com/desktop/2019/08/opera-63-initial-release/","status":"retired","engine":"Blink","engine_version":"76"},"64":{"release_date":"2019-10-07","release_notes":"https://blogs.opera.com/desktop/2019/10/opera-64-faster-more-private-more-fun/","status":"retired","engine":"Blink","engine_version":"77"},"65":{"release_date":"2019-11-13","release_notes":"https://blogs.opera.com/desktop/2019/11/opera-65-comes-with-an-improved-tracker-blocker-and-redesigned-address-bar/","status":"retired","engine":"Blink","engine_version":"78"},"66":{"release_date":"2020-01-07","release_notes":"https://blogs.opera.com/desktop/2020/01/opera-66-initial-release-makes-it-easier-to-reopen-closed-tabs-and-to-access-extensions/","status":"retired","engine":"Blink","engine_version":"79"},"67":{"release_date":"2020-03-03","release_notes":"https://blogs.opera.com/desktop/2020/03/opera-67-3575-53-stable-update/","status":"retired","engine":"Blink","engine_version":"80"},"68":{"release_date":"2020-04-22","release_notes":"https://blogs.opera.com/desktop/2020/04/opera-68-is-here-with-built-in-instagram-in-the-sidebar/","status":"retired","engine":"Blink","engine_version":"81"},"69":{"release_date":"2020-06-24","release_notes":"https://blogs.opera.com/desktop/2020/06/opera-69-comes-with-built-in-twitter/","status":"retired","engine":"Blink","engine_version":"83"},"70":{"release_date":"2020-07-27","release_notes":"https://blogs.opera.com/desktop/2020/07/opera-70-comes-with-easier-access-to-closed-tabs-simpler-searches-and-new-workspace-icons/","status":"retired","engine":"Blink","engine_version":"84"},"71":{"release_date":"2020-09-15","release_notes":"https://blogs.opera.com/desktop/2020/09/opera-71-update/","status":"retired","engine":"Blink","engine_version":"85"},"72":{"release_date":"2020-10-21","release_notes":"https://blogs.opera.com/desktop/2020/10/opera-72-update/","status":"retired","engine":"Blink","engine_version":"86"},"73":{"release_date":"2020-12-09","release_notes":"https://blogs.opera.com/desktop/2020/12/opera-73-update/","status":"retired","engine":"Blink","engine_version":"87"},"74":{"release_date":"2021-02-02","release_notes":"https://blogs.opera.com/desktop/2021/02/opera-74-stable/","status":"retired","engine":"Blink","engine_version":"88"},"75":{"release_date":"2021-03-24","release_notes":"https://blogs.opera.com/desktop/2021/03/opera-75-brings-easier-access-to-top-features-2/","status":"retired","engine":"Blink","engine_version":"89"},"76":{"release_date":"2021-04-28","release_notes":"https://blogs.opera.com/desktop/2021/04/opera-76-stable/","status":"retired","engine":"Blink","engine_version":"90"},"77":{"release_date":"2021-06-09","release_notes":"https://blogs.opera.com/desktop/2021/06/opera-77-stable/","status":"retired","engine":"Blink","engine_version":"91"},"78":{"release_date":"2021-08-03","release_notes":"https://blogs.opera.com/desktop/2021/08/opera-78-stable/","status":"retired","engine":"Blink","engine_version":"92"},"79":{"release_date":"2021-09-14","release_notes":"https://blogs.opera.com/desktop/2021/09/opera-79-stable/","status":"retired","engine":"Blink","engine_version":"93"},"80":{"release_date":"2021-10-05","release_notes":"https://blogs.opera.com/desktop/2021/10/opera-80-stable/","status":"current","engine":"Blink","engine_version":"94"},"81":{"status":"beta","engine":"Blink","engine_version":"95"},"3.5":{"release_date":"1998-11-18","status":"retired"},"3.6":{"release_date":"1999-05-06","status":"retired"},"5.1":{"release_date":"2001-04-10","status":"retired"},"7.1":{"release_date":"2003-04-11","status":"retired","engine":"Presto","engine_version":"1"},"7.2":{"release_date":"2003-09-23","status":"retired","engine":"Presto","engine_version":"1"},"7.5":{"release_date":"2004-05-12","status":"retired","engine":"Presto","engine_version":"1"},"8.5":{"release_date":"2005-09-20","status":"retired","engine":"Presto","engine_version":"1"},"9.1":{"release_date":"2006-12-18","status":"retired","engine":"Presto","engine_version":"2"},"9.2":{"release_date":"2007-04-11","status":"retired","engine":"Presto","engine_version":"2"},"9.5":{"release_date":"2008-06-12","status":"retired","engine":"Presto","engine_version":"2.1"},"9.6":{"release_date":"2008-10-08","release_notes":"https://dev.opera.com/blog/a-look-under-the-hood-of-opera-9-6/","status":"retired","engine":"Presto","engine_version":"2.1"},"10.1":{"release_date":"2009-11-23","release_notes":"https://dev.opera.com/blog/opera-10-10-and-10-2-alpha/","status":"retired","engine":"Presto","engine_version":"2.2"},"10.5":{"release_date":"2010-03-02","release_notes":"https://dev.opera.com/blog/opera-10-50-final-for-windows-is-out/","status":"retired","engine":"Presto","engine_version":"2.5"},"10.6":{"release_date":"2010-07-01","release_notes":"https://dev.opera.com/blog/hello-opera-10-60/","status":"retired","engine":"Presto","engine_version":"2.6"},"11.1":{"release_date":"2011-04-12","release_notes":"https://dev.opera.com/blog/unveiling-opera-11-10-final/","status":"retired","engine":"Presto","engine_version":"2.8"},"11.5":{"release_date":"2011-06-28","release_notes":"https://dev.opera.com/blog/opera-11-50-released-speed-dial-extensions-improved-standards-support/","status":"retired","engine":"Presto","engine_version":"2.9"},"11.6":{"release_date":"2011-12-06","release_notes":"https://dev.opera.com/blog/hello-opera-11-60/","status":"retired","engine":"Presto","engine_version":"2.10"},"12.1":{"release_date":"2012-11-20","release_notes":"https://dev.opera.com/blog/opera-12-10-is-out/","status":"retired","engine":"Presto","engine_version":"2.12"}}},"opera_android":{"name":"Opera Android","releases":{"11":{"release_date":"2011-03-22","release_notes":"https://dev.opera.com/blog/opera-mobile-11-for-maemo-meego-windows/","status":"retired","engine":"Presto","engine_version":"2.7"},"12":{"release_date":"2012-02-25","release_notes":"https://dev.opera.com/blog/opera-mobile-12-and-introducing-opera-mini-next/","status":"retired","engine":"Presto","engine_version":"2.10"},"14":{"release_date":"2013-05-21","release_notes":"https://dev.opera.com/blog/opera-14-for-android-is-out/","status":"retired","engine":"Blink","engine_version":"26"},"15":{"release_date":"2013-07-08","release_notes":"https://blogs.opera.com/news/2013/07/opera-15-for-android/","status":"retired","engine":"Blink","engine_version":"28"},"16":{"release_date":"2013-09-18","status":"retired","engine":"Blink","engine_version":"29"},"18":{"release_date":"2013-11-20","release_notes":"https://blogs.opera.com/news/2013/11/opera-18-android-tablet/","status":"retired","engine":"Blink","engine_version":"31"},"19":{"release_date":"2014-01-28","release_notes":"https://dev.opera.com/blog/opera-19/","status":"retired","engine":"Blink","engine_version":"32"},"20":{"release_date":"2014-03-06","release_notes":"https://forums.opera.com/topic/1081/opera-20-final-release","status":"retired","engine":"Blink","engine_version":"33"},"21":{"release_date":"2014-04-22","release_notes":"https://forums.opera.com/topic/2211/opera-21-final-release","status":"retired","engine":"Blink","engine_version":"34"},"22":{"release_date":"2014-06-17","release_notes":"https://forums.opera.com/topic/3446/opera-22","status":"retired","engine":"Blink","engine_version":"35"},"24":{"release_date":"2014-09-10","release_notes":"https://forums.opera.com/topic/5022/opera-24-final-release","status":"retired","engine":"Blink","engine_version":"37"},"25":{"release_date":"2014-10-16","release_notes":"https://forums.opera.com/topic/5715/opera-25-final-release","status":"retired","engine":"Blink","engine_version":"38"},"26":{"release_date":"2014-12-02","release_notes":"https://forums.opera.com/topic/6800/opera-26-final-release","status":"retired","engine":"Blink","engine_version":"39"},"27":{"release_date":"2015-01-29","release_notes":"https://forums.opera.com/topic/7871/opera-27-final-release","status":"retired","engine":"Blink","engine_version":"40"},"28":{"release_date":"2015-03-10","release_notes":"https://forums.opera.com/topic/8556/synced-bookmarks-and-improved-memory-usage-in-opera-28-for-android","status":"retired","engine":"Blink","engine_version":"41"},"29":{"release_date":"2015-04-28","release_notes":"https://dev.opera.com/blog/opera-29/","status":"retired","engine":"Blink","engine_version":"42"},"30":{"release_date":"2015-06-10","release_notes":"https://blogs.opera.com/mobile/2015/06/opera-30-android-sync-speeddials/","status":"retired","engine":"Blink","engine_version":"43"},"32":{"release_date":"2015-09-23","release_notes":"https://blogs.opera.com/mobile/2015/09/opera-32-add-to-home-screen/","status":"retired","engine":"Blink","engine_version":"45"},"33":{"release_date":"2015-11-03","release_notes":"https://forums.opera.com/topic/12480/opera-33-now-featuring-video-optimization-and-brand-new-icons","status":"retired","engine":"Blink","engine_version":"46"},"34":{"release_date":"2015-12-16","release_notes":"https://forums.opera.com/topic/13085/opera-34-for-android-released","status":"retired","engine":"Blink","engine_version":"47"},"35":{"release_date":"2016-02-04","release_notes":"https://blogs.opera.com/mobile/2016/02/save-space-on-your-android-phone-with-web-apps/","status":"retired","engine":"Blink","engine_version":"48"},"36":{"release_date":"2016-03-31","release_notes":"https://forums.opera.com/topic/14514/opera-36-released","status":"retired","engine":"Blink","engine_version":"49"},"37":{"release_date":"2016-06-16","release_notes":"https://forums.opera.com/topic/15753/opera-37-released","status":"retired","engine":"Blink","engine_version":"50"},"41":{"release_date":"2016-10-25","status":"retired","engine":"Blink","engine_version":"54"},"42":{"release_date":"2017-01-21","release_notes":"https://forums.opera.com/topic/18950/opera-for-android-42","status":"retired","engine":"Blink","engine_version":"55"},"43":{"release_date":"2017-09-27","release_notes":"https://forums.opera.com/topic/22708/opera-for-android-43","status":"retired","engine":"Blink","engine_version":"59"},"44":{"release_date":"2017-12-11","release_notes":"https://forums.opera.com/topic/23860/opera-for-android-44","status":"retired","engine":"Blink","engine_version":"60"},"45":{"release_date":"2018-02-15","release_notes":"https://forums.opera.com/topic/25124/opera-for-android-45","status":"retired","engine":"Blink","engine_version":"61"},"46":{"release_date":"2018-05-14","release_notes":"https://forums.opera.com/topic/26662/opera-for-android-46","status":"retired","engine":"Blink","engine_version":"63"},"47":{"release_date":"2018-07-23","release_notes":"https://forums.opera.com/topic/27794/opera-for-android-47","status":"retired","engine":"Blink","engine_version":"66"},"48":{"release_date":"2018-11-08","release_notes":"https://forums.opera.com/topic/29525/opera-for-android-48","status":"retired","engine":"Blink","engine_version":"69"},"49":{"release_date":"2018-12-07","release_notes":"https://forums.opera.com/topic/29983/opera-for-android-49","status":"retired","engine":"Blink","engine_version":"70"},"50":{"release_date":"2019-02-18","release_notes":"https://forums.opera.com/topic/31003/opera-for-android-50","status":"retired","engine":"Blink","engine_version":"71"},"51":{"release_date":"2019-03-21","release_notes":"https://forums.opera.com/topic/31467/opera-for-android-51-built-in-vpn","status":"retired","engine":"Blink","engine_version":"72"},"52":{"release_date":"2019-05-17","release_notes":"https://forums.opera.com/topic/32516/opera-for-android-52","status":"retired","engine":"Blink","engine_version":"73"},"53":{"release_date":"2019-07-11","release_notes":"https://forums.opera.com/topic/33558/opera-for-android-53","status":"retired","engine":"Blink","engine_version":"74"},"54":{"release_date":"2019-10-18","release_notes":"https://forums.opera.com/topic/35853/opera-for-android-54","status":"retired","engine":"Blink","engine_version":"76"},"55":{"release_date":"2019-12-03","release_notes":"https://forums.opera.com/topic/36858/opera-for-android-55","status":"retired","engine":"Blink","engine_version":"77"},"56":{"release_date":"2020-02-06","release_notes":"https://blogs.opera.com/mobile/2020/02/easy-reading-in-opera-for-android/","status":"retired","engine":"Blink","engine_version":"78"},"57":{"release_date":"2020-03-30","release_notes":"https://blogs.opera.com/mobile/2020/03/introducing-new-features-in-opera-for-android-57/","status":"retired","engine":"Blink","engine_version":"80"},"58":{"release_date":"2020-05-13","release_notes":"https://blogs.opera.com/mobile/2020/05/opera-for-android-58-handle-notifications-easily-and-group-speed-dials-for-a-cleaner-appearance/","status":"retired","engine":"Blink","engine_version":"81"},"59":{"release_date":"2020-06-30","release_notes":"https://blogs.opera.com/mobile/2020/06/opera-for-android-59/","status":"retired","engine":"Blink","engine_version":"83"},"60":{"release_date":"2020-09-23","release_notes":"https://blogs.opera.com/mobile/2020/09/keep-in-sync-with-opera-for-android-60/","status":"retired","engine":"Blink","engine_version":"85"},"61":{"release_date":"2020-12-07","release_notes":"https://blogs.opera.com/mobile/2020/12/new-opera-for-android-61/","status":"retired","engine":"Blink","engine_version":"86"},"62":{"release_date":"2021-02-16","release_notes":"https://blogs.opera.com/mobile/2021/02/the-opera-browser-for-android-hit-a-new-record-of-80-million-maus/","status":"retired","engine":"Blink","engine_version":"87"},"63":{"release_date":"2021-04-16","status":"retired","engine":"Blink","engine_version":"89"},"64":{"release_date":"2021-05-25","status":"current","engine":"Blink","engine_version":"91"},"10.1":{"release_date":"2010-11-09","release_notes":"https://dev.opera.com/blog/opera-mobile-10-1-beta-for-android-is-here/","status":"retired","engine":"Presto","engine_version":"2.5"},"11.1":{"release_date":"2011-06-30","release_notes":"https://dev.opera.com/blog/opera-mobile-11-1-new-features-and-additions/","status":"retired","engine":"Presto","engine_version":"2.8"},"11.5":{"release_date":"2011-10-12","status":"retired","engine":"Presto","engine_version":"2.9"},"12.1":{"release_date":"2012-10-09","release_notes":"https://dev.opera.com/blog/opera-mobile-12-1-with-spdy-web-sockets-flexbox-and-more/","status":"retired","engine":"Presto","engine_version":"2.11"}}},"safari":{"name":"Safari","preview_name":"TP","releases":{"1":{"release_date":"2003-06-23","status":"retired","engine":"WebKit","engine_version":"85"},"2":{"release_date":"2005-04-29","status":"retired","engine":"WebKit","engine_version":"412"},"3":{"release_date":"2007-10-26","status":"retired","engine":"WebKit","engine_version":"523.10"},"4":{"release_date":"2009-06-08","release_notes":"https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_4_0.html","status":"retired","engine":"WebKit","engine_version":"530.17"},"5":{"release_date":"2010-06-07","status":"retired","engine":"WebKit","engine_version":"533.16"},"6":{"release_date":"2012-07-25","release_notes":"https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_6_0.html","status":"retired","engine":"WebKit","engine_version":"536.25"},"7":{"release_date":"2013-10-22","release_notes":"https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_7_0.html","status":"retired","engine":"WebKit","engine_version":"537.71"},"8":{"release_date":"2014-10-16","release_notes":"https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_8_0.html","status":"retired","engine":"WebKit","engine_version":"538.35"},"9":{"release_date":"2015-09-30","release_notes":"https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_9_0.html","status":"retired","engine":"WebKit","engine_version":"601.1.56"},"10":{"release_date":"2016-09-20","release_notes":"https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_10_0.html","status":"retired","engine":"WebKit","engine_version":"602.1.50"},"11":{"release_date":"2017-09-19","release_notes":"https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Safari_11_0/Safari_11_0.html","status":"retired","engine":"WebKit","engine_version":"604.2.4"},"12":{"release_date":"2018-09-24","release_notes":"https://developer.apple.com/documentation/safari_release_notes/safari_12_release_notes","status":"retired","engine":"WebKit","engine_version":"606.1.36"},"13":{"release_date":"2019-09-19","release_notes":"https://developer.apple.com/documentation/safari_release_notes/safari_13_release_notes","status":"retired","engine":"WebKit","engine_version":"608.2.11"},"14":{"release_date":"2020-09-16","release_notes":"https://developer.apple.com/documentation/safari-release-notes/safari-14-release-notes","status":"retired","engine":"WebKit","engine_version":"610.1.28"},"15":{"release_date":"2021-09-20","release_notes":"https://developer.apple.com/documentation/safari-release-notes/safari-15-release-notes","status":"current","engine":"WebKit","engine_version":"612.1.29"},"1.1":{"release_date":"2003-10-24","status":"retired","engine":"WebKit","engine_version":"100"},"1.2":{"release_date":"2004-02-02","status":"retired","engine":"WebKit","engine_version":"125"},"1.3":{"release_date":"2005-04-15","status":"retired","engine":"WebKit","engine_version":"312"},"3.1":{"release_date":"2008-03-18","status":"retired","engine":"WebKit","engine_version":"525.13"},"5.1":{"release_date":"2011-07-20","status":"retired","engine":"WebKit","engine_version":"534.48"},"6.1":{"release_date":"2013-10-22","status":"retired","engine":"WebKit","engine_version":"537.43"},"9.1":{"release_date":"2016-03-21","release_notes":"https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_9_1.html","status":"retired","engine":"WebKit","engine_version":"601.5.17"},"10.1":{"release_date":"2017-03-27","release_notes":"https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_10_1.html","status":"retired","engine":"WebKit","engine_version":"603.2.1"},"11.1":{"release_date":"2018-04-12","release_notes":"https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_11_1.html","status":"retired","engine":"WebKit","engine_version":"605.1.33"},"12.1":{"release_date":"2019-03-25","release_notes":"https://developer.apple.com/documentation/safari_release_notes/safari_12_1_release_notes","status":"retired","engine":"WebKit","engine_version":"607.1.40"},"13.1":{"release_date":"2020-03-24","release_notes":"https://developer.apple.com/documentation/safari-release-notes/safari-13_1-release_notes","status":"retired","engine":"WebKit","engine_version":"609.1.20"},"14.1":{"release_date":"2021-04-26","release_notes":"https://developer.apple.com/documentation/safari-release-notes/safari-14_1-release-notes","status":"retired","engine":"WebKit","engine_version":"611.1.21.161.3"},"15.1":{"status":"beta","engine":"WebKit","engine_version":"612.2.6"}}},"safari_ios":{"name":"Safari on iOS","releases":{"1":{"status":"retired","engine":"WebKit","engine_version":"522.11","release_date":"2007-06-29"},"2":{"status":"retired","engine":"WebKit","engine_version":"525.18","release_date":"2008-07-11"},"3":{"status":"retired","engine":"WebKit","engine_version":"528.18","release_date":"2009-06-17"},"4":{"status":"retired","engine":"WebKit","engine_version":"532.9","release_date":"2010-06-21"},"5":{"status":"retired","engine":"WebKit","engine_version":"534.46","release_date":"2011-10-12"},"6":{"status":"retired","engine":"WebKit","engine_version":"536.26","release_date":"2012-09-10"},"7":{"status":"retired","engine":"WebKit","engine_version":"537.51","release_date":"2013-09-18"},"8":{"status":"retired","engine":"WebKit","engine_version":"600.1.4","release_date":"2014-09-17"},"9":{"status":"retired","engine":"WebKit","engine_version":"601.1.56","release_date":"2015-09-16"},"10":{"status":"retired","engine":"WebKit","engine_version":"602.1.50","release_date":"2016-09-13"},"11":{"status":"retired","engine":"WebKit","engine_version":"604.2.4","release_date":"2017-09-19"},"12":{"release_date":"2018-09-17","release_notes":"https://developer.apple.com/documentation/safari_release_notes/safari_12_release_notes","status":"retired","engine":"WebKit","engine_version":"606.1.36"},"13":{"release_date":"2019-09-19","release_notes":"https://developer.apple.com/documentation/safari_release_notes/safari_13_release_notes","status":"retired","engine":"WebKit","engine_version":"608.2.11"},"14":{"release_date":"2020-09-16","release_notes":"https://developer.apple.com/documentation/safari-release-notes/safari-14-release-notes","status":"retired","engine":"WebKit","engine_version":"610.1.28"},"15":{"release_date":"2021-09-20","release_notes":"https://developer.apple.com/documentation/safari-release-notes/safari-15-release-notes","status":"current","engine":"WebKit","engine_version":"612.1.29"},"3.2":{"status":"retired","engine":"WebKit","engine_version":"531.21","release_date":"2010-04-03"},"4.2":{"status":"retired","engine":"WebKit","engine_version":"533.17","release_date":"2010-11-22"},"6.1":{"status":"retired","engine":"WebKit","engine_version":"536.26","release_date":"2013-01-28"},"9.3":{"status":"retired","engine":"WebKit","engine_version":"601.5.17","release_date":"2016-03-21"},"10.3":{"status":"retired","engine":"WebKit","engine_version":"603.2.1","release_date":"2017-03-27"},"11.3":{"status":"retired","engine":"WebKit","engine_version":"605.1.33","release_date":"2018-03-29"},"12.2":{"release_date":"2019-03-25","release_notes":"https://developer.apple.com/documentation/safari_release_notes/safari_12_1_release_notes","status":"retired","engine":"WebKit","engine_version":"607.1.40"},"13.4":{"release_date":"2020-03-24","release_notes":"https://developer.apple.com/documentation/safari-release-notes/safari-13_1-release_notes","status":"retired","engine":"WebKit","engine_version":"609.1.20"},"14.5":{"release_date":"2021-04-26","release_notes":"https://developer.apple.com/documentation/safari-release-notes/safari-14_1-release-notes","status":"retired","engine":"WebKit","engine_version":"611.1.21.161.6"}}},"samsunginternet_android":{"name":"Samsung Internet","accepts_flags":false,"releases":{"1.0":{"release_date":"2013-04-27","status":"retired","engine":"WebKit","engine_version":"535.19"},"1.5":{"release_date":"2013-09-25","status":"retired","engine":"Blink","engine_version":"28"},"1.6":{"release_date":"2014-04-11","status":"retired","engine":"Blink","engine_version":"28"},"2.0":{"release_date":"2014-10-17","status":"retired","engine":"Blink","engine_version":"34"},"2.1":{"release_date":"2015-01-07","status":"retired","engine":"Blink","engine_version":"34"},"3.0":{"release_date":"2015-04-10","status":"retired","engine":"Blink","engine_version":"38"},"3.2":{"release_date":"2015-08-24","status":"retired","engine":"Blink","engine_version":"38"},"4.0":{"release_date":"2016-03-11","status":"retired","engine":"Blink","engine_version":"44"},"4.2":{"release_date":"2016-08-02","status":"retired","engine":"Blink","engine_version":"44"},"5.0":{"release_date":"2016-12-15","status":"retired","engine":"Blink","engine_version":"51"},"5.2":{"release_date":"2017-04-21","status":"retired","engine":"Blink","engine_version":"51"},"5.4":{"release_date":"2017-05-17","status":"retired","engine":"Blink","engine_version":"51"},"6.0":{"release_date":"2017-08-23","status":"retired","engine":"Blink","engine_version":"56"},"6.2":{"release_date":"2017-10-26","status":"retired","engine":"Blink","engine_version":"56"},"6.4":{"release_date":"2018-02-19","status":"retired","engine":"Blink","engine_version":"56"},"7.0":{"release_date":"2018-03-16","status":"retired","engine":"Blink","engine_version":"59"},"7.2":{"release_date":"2018-06-20","status":"retired","engine":"Blink","engine_version":"59"},"7.4":{"release_date":"2018-09-12","status":"retired","engine":"Blink","engine_version":"59"},"8.0":{"release_date":"2018-07-18","status":"retired","engine":"Blink","engine_version":"63"},"8.2":{"release_date":"2018-12-21","status":"retired","engine":"Blink","engine_version":"63"},"9.0":{"release_date":"2018-09-15","status":"retired","engine":"Blink","engine_version":"67"},"9.2":{"release_date":"2019-04-02","status":"retired","engine":"Blink","engine_version":"67"},"9.4":{"release_date":"2019-07-25","status":"retired","engine":"Blink","engine_version":"67"},"10.0":{"release_date":"2019-08-22","status":"retired","engine":"Blink","engine_version":"71"},"10.2":{"release_date":"2019-10-09","status":"retired","engine":"Blink","engine_version":"71"},"11.0":{"release_date":"2019-12-05","status":"retired","engine":"Blink","engine_version":"75"},"11.2":{"release_date":"2020-03-22","status":"retired","engine":"Blink","engine_version":"75"},"12.0":{"release_date":"2020-06-19","status":"retired","engine":"Blink","engine_version":"79"},"12.1":{"release_date":"2020-07-07","status":"retired","engine":"Blink","engine_version":"79"},"13.0":{"release_date":"2020-12-02","status":"retired","engine":"Blink","engine_version":"83"},"13.2":{"release_date":"2021-01-20","status":"retired","engine":"Blink","engine_version":"83"},"14.0":{"release_date":"2021-04-17","status":"retired","engine":"Blink","engine_version":"87"},"14.2":{"release_date":"2021-06-25","status":"retired","engine":"Blink","engine_version":"87"},"15.0":{"release_date":"2021-08-13","status":"current","engine":"Blink","engine_version":"90"},"16.0":{"status":"beta","engine":"Blink","engine_version":"92"}}},"webview_android":{"name":"WebView Android","accepts_flags":false,"releases":{"1":{"release_date":"2008-09-23","release_notes":"https://en.wikipedia.org/wiki/Android_version_history#Android_1.0_(API_1)","status":"retired","engine":"WebKit","engine_version":"523.12"},"2":{"release_date":"2009-10-26","release_notes":"https://en.wikipedia.org/wiki/Android_Eclair","status":"retired","engine":"WebKit","engine_version":"530.17"},"3":{"release_date":"2011-02-22","release_notes":"https://en.wikipedia.org/wiki/Android_Honeycomb","status":"retired","engine":"WebKit","engine_version":"534.13"},"4":{"release_date":"2011-10-18","release_notes":"https://en.wikipedia.org/wiki/Android_Ice_Cream_Sandwich","status":"retired","engine":"WebKit","engine_version":"534.30"},"37":{"release_date":"2014-09-03","release_notes":"https://chromereleases.googleblog.com/2014/09/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"37"},"38":{"release_date":"2014-10-08","release_notes":"https://chromereleases.googleblog.com/2014/10/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"38"},"39":{"release_date":"2014-11-12","release_notes":"https://chromereleases.googleblog.com/2014/11/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"39"},"40":{"release_date":"2015-01-21","release_notes":"https://chromereleases.googleblog.com/2015/01/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"40"},"41":{"release_date":"2015-03-11","release_notes":"https://chromereleases.googleblog.com/2015/03/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"41"},"42":{"release_date":"2015-04-15","release_notes":"https://chromereleases.googleblog.com/2015/04/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"42"},"43":{"release_date":"2015-05-27","release_notes":"https://chromereleases.googleblog.com/2015/05/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"43"},"44":{"release_date":"2015-07-29","release_notes":"https://chromereleases.googleblog.com/2015/07/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"44"},"45":{"release_date":"2015-09-01","release_notes":"https://chromereleases.googleblog.com/2015/09/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"45"},"46":{"release_date":"2015-10-14","release_notes":"https://chromereleases.googleblog.com/2015/10/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"46"},"47":{"release_date":"2015-12-02","release_notes":"https://chromereleases.googleblog.com/2015/12/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"47"},"48":{"release_date":"2016-01-26","status":"retired","engine":"Blink","engine_version":"48"},"49":{"release_date":"2016-03-09","release_notes":"https://chromereleases.googleblog.com/2016/03/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"49"},"50":{"release_date":"2016-04-13","status":"retired","engine":"Blink","engine_version":"50"},"51":{"release_date":"2016-06-08","release_notes":"https://chromereleases.googleblog.com/2016/06/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"51"},"52":{"release_date":"2016-07-27","release_notes":"https://chromereleases.googleblog.com/2016/07/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"52"},"53":{"release_date":"2016-09-07","release_notes":"https://chromereleases.googleblog.com/2016/09/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"53"},"54":{"release_date":"2016-10-19","release_notes":"https://chromereleases.googleblog.com/2016/10/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"54"},"55":{"release_date":"2016-12-06","release_notes":"https://chromereleases.googleblog.com/2016/12/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"55"},"56":{"release_date":"2017-02-01","release_notes":"https://chromereleases.googleblog.com/2017/02/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"56"},"57":{"release_date":"2017-03-16","release_notes":"https://chromereleases.googleblog.com/2017/03/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"57"},"58":{"release_date":"2017-04-25","release_notes":"https://chromereleases.googleblog.com/2017/04/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"58"},"59":{"release_date":"2017-06-06","release_notes":"https://chromereleases.googleblog.com/2017/06/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"59"},"60":{"release_date":"2017-08-01","release_notes":"https://chromereleases.googleblog.com/2017/08/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"60"},"61":{"release_date":"2017-09-05","release_notes":"https://chromereleases.googleblog.com/2017/09/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"61"},"62":{"release_date":"2017-10-24","release_notes":"https://chromereleases.googleblog.com/2017/10/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"62"},"63":{"release_date":"2017-12-05","release_notes":"https://chromereleases.googleblog.com/2017/12/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"63"},"64":{"release_date":"2018-01-23","release_notes":"https://chromereleases.googleblog.com/2018/01/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"64"},"65":{"release_date":"2017-03-06","release_notes":"https://chromereleases.googleblog.com/2018/03/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"65"},"66":{"release_date":"2017-04-17","release_notes":"https://chromereleases.googleblog.com/2018/04/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"66"},"67":{"release_date":"2018-05-31","release_notes":"https://chromereleases.googleblog.com/2018/05/chrome-for-android-update_31.html","status":"retired","engine":"Blink","engine_version":"67"},"68":{"release_date":"2018-07-24","release_notes":"https://chromereleases.googleblog.com/2018/07/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"68"},"69":{"release_date":"2018-09-04","release_notes":"https://chromereleases.googleblog.com/2018/09/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"69"},"70":{"release_date":"2018-10-17","release_notes":"https://chromereleases.googleblog.com/2018/10/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"70"},"71":{"release_date":"2018-12-04","release_notes":"https://chromereleases.googleblog.com/2018/12/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"71"},"72":{"release_date":"2019-01-29","release_notes":"https://chromereleases.googleblog.com/2019/01/chrome-for-android-update_29.html","status":"retired","engine":"Blink","engine_version":"72"},"73":{"release_date":"2019-03-12","release_notes":"https://chromereleases.googleblog.com/2019/03/chrome-for-android-update_12.html","status":"retired","engine":"Blink","engine_version":"73"},"74":{"release_date":"2019-04-24","release_notes":"https://chromereleases.googleblog.com/2019/04/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"74"},"75":{"release_date":"2019-06-04","release_notes":"https://chromereleases.googleblog.com/2019/06/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"75"},"76":{"release_date":"2019-07-30","release_notes":"https://chromereleases.googleblog.com/2019/07/chrome-for-android-update_30.html","status":"retired","engine":"Blink","engine_version":"76"},"77":{"release_date":"2019-09-10","release_notes":"https://chromereleases.googleblog.com/2019/09/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"77"},"78":{"release_date":"2019-10-22","release_notes":"https://chromereleases.googleblog.com/2019/10/chrome-for-android-update_22.html","status":"retired","engine":"Blink","engine_version":"78"},"79":{"release_date":"2019-12-17","release_notes":"https://chromereleases.googleblog.com/2019/12/chrome-for-android-update_17.html","status":"retired","engine":"Blink","engine_version":"79"},"80":{"release_date":"2020-02-04","release_notes":"https://chromereleases.googleblog.com/2020/02/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"80"},"81":{"release_date":"2020-04-07","release_notes":"https://chromereleases.googleblog.com/2020/04/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"81"},"83":{"release_date":"2020-05-19","release_notes":"https://chromereleases.googleblog.com/2020/05/chrome-for-android-update_19.html","status":"retired","engine":"Blink","engine_version":"83"},"84":{"release_date":"2020-07-27","release_notes":"https://chromereleases.googleblog.com/2020/07/chrome-for-android-update_27.html","status":"retired","engine":"Blink","engine_version":"84"},"85":{"release_date":"2020-08-25","release_notes":"https://chromereleases.googleblog.com/2020/08/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"85"},"86":{"release_date":"2020-10-20","release_notes":"https://chromereleases.googleblog.com/2020/10/chrome-for-android-update_20.html","status":"retired","engine":"Blink","engine_version":"86"},"87":{"release_date":"2020-11-17","release_notes":"https://chromereleases.googleblog.com/2020/11/chrome-for-android-update_17.html","status":"retired","engine":"Blink","engine_version":"87"},"88":{"release_date":"2021-01-19","release_notes":"https://chromereleases.googleblog.com/2021/01/chrome-for-android-update_19.html","status":"retired","engine":"Blink","engine_version":"88"},"89":{"release_date":"2021-03-02","release_notes":"https://chromereleases.googleblog.com/2021/03/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"89"},"90":{"release_date":"2021-04-13","release_notes":"https://chromereleases.googleblog.com/2021/04/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"90"},"91":{"release_date":"2021-05-25","release_notes":"https://chromereleases.googleblog.com/2021/05/chrome-for-android-update_01607414128.html","status":"retired","engine":"Blink","engine_version":"91"},"92":{"release_date":"2021-07-20","release_notes":"https://chromereleases.googleblog.com/2021/07/chrome-for-android-update_01500789893.html","status":"retired","engine":"Blink","engine_version":"92"},"93":{"release_date":"2021-08-31","release_notes":"https://chromereleases.googleblog.com/2021/08/chrome-for-android-update_0881967577.html","status":"retired","engine":"Blink","engine_version":"93"},"94":{"release_date":"2021-09-21","release_notes":"https://chromereleases.googleblog.com/2021/09/chrome-for-android-update_21.html","status":"current","engine":"Blink","engine_version":"94"},"95":{"release_date":"2021-10-19","status":"beta","engine":"Blink","engine_version":"95"},"96":{"release_date":"2021-11-16","status":"nightly","engine":"Blink","engine_version":"96"},"1.5":{"release_date":"2009-04-27","release_notes":"https://en.wikipedia.org/wiki/Android_Cupcake","status":"retired","engine":"WebKit","engine_version":"525.20"},"2.2":{"release_date":"2010-05-20","release_notes":"https://en.wikipedia.org/wiki/Android_Froyo","status":"retired","engine":"WebKit","engine_version":"533.1"},"4.4":{"release_date":"2013-12-09","release_notes":"https://chromereleases.googleblog.com/2013/10/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"30"},"4.4.3":{"release_date":"2014-06-02","release_notes":"https://chromereleases.googleblog.com/2014/02/chrome-for-android-update.html","status":"retired","engine":"Blink","engine_version":"33"}}}} \ No newline at end of file diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/shared/compatibility/dataset/css-properties.json firefox-trunk-95.0~a1~hg20211020r596404/devtools/shared/compatibility/dataset/css-properties.json --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/shared/compatibility/dataset/css-properties.json 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/shared/compatibility/dataset/css-properties.json 2021-10-20 19:28:16.000000000 +0000 @@ -1 +1 @@ -{"-moz-binding":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-moz-binding","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1","version_removed":"67","notes":["XBL is deprecated and being removed. See bug 1397874.","Available only in chrome and UA style sheets."]},"firefox_android":{"version_added":"4","version_removed":"67","notes":"Available only in chrome and UA style sheets."},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"-moz-context-properties":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-moz-context-properties","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"55","flags":[{"type":"preference","name":"svg.context-properties.content.enabled","value_to_set":"true"}],"notes":"With the preference set to false, the property still works with SVGs via chrome:// or resource:// URLs"},"firefox_android":{"version_added":"55","flags":[{"type":"preference","name":"svg.context-properties.content.enabled","value_to_set":"true"}],"notes":"With the preference set to false, the property still works with SVGs via chrome:// or resource:// URLs"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":false,"deprecated":false}}},"-moz-float-edge":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-moz-float-edge","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"-moz-force-broken-image-icon":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-moz-force-broken-image-icon","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"-moz-image-region":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-moz-image-region","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-moz-orient":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-moz-orient","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"6"},"firefox_android":{"version_added":"6"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":false}},"auto":{"__compat":{"description":"auto value","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"21","version_removed":"40","notes":"The auto value was equivalent to horizontal."},"firefox_android":{"version_added":"21","version_removed":"40","notes":"The auto value was equivalent to horizontal."},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"inline_and_block":{"__compat":{"description":"inline and block values","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"40"},"firefox_android":{"version_added":"40"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}}},"-moz-outline-radius-bottomleft":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-moz-outline-radius-bottomleft","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1","version_removed":"88","notes":"From Firefox 88, outline now follows the shape created by border-radius automatically, so this property is no longer needed (see bug 1694146)."},"firefox_android":{"version_added":"4","version_removed":"88","notes":"From Firefox 88, outline now follows the shape created by border-radius automatically, so this property is no longer needed (see bug 1694146)."},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"-moz-outline-radius-bottomright":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-moz-outline-radius-bottomright","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1","version_removed":"88","notes":"From Firefox 88, outline now follows the shape created by border-radius automatically, so this property is no longer needed (see bug 1694146)."},"firefox_android":{"version_added":"4","version_removed":"88","notes":"From Firefox 88, outline now follows the shape created by border-radius automatically, so this property is no longer needed (see bug 1694146)."},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"-moz-outline-radius-topleft":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-moz-outline-radius-topleft","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1","version_removed":"88","notes":"From Firefox 88, outline now follows the shape created by border-radius automatically, so this property is no longer needed (see bug 1694146)."},"firefox_android":{"version_added":"4","version_removed":"88","notes":"From Firefox 88, outline now follows the shape created by border-radius automatically, so this property is no longer needed (see bug 1694146)."},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"-moz-outline-radius-topright":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-moz-outline-radius-topright","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1","version_removed":"88","notes":"From Firefox 88, outline now follows the shape created by border-radius automatically, so this property is no longer needed (see bug 1694146)."},"firefox_android":{"version_added":"4","version_removed":"88","notes":"From Firefox 88, outline now follows the shape created by border-radius automatically, so this property is no longer needed (see bug 1694146)."},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"-moz-outline-radius":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-moz-outline-radius","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1","version_removed":"88","notes":"From Firefox 88, outline now follows the shape created by border-radius automatically, so this property is no longer needed (see bug 1694146)."},"firefox_android":{"version_added":"4","version_removed":"88","notes":"From Firefox 88, outline now follows the shape created by border-radius automatically, so this property is no longer needed (see bug 1694146)."},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"-moz-user-focus":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-moz-user-focus","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-moz-user-input":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-moz-user-input","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}},"auto":{"__compat":{"support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"disabled":{"__compat":{"support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1","version_removed":"60"},"firefox_android":{"version_added":"4","version_removed":"60"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"enabled":{"__compat":{"support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1","version_removed":"60"},"firefox_android":{"version_added":"4","version_removed":"60"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"none":{"__compat":{"support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}}},"-ms-grid-column-align":{"__compat":{"support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"16"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":"10"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"-ms-grid-column-span":{"__compat":{"support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"16"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":"10"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"-ms-grid-column":{"__compat":{"support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"16"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":"10"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"-ms-grid-row-align":{"__compat":{"support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"16"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":"10"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"-ms-grid-row-span":{"__compat":{"support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"16"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":"10"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"-ms-grid-row":{"__compat":{"support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"16"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":"10"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"-ms-scrollbar-track-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-ms-scrollbar-track-color","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":[{"version_added":"5","alternative_name":"scrollbar-track-color"},{"version_added":"8"}],"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"deprecated":true,"experimental":false,"standard_track":false}}},"-webkit-border-before":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-border-before","support":{"chrome":{"version_added":"8"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"15"},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-box-reflect":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-box-reflect","support":{"chrome":{"version_added":"4"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"4"},"safari_ios":{"version_added":"3.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2.2"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-line-clamp":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-line-clamp","spec_url":"https://drafts.csswg.org/css-overflow/#webkit-line-clamp","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"17"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"-webkit-mask-attachment":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-mask-attachment","support":{"chrome":{"version_added":"1","version_removed":"24"},"chrome_android":{"version_added":"18","version_removed":"25"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"4","version_removed":"7"},"safari_ios":{"version_added":"3.2","version_removed":"7"},"samsunginternet_android":{"version_added":"1.0","version_removed":"1.5"},"webview_android":{"version_added":"2","version_removed":"4.4"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-mask-box-image":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-mask-box-image","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"18"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3.1"},"safari_ios":{"version_added":"2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-mask-composite":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-mask-composite","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"18"},"firefox":{"version_added":"53"},"firefox_android":{"version_added":"53"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3.1"},"safari_ios":{"version_added":"2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-mask-position-x":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-mask-position-x","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"18"},"firefox":{"version_added":"49"},"firefox_android":{"version_added":"49"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3.1"},"safari_ios":{"version_added":"2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-mask-position-y":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-mask-position-y","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"18"},"firefox":{"version_added":"49"},"firefox_android":{"version_added":"49"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3.1"},"safari_ios":{"version_added":"2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-mask-repeat-x":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-mask-repeat-x","support":{"chrome":{"version_added":"3"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"18"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5","version_removed":"15"},"safari_ios":{"version_added":"5","version_removed":"15"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-mask-repeat-y":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-mask-repeat-y","support":{"chrome":{"version_added":"3"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"18"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"15"},"safari":{"version_added":"5","version_removed":"15"},"safari_ios":{"version_added":"5","version_removed":"15"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-overflow-scrolling":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-overflow-scrolling","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":"5","version_removed":"13"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-print-color-adjust":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-print-color-adjust","support":{"chrome":{"version_added":"17","notes":["Chrome does not print backgrounds of the <body> element. If this property is set to exact for the <body> element, it will apply only to its descendants.","Before Chrome 26, if background images are clipped (for example, when using background-image sprites) and -webkit-print-color-adjust is set to exact, then backgrounds will appear distorted when printed. Solid backgrounds and background images that are not clipped (i.e., backgrounds that have narrower and shorter than the element to which they are applied) are printed correctly. See Chromium bug 131054."]},"chrome_android":{"version_added":"18","notes":["Chrome does not print backgrounds of the <body> element. If this property is set to exact for the <body> element, it will apply only to its descendants.","Before Chrome 26, if background images are clipped (for example, when using background-image sprites) and -webkit-print-color-adjust is set to exact, then backgrounds will appear distorted when printed. Solid backgrounds and background images that are not clipped (i.e., backgrounds that have narrower and shorter than the element to which they are applied) are printed correctly. See Chromium bug 131054."]},"edge":{"version_added":"79","notes":"Edge does not print backgrounds of the <body> element. If this property is set to exact for the <body> element, it will apply only to its descendants."},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","notes":"Opera does not print backgrounds of the <body> element. If this property is set to exact for the <body> element, it will apply only to its descendants."},"opera_android":{"version_added":"15","notes":"Opera does not print backgrounds of the <body> element. If this property is set to exact for the <body> element, it will apply only to its descendants."},"safari":{"version_added":"6","notes":"Safari does not print backgrounds of the <body> element. If this property is set to exact for the <body> element, it will apply only to its descendants."},"safari_ios":{"version_added":"6","notes":"Safari does not print backgrounds of the <body> element. If this property is set to exact for the <body> element, it will apply only to its descendants."},"samsunginternet_android":{"version_added":"1.0","notes":["Samsung Internet does not print backgrounds of the <body> element. If this property is set to exact for the <body> element, it will apply only to its descendants.","Before Chrome 26, if background images are clipped (for example, when using background-image sprites) and -webkit-print-color-adjust is set to exact, then backgrounds will appear distorted when printed. Solid backgrounds and background images that are not clipped (i.e., backgrounds that have narrower and shorter than the element to which they are applied) are printed correctly. See Chromium bug 131054."]},"webview_android":{"version_added":"37","notes":"WebView does not print backgrounds of the <body> element. If this property is set to exact for the <body> element, it will apply only to its descendants."}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-tap-highlight-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-tap-highlight-color","support":{"chrome":{"version_added":"16"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":false},"safari_ios":{"version_added":"4"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-text-fill-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-text-fill-color","spec_url":"https://compat.spec.whatwg.org/#the-webkit-text-fill-color","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"49"},"firefox_android":{"version_added":"49"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"15"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-text-security":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-text-security","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-text-stroke-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-text-stroke-color","spec_url":"https://compat.spec.whatwg.org/#the-webkit-text-stroke-color","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"15"},"firefox":{"version_added":"49"},"firefox_android":{"version_added":"49"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"15"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-text-stroke-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-text-stroke-width","spec_url":"https://compat.spec.whatwg.org/#the-webkit-text-stroke-width","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"15"},"firefox":{"version_added":"49"},"firefox_android":{"version_added":"49"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"15"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"38"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-text-stroke":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-text-stroke","spec_url":"https://compat.spec.whatwg.org/#the-webkit-text-stroke","support":{"chrome":{"version_added":"4"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"15"},"firefox":{"version_added":"49"},"firefox_android":{"version_added":"49"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"4"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-touch-callout":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-touch-callout","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":"2"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"accent-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/accent-color","spec_url":"https://drafts.csswg.org/css-ui/#widget-accent","support":{"chrome":[{"version_added":"93"},{"version_added":"91","version_removed":"93","flags":[{"name":"#enable-experimental-web-platform-features","type":"preference","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"93"},{"version_added":"91","version_removed":"93","flags":[{"name":"#enable-experimental-web-platform-features","type":"preference","value_to_set":"enabled"}]}],"edge":[{"version_added":"93"},{"version_added":"91","version_removed":"93","flags":[{"name":"#enable-experimental-web-platform-features","type":"preference","value_to_set":"enabled"}]}],"firefox":[{"version_added":"92"},{"version_added":"90","version_removed":"92","flags":[{"type":"preference","name":"layout.css.accent-color.enabled","value_to_set":"enabled"}],"notes":"Enabled by default in Firefox Nightly."}],"firefox_android":{"version_added":"92"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":"93"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"align-content":{"flex_context":{"__compat":{"description":"Supported in Flex Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/align-content","spec_url":["https://drafts.csswg.org/css-align/#align-justify-content","https://drafts.csswg.org/css-flexbox/#align-content-property"],"support":{"chrome":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"21"}],"chrome_android":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"25"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"28"},{"prefix":"-webkit-","version_added":"49"}],"firefox_android":[{"version_added":"28"},{"prefix":"-webkit-","version_added":"49"}],"ie":{"version_added":"11"},"opera":{"version_added":"12.1"},"opera_android":{"version_added":"12.1"},"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":[{"version_added":"2.0"},{"prefix":"-webkit-","version_added":"1.5"}],"webview_android":[{"version_added":"4.4"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"baseline":{"__compat":{"description":"baseline","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"first_last_baseline":{"__compat":{"description":"first baseline and last baseline","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"left_right":{"__compat":{"description":"left and right","support":{"chrome":{"version_added":"93"},"chrome_android":{"version_added":"93"},"edge":{"version_added":"93"},"firefox":{"version_added":"52","version_removed":"60","notes":"align-content no longer accepts the left and right values"},"firefox_android":{"version_added":"52","version_removed":"60","notes":"align-content no longer accepts the left and right values"},"ie":{"version_added":false},"opera":{"version_added":false,"notes":"This value is recognized, but has no effect."},"opera_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"webview_android":{"version_added":"93"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"safe_unsafe":{"__compat":{"description":"safe and unsafe","support":{"chrome":{"version_added":false,"notes":"This value is recognized, but has no effect."},"chrome_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"edge":{"version_added":false,"notes":"This value is recognized, but has no effect."},"firefox":{"version_added":"63"},"firefox_android":{"version_added":"63"},"ie":{"version_added":false},"opera":{"version_added":false,"notes":"This value is recognized, but has no effect."},"opera_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"safari":{"version_added":false,"notes":"This value is recognized, but has no effect."},"safari_ios":{"version_added":false,"notes":"This value is recognized, but has no effect."},"samsunginternet_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"webview_android":{"version_added":false,"notes":"This value is recognized, but has no effect."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"space-evenly":{"__compat":{"description":"space-evenly","support":{"chrome":{"version_added":"60"},"chrome_android":{"version_added":"60"},"edge":{"version_added":"79"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"47"},"opera_android":{"version_added":"44"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"8.0"},"webview_android":{"version_added":"60"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"start_end":{"__compat":{"description":"start and end","support":{"chrome":{"version_added":"93"},"chrome_android":{"version_added":"93"},"edge":{"version_added":"93"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":false,"notes":"This value is recognized, but has no effect."},"opera_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"webview_android":{"version_added":"93"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"stretch":{"__compat":{"description":"stretch","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"grid_context":{"__compat":{"description":"Supported in Grid Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/align-content","spec_url":["https://drafts.csswg.org/css-align/#align-justify-content","https://drafts.csswg.org/css-flexbox/#align-content-property"],"support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"52"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.2"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"align-items":{"flex_context":{"__compat":{"description":"Supported in Flex Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/align-items","spec_url":["https://drafts.csswg.org/css-align/#align-items-property","https://drafts.csswg.org/css-flexbox/#align-items-property"],"support":{"chrome":[{"version_added":"52"},{"version_added":"29","partial_implementation":true,"notes":"Older versions of the specification treat absolute positioned children as though they are a 0 by 0 flex item. Later specification versions take the children out of the flow and set their positions based on align and justify properties. Chrome implements the new behavior beginning with Chrome 52."},{"prefix":"-webkit-","version_added":"21"}],"chrome_android":[{"version_added":"52"},{"version_added":"29","partial_implementation":true,"notes":"Older versions of the specification treat absolute positioned children as though they are a 0 by 0 flex item. Later specification versions take the children out of the flow and set their positions based on align and justify properties. Chrome implements the new behavior beginning with Chrome 52."},{"prefix":"-webkit-","version_added":"25"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"20","notes":"Multi-line flexbox has been supported since Firefox 28."},{"prefix":"-webkit-","version_added":"49"}],"firefox_android":[{"version_added":"20","notes":"Multi-line flexbox has been supported since Firefox 28."},{"prefix":"-webkit-","version_added":"49"}],"ie":{"version_added":"11","notes":"In Internet Explorer 10 and 11, if column flex items have align-items: center; set on them and their content is too large, then they will overflow the bounds of their container. See Flexbug #2."},"opera":{"version_added":"12.1"},"opera_android":{"version_added":"12.1"},"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":[{"version_added":"6.0"},{"version_added":"2.0","partial_implementation":true,"notes":"Older versions of the specification treat absolute positioned children as though they are a 0 by 0 flex item. Later specification versions take the children out of the flow and set their positions based on align and justify properties. Chrome implements the new behavior beginning with Samsung Internet 6.0."},{"prefix":"-webkit-","version_added":"1.5"}],"webview_android":[{"version_added":"52"},{"version_added":"4.4","partial_implementation":true,"notes":"Older versions of the specification treat absolute positioned children as though they are a 0 by 0 flex item. Later specification versions take the children out of the flow and set their positions based on align and justify properties. Chrome implements the new behavior beginning with Chrome 52."},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"first_last_baseline":{"__compat":{"description":"first baseline and last baseline","support":{"chrome":{"version_added":"59"},"chrome_android":{"version_added":"59"},"edge":{"version_added":"79"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":"46"},"opera_android":{"version_added":"43"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"59"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"left_right":{"__compat":{"description":"left and right","support":{"chrome":{"version_added":"93"},"chrome_android":{"version_added":"93"},"edge":{"version_added":"93"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":"93"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"safe_unsafe":{"__compat":{"description":"safe and unsafe","support":{"chrome":{"version_added":false,"notes":"This value is recognized, but has no effect."},"chrome_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"edge":{"version_added":false,"notes":"This value is recognized, but has no effect."},"firefox":{"version_added":"63"},"firefox_android":{"version_added":"63"},"ie":{"version_added":false},"opera":{"version_added":false,"notes":"This value is recognized, but has no effect."},"opera_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"safari":{"version_added":false,"notes":"This value is recognized, but has no effect."},"safari_ios":{"version_added":false,"notes":"This value is recognized, but has no effect."},"samsunginternet_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"webview_android":{"version_added":false,"notes":"This value is recognized, but has no effect."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"start_end":{"__compat":{"description":"start and end","support":{"chrome":{"version_added":"93"},"chrome_android":{"version_added":"93"},"edge":{"version_added":"93"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":"93"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"grid_context":{"__compat":{"description":"Supported in Grid Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/align-items","spec_url":["https://drafts.csswg.org/css-align/#align-items-property","https://drafts.csswg.org/css-flexbox/#align-items-property"],"support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"52"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.2"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"start_end":{"__compat":{"description":"start and end","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}}},"align-self":{"flex_context":{"__compat":{"description":"Supported in Flex Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/align-self","spec_url":["https://drafts.csswg.org/css-align/#align-self-property","https://drafts.csswg.org/css-flexbox/#align-items-property"],"support":{"chrome":[{"version_added":"36"},{"version_added":"29","partial_implementation":true,"notes":"Older versions of the specification treat absolute positioned children as though they are a 0 by 0 flex item. Later specification versions take the children out of the flow and set their positions based on align and justify properties. Chrome implements the new behavior beginning with Chrome 52."},{"prefix":"-webkit-","version_added":"21"}],"chrome_android":[{"version_added":"36"},{"version_added":"29","partial_implementation":true,"notes":"Older versions of the specification treat absolute positioned children as though they are a 0 by 0 flex item. Later specification versions take the children out of the flow and set their positions based on align and justify properties. Chrome implements the new behavior beginning with Chrome 52."},{"prefix":"-webkit-","version_added":"25"}],"edge":{"version_added":"12"},"firefox":[{"version_added":"20","notes":"Before Firefox 27, only single-line flexbox is supported."},{"prefix":"-webkit-","version_added":"49"}],"firefox_android":[{"version_added":"20","notes":"Before Firefox 27, only single-line flexbox is supported."},{"prefix":"-webkit-","version_added":"49"}],"ie":{"version_added":"11"},"opera":{"version_added":"12.1"},"opera_android":{"version_added":"12.1"},"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"6.1"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"6.1"}],"samsunginternet_android":[{"version_added":"3.0"},{"version_added":"2.0","partial_implementation":true,"notes":"Older versions of the specification treat absolute positioned children as though they are a 0 by 0 flex item. Later specification versions take the children out of the flow and set their positions based on align and justify properties. Chrome implements the new behavior beginning with Samsung Internet 6.0."},{"prefix":"-webkit-","version_added":"1.5"}],"webview_android":[{"version_added":"37"},{"version_added":"4.4","partial_implementation":true,"notes":"Older versions of the specification treat absolute positioned children as though they are a 0 by 0 flex item. Later specification versions take the children out of the flow and set their positions based on align and justify properties. Chrome implements the new behavior beginning with Chrome 52."},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"baseline":{"__compat":{"description":"baseline","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"first_last_baseline":{"__compat":{"description":"first baseline and last baseline","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"11.1"},"safari_ios":{"version_added":"11.3"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"left_right":{"__compat":{"description":"left and right","support":{"chrome":{"version_added":"93"},"chrome_android":{"version_added":"93"},"edge":{"version_added":"93"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":"93"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"safe_unsafe":{"__compat":{"description":"safe and unsafe","support":{"chrome":{"version_added":false,"notes":"This value is recognized, but has no effect."},"chrome_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"edge":{"version_added":false,"notes":"This value is recognized, but has no effect."},"firefox":{"version_added":"63"},"firefox_android":{"version_added":"63"},"ie":{"version_added":false},"opera":{"version_added":false,"notes":"This value is recognized, but has no effect."},"opera_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"safari":{"version_added":false,"notes":"This value is recognized, but has no effect."},"safari_ios":{"version_added":false,"notes":"This value is recognized, but has no effect."},"samsunginternet_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"webview_android":{"version_added":false,"notes":"This value is recognized, but has no effect."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"start_end":{"__compat":{"description":"start and end","support":{"chrome":{"version_added":"93"},"chrome_android":{"version_added":"93"},"edge":{"version_added":"93"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"93"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"stretch":{"__compat":{"description":"stretch","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"grid_context":{"__compat":{"description":"Supported in Grid Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/align-self","spec_url":["https://drafts.csswg.org/css-align/#align-self-property","https://drafts.csswg.org/css-flexbox/#align-items-property"],"support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"52"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"prefix":"-ms-","version_added":"10","partial_implementation":true,"notes":"Internet Explorer 10 and 11 have the property -ms-grid-row-align, which acts in a similar way to align-self."},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.2"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"align-tracks":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/align-tracks","spec_url":"https://drafts.csswg.org/css-grid-3/#tracks-alignment","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"77","flags":[{"type":"preference","name":"layout.css.grid-template-masonry-value.enabled","value_to_set":"true"}]},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"all":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/all","spec_url":"https://drafts.csswg.org/css-cascade/#all-shorthand","support":{"chrome":{"version_added":"37"},"chrome_android":{"version_added":"37"},"edge":{"version_added":"79"},"firefox":{"version_added":"27"},"firefox_android":{"version_added":"27"},"ie":{"version_added":false},"opera":{"version_added":"24"},"opera_android":{"version_added":"24"},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":"3.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"revert":{"__compat":{"description":"revert","support":{"chrome":{"version_added":"84"},"chrome_android":{"version_added":"84"},"edge":{"version_added":"84"},"firefox":{"version_added":"67"},"firefox_android":{"version_added":"67"},"ie":{"version_added":false},"opera":{"version_added":"70"},"opera_android":{"version_added":false},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"84"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"alt":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/alt","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"8"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"8"}],"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"animation-delay":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/animation-delay","spec_url":"https://drafts.csswg.org/css-animations/#animation-delay","support":{"chrome":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"3"}],"chrome_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"18"}],"edge":{"version_added":"12"},"firefox":[{"version_added":"16","notes":"Before Firefox 57, Firefox does not repaint elements outside the viewport that are animated into the viewport with a delay. This bug affects only some platforms, such as Windows."},{"version_added":"49","prefix":"-webkit-"},{"prefix":"-moz-","version_added":"5"}],"firefox_android":[{"version_added":"16"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-moz-","version_added":"5"}],"ie":{"version_added":"10"},"opera":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"15"},{"version_added":"12.1","version_removed":"15"},{"prefix":"-o-","version_added":"12","version_removed":"15"}],"opera_android":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"14"},{"version_added":"12.1","version_removed":"14"},{"prefix":"-o-","version_added":"12","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"4"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3.2"}],"samsunginternet_android":[{"version_added":"4.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"animation-direction":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/animation-direction","spec_url":"https://drafts.csswg.org/css-animations/#animation-direction","support":{"chrome":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"3"}],"chrome_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"version_added":"49","prefix":"-webkit-"},{"prefix":"-moz-","version_added":"5"}],"firefox_android":[{"version_added":"16"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-moz-","version_added":"5"}],"ie":{"version_added":"10"},"opera":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"15"},{"version_added":"12.1","version_removed":"15"},{"prefix":"-o-","version_added":"12","version_removed":"15"}],"opera_android":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"14"},{"version_added":"12.1","version_removed":"14"},{"prefix":"-o-","version_added":"12","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"4"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3.2"}],"samsunginternet_android":[{"version_added":"4.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"alternate-reverse":{"__compat":{"description":"alternate-reverse","support":{"chrome":{"version_added":"19"},"chrome_android":{"version_added":"25"},"edge":{"version_added":"12"},"firefox":{"version_added":"16"},"firefox_android":{"version_added":"16"},"ie":{"version_added":"10"},"opera":{"version_added":"12.1"},"opera_android":{"version_added":"12.1"},"safari":{"version_added":"6"},"safari_ios":{"version_added":"6"},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"reverse":{"__compat":{"description":"reverse","support":{"chrome":{"version_added":"19"},"chrome_android":{"version_added":"25"},"edge":{"version_added":"12"},"firefox":{"version_added":"16"},"firefox_android":{"version_added":"16"},"ie":{"version_added":"10"},"opera":{"version_added":"12.1"},"opera_android":{"version_added":"12.1"},"safari":{"version_added":"6"},"safari_ios":{"version_added":"6"},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"animation-duration":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/animation-duration","spec_url":"https://drafts.csswg.org/css-animations/#animation-duration","support":{"chrome":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"3"}],"chrome_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"version_added":"49","prefix":"-webkit-"},{"prefix":"-moz-","version_added":"5"}],"firefox_android":[{"version_added":"16"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-moz-","version_added":"5"}],"ie":{"version_added":"10","partial_implementation":true,"notes":"Once the element has loaded, changing the value of this property has no effect."},"opera":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"15"},{"version_added":"12.1","version_removed":"15"},{"prefix":"-o-","version_added":"12","version_removed":"15"}],"opera_android":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"14"},{"version_added":"12.1","version_removed":"14"},{"prefix":"-o-","version_added":"12","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"4"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"4.2"}],"samsunginternet_android":[{"version_added":"4.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"2"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"animation-fill-mode":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/animation-fill-mode","spec_url":"https://drafts.csswg.org/css-animations/#animation-fill-mode","support":{"chrome":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"3"}],"chrome_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"version_added":"49","prefix":"-webkit-"},{"prefix":"-moz-","version_added":"5"}],"firefox_android":[{"version_added":"16"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-moz-","version_added":"5"}],"ie":{"version_added":"10"},"opera":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"15"},{"version_added":"12.1","version_removed":"15"},{"prefix":"-o-","version_added":"12","version_removed":"15"}],"opera_android":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"14"},{"version_added":"12.1","version_removed":"14"},{"prefix":"-o-","version_added":"12","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"5"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"4"}],"samsunginternet_android":[{"version_added":"4.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"animation-iteration-count":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/animation-iteration-count","spec_url":"https://drafts.csswg.org/css-animations/#animation-iteration-count","support":{"chrome":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"3"}],"chrome_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"version_added":"49","prefix":"-webkit-"},{"prefix":"-moz-","version_added":"5"}],"firefox_android":[{"version_added":"16"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-moz-","version_added":"5"}],"ie":{"version_added":"10"},"opera":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"15"},{"version_added":"12.1","version_removed":"15"},{"prefix":"-o-","version_added":"12","version_removed":"15"}],"opera_android":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"14"},{"version_added":"12.1","version_removed":"14"},{"prefix":"-o-","version_added":"12","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"4"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3.2"}],"samsunginternet_android":[{"version_added":"4.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"animation-name":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/animation-name","spec_url":"https://drafts.csswg.org/css-animations/#animation-name","support":{"chrome":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"3"}],"chrome_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-moz-","version_added":"5"}],"firefox_android":[{"version_added":"16"},{"version_added":"49","prefix":"-webkit-"},{"prefix":"-moz-","version_added":"5"}],"ie":{"version_added":"10"},"opera":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"15"},{"version_added":"12.1","version_removed":"15"},{"prefix":"-o-","version_added":"12","version_removed":"15"}],"opera_android":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"14"},{"version_added":"12.1","version_removed":"14"},{"prefix":"-o-","version_added":"12","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"4"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3.2"}],"samsunginternet_android":[{"version_added":"4.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"animation-play-state":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/animation-play-state","spec_url":"https://drafts.csswg.org/css-animations/#animation-play-state","support":{"chrome":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"3"}],"chrome_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-moz-","version_added":"5"}],"firefox_android":[{"version_added":"16"},{"version_added":"49","prefix":"-webkit-"},{"prefix":"-moz-","version_added":"5"}],"ie":{"version_added":"10"},"opera":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"15"},{"version_added":"12.1","version_removed":"15"},{"prefix":"-o-","version_added":"12","version_removed":"15"}],"opera_android":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"14"},{"version_added":"12.1","version_removed":"14"},{"prefix":"-o-","version_added":"12","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"4"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"2"}],"samsunginternet_android":[{"version_added":"4.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"animation-timing-function":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/animation-timing-function","spec_url":"https://drafts.csswg.org/css-animations/#animation-timing-function","support":{"chrome":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"3"}],"chrome_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-moz-","version_added":"5"}],"firefox_android":[{"version_added":"16"},{"version_added":"49","prefix":"-webkit-"},{"prefix":"-moz-","version_added":"5"}],"ie":{"version_added":"10"},"opera":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"15"},{"version_added":"12.1","version_removed":"15"},{"prefix":"-o-","version_added":"12","version_removed":"15"}],"opera_android":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"14"},{"version_added":"12.1","version_removed":"14"},{"prefix":"-o-","version_added":"12","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"4"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3.2"}],"samsunginternet_android":[{"version_added":"4.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"jump":{"__compat":{"description":"jump- keywords for steps()","support":{"chrome":{"version_added":"77"},"chrome_android":{"version_added":"77"},"edge":{"version_added":"79"},"firefox":{"version_added":"65"},"firefox_android":{"version_added":"65"},"ie":{"version_added":false},"opera":{"version_added":"64"},"opera_android":{"version_added":"55"},"safari":{"version_added":"14"},"safari_ios":{"version_added":"14"},"samsunginternet_android":{"version_added":"12.0"},"webview_android":{"version_added":"77"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"animation":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/animation","spec_url":"https://drafts.csswg.org/css-animations/#animation","support":{"chrome":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"3"}],"chrome_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-moz-","version_added":"5"}],"firefox_android":[{"version_added":"16"},{"version_added":"49","prefix":"-webkit-"},{"prefix":"-moz-","version_added":"5"}],"ie":{"version_added":"10"},"opera":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"15"},{"version_added":"12.1","version_removed":"15"},{"prefix":"-o-","version_added":"12","version_removed":"15"}],"opera_android":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"14"},{"version_added":"12.1","version_removed":"14"},{"prefix":"-o-","version_added":"12","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"4"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3.2"}],"samsunginternet_android":[{"version_added":"4.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"2","notes":"The animation-fill-mode property is not supported in Android browsers below 2.3."}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"appearance":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/appearance","spec_url":"https://drafts.csswg.org/css-ui/#appearance-switching","support":{"chrome":[{"version_added":"84"},{"version_added":"1","partial_implementation":true,"prefix":"-webkit-"}],"chrome_android":[{"version_added":"84"},{"version_added":"18","partial_implementation":true,"prefix":"-webkit-"}],"edge":[{"version_added":"84"},{"version_added":"12","partial_implementation":true,"prefix":"-webkit-"}],"firefox":[{"version_added":"80"},{"version_added":"1","partial_implementation":true,"prefix":"-moz-"},{"version_added":"64","partial_implementation":true,"prefix":"-webkit-"}],"firefox_android":[{"version_added":"80"},{"version_added":"4","partial_implementation":true,"prefix":"-moz-"},{"version_added":"64","partial_implementation":true,"prefix":"-webkit-"}],"ie":{"version_added":false},"opera":[{"version_added":"70"},{"version_added":"15","partial_implementation":true,"prefix":"-webkit-"}],"opera_android":[{"version_added":"60"},{"version_added":"14","partial_implementation":true,"prefix":"-webkit-"}],"safari":{"version_added":"3","partial_implementation":true,"prefix":"-webkit-"},"safari_ios":{"version_added":"1","partial_implementation":true,"prefix":"-webkit-"},"samsunginternet_android":{"version_added":"1.0","partial_implementation":true,"prefix":"-webkit-"},"webview_android":[{"version_added":"84"},{"version_added":"1","partial_implementation":true,"prefix":"-webkit-"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"auto":{"__compat":{"support":{"chrome":{"version_added":"83"},"chrome_android":{"version_added":"83"},"edge":{"version_added":"83"},"firefox":{"version_added":"80"},"firefox_android":{"version_added":"80"},"ie":{"version_added":false},"opera":{"version_added":"69"},"opera_android":{"version_added":"59"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"13.0"},"webview_android":{"version_added":"83"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"compat-auto":{"__compat":{"description":"<compat-auto> (compatibility values searchfield, textarea, push-button, slider-horizontal, checkbox, radio, square-button, menulist, listbox, meter, progress-bar, button)","support":{"chrome":[{"version_added":"83"},{"version_added":"1","partial_implementation":true}],"chrome_android":[{"version_added":"83"},{"version_added":"18","partial_implementation":true}],"edge":[{"version_added":"83"},{"version_added":"12","partial_implementation":true}],"firefox":[{"version_added":"80"},{"version_added":"1","partial_implementation":true}],"firefox_android":[{"version_added":"80"},{"version_added":"4","partial_implementation":true}],"ie":{"version_added":false},"opera":[{"version_added":"69"},{"version_added":"15","partial_implementation":true}],"opera_android":[{"version_added":"59"},{"version_added":"14","partial_implementation":true}],"safari":{"version_added":"3","partial_implementation":true},"safari_ios":{"version_added":"1","partial_implementation":true},"samsunginternet_android":{"version_added":"1.0","partial_implementation":true},"webview_android":[{"version_added":"83"},{"version_added":"1","partial_implementation":true}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"menulist-button":{"__compat":{"description":"menulist-button","support":{"chrome":[{"version_added":"83"},{"version_added":"1","partial_implementation":true}],"chrome_android":[{"version_added":"83"},{"version_added":"18","partial_implementation":true}],"edge":[{"version_added":"83"},{"version_added":"12","partial_implementation":true}],"firefox":[{"version_added":"80"},{"version_added":"1","partial_implementation":true,"notes":"See bug 1481615."}],"firefox_android":[{"version_added":"80"},{"version_added":"4","partial_implementation":true,"notes":"See bug 1481615."}],"ie":{"version_added":false},"opera":[{"version_added":"69"},{"version_added":"15","partial_implementation":true}],"opera_android":[{"version_added":"59"},{"version_added":"14","partial_implementation":true}],"safari":{"version_added":"3","partial_implementation":true},"safari_ios":{"version_added":"1","partial_implementation":true},"samsunginternet_android":{"version_added":"1.0","partial_implementation":true},"webview_android":[{"version_added":"83"},{"version_added":"1","partial_implementation":true}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"none":{"__compat":{"support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":[{"version_added":"54"},{"version_added":"1","partial_implementation":true,"notes":"Doesn't work with <input type=\"checkbox\"> and <input type=\"radio\">."}],"firefox_android":[{"version_added":"54"},{"version_added":"4","partial_implementation":true,"notes":"Doesn't work with <input type=\"checkbox\"> and <input type=\"radio\">."}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"3"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"textfield":{"__compat":{"support":{"chrome":[{"version_added":"83"},{"version_added":"1","partial_implementation":true}],"chrome_android":[{"version_added":"83"},{"version_added":"18","partial_implementation":true}],"edge":[{"version_added":"83"},{"version_added":"12","partial_implementation":true}],"firefox":[{"version_added":"80"},{"version_added":"1","partial_implementation":true}],"firefox_android":[{"version_added":"80"},{"version_added":"4","partial_implementation":true}],"ie":{"version_added":false},"opera":[{"version_added":"69"},{"version_added":"15","partial_implementation":true}],"opera_android":[{"version_added":"59"},{"version_added":"14","partial_implementation":true}],"safari":{"version_added":"3","partial_implementation":true},"safari_ios":{"version_added":"1","partial_implementation":true},"samsunginternet_android":{"version_added":"1.0","partial_implementation":true},"webview_android":[{"version_added":"83"},{"version_added":"1","partial_implementation":true}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"aspect-ratio":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/aspect-ratio","spec_url":"https://drafts.csswg.org/css-sizing-4/#aspect-ratio","support":{"chrome":[{"version_added":"88"},{"version_added":"84","flags":[{"type":"preference","name":"#enable-experimental-web-platform-features","value_to_set":"Enabled"}]}],"chrome_android":{"version_added":"88"},"edge":{"version_added":"88"},"firefox":[{"version_added":"89"},{"version_added":"83","partial_implementation":true,"notes":"Firefox 83 implements aspect-ratio for flex items.","flags":[{"type":"preference","name":"layout.css.aspect-ratio.enabled","value_to_set":"true"}]},{"version_added":"81","partial_implementation":true,"notes":"Firefox 81 implements aspect-ratio for blocks and replaced elements.","flags":[{"type":"preference","name":"layout.css.aspect-ratio.enabled","value_to_set":"true"}]}],"firefox_android":{"version_added":"89"},"ie":{"version_added":false},"opera":{"version_added":"74"},"opera_android":{"version_added":"63"},"safari":{"version_added":"15"},"safari_ios":{"version_added":"15"},"samsunginternet_android":{"version_added":"15.0"},"webview_android":{"version_added":"88"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"backdrop-filter":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/backdrop-filter","spec_url":"https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty","support":{"chrome":[{"version_added":"76"},{"version_added":"47","flags":[{"type":"preference","name":"Enable Experimental Web Platform Features"}]}],"chrome_android":[{"version_added":"76"},{"version_added":"47","flags":[{"type":"preference","name":"Enable Experimental Web Platform Features"}]}],"edge":{"version_added":"17"},"firefox":{"version_added":"70","flags":[{"type":"preference","name":"layout.css.backdrop-filter.enabled","value_to_set":"true"},{"type":"preference","name":"gfx.webrender.all","value_to_set":"true"}]},"firefox_android":{"version_added":false,"notes":"See bug 1178765."},"ie":{"version_added":false},"opera":[{"version_added":"63"},{"version_added":"34","flags":[{"type":"preference","name":"Enable Experimental Web Platform Features"}]}],"opera_android":[{"version_added":"54"},{"version_added":"34","flags":[{"type":"preference","name":"Enable Experimental Web Platform Features"}]}],"safari":{"prefix":"-webkit-","version_added":"9"},"safari_ios":{"prefix":"-webkit-","version_added":"9"},"samsunginternet_android":{"version_added":"12.0"},"webview_android":{"version_added":"76"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"backface-visibility":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/backface-visibility","spec_url":"https://drafts.csswg.org/css-transforms-2/#backface-visibility-property","support":{"chrome":[{"version_added":"36"},{"prefix":"-webkit-","version_added":"12"}],"chrome_android":[{"version_added":"36"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"prefix":"-moz-","version_added":"10"},{"version_added":"49","prefix":"-webkit-"}],"firefox_android":[{"version_added":"16"},{"prefix":"-moz-","version_added":"10"},{"version_added":"49","prefix":"-webkit-"}],"ie":{"version_added":"10"},"opera":[{"version_added":"23"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"24"},{"prefix":"-webkit-","version_added":"14"}],"safari":{"prefix":"-webkit-","version_added":"5.1"},"safari_ios":{"prefix":"-webkit-","version_added":"5"},"samsunginternet_android":[{"version_added":"3.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"37"},{"prefix":"-webkit-","version_added":"3"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"background-attachment":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/background-attachment","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-background-attachment","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"3.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"fixed":{"__compat":{"description":"fixed","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"25"},"firefox_android":{"version_added":"25"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"14"},"safari":[{"version_added":"14","partial_implementation":true,"notes":"local is recognized but has no effect due to a bug."},{"version_added":"3.1","version_removed":"14"}],"safari_ios":{"version_added":"5","partial_implementation":true,"notes":"local is recognized but has no effect."},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"local":{"__compat":{"description":"local","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"25"},"firefox_android":{"version_added":"25"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"14"},"safari":[{"version_added":"13","partial_implementation":true,"notes":"local is recognized but has no effect due to a bug."},{"version_added":"5","version_removed":"13"}],"safari_ios":[{"version_added":"13","partial_implementation":true,"notes":"local is recognized but has no effect due to a bug."},{"version_added":"4.2","version_removed":"13","notes":"If -webkit-overflow-scrolling: touch is set, then local has no effect."}],"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"multiple_backgrounds":{"__compat":{"description":"Multiple backgrounds","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3.6"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"11"},"safari":{"version_added":"1.3"},"safari_ios":{"version_added":"3.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"background-blend-mode":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/background-blend-mode","spec_url":"https://drafts.fxtf.org/compositing/#background-blend-mode","support":{"chrome":{"version_added":"35"},"chrome_android":{"version_added":"35"},"edge":{"version_added":"79"},"firefox":{"version_added":"30"},"firefox_android":{"version_added":"30"},"ie":{"version_added":false},"opera":{"version_added":"22"},"opera_android":{"version_added":"22"},"safari":{"version_added":"8"},"safari_ios":{"version_added":"8"},"samsunginternet_android":{"version_added":"3.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"background-clip":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/background-clip","spec_url":"https://drafts.csswg.org/css-backgrounds/#background-clip","support":{"chrome":[{"version_added":"1"},{"version_added":"1","version_removed":"64","prefix":"-webkit-","notes":"Chrome accepts alternate synonyms to its values: padding, border, and content."}],"chrome_android":[{"version_added":"18"},{"version_added":"18","version_removed":"64","prefix":"-webkit-","notes":"Chrome accepts alternate synonyms to its values: padding, border, and content."}],"edge":{"version_added":"12"},"firefox":[{"version_added":"4"},{"version_added":"49","prefix":"-webkit-"},{"version_added":"1","version_removed":"4","partial_implementation":true,"prefix":"-moz-","notes":"Used the -moz-background-clip: padding | border syntax."}],"firefox_android":[{"version_added":"14"},{"version_added":"49","prefix":"-webkit-"}],"ie":{"version_added":"9","notes":"In IE 7 and IE 8 of Internet Explorer, this property always behaved like background-clip: padding when overflow was hidden, auto, or scroll."},"opera":[{"version_added":"10.5"},{"version_added":"15","version_removed":"51","prefix":"-webkit-","notes":"Opera accepts alternate synonyms to its values: padding, border, and content."}],"opera_android":[{"version_added":"11"},{"version_added":"14","version_removed":"47","prefix":"-webkit-","notes":"Opera accepts alternate synonyms to its values: padding, border, and content."}],"safari":{"version_added":"3","prefix":"-webkit-","notes":"Safari accepts alternate synonyms to its values: padding, border, and content."},"safari_ios":{"version_added":"1","prefix":"-webkit-","notes":"Safari accepts alternate synonyms to its values: padding, border, and content."},"samsunginternet_android":[{"version_added":"1.0"},{"prefix":"-webkit-","version_added":"1.0","version_removed":"9.0","notes":"Chrome accepts alternate synonyms to its values: padding, border, and content."}],"webview_android":[{"version_added":"4"},{"version_added":"≤37","version_removed":"64","prefix":"-webkit-","notes":"WebView accepts alternate synonyms to its values: padding, border, and content."}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"content-box":{"__compat":{"description":"content-box","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"4"},"firefox_android":{"version_added":"14"},"ie":{"version_added":"9","notes":"In IE 7 and IE 9 of Internet Explorer, it always behaved like background-clip: padding if overflow: hidden | auto | scroll"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"11"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"text":{"__compat":{"description":"text","support":{"chrome":{"version_added":"3","partial_implementation":true,"notes":["This value is supported with the prefixed version of the property only.","According to the WebKit blog, text decorations or shadows are not included in the clipping."]},"chrome_android":{"version_added":"18","partial_implementation":true,"notes":["This value is supported with the prefixed version of the property only.","According to the WebKit blog, text decorations or shadows are not included in the clipping."]},"edge":[{"version_added":"15"},{"version_added":"12","partial_implementation":true,"notes":"Before Edge 15, this value was supported with the prefixed version of the property only."}],"firefox":{"version_added":"49"},"firefox_android":{"version_added":"49"},"ie":{"version_added":false},"opera":{"version_added":"15","partial_implementation":true,"notes":["This value is supported with the prefixed version of the property only.","According to the WebKit blog, text decorations or shadows are not included in the clipping."]},"opera_android":{"version_added":"14","partial_implementation":true,"notes":["This value is supported with the prefixed version of the property only.","According to the WebKit blog, text decorations or shadows are not included in the clipping."]},"safari":{"version_added":"4","partial_implementation":true,"notes":["This value is supported with the prefixed version of the property only.","According to the WebKit blog, text decorations or shadows are not included in the clipping."]},"safari_ios":{"version_added":"3.2","partial_implementation":true,"notes":["This value is supported with the prefixed version of the property only.","According to the WebKit blog, text decorations or shadows are not included in the clipping."]},"samsunginternet_android":{"version_added":"1.0","partial_implementation":true,"notes":["This value is supported with the prefixed version of the property only.","According to the WebKit blog, text decorations or shadows are not included in the clipping."]},"webview_android":{"version_added":"≤37","partial_implementation":true,"notes":["This value is supported with the prefixed version of the property only.","According to the WebKit blog, text decorations or shadows are not included in the clipping."]}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"background-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/background-color","spec_url":"https://drafts.csswg.org/css-backgrounds/#background-color","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4","notes":"In Internet Explorer 8 and 9, there is a bug where a computed background-color of transparent causes click events to not get fired on overlaid elements."},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"background-image":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/background-image","spec_url":"https://drafts.csswg.org/css-backgrounds/#background-image","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"If the browser.display.use_document_colors user preference in about:config is set to false, background images will not be displayed."},"firefox_android":{"version_added":"4","notes":"If the browser.display.use_document_colors user preference in about:config is set to false, background images will not be displayed."},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"element":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/element()","spec_url":"https://drafts.csswg.org/css-images-4/#element-notation","description":"element()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"4","prefix":"-moz-"},"firefox_android":{"version_added":"4","prefix":"-moz-"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"gradients":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/gradient","spec_url":"https://drafts.csswg.org/css-images-4/#gradients","description":"Gradients","support":{"chrome":{"version_added":"1","notes":"Some versions support only experimental gradients prefixed with -webkit."},"chrome_android":{"version_added":"18","notes":"Some versions support only experimental gradients prefixed with -webkit."},"edge":{"version_added":"12"},"firefox":{"version_added":"3.6","notes":"Some versions support only experimental gradients prefixed with -moz."},"firefox_android":{"version_added":"4","notes":"Some versions support only experimental gradients prefixed with -moz."},"ie":{"version_added":"10"},"opera":{"version_added":"11","notes":"Some versions support only experimental gradients prefixed with -o."},"opera_android":{"version_added":"14","notes":"Some versions support only experimental gradients prefixed with -webkit."},"safari":{"version_added":"4","notes":"Some versions support only experimental gradients prefixed with -webkit."},"safari_ios":{"version_added":"3.2","notes":"Some versions support only experimental gradients prefixed with -webkit."},"samsunginternet_android":{"version_added":"1.0","notes":"Some versions support only experimental gradients prefixed with -webkit."},"webview_android":{"version_added":"≤37","notes":"Some versions support only experimental gradients prefixed with -webkit."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"image-rect":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-moz-image-rect","description":"image-rect()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"prefix":"-moz-","version_added":"4"},"firefox_android":{"prefix":"-moz-","version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"image-set":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/image-set()","spec_url":"https://drafts.csswg.org/css-images-4/#image-set-notation","description":"image-set()","support":{"chrome":{"prefix":"-webkit-","version_added":"21"},"chrome_android":{"prefix":"-webkit-","version_added":"25"},"edge":{"prefix":"-webkit-","version_added":"79"},"firefox":{"version_added":false,"notes":"See bug 1107646."},"firefox_android":{"version_added":false,"notes":"See bug 1107646."},"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":{"prefix":"-webkit-","version_added":"6","partial_implementation":true,"notes":"Support for url images only and x is the only supported resolution unit. See bug 160934."},"safari_ios":{"prefix":"-webkit-","version_added":"6","partial_implementation":true,"notes":"Support for url images only and x is the only supported resolution unit. See bug 160934."},"samsunginternet_android":{"prefix":"-webkit-","version_added":"1.5"},"webview_android":{"prefix":"-webkit-","version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"multiple_backgrounds":{"__compat":{"description":"Multiple backgrounds","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3.6"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"svg_images":{"__compat":{"description":"SVG images","support":{"chrome":{"version_added":"8"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"4"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"9.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5","notes":"Support of SVG in CSS background is incomplete."},"safari_ios":{"version_added":"5","notes":"Support of SVG in CSS background is incomplete."},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"background-origin":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/background-origin","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-background-origin","support":{"chrome":[{"version_added":"1"},{"version_added":"1","version_removed":"64","prefix":"-webkit-","notes":"Chrome accepts alternate synonyms to its values: padding, border, and content."}],"chrome_android":[{"version_added":"18"},{"version_added":"18","version_removed":"64","prefix":"-webkit-","notes":"Chrome accepts alternate synonyms to its values: padding, border, and content."}],"edge":{"version_added":"12"},"firefox":[{"version_added":"4"},{"version_added":"49","prefix":"-webkit-"},{"version_added":"1","version_removed":"4","partial_implementation":true,"prefix":"-moz-","notes":"Used the -moz-background-clip: padding | border syntax."}],"firefox_android":[{"version_added":"14"},{"version_added":"49","prefix":"-webkit-"}],"ie":{"version_added":"9","notes":"In IE 7 and before, Internet explorer was behaving as if background-origin: border-box was set. In Internet Explorer 8, as if background-origin: padding-box, the regular default value, was set."},"opera":[{"version_added":"10.5"},{"version_added":"15","version_removed":"51","prefix":"-webkit-","notes":"Opera accepts alternate synonyms to its values: padding, border, and content."}],"opera_android":[{"version_added":"11"},{"version_added":"14","version_removed":"47","prefix":"-webkit-","notes":"Opera accepts alternate synonyms to its values: padding, border, and content."}],"safari":[{"version_added":"3"},{"version_added":"3","prefix":"-webkit-","notes":"Webkit accepts alternate synonyms to its values: padding, border, and content."}],"safari_ios":[{"version_added":"1"},{"version_added":"1","prefix":"-webkit-","notes":"Webkit accepts alternate synonyms to its values: padding, border, and content."}],"samsunginternet_android":[{"version_added":"1.0"},{"prefix":"-webkit-","version_added":"1.0","version_removed":"9.0","notes":"Chrome accepts alternate synonyms to its values: padding, border, and content."}],"webview_android":[{"version_added":"4"},{"version_added":"4","version_removed":"64","prefix":"-webkit-","notes":"WebView accepts alternate synonyms to its values: padding, border, and content."}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"content-box":{"__compat":{"description":"content-box","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"4"},"firefox_android":{"version_added":"14"},"ie":{"version_added":"9","notes":"In IE 7 and IE 9 of Internet Explorer, it always behaved like background-clip: padding if overflow: hidden | auto | scroll."},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"11"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"background-position-x":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/background-position-x","spec_url":"https://drafts.csswg.org/css-backgrounds-4/#background-position-longhands","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"49"},"firefox_android":{"version_added":"49"},"ie":{"version_added":"6"},"opera":{"version_added":"15"},"opera_android":{"version_added":"18"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"two_value_syntax":{"__compat":{"description":"Two-value syntax (support for offsets from any edge)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"79"},"firefox":{"version_added":"49"},"firefox_android":{"version_added":"49"},"ie":{"version_added":"9"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"background-position-y":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/background-position-y","spec_url":"https://drafts.csswg.org/css-backgrounds-4/#background-position-longhands","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"49"},"firefox_android":{"version_added":"49"},"ie":{"version_added":"6"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"2_value_syntax":{"__compat":{"description":"Two-value syntax (support for offsets from any edge)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"79"},"firefox":{"version_added":"49"},"firefox_android":{"version_added":"49"},"ie":{"version_added":"9"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"background-position":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/background-position","spec_url":"https://drafts.csswg.org/css-backgrounds/#background-position","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"multiple_backgrounds":{"__compat":{"description":"Multiple backgrounds","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3.6"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"4_value_syntax":{"__compat":{"description":"Four-value syntax (support for offsets from any edge)","support":{"chrome":{"version_added":"25"},"chrome_android":{"version_added":"25"},"edge":{"version_added":"12"},"firefox":{"version_added":"13"},"firefox_android":{"version_added":"14"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"7"},"safari_ios":{"version_added":"7"},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"background-repeat":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/background-repeat","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-background-repeat","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"multiple_backgrounds":{"__compat":{"description":"Multiple backgrounds","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3.6"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"11"},"safari":{"version_added":"1.3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"round_space":{"__compat":{"description":"round and space keywords","support":{"chrome":{"version_added":"30"},"chrome_android":{"version_added":"30"},"edge":{"version_added":"12"},"firefox":{"version_added":"49"},"firefox_android":{"version_added":"49"},"ie":{"version_added":"9"},"opera":[{"version_added":"17"},{"version_added":"10.5","version_removed":"15"}],"opera_android":{"version_added":"18"},"safari":{"version_added":"7"},"safari_ios":{"version_added":"8"},"samsunginternet_android":{"version_added":"2.0"},"webview_android":{"version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"2-value":{"__compat":{"description":"Two-value syntax (different values for x & y directions)","support":{"chrome":{"version_added":"3"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"13"},"firefox_android":{"version_added":"14"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"11"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"background-size":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/background-size","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-background-size","support":{"chrome":[{"version_added":"3"},{"version_added":"1","prefix":"-webkit-","notes":"WebKit-based browsers originally implemented an older draft of CSS3 background-size in which an omitted second value is treated as duplicating the first value; this draft does not include the contain or cover keywords."}],"chrome_android":[{"version_added":"18"},{"version_added":"18","prefix":"-webkit-","notes":"WebKit-based browsers originally implemented an older draft of CSS3 background-size in which an omitted second value is treated as duplicating the first value; this draft does not include the contain or cover keywords."}],"edge":{"version_added":"12"},"firefox":[{"version_added":"4"},{"version_added":"49","prefix":"-webkit-"},{"prefix":"-moz-","version_added":"3.6","version_removed":"4"}],"firefox_android":[{"version_added":"4"},{"version_added":"49","prefix":"-webkit-"}],"ie":{"version_added":"9"},"opera":[{"version_added":"10"},{"version_added":"15","prefix":"-webkit-","notes":"WebKit-based browsers originally implemented an older draft of CSS3 background-size in which an omitted second value is treated as duplicating the first value; this draft does not include the contain or cover keywords."},{"version_added":"9.5","version_removed":"15","prefix":"-o-","notes":"Opera 9.5's computation of the background positioning area is incorrect for fixed backgrounds. Opera 9.5 also interprets the two-value form as a horizontal scaling factor and, from appearances, a vertical clipping dimension. This has been fixed in Opera 10."}],"opera_android":[{"version_added":"10.1"},{"version_added":"14","prefix":"-webkit-","notes":"WebKit-based browsers originally implemented an older draft of CSS3 background-size in which an omitted second value is treated as duplicating the first value; this draft does not include the contain or cover keywords."},{"version_added":"10.1","version_removed":"14","prefix":"-o-","notes":"Opera 9.5's computation of the background positioning area is incorrect for fixed backgrounds. Opera 9.5 also interprets the two-value form as a horizontal scaling factor and, from appearances, a vertical clipping dimension. This has been fixed in Opera 10."}],"safari":[{"version_added":"5"},{"prefix":"-webkit-","version_added":"3","notes":"WebKit-based browsers originally implemented an older draft of CSS3 background-size in which an omitted second value is treated as duplicating the first value; this draft does not include the contain or cover keywords."}],"safari_ios":[{"version_added":"4.2"},{"prefix":"-webkit-","version_added":"1","notes":"WebKit-based browsers originally implemented an older draft of CSS3 background-size in which an omitted second value is treated as duplicating the first value; this draft does not include the contain or cover keywords."}],"samsunginternet_android":[{"version_added":"1.0"},{"prefix":"-webkit-","version_added":"1.0","notes":"WebKit-based browsers originally implemented an older draft of CSS3 background-size in which an omitted second value is treated as duplicating the first value; this draft does not include the contain or cover keywords."}],"webview_android":[{"version_added":"2.2"},{"version_added":"≤37","prefix":"-webkit-","notes":"WebKit-based browsers originally implemented an older draft of CSS3 background-size in which an omitted second value is treated as duplicating the first value; this draft does not include the contain or cover keywords."}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"contain_and_cover":{"__compat":{"description":"contain and cover","support":{"chrome":{"version_added":"3"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3.6"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"10"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"background":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/background","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-background","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"SVG_image_as_background":{"__compat":{"description":"SVG image as background","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"4"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"9.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"3.1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"background-clip":{"__compat":{"description":"Values of background-clip longhand","support":{"chrome":{"version_added":"21"},"chrome_android":{"version_added":"25"},"edge":{"version_added":"12"},"firefox":{"version_added":"22"},"firefox_android":{"version_added":"22"},"ie":{"version_added":"9"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"4"},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"3"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"background-origin":{"__compat":{"description":"Values of background-origin longhand","support":{"chrome":{"version_added":"21"},"chrome_android":{"version_added":"25"},"edge":{"version_added":"12"},"firefox":{"version_added":"22"},"firefox_android":{"version_added":"22"},"ie":{"version_added":"9"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"4"},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"3"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"background-size":{"__compat":{"description":"Values of background-size longhand","support":{"chrome":{"version_added":"21"},"chrome_android":{"version_added":"25"},"edge":{"version_added":"12"},"firefox":{"version_added":"9"},"firefox_android":{"version_added":"18"},"ie":{"version_added":"9"},"opera":{"version_added":"21"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"4"},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"3"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"multiple_backgrounds":{"__compat":{"description":"Multiple backgrounds","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3.6"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"block-size":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/block-size","spec_url":"https://drafts.csswg.org/css-logical/#dimension-properties","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"fit-content":{"__compat":{"description":"fit-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"prefix":"-moz-","version_added":"41"},"firefox_android":{"prefix":"-moz-","version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"alternative_name":"-webkit-fill-available","version_added":"5.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"fit-content_function":{"__compat":{"description":"fit-content()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"firefox_android":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"max-content":{"__compat":{"description":"max-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"min-content":{"__compat":{"description":"min-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"border-block-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-block-color","spec_url":"https://drafts.csswg.org/css-logical/#propdef-border-block-color","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]},"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":false},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-block-end-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-block-end-color","spec_url":"https://drafts.csswg.org/css-logical/#border-color","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-block-end-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-block-end-style","spec_url":"https://drafts.csswg.org/css-logical/#border-style","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-block-end-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-block-end-width","spec_url":"https://drafts.csswg.org/css-logical/#border-width","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-block-end":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-block-end","spec_url":"https://drafts.csswg.org/css-logical/#border-shorthands","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-block-start-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-block-start-color","spec_url":"https://drafts.csswg.org/css-logical/#border-color","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-block-start-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-block-start-style","spec_url":"https://drafts.csswg.org/css-logical/#border-style","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-block-start-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-block-start-width","spec_url":"https://drafts.csswg.org/css-logical/#border-width","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-block-start":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-block-start","spec_url":"https://drafts.csswg.org/css-logical/#border-shorthands","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-block-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-block-style","spec_url":"https://drafts.csswg.org/css-logical/#propdef-border-block-style","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]},"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"48","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-block-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-block-width","spec_url":"https://drafts.csswg.org/css-logical/#propdef-border-block-width","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]},"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":[{"version_added":false},{"version_added":"48","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-block":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-block","spec_url":"https://drafts.csswg.org/css-logical/#propdef-border-block","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]},"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"48","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-bottom-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-bottom-color","spec_url":"https://drafts.csswg.org/css-backgrounds/#border-color","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"Firefox also supports the non-standard -moz-border-bottom-colors CSS property that sets the bottom border to multiple colors."},"firefox_android":{"version_added":"4","notes":"Firefox also supports the non-standard -moz-border-bottom-colors CSS property that sets the bottom border to multiple colors."},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-bottom-left-radius":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-bottom-left-radius","spec_url":"https://drafts.csswg.org/css-backgrounds/#border-radius","support":{"chrome":[{"version_added":"4"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"18"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"4","notes":"Prior to Firefox 50, border styles of rounded corners were always rendered as if border-style was solid. This has been fixed in Firefox 50."},{"prefix":"-webkit-","version_added":"49"},{"alternative_name":"-moz-border-radius-bottomleft","version_added":"1","version_removed":"12"}],"firefox_android":[{"version_added":"4","notes":"Prior to Firefox 50, border styles of rounded corners were always rendered as if border-style was solid. This has been fixed in Firefox 50."},{"prefix":"-webkit-","version_added":"49"},{"alternative_name":"-moz-border-radius-bottomleft","version_added":"4","version_removed":"14"}],"ie":{"version_added":"9"},"opera":[{"version_added":"10.5"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"5"},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"4.2"},{"prefix":"-webkit-","version_added":"1"}],"samsunginternet_android":[{"version_added":"1.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"≤37"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"elliptical_corners":{"__compat":{"description":"Elliptical corners","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3.5"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"percentages":{"__compat":{"description":"Percentages","support":{"chrome":{"version_added":"4"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":[{"version_added":"4"},{"version_added":"1","notes":"Before Firefox 4, the <percentage> was relative to the width of the box even when specifying the radius for a height. This implied that -moz-border-radius-bottomleft was always drawing an arc of circle, and never an ellipse, when followed by a single value."}],"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"border-bottom-right-radius":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-bottom-right-radius","spec_url":"https://drafts.csswg.org/css-backgrounds/#border-radius","support":{"chrome":[{"version_added":"4"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"18"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"4","notes":"Prior to Firefox 50, border styles of rounded corners were always rendered as if border-style was solid. This has been fixed in Firefox 50."},{"prefix":"-webkit-","version_added":"49"},{"alternative_name":"-moz-border-radius-bottomright","version_added":"1","version_removed":"12"}],"firefox_android":[{"version_added":"4","notes":"Prior to Firefox 50, border styles of rounded corners were always rendered as if border-style was solid. This has been fixed in Firefox 50."},{"prefix":"-webkit-","version_added":"49"},{"alternative_name":"-moz-border-radius-bottomright","version_added":"4","version_removed":"14"}],"ie":{"version_added":"9"},"opera":[{"version_added":"10.5"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"5"},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"4.2"},{"prefix":"-webkit-","version_added":"1"}],"samsunginternet_android":[{"version_added":"1.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"≤37"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"elliptical_corners":{"__compat":{"description":"Elliptical corners","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3.5"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"percentages":{"__compat":{"description":"Percentages","support":{"chrome":{"version_added":"4"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":[{"version_added":"4"},{"version_added":"1","notes":"Before Firefox 4, the <percentage> was relative to the width of the box even when specifying the radius for a height. This implied that -moz-border-radius-bottomright was always drawing an arc of circle, and never an ellipse, when followed by a single value."}],"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"border-bottom-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-bottom-style","spec_url":"https://drafts.csswg.org/css-backgrounds/#border-style","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"Prior to Firefox 50, border styles of rounded corners (with border-radius) were always rendered as if border-bottom-style was solid. This has been fixed in Firefox 50."},"firefox_android":{"version_added":"4","notes":"Prior to Firefox 50, border styles of rounded corners (with border-radius) were always rendered as if border-bottom-style was solid. This has been fixed in Firefox 50."},"ie":{"version_added":"5.5"},"opera":{"version_added":"9.2"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-bottom-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-bottom-width","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-border-width","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2.2"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-bottom":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-bottom","spec_url":"https://drafts.csswg.org/css-backgrounds/#border-shorthands","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-collapse":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-collapse","spec_url":"https://drafts.csswg.org/css2/#propdef-border-collapse","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"5"},"opera":{"version_added":"4"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"3"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2.2"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-color","spec_url":["https://drafts.csswg.org/css-logical/#logical-shorthand-keyword","https://drafts.csswg.org/css-backgrounds/#border-color"],"support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"Firefox also supports the following non-standard CSS properties to set the border sides to multiple colors: -moz-border-top-colors, -moz-border-right-colors, -moz-border-bottom-colors, -moz-border-left-colors"},"firefox_android":{"version_added":"4","notes":"Firefox also supports the following non-standard CSS properties to set the border sides to multiple colors: -moz-border-top-colors, -moz-border-right-colors, -moz-border-bottom-colors, -moz-border-left-colors"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-end-end-radius":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-end-end-radius","spec_url":"https://drafts.csswg.org/css-logical/#border-radius-properties","support":{"chrome":{"version_added":"89"},"chrome_android":{"version_added":"89"},"edge":{"version_added":"89"},"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":{"version_added":"75"},"opera_android":{"version_added":false},"safari":{"version_added":"15"},"safari_ios":{"version_added":"15"},"samsunginternet_android":{"version_added":"15.0"},"webview_android":{"version_added":"89"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-end-start-radius":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-end-start-radius","spec_url":"https://drafts.csswg.org/css-logical/#border-radius-properties","support":{"chrome":{"version_added":"89"},"chrome_android":{"version_added":"89"},"edge":{"version_added":"89"},"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":{"version_added":"75"},"opera_android":{"version_added":false},"safari":{"version_added":"15"},"safari_ios":{"version_added":"15"},"samsunginternet_android":{"version_added":"15.0"},"webview_android":{"version_added":"89"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-image-outset":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-image-outset","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-border-image-outset","support":{"chrome":{"version_added":"15"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"15"},"firefox_android":{"version_added":"15"},"ie":{"version_added":"11"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"6"},"safari_ios":{"version_added":"6"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-image-repeat":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-image-repeat","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-border-image-repeat","support":{"chrome":{"version_added":"15"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"15"},"firefox_android":{"version_added":"15"},"ie":{"version_added":"11"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"6"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"round":{"__compat":{"description":"round","support":{"chrome":{"version_added":"30"},"chrome_android":{"version_added":"30"},"edge":{"version_added":"12"},"firefox":{"version_added":"15"},"firefox_android":{"version_added":"15"},"ie":{"version_added":"11"},"opera":{"version_added":"17"},"opera_android":{"version_added":"18"},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":"2.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"space":{"__compat":{"description":"space","support":{"chrome":{"version_added":"56"},"chrome_android":{"version_added":"56"},"edge":{"version_added":"12"},"firefox":{"version_added":"50"},"firefox_android":{"version_added":"50"},"ie":{"version_added":"11"},"opera":{"version_added":"43"},"opera_android":{"version_added":"43"},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"56"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"border-image-slice":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-image-slice","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-border-image-slice","support":{"chrome":{"version_added":"15"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"15","notes":["Small SVGs are incorrectly stretched, because percentages in border-image-slice are computed to integers instead of floats (bug 1284797).","Until Firefox 47, SVGs without viewport were not sliced correctly (bug 619500).","From Firefox 48 until Firefox 49, SVGs without viewport are displayed the same as SVGs with viewport, but if the slices are not exactly 50%, they are incorrectly stretched (bug 1264809).","Until Firefox 57, an issue persisted for SVGs without viewport when e10s was disabled (bug 1290782)."]},"firefox_android":{"version_added":"15","notes":["Small SVGs are incorrectly stretched, because percentages in border-image-slice are computed to integers instead of floats (bug 1284797).","Until Firefox 47, SVGs without viewport were not sliced correctly (bug 619500).","From Firefox 48 until Firefox 49, SVGs without viewport are displayed the same as SVGs with viewport, but if the slices are not exactly 50%, they are incorrectly stretched (bug 1264809).","Until Firefox 57, an issue persisted for SVGs without viewport when e10s was disabled (bug 1290782)."]},"ie":{"version_added":"11"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"6"},"safari_ios":{"version_added":"6"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":[{"version_added":"≤37"},{"prefix":"-webkit-","version_added":"4"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-image-source":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-image-source","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-border-image-source","support":{"chrome":{"version_added":"15"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"15"},"firefox_android":{"version_added":"15"},"ie":{"version_added":"11"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"6"},"safari_ios":{"version_added":"6"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-image-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-image-width","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-border-image-width","support":{"chrome":{"version_added":"15"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"13"},"firefox_android":{"version_added":"14"},"ie":{"version_added":"11"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"6"},"safari_ios":{"version_added":"6"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-image":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-image","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-border-image","support":{"chrome":[{"version_added":"16"},{"prefix":"-webkit-","version_added":"7"}],"chrome_android":[{"version_added":"18"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"15","notes":["Small SVGs are incorrectly stretched, because percentages in border-image-slice are computed to integers instead of floats (bug 1284797).","Until Firefox 47, SVGs without viewport were not sliced correctly (bug 619500).","From Firefox 48 until Firefox 49, SVGs without viewport are displayed the same as SVGs with viewport, but if the slices are not exactly 50%, they are incorrectly stretched (bug 1264809).","Until Firefox 57, an issue persisted for SVGs without viewport when e10s was disabled (bug 1290782)."]},{"version_added":"3.5","prefix":"-moz-","notes":"An earlier version of the specification was implemented, prefixed, in Firefox versions prior to 15."}],"firefox_android":[{"version_added":"15","notes":["Small SVGs are incorrectly stretched, because percentages in border-image-slice are computed to integers instead of floats (bug 1284797).","Until Firefox 47, SVGs without viewport were not sliced correctly (bug 619500).","From Firefox 48 until Firefox 49, SVGs without viewport are displayed the same as SVGs with viewport, but if the slices are not exactly 50%, they are incorrectly stretched (bug 1264809).","Until Firefox 57, an issue persisted for SVGs without viewport when e10s was disabled (bug 1290782)."]},{"version_added":"4","prefix":"-moz-","notes":"An earlier version of the specification was implemented, prefixed, in Firefox versions prior to 15."}],"ie":{"version_added":"11"},"opera":[{"version_added":"11"},{"version_added":"10.5","version_removed":"15","prefix":"-o-"}],"opera_android":[{"version_added":"11"},{"version_added":"11","version_removed":"14","prefix":"-o-"}],"safari":[{"version_added":"6"},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"6"},{"prefix":"-webkit-","version_added":"3.2"}],"samsunginternet_android":[{"version_added":"1.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"≤37"},{"prefix":"-webkit-","version_added":"2"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"fill":{"__compat":{"support":{"chrome":{"version_added":"16"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"15"},"firefox_android":{"version_added":"15"},"ie":{"version_added":"11"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"6"},"safari_ios":{"version_added":"6"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"gradient":{"__compat":{"description":"<gradient>","support":{"chrome":{"version_added":"7"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"29"},"firefox_android":{"version_added":"29"},"ie":{"version_added":"11"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"4"},"safari_ios":{"version_added":"3.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"optional_border_image_slice":{"__compat":{"description":"optional <border-image-slice>","support":{"chrome":{"version_added":"16"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"15"},"firefox_android":{"version_added":"15"},"ie":{"version_added":"11"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"6"},"safari_ios":{"version_added":"6"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"border-inline-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-inline-color","spec_url":"https://drafts.csswg.org/css-logical/#propdef-border-inline-color","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]},"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"48","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-inline-end-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-inline-end-color","spec_url":"https://drafts.csswg.org/css-logical/#border-color","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":[{"version_added":"41"},{"version_added":"3","alternative_name":"-moz-border-end-color"}],"firefox_android":[{"version_added":"41"},{"version_added":"4","alternative_name":"-moz-border-end-color"}],"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-inline-end-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-inline-end-style","spec_url":"https://drafts.csswg.org/css-logical/#border-style","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":[{"version_added":"41"},{"version_added":"3","alternative_name":"-moz-border-end-style"}],"firefox_android":[{"version_added":"41"},{"version_added":"4","alternative_name":"-moz-border-end-style"}],"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-inline-end-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-inline-end-width","spec_url":"https://drafts.csswg.org/css-logical/#border-width","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":[{"version_added":"41"},{"version_added":"3","alternative_name":"-moz-border-end-width"}],"firefox_android":[{"version_added":"41"},{"version_added":"4","alternative_name":"-moz-border-end-width"}],"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-inline-end":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-inline-end","spec_url":"https://drafts.csswg.org/css-logical/#border-shorthands","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-inline-start-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-inline-start-color","spec_url":"https://drafts.csswg.org/css-logical/#border-color","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":[{"version_added":"41"},{"version_added":"3","alternative_name":"-moz-border-start-color"}],"firefox_android":[{"version_added":"41"},{"version_added":"4","alternative_name":"-moz-border-start-color"}],"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-inline-start-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-inline-start-style","spec_url":"https://drafts.csswg.org/css-logical/#border-style","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":[{"version_added":"41"},{"version_added":"3","alternative_name":"-moz-border-start-style"}],"firefox_android":[{"version_added":"41"},{"version_added":"4","alternative_name":"-moz-border-start-style"}],"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-inline-start-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-inline-start-width","spec_url":"https://drafts.csswg.org/css-logical/#border-width","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-inline-start":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-inline-start","spec_url":"https://drafts.csswg.org/css-logical/#border-shorthands","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-inline-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-inline-style","spec_url":"https://drafts.csswg.org/css-logical/#propdef-border-inline-style","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]},"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"48","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-inline-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-inline-width","spec_url":"https://drafts.csswg.org/css-logical/#propdef-border-inline-width","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]},"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"48","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-inline":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-inline","spec_url":"https://drafts.csswg.org/css-logical/#propdef-border-inline","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]},"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"48","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-left-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-left-color","spec_url":"https://drafts.csswg.org/css-backgrounds/#border-color","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"Firefox also supports the non-standard -moz-border-left-colors CSS property that sets the bottom border to multiple colors."},"firefox_android":{"version_added":"4","notes":"Firefox also supports the non-standard -moz-border-left-colors CSS property that sets the bottom border to multiple colors."},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-left-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-left-style","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-border-style","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"Prior to Firefox 50, border styles of rounded corners (with border-radius) were always rendered as if border-bottom-style was solid. This has been fixed in Firefox 50."},"firefox_android":{"version_added":"14","notes":"Prior to Firefox 50, border styles of rounded corners (with border-radius) were always rendered as if border-bottom-style was solid. This has been fixed in Firefox 50."},"ie":{"version_added":"5.5"},"opera":{"version_added":"9.2"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2.2"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-left-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-left-width","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-border-width","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2.2"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-left":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-left","spec_url":"https://drafts.csswg.org/css-backgrounds/#border-shorthands","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-radius":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-radius","spec_url":"https://drafts.csswg.org/css-backgrounds/#border-radius","support":{"chrome":[{"version_added":"4","notes":"Chrome ignores border-radius on <select> elements unless -webkit-appearance is overridden to an appropriate value."},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":{"version_added":"18"},"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"4","notes":["Prior to Firefox 50, border styles of rounded corners (with border-radius) were always rendered as if border-style was solid. This has been fixed in Firefox 50.","To conform to the CSS3 standard, Firefox 4 changes the handling of <percentage> values to match the specification. You can specify an ellipse as a border on an arbitrary sized element with border-radius: 50%;. Firefox 4 also makes rounded corners clip content and images if overflow: visible is not set."]},{"prefix":"-moz-","version_added":"1","version_removed":"12"}],"firefox_android":[{"version_added":"4","notes":"Prior to Firefox 50, border styles of rounded corners (with border-radius) were always rendered as if border-style was solid. This has been fixed in Firefox 50."},{"prefix":"-moz-","version_added":"4","version_removed":"14"}],"ie":{"version_added":"9"},"opera":{"version_added":"10.5","notes":"Before Opera 11.60, replaced elements with border-radius do not have rounded corners."},"opera_android":{"version_added":"11"},"safari":[{"version_added":"5","notes":"Safari ignores border-radius on <select> elements unless -webkit-appearance is overridden to an appropriate value."},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"4.2","notes":"Safari ignores border-radius on <select> elements unless -webkit-appearance is overridden to an appropriate value."},{"prefix":"-webkit-","version_added":"1"}],"samsunginternet_android":{"version_added":"1.0"},"webview_android":[{"version_added":"≤37"},{"version_added":"2","prefix":"-webkit-"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"elliptical_borders":{"__compat":{"description":"Elliptical borders","support":{"chrome":{"version_added":"1","notes":"Before Chrome 4, the slash / notation is unsupported. If two values are specified, then an elliptical border is drawn on all four corners. -webkit-border-radius: 40px 10px; is equivalent to border-radius: 40px / 10px;."},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"4"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"11"},"safari":{"version_added":"3","notes":"Before Safari 5, the slash / notation is unsupported. If two values are specified, then an elliptical border is drawn on all four corners. -webkit-border-radius: 40px 10px; is equivalent to border-radius: 40px / 10px;."},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"percentages":{"__compat":{"description":"Percentages","support":{"chrome":{"version_added":"8"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"4","notes":"<percentage> values are implemented in a non-standard way prior to Firefox 4. Both horizontal and vertical radii were relative to the width of the border box."},"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"11.5","notes":"The implementation of <percentage> values was buggy in Opera prior to 11.50."},"opera_android":{"version_added":"11.5"},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"6"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"4_values_for_4_corners":{"__compat":{"description":"4 values for 4 corners","support":{"chrome":{"version_added":"4"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"4"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"11"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"border-right-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-right-color","spec_url":"https://drafts.csswg.org/css-backgrounds/#border-color","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"Firefox also supports the non-standard -moz-border-right-colors CSS property that sets the right border to multiple colors."},"firefox_android":{"version_added":"4","notes":"Firefox also supports the non-standard -moz-border-right-colors CSS property that sets the right border to multiple colors."},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-right-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-right-style","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-border-style","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"Prior to Firefox 50, border styles of rounded corners (with border-radius) were always rendered as if border-bottom-style was solid. This has been fixed in Firefox 50."},"firefox_android":{"version_added":"14","notes":"Prior to Firefox 50, border styles of rounded corners (with border-radius) were always rendered as if border-bottom-style was solid. This has been fixed in Firefox 50."},"ie":{"version_added":"5.5"},"opera":{"version_added":"9.2"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-right-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-right-width","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-border-width","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2.2"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-right":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-right","spec_url":"https://drafts.csswg.org/css-backgrounds/#border-shorthands","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"14"},"ie":{"version_added":"5.5"},"opera":{"version_added":"9.2"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-spacing":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-spacing","spec_url":"https://drafts.csswg.org/css2/#separated-borders","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"4"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-start-end-radius":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-start-end-radius","spec_url":"https://drafts.csswg.org/css-logical/#border-radius-properties","support":{"chrome":{"version_added":"89"},"chrome_android":{"version_added":"89"},"edge":{"version_added":"89"},"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":{"version_added":"75"},"opera_android":{"version_added":false},"safari":{"version_added":"15"},"safari_ios":{"version_added":"15"},"samsunginternet_android":{"version_added":"15.0"},"webview_android":{"version_added":"89"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-start-start-radius":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-start-start-radius","spec_url":"https://drafts.csswg.org/css-logical/#border-radius-properties","support":{"chrome":{"version_added":"89"},"chrome_android":{"version_added":"89"},"edge":{"version_added":"89"},"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":{"version_added":"75"},"opera_android":{"version_added":false},"safari":{"version_added":"15"},"safari_ios":{"version_added":"15"},"samsunginternet_android":{"version_added":"15.0"},"webview_android":{"version_added":"89"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-style","spec_url":"https://drafts.csswg.org/css-backgrounds/#border-style","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"Prior to Firefox 50, border styles of rounded corners were always rendered as if border-style was solid. This has been fixed in Firefox 50."},"firefox_android":{"version_added":"4","notes":"Prior to Firefox 50, border styles of rounded corners were always rendered as if border-style was solid. This has been fixed in Firefox 50."},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"3"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"3"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-top-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-top-color","spec_url":"https://drafts.csswg.org/css-backgrounds/#border-color","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"Firefox also supports the non-standard -moz-border-top-colors CSS property that sets the top border to multiple colors."},"firefox_android":{"version_added":"4","notes":"Firefox also supports the non-standard -moz-border-top-colors CSS property that sets the top border to multiple colors."},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-top-left-radius":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-top-left-radius","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-border-radius","support":{"chrome":[{"version_added":"4"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"18"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"4","notes":"Prior to Firefox 50, border styles of rounded corners were always rendered as if border-style was solid. This has been fixed in Firefox 50."},{"prefix":"-webkit-","version_added":"49"},{"alternative_name":"-moz-border-radius-topleft","version_added":"1","version_removed":"12"}],"firefox_android":[{"version_added":"4","notes":"Prior to Firefox 50, border styles of rounded corners were always rendered as if border-style was solid. This has been fixed in Firefox 50."},{"prefix":"-webkit-","version_added":"49"},{"alternative_name":"-moz-border-radius-topleft","version_added":"4","version_removed":"14"}],"ie":{"version_added":"9"},"opera":[{"version_added":"10.5"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"5"},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"4.2"},{"prefix":"-webkit-","version_added":"1"}],"samsunginternet_android":[{"version_added":"1.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"≤37"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"elliptical_corners":{"__compat":{"description":"Elliptical corners","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3.5"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"percentages":{"__compat":{"description":"Percentages","support":{"chrome":{"version_added":"4"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":[{"version_added":"4"},{"version_added":"1","notes":"Before Firefox 4, the <percentage> was relative to the width of the box even when specifying the radius for a height. This implied that -moz-border-radius-topleft was always drawing an arc of circle, and never an ellipse, when followed by a single value."}],"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"border-top-right-radius":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-top-right-radius","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-border-radius","support":{"chrome":[{"version_added":"4"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"18"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"4","notes":"Prior to Firefox 50, border styles of rounded corners were always rendered as if border-style was solid. This has been fixed in Firefox 50."},{"prefix":"-webkit-","version_added":"49"},{"alternative_name":"-moz-border-radius-topright","version_added":"1","version_removed":"12"}],"firefox_android":[{"version_added":"4","notes":"Prior to Firefox 50, border styles of rounded corners were always rendered as if border-style was solid. This has been fixed in Firefox 50."},{"prefix":"-webkit-","version_added":"49"},{"alternative_name":"-moz-border-radius-topright","version_added":"4","version_removed":"14"}],"ie":{"version_added":"9"},"opera":[{"version_added":"10.5"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"5"},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"4.2"},{"prefix":"-webkit-","version_added":"1"}],"samsunginternet_android":[{"version_added":"1.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"≤37"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"elliptical_corners":{"__compat":{"description":"Elliptical corners","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3.5"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"percentages":{"__compat":{"description":"Percentages","support":{"chrome":{"version_added":"4"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":[{"version_added":"4"},{"version_added":"1","notes":"Before Firefox 4, the <percentage> was relative to the width of the box even when specifying the radius for a height. This implied that -moz-border-radius-topright was always drawing an arc of circle, and never an ellipse, when followed by a single value."}],"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"border-top-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-top-style","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-border-style","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"Prior to Firefox 50, border styles of rounded corners (with border-radius) were always rendered as if border-top-style was solid. This has been fixed in Firefox 50."},"firefox_android":{"version_added":"4","notes":"Prior to Firefox 50, border styles of rounded corners (with border-radius) were always rendered as if border-top-style was solid. This has been fixed in Firefox 50."},"ie":{"version_added":"5.5"},"opera":{"version_added":"9.2"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-top-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-top-width","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-border-width","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2.2"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-top":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-top","spec_url":"https://drafts.csswg.org/css-backgrounds/#border-shorthands","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-width","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-border-width","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"3"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border","spec_url":"https://drafts.csswg.org/css-backgrounds/#propdef-border","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"bottom":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/bottom","spec_url":"https://drafts.csswg.org/css-position/#insets","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"5","notes":"In Internet Explorer versions before 7, when both top and bottom are specified, the element position is overconstrained and the top property has precedence; the computed value of bottom is set to -top, while its specified value is ignored."},"opera":{"version_added":"6"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"box-align":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/box-align","support":{"chrome":{"version_added":"1","prefix":"-webkit-"},"chrome_android":{"version_added":"18","prefix":"-webkit-"},"edge":{"version_added":"12","prefix":"-webkit-"},"firefox":[{"version_added":"1","prefix":"-moz-"},{"version_added":"49","prefix":"-webkit-"}],"firefox_android":[{"version_added":"4","prefix":"-moz-"},{"version_added":"49","prefix":"-webkit-"}],"ie":{"version_added":false},"opera":{"version_added":"15","prefix":"-webkit-"},"opera_android":{"version_added":"14","prefix":"-webkit-"},"safari":[{"version_added":"3","prefix":"-webkit-"},{"version_added":"1.1","version_removed":"3","prefix":"-khtml-"}],"safari_ios":{"version_added":"1","prefix":"-webkit-"},"samsunginternet_android":{"version_added":"1.0","prefix":"-webkit-"},"webview_android":{"version_added":"≤37","prefix":"-webkit-"}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"box-decoration-break":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/box-decoration-break","spec_url":"https://drafts.csswg.org/css-break/#break-decoration","support":{"chrome":{"prefix":"-webkit-","version_added":"22","notes":"This property is only supported for inline elements."},"chrome_android":{"prefix":"-webkit-","version_added":"18","notes":"This property is only supported for inline elements."},"edge":{"prefix":"-webkit-","version_added":"79","notes":"This property is only supported for inline elements."},"firefox":[{"version_added":"32"},{"alternative_name":"-moz-background-inline-policy","version_added":"1","version_removed":"32"}],"firefox_android":[{"version_added":"32"},{"alternative_name":"-moz-background-inline-policy","version_added":"4","version_removed":"32"}],"ie":{"version_added":false},"opera":[{"prefix":"-webkit-","version_added":"15"},{"version_added":"11.5","version_removed":"15"}],"opera_android":[{"prefix":"-webkit-","version_added":"14"},{"version_added":"11.5","version_removed":"14"}],"safari":{"version_added":"7","prefix":"-webkit-","notes":"This property is only supported for inline elements."},"safari_ios":{"version_added":"7","prefix":"-webkit-","notes":"This property is only supported for inline elements."},"samsunginternet_android":{"version_added":"1.0","prefix":"-webkit-","notes":"This property is only supported for inline elements."},"webview_android":{"prefix":"-webkit-","version_added":"≤37","notes":"This property is only supported for inline elements."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"box-direction":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/box-direction","support":{"chrome":{"prefix":"-webkit-","version_added":"1"},"chrome_android":{"prefix":"-webkit-","version_added":"18"},"edge":{"prefix":"-webkit-","version_added":"12"},"firefox":[{"prefix":"-moz-","version_added":"1"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-webkit-","version_added":"48","flags":[{"type":"preference","name":"layout.css.prefixes.webkit","value_to_set":"true"}]}],"firefox_android":[{"prefix":"-moz-","version_added":"4"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-webkit-","version_added":"48","flags":[{"type":"preference","name":"layout.css.prefixes.webkit","value_to_set":"true"}]}],"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":[{"prefix":"-webkit-","version_added":"3"},{"prefix":"-khtml-","version_added":"1.1","version_removed":"3"}],"safari_ios":{"prefix":"-webkit-","version_added":"1"},"samsunginternet_android":{"version_added":"1.0","prefix":"-webkit-"},"webview_android":{"prefix":"-webkit-","version_added":"≤37"}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"box-flex-group":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/box-flex-group","support":{"chrome":{"version_added":"1","version_removed":"67","prefix":"-webkit-"},"chrome_android":{"version_added":"18","version_removed":"67","prefix":"-webkit-"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"54","prefix":"-webkit-"},"opera_android":{"version_added":"14","version_removed":"48","prefix":"-webkit-"},"safari":[{"version_added":"3","prefix":"-webkit-"},{"version_added":"1.1","version_removed":"3","prefix":"-khtml-"}],"safari_ios":{"version_added":"1","prefix":"-webkit-"},"samsunginternet_android":{"version_added":"1.0","version_removed":"9.0","prefix":"-webkit-"},"webview_android":{"version_added":"≤37","version_removed":"67","prefix":"-webkit-"}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"box-flex":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/box-flex","support":{"chrome":{"prefix":"-webkit-","version_added":"1"},"chrome_android":{"prefix":"-webkit-","version_added":"18"},"edge":{"prefix":"-webkit-","version_added":"12"},"firefox":[{"prefix":"-moz-","version_added":"1"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-webkit-","version_added":"48","flags":[{"type":"preference","name":"layout.css.prefixes.webkit","value_to_set":"true"}]}],"firefox_android":[{"prefix":"-moz-","version_added":"4"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-webkit-","version_added":"48","flags":[{"type":"preference","name":"layout.css.prefixes.webkit","value_to_set":"true"}]}],"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":[{"prefix":"-webkit-","version_added":"3"},{"prefix":"-khtml-","version_added":"1.1","version_removed":"3"}],"safari_ios":{"prefix":"-webkit-","version_added":"1"},"samsunginternet_android":{"version_added":"1.0","prefix":"-webkit-"},"webview_android":{"prefix":"-webkit-","version_added":"≤37"}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"box-lines":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/box-lines","support":{"chrome":{"version_added":"1","version_removed":"67","prefix":"-webkit-"},"chrome_android":{"version_added":"18","version_removed":"67","prefix":"-webkit-"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"54","prefix":"-webkit-"},"opera_android":{"version_added":"14","version_removed":"48","prefix":"-webkit-"},"safari":[{"version_added":"3","prefix":"-webkit-"},{"version_added":"1.1","version_removed":"3","prefix":"-khtml-"}],"safari_ios":{"version_added":"1","prefix":"-webkit-"},"samsunginternet_android":{"version_added":"1.0","version_removed":"9.0","prefix":"-webkit-"},"webview_android":{"version_added":"≤37","version_removed":"67","prefix":"-webkit-"}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"box-ordinal-group":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/box-ordinal-group","support":{"chrome":{"version_added":"1","prefix":"-webkit-"},"chrome_android":{"version_added":"18","prefix":"-webkit-"},"edge":{"version_added":"12","prefix":"-webkit-"},"firefox":[{"prefix":"-moz-","version_added":"1"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-webkit-","version_added":"48","flags":[{"type":"preference","name":"layout.css.prefixes.webkit","value_to_set":"true"}]}],"firefox_android":[{"prefix":"-moz-","version_added":"4"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-webkit-","version_added":"48","flags":[{"type":"preference","name":"layout.css.prefixes.webkit","value_to_set":"true"}]}],"ie":{"version_added":false},"opera":{"version_added":"15","prefix":"-webkit-"},"opera_android":{"version_added":"14","prefix":"-webkit-"},"safari":[{"version_added":"3","prefix":"-webkit-"},{"version_added":"1.1","version_removed":"3","prefix":"-khtml-"}],"safari_ios":{"version_added":"1","prefix":"-webkit-"},"samsunginternet_android":{"version_added":"1.0","prefix":"-webkit-"},"webview_android":{"version_added":"≤37","prefix":"-webkit-"}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"box-orient":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/box-orient","support":{"chrome":{"prefix":"-webkit-","version_added":"1"},"chrome_android":{"prefix":"-webkit-","version_added":"18"},"edge":{"prefix":"-webkit-","version_added":"12"},"firefox":[{"prefix":"-moz-","version_added":"1"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-webkit-","version_added":"48","flags":[{"type":"preference","name":"layout.css.prefixes.webkit","value_to_set":"true"}]}],"firefox_android":[{"prefix":"-moz-","version_added":"4"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-webkit-","version_added":"48","flags":[{"type":"preference","name":"layout.css.prefixes.webkit","value_to_set":"true"}]}],"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":[{"prefix":"-webkit-","version_added":"3"},{"prefix":"-khtml-","version_added":"1.1","version_removed":"3"}],"safari_ios":{"prefix":"-webkit-","version_added":"1"},"samsunginternet_android":{"prefix":"-webkit-","version_added":"1.0"},"webview_android":{"prefix":"-webkit-","version_added":"≤37"}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"box-pack":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/box-pack","support":{"chrome":{"prefix":"-webkit-","version_added":"1"},"chrome_android":{"prefix":"-webkit-","version_added":"18"},"edge":{"prefix":"-webkit-","version_added":"12"},"firefox":[{"prefix":"-moz-","version_added":"1"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-webkit-","version_added":"48","flags":[{"type":"preference","name":"layout.css.prefixes.webkit","value_to_set":"true"}]}],"firefox_android":[{"prefix":"-moz-","version_added":"4"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-webkit-","version_added":"48","flags":[{"type":"preference","name":"layout.css.prefixes.webkit","value_to_set":"true"}]}],"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":[{"prefix":"-webkit-","version_added":"3"},{"prefix":"-khtml-","version_added":"1.1","version_removed":"3"}],"safari_ios":{"prefix":"-webkit-","version_added":"1"},"samsunginternet_android":{"prefix":"-webkit-","version_added":"1.0"},"webview_android":{"prefix":"-webkit-","version_added":"≤37"}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"box-shadow":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/box-shadow","spec_url":"https://drafts.csswg.org/css-backgrounds/#box-shadow","support":{"chrome":[{"version_added":"10","notes":"Shadows affect layout in this browser. For example, if you cast an outer shadow to a box with a width of 100%, then you'll see a scrollbar."},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"18","notes":"Shadows affect layout in this browser. For example, if you cast an outer shadow to a box with a width of 100%, then you'll see a scrollbar."},{"prefix":"-webkit-","version_added":"18"}],"edge":{"version_added":"12"},"firefox":[{"version_added":"4","notes":"Shadows affect layout in this browser. For example, if you cast an outer shadow to a box with a width of 100%, then you'll see a scrollbar."},{"prefix":"-moz-","version_added":"3.5","version_removed":"13"},{"prefix":"-webkit-","version_added":"49"}],"firefox_android":[{"version_added":"4","notes":"Shadows affect layout in this browser. For example, if you cast an outer shadow to a box with a width of 100%, then you'll see a scrollbar."},{"prefix":"-moz-","version_added":"4","version_removed":"14"},{"prefix":"-webkit-","version_added":"49"}],"ie":{"version_added":"9","notes":["To use box-shadow in Internet Explorer 9 or later, you must set border-collapse to separate.","Since version 5.5, Internet Explorer supports Microsoft's DropShadow and Shadow Filter. You can use this proprietary extension to cast a drop shadow (though the syntax and the effect are different from CSS3)"]},"opera":{"version_added":"10.5","notes":"Shadows affect layout in this browser. For example, if you cast an outer shadow to a box with a width of 100%, then you'll see a scrollbar."},"opera_android":[{"version_added":"14","notes":"Shadows affect layout in this browser. For example, if you cast an outer shadow to a box with a width of 100%, then you'll see a scrollbar."},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"5.1","notes":"Shadows affect layout in this browser. For example, if you cast an outer shadow to a box with a width of 100%, then you'll see a scrollbar."},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"5","notes":"Shadows affect layout in this browser. For example, if you cast an outer shadow to a box with a width of 100%, then you'll see a scrollbar."},{"prefix":"-webkit-","version_added":"1"}],"samsunginternet_android":[{"version_added":"1.0","notes":"Shadows affect layout in this browser. For example, if you cast an outer shadow to a box with a width of 100%, then you'll see a scrollbar."},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"≤37","notes":"Shadows affect layout in this browser. For example, if you cast an outer shadow to a box with a width of 100%, then you'll see a scrollbar."},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"inset":{"__compat":{"description":"inset","support":{"chrome":[{"version_added":"10"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"18"},{"prefix":"-webkit-","version_added":"18"}],"edge":{"version_added":"12"},"firefox":[{"version_added":"4"},{"prefix":"-moz-","version_added":"3.5","version_removed":"13"}],"firefox_android":[{"version_added":"4"},{"prefix":"-moz-","version_added":"4","version_removed":"14"}],"ie":{"version_added":"9","partial_implementation":true,"notes":["To use box-shadow in Internet Explorer 9 or later, you must set border-collapse to separate.","inset must be the last keyword in the declaration."]},"opera":{"version_added":"10.5"},"opera_android":[{"version_added":"14"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"5.1"},{"prefix":"-webkit-","version_added":"5"}],"safari_ios":[{"version_added":"5"},{"prefix":"-webkit-","version_added":"4.2"}],"samsunginternet_android":[{"version_added":"1.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"≤37"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"multiple_shadows":{"__compat":{"description":"Multiple shadows","support":{"chrome":[{"version_added":"10"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"18"},{"prefix":"-webkit-","version_added":"18"}],"edge":{"version_added":"12"},"firefox":[{"version_added":"4"},{"prefix":"-moz-","version_added":"3.5","version_removed":"13"}],"firefox_android":[{"version_added":"4"},{"prefix":"-moz-","version_added":"4","version_removed":"14"}],"ie":{"version_added":"9","notes":"To use box-shadow in Internet Explorer 9 or later, you must set border-collapse to separate."},"opera":{"version_added":"10.5"},"opera_android":[{"version_added":"14"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"5.1"},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"5"},{"prefix":"-webkit-","version_added":"1"}],"samsunginternet_android":[{"version_added":"1.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"≤37"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"spread_radius":{"__compat":{"description":"Spread radius","support":{"chrome":[{"version_added":"10"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"18"},{"prefix":"-webkit-","version_added":"18"}],"edge":{"version_added":"12"},"firefox":[{"version_added":"4"},{"prefix":"-moz-","version_added":"3.5","version_removed":"13"}],"firefox_android":[{"version_added":"4"},{"prefix":"-moz-","version_added":"4","version_removed":"14"}],"ie":{"version_added":"9","notes":"To use box-shadow in Internet Explorer 9 or later, you must set border-collapse to separate."},"opera":{"version_added":"10.5"},"opera_android":[{"version_added":"14"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"5.1"},{"prefix":"-webkit-","version_added":"5"}],"safari_ios":[{"version_added":"5"},{"prefix":"-webkit-","version_added":"4.2"}],"samsunginternet_android":[{"version_added":"1.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"≤37"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"box-sizing":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/box-sizing","spec_url":"https://drafts.csswg.org/css-sizing/#box-sizing","support":{"chrome":[{"version_added":"10","notes":"box-sizing is not respected when the height is calculated from window.getComputedStyle()."},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"18","notes":"box-sizing is not respected when the height is calculated from window.getComputedStyle()."},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"29"},{"prefix":"-moz-","version_added":"1","notes":"Before Firefox 23, box-sizing is not respected when the height is calculated from window.getComputedStyle()."},{"prefix":"-webkit-","version_added":"49"}],"firefox_android":[{"version_added":"29"},{"prefix":"-moz-","version_added":"4","notes":"Before Firefox 23, box-sizing is not respected when the height is calculated from window.getComputedStyle()."},{"prefix":"-webkit-","version_added":"49"}],"ie":{"version_added":"8","notes":"box-sizing is not respected when the height is calculated from window.getComputedStyle()."},"opera":{"version_added":"7"},"opera_android":[{"version_added":"14","notes":"box-sizing is not respected when the height is calculated from window.getComputedStyle()."},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"5.1"},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"6"},{"prefix":"-webkit-","version_added":"1"}],"samsunginternet_android":[{"version_added":"1.0","notes":"box-sizing is not respected when the height is calculated from window.getComputedStyle()."},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"4","notes":"box-sizing is not respected when the height is calculated from window.getComputedStyle()."},{"prefix":"-webkit-","version_added":"2"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"padding-box":{"__compat":{"description":"padding-box","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1","version_removed":"50"},"firefox_android":{"version_added":"4","version_removed":"50"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}}},"break-after":{"multicol_context":{"__compat":{"description":"Supported in Multi-column Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/break-after","spec_url":["https://drafts.csswg.org/css-break/#break-between","https://drafts.csswg.org/css-regions/#region-flow-break","https://drafts.csswg.org/css-multicol/#break-before-break-after-break-inside"],"support":{"chrome":{"version_added":"50"},"chrome_android":{"version_added":"50"},"edge":{"version_added":"12"},"firefox":{"version_added":"65","partial_implementation":true,"notes":"Only supported in print mode. See bug 1675322."},"firefox_android":{"version_added":"65","partial_implementation":true,"notes":"Only supported in print mode. See bug 1675322."},"ie":{"version_added":"10"},"opera":[{"version_added":"37"},{"version_added":"11.1","version_removed":"12.1"}],"opera_android":[{"version_added":"37"},{"version_added":"11.1","version_removed":"12.1"}],"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"50"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"always":{"__compat":{"description":"always","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"65","partial_implementation":true,"notes":"Only supported in print mode. See bug 1675322."},"firefox_android":{"version_added":"65","partial_implementation":true,"notes":"Only supported in print mode. See bug 1675322."},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"avoid-column":{"__compat":{"description":"avoid-column","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"column":{"__compat":{"description":"column","support":{"chrome":{"version_added":"50"},"chrome_android":{"version_added":"50"},"edge":{"version_added":"12"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":"10"},"opera":[{"version_added":"11.1","version_removed":"15"},{"version_added":"37"}],"opera_android":[{"version_added":"11.1","version_removed":"14"},{"version_added":"37"}],"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"50"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"paged_context":{"__compat":{"description":"Supported in Paged Media","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/break-after","spec_url":["https://drafts.csswg.org/css-break/#break-between","https://drafts.csswg.org/css-regions/#region-flow-break","https://drafts.csswg.org/css-multicol/#break-before-break-after-break-inside"],"support":{"chrome":{"version_added":"50"},"chrome_android":{"version_added":"50"},"edge":{"version_added":"12"},"firefox":{"version_added":"65"},"firefox_android":{"version_added":"65"},"ie":{"version_added":"10"},"opera":[{"version_added":"37"},{"version_added":"11.1","version_removed":"12.1"}],"opera_android":[{"version_added":"37"},{"version_added":"11.1","version_removed":"12.1"}],"safari":{"version_added":"10"},"safari_ios":{"version_added":"10"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"50"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"always":{"__compat":{"description":"always","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"79"},"firefox":{"version_added":"65"},"firefox_android":{"version_added":"65"},"ie":{"version_added":"10"},"opera":{"version_added":"11.1","version_removed":"12.1"},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":"50"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"page":{"__compat":{"description":"page and avoid-page","support":{"chrome":{"version_added":"50"},"chrome_android":{"version_added":"50"},"edge":{"version_added":"12"},"firefox":{"version_added":"65"},"firefox_android":{"version_added":"65"},"ie":{"version_added":"10"},"opera":{"version_added":"11.1","version_removed":"12.1"},"opera_android":{"version_added":"37"},"safari":{"version_added":"10"},"safari_ios":{"version_added":"10"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"50"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"recto":{"__compat":{"description":"recto and verso","support":{"chrome":{"version_added":false,"notes":"See bug 538475."},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}}},"break-before":{"multicol_context":{"__compat":{"description":"Supported in Multi-column Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/break-before","spec_url":["https://drafts.csswg.org/css-break/#break-between","https://drafts.csswg.org/css-regions/#region-flow-break","https://drafts.csswg.org/css-multicol/#break-before-break-after-break-inside"],"support":{"chrome":{"version_added":"50"},"chrome_android":{"version_added":"50"},"edge":{"version_added":"12"},"firefox":{"version_added":"65","partial_implementation":true,"notes":"Only supported in print mode. See bug 1675322."},"firefox_android":{"version_added":"65","partial_implementation":true,"notes":"Only supported in print mode. See bug 1675322."},"ie":{"version_added":"10"},"opera":[{"version_added":"37"},{"version_added":"11.1","version_removed":"12.1"}],"opera_android":[{"version_added":"37"},{"version_added":"11.1","version_removed":"12.1"}],"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"50"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"always":{"__compat":{"description":"always","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"65","partial_implementation":true,"notes":"Only supported in print mode. See bug 1675322."},"firefox_android":{"version_added":"65","partial_implementation":true,"notes":"Only supported in print mode. See bug 1675322."},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"avoid-column":{"__compat":{"description":"avoid-column","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":"10"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"column":{"__compat":{"description":"column","support":{"chrome":{"version_added":"51"},"chrome_android":{"version_added":"51"},"edge":{"version_added":"12"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":"10"},"opera":{"version_added":"38"},"opera_android":{"version_added":"41"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"51"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"paged_context":{"__compat":{"description":"Supported in Paged Media","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/break-before","spec_url":["https://drafts.csswg.org/css-break/#break-between","https://drafts.csswg.org/css-regions/#region-flow-break","https://drafts.csswg.org/css-multicol/#break-before-break-after-break-inside"],"support":{"chrome":{"version_added":"50"},"chrome_android":{"version_added":"50"},"edge":{"version_added":"12"},"firefox":{"version_added":"65"},"firefox_android":{"version_added":"65"},"ie":{"version_added":"10"},"opera":[{"version_added":"37"},{"version_added":"11.1","version_removed":"12.1"}],"opera_android":[{"version_added":"37"},{"version_added":"11.1","version_removed":"12.1"}],"safari":{"version_added":"10"},"safari_ios":{"version_added":"10"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"50"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"always":{"__compat":{"description":"always","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"79"},"firefox":{"version_added":"65"},"firefox_android":{"version_added":"65"},"ie":{"version_added":"10"},"opera":{"version_added":"11.1","version_removed":"12.1"},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":"50"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"page":{"__compat":{"description":"page and avoid-page","support":{"chrome":{"version_added":"50"},"chrome_android":{"version_added":"50"},"edge":{"version_added":"12"},"firefox":{"version_added":"65"},"firefox_android":{"version_added":"65"},"ie":{"version_added":"10"},"opera":{"version_added":"11.1","version_removed":"12.1"},"opera_android":{"version_added":"37"},"safari":{"version_added":"10"},"safari_ios":{"version_added":"10"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"50"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"recto":{"__compat":{"description":"recto and verso","support":{"chrome":{"version_added":false,"notes":"See bug 538475."},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}}},"break-inside":{"multicol_context":{"__compat":{"description":"Supported in Multi-column Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/break-inside","spec_url":["https://drafts.csswg.org/css-break/#break-within","https://drafts.csswg.org/css-regions/#region-flow-break","https://drafts.csswg.org/css-multicol/#break-before-break-after-break-inside"],"support":{"chrome":{"version_added":"50"},"chrome_android":{"version_added":"50"},"edge":{"version_added":"12"},"firefox":{"version_added":"65"},"firefox_android":{"version_added":"65"},"ie":{"version_added":"10"},"opera":[{"version_added":"37"},{"version_added":"11.1","version_removed":"12.1"}],"opera_android":[{"version_added":"37"},{"version_added":"11.1","version_removed":"12.1"}],"safari":{"version_added":"10"},"safari_ios":{"version_added":"10"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"50"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"column":{"__compat":{"description":"column and avoid-column","support":{"chrome":{"version_added":"50"},"chrome_android":{"version_added":"50"},"edge":{"version_added":"12"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":"10"},"opera":{"version_added":"37"},"opera_android":{"version_added":"37"},"safari":{"version_added":"10"},"safari_ios":{"version_added":"10"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"50"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"paged_context":{"__compat":{"description":"Supported in Paged Media","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/break-inside","spec_url":["https://drafts.csswg.org/css-break/#break-within","https://drafts.csswg.org/css-regions/#region-flow-break","https://drafts.csswg.org/css-multicol/#break-before-break-after-break-inside"],"support":{"chrome":{"version_added":"50"},"chrome_android":{"version_added":"50"},"edge":{"version_added":"12"},"firefox":{"version_added":"65"},"firefox_android":{"version_added":"65"},"ie":{"version_added":"10"},"opera":[{"version_added":"37"},{"version_added":"11.1","version_removed":"12.1"}],"opera_android":[{"version_added":"37"},{"version_added":"11.1","version_removed":"12.1"}],"safari":{"version_added":"10"},"safari_ios":{"version_added":"10"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"50"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"page":{"__compat":{"description":"page and avoid-page","support":{"chrome":{"version_added":"51"},"chrome_android":{"version_added":"51"},"edge":{"version_added":"12"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":[{"version_added":"38"},{"version_added":"11.1","version_removed":"12.1"}],"opera_android":[{"version_added":"41"},{"version_added":"11.1","version_removed":"12.1"}],"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"51"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}}},"caption-side":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/caption-side","spec_url":"https://drafts.csswg.org/css-logical/#caption-side","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"4"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"non_standard_values":{"__compat":{"description":"Non-standard values left, right, top-outside, and bottom-outside","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1","version_removed":"87"},"firefox_android":{"version_added":"4","version_removed":"87"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"writing-mode_relative_values":{"__compat":{"description":"top and bottom are relative to the writing-mode value","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"42"},"firefox_android":{"version_added":"42"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"caret-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/caret-color","spec_url":"https://drafts.csswg.org/css-ui/#caret-color","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":"53"},"firefox_android":{"version_added":"53"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"11.1"},"safari_ios":{"version_added":"11.3","partial_implementation":true,"notes":"While the property is recognized, implementation is incomplete. The color of the caret does not always match the color set for the element in focus. See bug 177489."},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"clear":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/clear","spec_url":"https://drafts.csswg.org/css-logical/#float-clear","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"flow_relative_values":{"__compat":{"description":"Flow-relative values inline-start and inline-end","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"55"},"firefox_android":{"version_added":"55"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"clip-path":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/clip-path","spec_url":["https://drafts.fxtf.org/css-masking/#the-clip-path","https://drafts.csswg.org/css-shapes/#supported-basic-shapes"],"support":{"chrome":[{"version_added":"55"},{"prefix":"-webkit-","version_added":"23"}],"chrome_android":[{"version_added":"55"},{"prefix":"-webkit-","version_added":"25"}],"edge":{"version_added":"12","notes":"Edge only supports clip paths defined by url()."},"firefox":{"version_added":"3.5"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"10","notes":"Internet Explorer only supports clip paths defined by url()."},"opera":[{"version_added":"42"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"42"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9.1"},{"prefix":"-webkit-","version_added":"6.1"}],"safari_ios":[{"version_added":"9.3"},{"prefix":"-webkit-","version_added":"6.1"}],"samsunginternet_android":[{"version_added":"6.0"},{"prefix":"-webkit-","version_added":"1.5"}],"webview_android":[{"version_added":"55"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"animations":{"__compat":{"description":"Animations","support":{"chrome":{"version_added":"55"},"chrome_android":{"version_added":"55"},"edge":{"version_added":"79"},"firefox":{"version_added":"49"},"firefox_android":{"version_added":"49"},"ie":{"version_added":false},"opera":{"version_added":"42"},"opera_android":{"version_added":"42"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"55"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"basic_shape":{"__compat":{"description":"<basic-shape>","support":{"chrome":{"version_added":"23"},"chrome_android":{"version_added":"25"},"edge":{"version_added":"79"},"firefox":{"version_added":"54"},"firefox_android":{"version_added":"54"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"6.1"},"safari_ios":{"version_added":"6.1"},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"fill_and_stroke_box":{"__compat":{"description":"fill-box and stroke-box","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"51","notes":"This value was supported before Firefox 51, but as an alias to border-box."},"firefox_android":{"version_added":"51","notes":"This value was supported before Firefox 51, but as an alias to border-box."},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"html":{"__compat":{"description":"On HTML elements","support":{"chrome":{"version_added":"23"},"chrome_android":{"version_added":"25"},"edge":{"version_added":"79"},"firefox":{"version_added":"3.5"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"6.1"},"safari_ios":{"version_added":"6.1"},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"path":{"__compat":{"description":"path()","support":{"chrome":{"version_added":"88"},"chrome_android":{"version_added":"88"},"edge":{"version_added":"88"},"firefox":[{"version_added":"71"},{"version_added":"63","flags":[{"type":"preference","name":"layout.css.clip-path-path.enabled","value_to_set":"true"}]}],"firefox_android":{"version_added":"63","flags":[{"type":"preference","name":"layout.css.clip-path-path.enabled","value_to_set":"true"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":[{"version_added":"13.1"},{"prefix":"-webkit-","version_added":"10"}],"safari_ios":[{"version_added":"13"},{"prefix":"-webkit-","version_added":"10"}],"samsunginternet_android":{"version_added":"15.0"},"webview_android":{"version_added":"88"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"svg":{"__compat":{"description":"On SVG elements","support":{"chrome":{"version_added":"23"},"chrome_android":{"version_added":"25"},"edge":{"version_added":"12"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":"10"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"6.1"},"safari_ios":{"version_added":"6.1"},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"clip":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/clip","spec_url":"https://drafts.fxtf.org/css-masking/#clip-property","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4","notes":"Before Internet Explorer 7, Internet Explorer incorrectly interprets clip: auto as clip: rect(auto, auto, auto, auto)."},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1","notes":"Safari incorrectly interprets clip: auto as clip: rect(auto, auto, auto, auto)."},"safari_ios":{"version_added":"1","notes":"Safari incorrectly interprets clip: auto as clip: rect(auto, auto, auto, auto)."},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":true}}},"color-adjust":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/color-adjust","spec_url":"https://drafts.csswg.org/css-color-adjust/#perf","support":{"chrome":{"version_added":"49","alternative_name":"-webkit-print-color-adjust"},"chrome_android":{"version_added":"49","alternative_name":"-webkit-print-color-adjust"},"edge":{"version_added":"79","alternative_name":"-webkit-print-color-adjust"},"firefox":{"version_added":"48"},"firefox_android":{"version_added":"48"},"ie":{"version_added":false},"opera":{"version_added":"15","alternative_name":"-webkit-print-color-adjust"},"opera_android":{"version_added":"36","alternative_name":"-webkit-print-color-adjust"},"safari":{"version_added":"6","alternative_name":"-webkit-print-color-adjust"},"safari_ios":{"version_added":"6","alternative_name":"-webkit-print-color-adjust"},"samsunginternet_android":{"version_added":"5.0","alternative_name":"-webkit-print-color-adjust"},"webview_android":{"version_added":"49","alternative_name":"-webkit-print-color-adjust"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"color-scheme":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/color-scheme","spec_url":"https://drafts.csswg.org/css-color-adjust/#color-scheme-prop","support":{"chrome":{"version_added":"81"},"chrome_android":{"version_added":"81"},"edge":{"version_added":"81"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"68"},"opera_android":{"version_added":"58"},"safari":{"version_added":"13"},"safari_ios":{"version_added":"13"},"samsunginternet_android":{"version_added":"13.0"},"webview_android":{"version_added":"81"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"only_dark":{"__compat":{"description":"only dark keyword","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"13"},"safari_ios":{"version_added":"13"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"only_light":{"__compat":{"description":"only light keyword","support":{"chrome":{"version_added":"81","version_removed":"85"},"chrome_android":{"version_added":"81","version_removed":"85"},"edge":{"version_added":"81","version_removed":"85"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"68","version_removed":"71"},"opera_android":{"version_added":"58","version_removed":"60"},"safari":{"version_added":"13"},"safari_ios":{"version_added":"13"},"samsunginternet_android":{"version_added":"13.0","version_removed":"14.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/color","spec_url":"https://drafts.csswg.org/css-color/#the-color-property","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"3"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"column-count":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/column-count","spec_url":"https://drafts.csswg.org/css-multicol/#cc","support":{"chrome":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"52"},{"prefix":"-moz-","version_added":"1.5","version_removed":"74","notes":"Prior to version 37, multiple columns didn't work with display: table-caption elements."}],"firefox_android":[{"version_added":"52"},{"prefix":"-moz-","version_added":"4","notes":"Prior to version 37, multiple columns didn't work with display: table-caption elements."}],"ie":{"version_added":"10"},"opera":[{"version_added":"11.1"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"11.1"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"1"}],"samsunginternet_android":[{"version_added":"5.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"column-fill":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/column-fill","spec_url":"https://drafts.csswg.org/css-multicol/#cf","support":{"chrome":{"version_added":"50"},"chrome_android":{"version_added":"50"},"edge":{"version_added":"12"},"firefox":[{"version_added":"52"},{"prefix":"-moz-","version_added":"13","version_removed":"74"}],"firefox_android":[{"version_added":"52"},{"prefix":"-moz-","version_added":"14"}],"ie":{"version_added":"10"},"opera":{"version_added":"37"},"opera_android":{"version_added":"37"},"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"8"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"8"}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"50"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"balance-all":{"__compat":{"description":"balance-all","support":{"chrome":{"version_added":false,"notes":"See bug 909596."},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}}},"column-gap":{"flex_context":{"__compat":{"description":"Supported in Flex Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/column-gap","spec_url":["https://drafts.csswg.org/css-align/#column-row-gap","https://drafts.csswg.org/css-grid/#gutters","https://drafts.csswg.org/css-multicol/#column-gap"],"support":{"chrome":{"version_added":"84"},"chrome_android":{"version_added":"84"},"edge":{"version_added":"84"},"firefox":{"version_added":"63"},"firefox_android":{"version_added":"63"},"ie":{"version_added":false},"opera":{"version_added":"70"},"opera_android":{"version_added":false},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"84"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid_context":{"__compat":{"description":"Supported in Grid Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/column-gap","spec_url":["https://drafts.csswg.org/css-align/#column-row-gap","https://drafts.csswg.org/css-grid/#gutters","https://drafts.csswg.org/css-multicol/#column-gap"],"support":{"chrome":[{"version_added":"66"},{"version_added":"57","alternative_name":"grid-column-gap"}],"chrome_android":[{"version_added":"66"},{"version_added":"57","alternative_name":"grid-column-gap"}],"edge":[{"version_added":"16"},{"version_added":"16","alternative_name":"grid-column-gap"}],"firefox":[{"version_added":"61"},{"version_added":"52","alternative_name":"grid-column-gap"}],"firefox_android":[{"version_added":"61"},{"version_added":"52","alternative_name":"grid-column-gap"}],"ie":{"version_added":false},"opera":[{"version_added":"53"},{"version_added":"44","alternative_name":"grid-column-gap"}],"opera_android":[{"version_added":"47"},{"version_added":"43","alternative_name":"grid-column-gap"}],"safari":[{"version_added":"12"},{"version_added":"10.1","alternative_name":"grid-column-gap"}],"safari_ios":[{"version_added":"12"},{"version_added":"10.3","alternative_name":"grid-column-gap"}],"samsunginternet_android":[{"version_added":"9.0"},{"alternative_name":"grid-column-gap","version_added":"6.0"}],"webview_android":[{"version_added":"66"},{"version_added":"57","alternative_name":"grid-column-gap"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"multicol_context":{"__compat":{"description":"Supported in Multi-column Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/column-gap","spec_url":["https://drafts.csswg.org/css-align/#column-row-gap","https://drafts.csswg.org/css-grid/#gutters","https://drafts.csswg.org/css-multicol/#column-gap"],"support":{"chrome":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"52"},{"prefix":"-moz-","version_added":"1.5","version_removed":"74","notes":"Before Firefox 3, the default value for the normal keyword was 0 and not 1em."}],"firefox_android":[{"version_added":"52"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":"10"},"opera":[{"version_added":"37"},{"prefix":"-webkit-","version_added":"15"},{"version_added":"11.1","version_removed":"15"}],"opera_android":[{"version_added":"37"},{"prefix":"-webkit-","version_added":"14"},{"version_added":"11.1","version_removed":"14"}],"safari":[{"version_added":"10"},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"10"},{"prefix":"-webkit-","version_added":"3"}],"samsunginternet_android":[{"version_added":"5.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"calc_values":{"__compat":{"description":"calc() values","support":{"chrome":{"version_added":"66"},"chrome_android":{"version_added":"66"},"edge":{"version_added":"16"},"firefox":{"version_added":"61"},"firefox_android":{"version_added":"61"},"ie":{"version_added":false},"opera":{"version_added":"53"},"opera_android":{"version_added":"47"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"9.0"},"webview_android":{"version_added":"66"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"percentage_values":{"__compat":{"description":"<percentage> values","support":{"chrome":{"version_added":"66"},"chrome_android":{"version_added":"66"},"edge":{"version_added":"16"},"firefox":{"version_added":"61"},"firefox_android":{"version_added":"61"},"ie":{"version_added":false},"opera":{"version_added":"53"},"opera_android":{"version_added":"47"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"9.0"},"webview_android":{"version_added":"66"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}}},"column-rule-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/column-rule-color","spec_url":"https://drafts.csswg.org/css-multicol/#crc","support":{"chrome":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"52"},{"prefix":"-moz-","version_added":"3.5","version_removed":"74"}],"firefox_android":[{"version_added":"52"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":"10"},"opera":[{"version_added":"11.1"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"11.1"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"1"}],"samsunginternet_android":[{"version_added":"5.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"column-rule-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/column-rule-style","spec_url":"https://drafts.csswg.org/css-multicol/#crs","support":{"chrome":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"52"},{"prefix":"-moz-","version_added":"3.5","version_removed":"74"}],"firefox_android":[{"version_added":"52"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":"10"},"opera":[{"version_added":"11.1"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"11.1"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"1"}],"samsunginternet_android":[{"version_added":"5.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"column-rule-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/column-rule-width","spec_url":"https://drafts.csswg.org/css-multicol/#crw","support":{"chrome":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"52"},{"prefix":"-moz-","version_added":"3.5","version_removed":"74"}],"firefox_android":[{"version_added":"52"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":"10"},"opera":[{"version_added":"11.1"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"11.1"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"1"}],"samsunginternet_android":[{"version_added":"5.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"column-rule":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/column-rule","spec_url":"https://drafts.csswg.org/css-multicol/#column-rule","support":{"chrome":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"52"},{"prefix":"-moz-","version_added":"3.5","version_removed":"74","notes":"Before Firefox 3, the default value for the normal keyword was 0 and not 1em."}],"firefox_android":[{"version_added":"52"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":"10"},"opera":[{"version_added":"11.1"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"11.1"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"1"}],"samsunginternet_android":[{"version_added":"5.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"column-span":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/column-span","spec_url":"https://drafts.csswg.org/css-multicol/#column-span","support":{"chrome":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"6"}],"chrome_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"71"},{"version_added":"65","flags":[{"type":"preference","name":"layout.css.column-span.enabled","value_to_set":"true"}]}],"firefox_android":{"version_added":"65","flags":[{"type":"preference","name":"layout.css.column-span.enabled","value_to_set":"true"}]},"ie":{"version_added":"10"},"opera":[{"version_added":"11.1"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"11.1"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"5.1"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"5"}],"samsunginternet_android":[{"version_added":"5.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"column-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/column-width","spec_url":["https://drafts.csswg.org/css-sizing/#column-sizing","https://drafts.csswg.org/css-multicol/#cw"],"support":{"chrome":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"50"},{"prefix":"-moz-","version_added":"1.5","version_removed":"74","notes":"Prior to version 37, multiple columns didn't work with display: table-caption elements."}],"firefox_android":[{"version_added":"50"},{"prefix":"-moz-","version_added":"4","notes":"Prior to version 37, multiple columns didn't work with display: table-caption elements."}],"ie":{"version_added":"10"},"opera":[{"version_added":"11.1"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"11.1"},{"prefix":"-webkit-","version_added":"15"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"1"}],"samsunginternet_android":[{"version_added":"5.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"intrinsic_sizes":{"__compat":{"description":"Intrinsic sizes","support":{"chrome":{"version_added":false,"notes":"See bug 964183."},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}}},"columns":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/columns","spec_url":"https://drafts.csswg.org/css-multicol/#columns","support":{"chrome":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"50"}],"chrome_android":{"version_added":"50"},"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"52"},{"prefix":"-moz-","version_added":"9","version_removed":"74","notes":"Prior to version 37, multiple columns didn't work with display: table-caption elements."}],"firefox_android":[{"version_added":"52"},{"prefix":"-moz-","version_added":"22","notes":"Prior to version 37, multiple columns didn't work with display: table-caption elements."}],"ie":{"version_added":"10"},"opera":[{"version_added":"11.1"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"11.1"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3.2"}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"2"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"contain-intrinsic-size":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/contain-intrinsic-size","spec_url":"https://drafts.csswg.org/css-sizing-4/#propdef-contain-intrinsic-size","support":{"chrome":{"version_added":"83"},"chrome_android":{"version_added":"83"},"edge":{"version_added":"83"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"69"},"opera_android":{"version_added":"59"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"13.0"},"webview_android":{"version_added":"83"}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"contain":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/contain","spec_url":"https://drafts.csswg.org/css-contain/#contain-property","support":{"chrome":{"version_added":"52"},"chrome_android":{"version_added":"52"},"edge":{"version_added":"79"},"firefox":[{"version_added":"69","notes":"Firefox does not support the style value."},{"version_added":"41","flags":[{"type":"preference","name":"layout.css.contain.enabled","value_to_set":"true"}]}],"firefox_android":{"version_added":"41","flags":[{"type":"preference","name":"layout.css.contain.enabled","value_to_set":"true"}],"notes":"Firefox does not support the style value."},"ie":{"version_added":false},"opera":{"version_added":"40"},"opera_android":{"version_added":"41"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"52"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"content-visibility":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/content-visibility","spec_url":"https://drafts.csswg.org/css-contain/#content-visibility","support":{"chrome":{"version_added":"85"},"chrome_android":{"version_added":"85"},"edge":{"version_added":"85"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"71"},"opera_android":{"version_added":"60"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"85"}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"content":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/content","spec_url":"https://drafts.csswg.org/css-content/#content-property","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"4"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"alt_text":{"__compat":{"description":"Alternative text after /","support":{"chrome":{"version_added":"77"},"chrome_android":{"version_added":"77"},"edge":{"version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"64"},"opera_android":{"version_added":"55"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"12.0"},"webview_android":{"version_added":"77"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"element_replacement":{"__compat":{"description":"Element replacement","support":{"chrome":{"version_added":"28"},"chrome_android":{"version_added":"28"},"edge":{"version_added":"79"},"firefox":{"version_added":"63"},"firefox_android":{"version_added":"63"},"ie":{"version_added":false},"opera":{"version_added":"7"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"none_applies_to_elements":{"__compat":{"description":"content: none for elements","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.element-content-none.enabled"}]},"firefox_android":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.element-content-none.enabled"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"url":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/url()","spec_url":"https://drafts.csswg.org/css-values/#urls","description":"url()","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"counter-increment":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/counter-increment","spec_url":"https://drafts.csswg.org/css-lists/#increment-set","support":{"chrome":{"version_added":"2"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"25"},"ie":{"version_added":"8"},"opera":{"version_added":"9.2"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"counter-reset":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/counter-reset","spec_url":"https://drafts.csswg.org/css-lists/#counter-reset","support":{"chrome":{"version_added":"2"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"25"},"ie":{"version_added":"8"},"opera":{"version_added":"9.2"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"counter-set":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/counter-set","spec_url":"https://drafts.csswg.org/css-lists/#propdef-counter-set","support":{"chrome":{"version_added":"85"},"chrome_android":{"version_added":"85"},"edge":{"version_added":"85"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"71"},"opera_android":{"version_added":"60"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"85"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"cursor":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/cursor","spec_url":"https://drafts.csswg.org/css-ui/#cursor","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"Starting in Firefox 67, the maximum size allowed for custom cursors is 32x32 pixels due to cursors being misused by certain malicious sites."},"firefox_android":{"version_added":false},"ie":{"version_added":"4","notes":"In Internet Explorer 11, when cursor is applied to an element and this element is underneath an open <select> menu and the user hovers over a <select> menu item that's on top of said element, the cursor for said element will be displayed rather than the <select>'s normal cursor. See bug 817822."},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"alias":{"__compat":{"description":"alias","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1.5"},"firefox_android":{"version_added":false},"ie":{"version_added":"10"},"opera":{"version_added":"10.6"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"all-scroll":{"__compat":{"description":"all-scroll","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1.5"},"firefox_android":{"version_added":false},"ie":{"version_added":"6"},"opera":{"version_added":"10.6"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"auto":{"__compat":{"description":"auto","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":false},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"bidirectional_resize":{"__compat":{"description":"Bidirectional resize cursors (ew-resize, nesw-resize, ns-resize, and nwse-resize)","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1.5"},"firefox_android":{"version_added":false},"ie":{"version_added":"10"},"opera":{"version_added":"10.6"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"cell":{"__compat":{"description":"cell","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1.5"},"firefox_android":{"version_added":false},"ie":{"version_added":"10"},"opera":{"version_added":"10.6"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"col-resize":{"__compat":{"description":"col-resize","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1.5"},"firefox_android":{"version_added":false},"ie":{"version_added":"6"},"opera":{"version_added":"10.6"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"context-menu":{"__compat":{"description":"context-menu","support":{"chrome":{"version_added":"1","notes":"This cursor is only supported on macOS and Linux."},"chrome_android":{"version_added":"18","notes":"This cursor is only supported on macOS and Linux."},"edge":{"version_added":"12"},"firefox":{"version_added":"1.5","notes":"This cursor is only supported on macOS and Linux."},"firefox_android":{"version_added":false},"ie":{"version_added":"10"},"opera":{"version_added":"10.6"},"opera_android":{"version_added":"14","notes":"This cursor is only supported on macOS and Linux."},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0","notes":"This cursor is only supported on macOS and Linux."},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"copy":{"__compat":{"description":"copy","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1.5"},"firefox_android":{"version_added":false},"ie":{"version_added":"10"},"opera":{"version_added":"10.6"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"crosshair":{"__compat":{"description":"crosshair","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":false},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"default":{"__compat":{"description":"default","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":false},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grab":{"__compat":{"description":"Grab cursors (grab and grabbing)","support":{"chrome":[{"version_added":"68","notes":"Chrome also continues to support the prefixed versions."},{"prefix":"-webkit-","version_added":"1","notes":"Chrome 22 added Windows support."}],"chrome_android":[{"version_added":"68","notes":"Chrome also continues to support the prefixed versions."},{"prefix":"-webkit-","version_added":"18","notes":"Chrome 22 added Windows support."}],"edge":{"version_added":"14"},"firefox":[{"version_added":"27"},{"prefix":"-moz-","version_added":"1.5"}],"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":[{"version_added":"55","notes":"Opera also continues to support the prefixed versions."},{"prefix":"-webkit-","version_added":"15","notes":"Opera 22 added Windows support."}],"opera_android":[{"version_added":"48","notes":"Opera also continues to support the prefixed versions."},{"prefix":"-webkit-","version_added":"14","notes":"Opera 22 added Windows support."}],"safari":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"4"}],"safari_ios":{"version_added":"1"},"samsunginternet_android":[{"version_added":"10.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"help":{"__compat":{"description":"help","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":false},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"inherit":{"__compat":{"description":"inherit","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":false},"ie":{"version_added":"8"},"opera":{"version_added":"9"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"move":{"__compat":{"description":"move","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":false},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"no-drop":{"__compat":{"description":"no-drop","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1.5"},"firefox_android":{"version_added":false},"ie":{"version_added":"6"},"opera":{"version_added":"10.6"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"none":{"__compat":{"description":"none","support":{"chrome":{"version_added":"5"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3"},"firefox_android":{"version_added":false},"ie":{"version_added":"9"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"not-allowed":{"__compat":{"description":"not-allowed","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1.5"},"firefox_android":{"version_added":false},"ie":{"version_added":"6"},"opera":{"version_added":"10.6"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"pointer":{"__compat":{"description":"pointer","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":false},"ie":{"version_added":"6"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"progress":{"__compat":{"description":"progress","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":false},"ie":{"version_added":"6"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"row-resize":{"__compat":{"description":"row-resize","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1.5"},"firefox_android":{"version_added":false},"ie":{"version_added":"6"},"opera":{"version_added":"10.6"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"text":{"__compat":{"description":"text","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":false},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"unidirectional_resize":{"__compat":{"description":"Unidirectional resize cursors (n-resize, e-resize, s-resize, w-resize, ne-resize, nw-resize, se-resize, and sw-resize)","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":false},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"url":{"__compat":{"description":"url()","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1.5","notes":"Firefox 4 added macOS support."},"firefox_android":{"version_added":false},"ie":{"version_added":"6"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"url_positioning_syntax":{"__compat":{"description":"url() positioning syntax","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"1.5","notes":"Firefox 4 added macOS support."},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"vertical-text":{"__compat":{"description":"vertical-text","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1.5"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"10.6"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"wait":{"__compat":{"description":"wait","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":false},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"zoom":{"__compat":{"description":"Zoom cursors (zoom-in and zoom-out)","support":{"chrome":[{"version_added":"37"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"37"},{"prefix":"-webkit-","version_added":"18"}],"edge":{"version_added":"12"},"firefox":[{"version_added":"24"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":[{"version_added":"24"},{"prefix":"-webkit-","version_added":"15","version_removed":"23"}],"opera_android":[{"version_added":"24"},{"prefix":"-webkit-","version_added":"14","version_removed":"24"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":{"version_added":"1"},"samsunginternet_android":[{"version_added":"3.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"custom-property":{"__compat":{"description":"--*","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/--*","spec_url":"https://drafts.csswg.org/css-variables/#defining-variables","support":{"chrome":{"version_added":"49"},"chrome_android":{"version_added":"49"},"edge":{"version_added":"15"},"firefox":{"version_added":"31"},"firefox_android":{"version_added":"31"},"ie":{"version_added":false},"opera":{"version_added":"36"},"opera_android":{"version_added":"36"},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"49"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"env":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/env()","spec_url":"https://drafts.csswg.org/css-env/#env-function","description":"env()","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"65"},"firefox_android":{"version_added":"65"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":[{"version_added":"11.1"},{"version_added":"11","version_removed":"11.1","alternative_name":"constant"}],"safari_ios":[{"version_added":"11.3"},{"version_added":"11","version_removed":"11.3","alternative_name":"constant"}],"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"var":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/var()","spec_url":"https://drafts.csswg.org/css-variables/#using-variables","description":"var()","support":{"chrome":{"version_added":"49"},"chrome_android":{"version_added":"49"},"edge":{"version_added":"15"},"firefox":{"version_added":"31"},"firefox_android":{"version_added":"31"},"ie":{"version_added":false},"opera":{"version_added":"36"},"opera_android":{"version_added":"36"},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"50"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"direction":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/direction","spec_url":"https://drafts.csswg.org/css-writing-modes/#direction","support":{"chrome":{"version_added":"2"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"5.5"},"opera":{"version_added":"9.2"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"display":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/display","spec_url":"https://drafts.csswg.org/css-display/#the-display-properties","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"contents":{"__compat":{"support":{"chrome":{"version_added":"65"},"chrome_android":{"version_added":"65"},"edge":{"version_added":"79"},"firefox":{"version_added":"37"},"firefox_android":{"version_added":"37"},"ie":{"version_added":false},"opera":{"version_added":"52"},"opera_android":{"version_added":"47"},"safari":{"version_added":"11.1"},"safari_ios":{"version_added":"11.3"},"samsunginternet_android":{"version_added":"9.0"},"webview_android":{"version_added":"65"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"contents_unusual":{"__compat":{"description":"Specific behavior of unusual elements when display: contents is applied to them","support":{"chrome":{"version_added":"65"},"chrome_android":{"version_added":"65"},"edge":{"version_added":"79"},"firefox":{"version_added":"59"},"firefox_android":{"version_added":"59"},"ie":{"version_added":false},"opera":{"version_added":"52"},"opera_android":{"version_added":"47"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"9.0"},"webview_android":{"version_added":"65"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"display-outside":{"__compat":{"description":"<display-outside>","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/display-outside","spec_url":"https://drafts.csswg.org/css-display/#typedef-display-outside","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"flex":{"__compat":{"support":{"chrome":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"21"}],"chrome_android":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"25"}],"edge":{"version_added":"12"},"firefox":{"version_added":"20","notes":"Firefox 28 added multi-line flexbox support."},"firefox_android":{"version_added":"20","notes":"Firefox 28 added multi-line flexbox support."},"ie":[{"version_added":"11","partial_implementation":true,"notes":"IE incorrectly positions inline block content inside flex containers. See the discussion on Microsoft Answers."},{"alternative_name":"-ms-flexbox","version_added":"8","partial_implementation":true,"notes":"IE incorrectly positions inline block content inside flex containers. See the discussion on Microsoft Answers."}],"opera":[{"version_added":"16"},{"prefix":"-webkit-","version_added":"15"},{"version_added":"12.1","version_removed":"15"}],"opera_android":[{"version_added":"16"},{"prefix":"-webkit-","version_added":"14"},{"version_added":"12.1","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":[{"version_added":"2.0"},{"prefix":"-webkit-","version_added":"1.5"}],"webview_android":[{"version_added":"4.4"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"flow-root":{"__compat":{"support":{"chrome":{"version_added":"58"},"chrome_android":{"version_added":"58"},"edge":{"version_added":"79"},"firefox":{"version_added":"53"},"firefox_android":{"version_added":"53"},"ie":{"version_added":false},"opera":{"version_added":"45"},"opera_android":{"version_added":"43"},"safari":{"version_added":"13"},"safari_ios":{"version_added":"13"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"58"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid":{"__compat":{"support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":[{"version_added":"16"},{"prefix":"-ms-","version_added":"12"}],"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"prefix":"-ms-","version_added":"10","partial_implementation":true,"notes":"Internet Explorer implements an older version of the specification."},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"notes":"Samsung Internet added this earlier than the corresponding Chrome version would indicate.","version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"inline-block":{"__compat":{"support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":[{"version_added":"8"},{"version_added":"6","partial_implementation":true,"notes":"Until Internet Explorer 8, inline-block is only for natural inline elements."}],"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"inline-flex":{"__compat":{"support":{"chrome":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"21"}],"chrome_android":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"25"}],"edge":{"version_added":"12"},"firefox":{"version_added":"20","notes":"Firefox 28 added multi-line flexbox support."},"firefox_android":{"version_added":"20","notes":"Firefox 28 added multi-line flexbox support."},"ie":[{"version_added":"11"},{"alternative_name":"-ms-inline-flexbox","version_added":"8"}],"opera":[{"version_added":"16"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"16"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"6.1"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"6.1"}],"samsunginternet_android":[{"version_added":"2.0"},{"prefix":"-webkit-","version_added":"1.5"}],"webview_android":[{"version_added":"4.4"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"inline-grid":{"__compat":{"support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":[{"version_added":"16"},{"prefix":"-ms-","version_added":"12"}],"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"prefix":"-ms-","version_added":"10","partial_implementation":true,"notes":"Internet Explorer implements an older version of the specification."},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"notes":"Samsung Internet added this earlier than the corresponding Chrome version would indicate.","version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"inline-table":{"__compat":{"support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"list-item":{"__compat":{"support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"6"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"legend-support":{"__compat":{"description":"Supported on <legend>","support":{"chrome":{"version_added":"71"},"chrome_android":{"version_added":"71"},"edge":{"version_added":"79"},"firefox":{"version_added":"64"},"firefox_android":{"version_added":"64"},"ie":{"version_added":false},"opera":{"version_added":"58"},"opera_android":{"version_added":"50"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"71"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"multi-keyword_values":{"__compat":{"description":"Multi-keyword values","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"70"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"none":{"__compat":{"support":{"chrome":{"version_added":"1","notes":"Chrome 65 stopped creating layout objects for elements inside an <iframe> with display:none applied."},"chrome_android":{"version_added":"18","notes":"Chrome 65 stopped creating layout objects for elements inside an <iframe> with display:none applied."},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"7","notes":"Opera 52 stopped creating layout objects for elements inside an <iframe> with display:none applied."},"opera_android":{"version_added":"10.1","notes":"Opera Android 47 stopped creating layout objects for elements inside an <iframe> with display:none applied."},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0","notes":"Chrome 65 stopped creating layout objects for elements inside an <iframe> with display:none applied."},"webview_android":{"version_added":"≤37","notes":"WebView 65 stopped creating layout objects for elements inside an <iframe> with display:none applied."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ruby_values":{"__compat":{"description":"ruby, ruby-base, ruby-base-container, ruby-text, and ruby-text-container","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"79"},"firefox":{"version_added":"38"},"firefox_android":{"version_added":"38"},"ie":{"version_added":"7"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"table_values":{"__compat":{"description":"table, table-cell, table-column, table-column-group, table-footer-group, table-header-group, table-row, and table-row-group","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"xul_box_values":{"__compat":{"description":"-moz-box and -moz-inline-box","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"1","version_removed":"64","notes":"This is still available to Firefox UI code."},{"version_added":"62","flags":[{"name":"layout.css.xul-box-display-values.content.enabled","type":"preference","value_to_set":"true"}]}],"firefox_android":[{"version_added":"4","version_removed":"64","notes":"This is still available to Firefox UI code."},{"version_added":"62","flags":[{"name":"layout.css.xul-box-display-values.content.enabled","type":"preference","value_to_set":"true"}]}],"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"xul_deck_values":{"__compat":{"description":"-moz-deck","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"1","version_removed":"62","notes":"This is still available to Firefox UI code."},{"version_added":"62","flags":[{"name":"layout.css.xul-display-values.content.enabled","type":"preference","value_to_set":"true"}]}],"firefox_android":[{"version_added":"4","version_removed":"62","notes":"This is still available to Firefox UI code."},{"version_added":"62","flags":[{"name":"layout.css.xul-display-values.content.enabled","type":"preference","value_to_set":"true"}]}],"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"xul_grid_values":{"__compat":{"description":"-moz-grid, -moz-grid-group, and -moz-grid-line","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"1","version_removed":"62","notes":"This is still available to Firefox UI code."},{"version_added":"62","flags":[{"name":"layout.css.xul-display-values.content.enabled","type":"preference","value_to_set":"true"}]}],"firefox_android":[{"version_added":"4","version_removed":"62","notes":"This is still available to Firefox UI code."},{"version_added":"62","flags":[{"name":"layout.css.xul-display-values.content.enabled","type":"preference","value_to_set":"true"}]}],"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"xul_inline_grid_stack":{"__compat":{"description":"-moz-inline-grid and -moz-inline-stack","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"1","version_removed":"62","notes":"Available to Firefox UI code."},{"version_added":"62","version_removed":"70","flags":[{"name":"layout.css.xul-display-values.content.enabled","type":"preference","value_to_set":"true"}]}],"firefox_android":[{"version_added":"4","version_removed":"62","notes":"This is still available to Firefox UI code."},{"version_added":"62","flags":[{"name":"layout.css.xul-display-values.content.enabled","type":"preference","value_to_set":"true"}]}],"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"xul_popup_values":{"__compat":{"description":"-moz-popup","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"1","version_removed":"62","notes":"This is still available to Firefox UI code."},{"version_added":"62","flags":[{"name":"layout.css.xul-display-values.content.enabled","type":"preference","value_to_set":"true"}]}],"firefox_android":[{"version_added":"4","version_removed":"62","notes":"This is still available to Firefox UI code."},{"version_added":"62","flags":[{"name":"layout.css.xul-display-values.content.enabled","type":"preference","value_to_set":"true"}]}],"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"xul_stack_value":{"__compat":{"description":"-moz-stack","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"1","version_removed":"62","notes":"This is still available to Firefox UI code."},{"version_added":"62","flags":[{"name":"layout.css.xul-display-values.content.enabled","type":"preference","value_to_set":"true"}]}],"firefox_android":[{"version_added":"4","version_removed":"62","notes":"This is still available to Firefox UI code."},{"version_added":"62","flags":[{"name":"layout.css.xul-display-values.content.enabled","type":"preference","value_to_set":"true"}]}],"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}}},"empty-cells":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/empty-cells","spec_url":"https://drafts.csswg.org/css2/#empty-cells","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"4"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"filter":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/filter","spec_url":"https://drafts.fxtf.org/filter-effects/#FilterProperty","support":{"chrome":[{"version_added":"53"},{"prefix":"-webkit-","version_added":"18","notes":"In Chrome 18 to 19, the saturate() function only takes integers instead of decimal or percentage values. From Chrome 20, this bug is fixed."}],"chrome_android":{"version_added":"53"},"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"35"},{"prefix":"-webkit-","version_added":"49"}],"firefox_android":[{"version_added":"35"},{"prefix":"-webkit-","version_added":"49"}],"ie":{"version_added":false,"notes":"Internet Explorer 4 to 9 implemented a non-standard filter property. The syntax was completely different from this one and is not documented here."},"opera":[{"version_added":"40"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"41"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9.1"},{"prefix":"-webkit-","version_added":"6"}],"safari_ios":[{"version_added":"9.3"},{"prefix":"-webkit-","version_added":"6"}],"samsunginternet_android":{"version_added":"6.0"},"webview_android":[{"version_added":"53"},{"prefix":"-webkit-","version_added":"4.4"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"svg":{"__compat":{"description":"On SVG elements","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"35"},"firefox_android":{"version_added":"35"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"flex-basis":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/flex-basis","spec_url":"https://drafts.csswg.org/css-flexbox/#flex-basis-property","support":{"chrome":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"22"}],"chrome_android":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"25"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"22","notes":"Since Firefox 28, multi-line flexbox is supported."},{"prefix":"-webkit-","version_added":"49"}],"firefox_android":[{"version_added":"22","notes":"Since Firefox 28, multi-line flexbox is supported."},{"prefix":"-webkit-","version_added":"49"}],"ie":{"version_added":"11","notes":"When a non-auto flex-basis is specified, Internet Explorer 10 and 11 always uses a content-box box model to calculate the size of a flex item, even if box-sizing: border-box is applied to the element. See Flexbug #7 for more info."},"opera":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":[{"version_added":"2.0"},{"prefix":"-webkit-","version_added":"1.5"}],"webview_android":[{"version_added":"4.4"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"auto":{"__compat":{"description":"auto","support":{"chrome":{"version_added":"22"},"chrome_android":{"version_added":"25"},"edge":{"version_added":"12"},"firefox":{"version_added":"22"},"firefox_android":{"version_added":"22"},"ie":{"version_added":"11"},"opera":{"version_added":"12.1"},"opera_android":{"version_added":"12.1"},"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"content":{"__compat":{"description":"content","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"79"},"firefox":{"version_added":"61"},"firefox_android":{"version_added":"61"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"max-content":{"__compat":{"support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"66"},{"version_added":"22","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"22","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"min-content":{"__compat":{"support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"66"},{"version_added":"22","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"22","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"flex-direction":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/flex-direction","spec_url":"https://drafts.csswg.org/css-flexbox/#flex-direction-property","support":{"chrome":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"21"}],"chrome_android":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"25"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"20","partial_implementation":true,"notes":["Since Firefox 28, multi-line flexbox is supported.","Before Firefox 81, overflow with *-reverse is unsupported. See bug 1042151."]},{"prefix":"-webkit-","version_added":"49"}],"firefox_android":[{"version_added":"20","partial_implementation":true,"notes":["Since Firefox 28, multi-line flexbox is supported.","Before Firefox 81, overflow with *-reverse is unsupported. See bug 1042151."]},{"prefix":"-webkit-","version_added":"49"}],"ie":[{"version_added":"11"},{"prefix":"-ms-","version_added":"10"}],"opera":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":[{"version_added":"2.0"},{"prefix":"-webkit-","version_added":"1.5"}],"webview_android":[{"version_added":"4.4"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"flex-flow":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/flex-flow","spec_url":"https://drafts.csswg.org/css-flexbox/#flex-flow-property","support":{"chrome":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"21"}],"chrome_android":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"25"}],"edge":{"version_added":"12"},"firefox":[{"version_added":"28"},{"prefix":"-webkit-","version_added":"49"}],"firefox_android":[{"version_added":"28"},{"prefix":"-webkit-","version_added":"49"}],"ie":{"version_added":"11"},"opera":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":[{"version_added":"2.0"},{"prefix":"-webkit-","version_added":"1.5"}],"webview_android":[{"version_added":"4.4"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"flex-grow":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/flex-grow","spec_url":"https://drafts.csswg.org/css-flexbox/#flex-grow-property","support":{"chrome":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"22"}],"chrome_android":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"25"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":{"version_added":"20","notes":"Since Firefox 28, multi-line flexbox is supported."},"firefox_android":{"version_added":"20","notes":"Since Firefox 28, multi-line flexbox is supported."},"ie":[{"version_added":"11"},{"version_added":"10","alternative_name":"-ms-flex-positive"}],"opera":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":[{"version_added":"2.0"},{"prefix":"-webkit-","version_added":"1.5"}],"webview_android":[{"version_added":"4.4"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"less_than_zero_animate":{"__compat":{"description":"<0 animate","support":{"chrome":{"version_added":"49"},"chrome_android":{"version_added":"49"},"edge":{"version_added":"79"},"firefox":{"version_added":"32","notes":"Before Firefox 32, Firefox wasn't able to animate values starting or stopping at 0."},"firefox_android":{"version_added":"32","notes":"Before Firefox 32, Firefox wasn't able to animate values starting or stopping at 0."},"ie":{"version_added":false},"opera":{"version_added":"36"},"opera_android":{"version_added":"36"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"49"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}}},"flex-shrink":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/flex-shrink","spec_url":"https://drafts.csswg.org/css-flexbox/#flex-shrink-property","support":{"chrome":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"22"}],"chrome_android":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"25"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"20","notes":["Since Firefox 28, multi-line flexbox is supported.","Before Firefox 32, Firefox wasn't able to animate values starting or stopping at 0."]},{"prefix":"-webkit-","version_added":"49"}],"firefox_android":[{"version_added":"20","notes":["Since Firefox 28, multi-line flexbox is supported.","Before Firefox 32, Firefox wasn't able to animate values starting or stopping at 0."]},{"prefix":"-webkit-","version_added":"49"}],"ie":{"version_added":"10","notes":"Internet Explorer 10 uses 0 instead of 1 as the initial value for the flex-shrink property. A workaround is to always set an explicit value for flex-shrink. See Flexbug #6 for more info."},"opera":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"8"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"8"}],"samsunginternet_android":[{"version_added":"2.0"},{"prefix":"-webkit-","version_added":"1.5"}],"webview_android":[{"version_added":"4.4"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"flex-wrap":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/flex-wrap","spec_url":"https://drafts.csswg.org/css-flexbox/#flex-wrap-property","support":{"chrome":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"21"}],"chrome_android":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"25"}],"edge":{"version_added":"12"},"firefox":{"version_added":"28"},"firefox_android":{"version_added":"52"},"ie":{"version_added":"11","partial_implementation":true,"notes":"Partial support due to large number of bugs present. See Flexbugs."},"opera":{"version_added":"17"},"opera_android":{"version_added":"18"},"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"6.1"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"6.1"}],"samsunginternet_android":[{"version_added":"2.0"},{"prefix":"-webkit-","version_added":"1.5"}],"webview_android":[{"version_added":"4.4"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"flex":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/flex","spec_url":"https://drafts.csswg.org/css-flexbox/#flex-property","support":{"chrome":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"21"}],"chrome_android":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"25"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"20","notes":["Since Firefox 28, multi-line flexbox is supported.","Before Firefox 32, Firefox wasn't able to animate values starting or stopping at 0.","Until Firefox 61, flex items that are sized according to their content are sized using fit-content, not max-content."]},{"prefix":"-webkit-","version_added":"49"}],"firefox_android":[{"version_added":"20","notes":["Since Firefox 28, multi-line flexbox is supported.","Before Firefox 32, Firefox wasn't able to animate values starting or stopping at 0.","Until Firefox 61, flex items that are sized according to their content are sized using fit-content, not max-content."]},{"prefix":"-webkit-","version_added":"49"}],"ie":[{"version_added":"11","notes":["Internet Explorer 11 ignores uses of calc() in the flex-basis part of the flex syntax. This can be worked around by using the longhand properties instead of the shorthand. See Flexbug #8 for more info.","Internet Explorer 11 considers a unitless value in the flex-basis part to be syntactically invalid (and will thus be ignored). A workaround is to always include a unit in the flex-basis part of the flex shorthand value. See Flexbug #4 for more info."]},{"prefix":"-ms-","version_added":"10","notes":["Internet Explorer 10 and 11 ignore uses of calc() in the flex-basis part of the flex syntax. This can be worked around by using the longhand properties instead of the shorthand. See Flexbug #8 for more info.","Internet Explorer 10 and 11 consider a unitless value in the flex-basis part to be syntactically invalid (and will thus be ignored). A workaround is to always include a unit in the flex-basis part of the flex shorthand value. See Flexbug #4 for more info."]}],"opera":{"version_added":"12.1"},"opera_android":{"version_added":"12.1"},"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":[{"version_added":"2.0"},{"prefix":"-webkit-","version_added":"1.5"}],"webview_android":[{"version_added":"4.4"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"float":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/float","spec_url":"https://drafts.csswg.org/css-logical/#float-clear","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"flow_relative_values":{"__compat":{"description":"Flow-relative values inline-start and inline-end","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"55"},"firefox_android":{"version_added":"55"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"font-family":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-family","spec_url":["https://drafts.csswg.org/css-fonts/#generic-font-families","https://drafts.csswg.org/css-fonts/#font-family-prop"],"support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"Not supported on option elements. See bug 1536148."},"firefox_android":{"version_added":"4"},"ie":{"version_added":"3"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"system_ui":{"__compat":{"description":"system-ui","support":{"chrome":{"version_added":"56"},"chrome_android":{"version_added":"56"},"edge":{"version_added":"79"},"firefox":[{"version_added":"92"},{"alternative_name":"-apple-system","version_added":"43","notes":"Supported on macOS only."}],"firefox_android":{"version_added":"92"},"ie":{"version_added":false},"opera":{"version_added":"43"},"opera_android":{"version_added":"43"},"safari":{"alternative_name":"-apple-system","version_added":"9","notes":"Supported since macOS 10.11."},"safari_ios":{"alternative_name":"-apple-system","version_added":"9"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"56"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"font-feature-settings":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-feature-settings","spec_url":"https://drafts.csswg.org/css-fonts/#font-feature-settings-prop","support":{"chrome":[{"version_added":"48"},{"prefix":"-webkit-","version_added":"16"}],"chrome_android":{"version_added":"48"},"edge":{"version_added":"15"},"firefox":[{"version_added":"34","notes":"The ISO/IEC CD 14496-22 3rd edition suggests using the ssty feature to provide glyph variants more suitable for use in scripts (for example primes used as superscripts). Starting with Firefox 29, this is done automatically by the MathML rendering engine. The ISO/IEC CD 14496-22 3rd edition also suggests applying the dtls feature to letters when placing mathematical accents to get dotless forms (for example dotless i, j with a hat). Starting with Firefox 35, this is done automatically by the MathML rendering engine. You can override the default values determined by the MathML rendering engine with CSS."},{"prefix":"-moz-","version_added":"15","notes":"From Firefox 4 to Firefox 14 (inclusive), Firefox supported an older, slightly different syntax. See OpenType Font Feature support in Firefox 4."}],"firefox_android":[{"version_added":"34","notes":"The ISO/IEC CD 14496-22 3rd edition suggests using the ssty feature to provide glyph variants more suitable for use in scripts (for example primes used as superscripts). Starting with Firefox 29, this is done automatically by the MathML rendering engine. The ISO/IEC CD 14496-22 3rd edition also suggests applying the dtls feature to letters when placing mathematical accents to get dotless forms (for example dotless i, j with a hat). Starting with Firefox 35, this is done automatically by the MathML rendering engine. You can override the default values determined by the MathML rendering engine with CSS."},{"prefix":"-moz-","version_added":"15","notes":"From Firefox 4 to Firefox 14 (inclusive), Firefox supported an older, slightly different syntax. See OpenType Font Feature support in Firefox 4."}],"ie":{"version_added":"10"},"opera":[{"version_added":"35"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"35"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9.1"},{"partial_implementation":true,"version_added":"4","version_removed":"6"}],"safari_ios":[{"version_added":"9.3"},{"partial_implementation":true,"version_added":"3.2","version_removed":"6.1"}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"font-kerning":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-kerning","spec_url":"https://drafts.csswg.org/css-fonts/#font-kerning-prop","support":{"chrome":[{"version_added":"33"},{"prefix":"-webkit-","version_added":"29","version_removed":"33"}],"chrome_android":[{"version_added":"33"},{"prefix":"-webkit-","version_added":"29","version_removed":"33"}],"edge":{"version_added":"79"},"firefox":{"version_added":"32"},"firefox_android":{"version_added":"32"},"ie":{"version_added":false},"opera":[{"version_added":"20"},{"prefix":"-webkit-","version_added":"16","version_removed":"20"}],"opera_android":[{"version_added":"20"},{"prefix":"-webkit-","version_added":"16","version_removed":"20"}],"safari":[{"version_added":"9"},{"version_added":"6","prefix":"-webkit-"}],"safari_ios":[{"version_added":"9"},{"version_added":"6","prefix":"-webkit-"}],"samsunginternet_android":[{"version_added":"2.0"},{"prefix":"-webkit-","version_added":"1.0","version_removed":"2.0"}],"webview_android":[{"version_added":"4.4.3"},{"prefix":"-webkit-","version_added":"4.4","version_removed":"4.4.3"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"font-language-override":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-language-override","spec_url":"https://drafts.csswg.org/css-fonts/#font-language-override-prop","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"34"},{"prefix":"-moz-","version_added":"4"}],"firefox_android":[{"version_added":"34"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"font-optical-sizing":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-optical-sizing","spec_url":"https://drafts.csswg.org/css-fonts/#font-optical-sizing-def","support":{"chrome":{"version_added":"79"},"chrome_android":{"version_added":"79"},"edge":{"version_added":"17"},"firefox":{"version_added":"62"},"firefox_android":{"version_added":"62"},"ie":{"version_added":false},"opera":{"version_added":"66"},"opera_android":{"version_added":false},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"12.0"},"webview_android":{"version_added":"79"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"font-size-adjust":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-size-adjust","spec_url":"https://drafts.csswg.org/css-fonts-5/#font-size-adjust-prop","support":{"chrome":{"version_added":"43","version_removed":"91","flags":[{"type":"preference","name":"Enable experimental Web Platform features"}]},"chrome_android":{"version_added":"43","version_removed":"91","flags":[{"type":"preference","name":"Enable experimental Web Platform features"}]},"edge":{"version_added":"79","version_removed":"91","flags":[{"type":"preference","name":"Enable experimental Web Platform features"}]},"firefox":[{"version_added":"40"},{"version_added":"3","notes":"Before Firefox 40, font-size-adjust: 0 was incorrectly interpreted as font-size-adjust: none (bug 1144885)."},{"version_added":"1","notes":"Before Firefox 3, font-size-adjust was supported on Windows only."}],"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":"30","version_removed":"77","flags":[{"type":"preference","name":"Enable experimental Web Platform features"}]},"opera_android":{"version_added":"30","version_removed":"64","flags":[{"type":"preference","name":"Enable experimental Web Platform features"}]},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"two-values":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-size-adjust","spec_url":"https://drafts.csswg.org/css-fonts-5/#font-size-adjust-prop","description":"Two-value syntax","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"92"},{"version_added":"91","version_removed":"92","flags":[{"type":"preference","name":"layout.css.font-size-adjust.basis.enabled"}]}],"firefox_android":[{"version_added":"92"},{"version_added":"91","version_removed":"92","flags":[{"type":"preference","name":"layout.css.font-size-adjust.basis.enabled"}]}],"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"font-size":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-size","spec_url":"https://drafts.csswg.org/css-fonts/#font-size-prop","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"5.5"},"opera":{"version_added":"7"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"rem_values":{"__compat":{"description":"rem values","support":{"chrome":{"version_added":"31"},"chrome_android":{"version_added":"42"},"edge":{"version_added":"12"},"firefox":{"version_added":"31","notes":["Before Firefox 57, animations using em units are not affected by changes to the font-size of the animated element's parent (bug 1254424).","Before Firefox 57, some language settings' inherited font-size is smaller than expected (bug 1391341)."]},"firefox_android":{"version_added":"31"},"ie":[{"version_added":"11"},{"partial_implementation":true,"version_added":"9","version_removed":"10"}],"opera":{"version_added":"28"},"opera_android":{"version_added":"28"},"safari":{"version_added":"7"},"safari_ios":{"version_added":"7"},"samsunginternet_android":{"version_added":"4.0"},"webview_android":{"version_added":"4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"xxx-large":{"__compat":{"description":"xxx-large keyword","support":{"chrome":{"version_added":"79"},"chrome_android":{"version_added":"79"},"edge":{"version_added":"79"},"firefox":{"version_added":"70"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"12.0"},"webview_android":{"version_added":"79"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"font-smooth":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-smooth","support":{"chrome":{"version_added":"5","alternative_name":"-webkit-font-smoothing"},"chrome_android":{"version_added":"18","alternative_name":"-webkit-font-smoothing"},"edge":{"version_added":"79","alternative_name":"-webkit-font-smoothing"},"firefox":{"version_added":"25","alternative_name":"-moz-osx-font-smoothing","notes":"Only works on macOS."},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","alternative_name":"-webkit-font-smoothing"},"opera_android":{"version_added":"14","alternative_name":"-webkit-font-smoothing"},"safari":{"version_added":"4","alternative_name":"-webkit-font-smoothing"},"safari_ios":{"version_added":"3.2","alternative_name":"-webkit-font-smoothing"},"samsunginternet_android":{"alternative_name":"-webkit-font-smoothing","version_added":"1.0"},"webview_android":{"version_added":"≤37","alternative_name":"-webkit-font-smoothing"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"font-stretch":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-stretch","spec_url":"https://drafts.csswg.org/css-fonts/#font-stretch-prop","support":{"chrome":{"version_added":"60","notes":"A font-stretch definition must be added to the @font-face before this property will function."},"chrome_android":{"version_added":"60","notes":"A font-stretch definition must be added to the @font-face before this property will function."},"edge":{"version_added":"12"},"firefox":{"version_added":"9"},"firefox_android":{"version_added":"9"},"ie":{"version_added":"9"},"opera":{"version_added":"47","notes":"A font-stretch definition must be added to the @font-face before this property will function."},"opera_android":{"version_added":"44","notes":"A font-stretch definition must be added to the @font-face before this property will function."},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"8.0","notes":"A font-stretch definition must be added to the @font-face before this property will function."},"webview_android":{"version_added":"60","notes":"A font-stretch definition must be added to the @font-face before this property will function."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"percentage":{"__compat":{"description":"<percentage> syntax","support":{"chrome":{"version_added":"62"},"chrome_android":{"version_added":"62"},"edge":{"version_added":"18"},"firefox":{"version_added":"61"},"firefox_android":{"version_added":"61"},"ie":{"version_added":false},"opera":{"version_added":"49"},"opera_android":{"version_added":"46"},"safari":{"version_added":"11.1"},"safari_ios":{"version_added":"11.3"},"samsunginternet_android":{"version_added":"8.0"},"webview_android":{"version_added":"62"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"font-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-style","spec_url":"https://drafts.csswg.org/css-fonts/#font-style-prop","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"Before Firefox 44, oblique was not distinguished from italic."},"firefox_android":{"version_added":"4","notes":"Before Firefox 44, oblique was not distinguished from italic."},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"oblique-angle":{"__compat":{"description":"oblique can accept an <angle>","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"61"},"firefox_android":{"version_added":"61"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"font-synthesis":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-synthesis","spec_url":"https://drafts.csswg.org/css-fonts/#font-synthesis","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"34"},"firefox_android":{"version_added":"34"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"font-variant-alternates":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-variant-alternates","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"34"},"firefox_android":{"version_added":"34"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":true}},"annotation":{"__compat":{"description":"annotation()","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-variant-alternates#annotation()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"34"},"firefox_android":{"version_added":"34"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":true}}},"character_variant":{"__compat":{"description":"character-variant()","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-variant-alternates#character-variant()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"34"},"firefox_android":{"version_added":"34"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":true}}},"ornaments":{"__compat":{"description":"ornaments()","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-variant-alternates#ornaments()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"34"},"firefox_android":{"version_added":"34"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":true}}},"styleset":{"__compat":{"description":"styleset()","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-variant-alternates#styleset()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"34"},"firefox_android":{"version_added":"34"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":true}}},"stylistic":{"__compat":{"description":"stylistic()","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-variant-alternates#stylistic()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"34"},"firefox_android":{"version_added":"34"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":true}}},"swash":{"__compat":{"description":"swash()","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-variant-alternates#swash()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"34"},"firefox_android":{"version_added":"34"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":true}}}},"font-variant-caps":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-variant-caps","spec_url":"https://drafts.csswg.org/css-fonts/#font-variant-caps-prop","support":{"chrome":{"version_added":"52"},"chrome_android":{"version_added":"52"},"edge":{"version_added":"79"},"firefox":{"version_added":"34"},"firefox_android":{"version_added":"34"},"ie":{"version_added":false},"opera":{"version_added":"39"},"opera_android":{"version_added":"41"},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"52"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"font-variant-east-asian":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-variant-east-asian","spec_url":"https://drafts.csswg.org/css-fonts/#font-variant-east-asian-prop","support":{"chrome":{"version_added":"63"},"chrome_android":{"version_added":"63"},"edge":{"version_added":"79"},"firefox":{"version_added":"34"},"firefox_android":{"version_added":"34"},"ie":{"version_added":false},"opera":{"version_added":"50"},"opera_android":{"version_added":"46"},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":"8.0"},"webview_android":{"version_added":"63"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"font-variant-ligatures":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-variant-ligatures","spec_url":"https://drafts.csswg.org/css-fonts/#font-variant-ligatures-prop","support":{"chrome":[{"version_added":"34"},{"prefix":"-webkit-","version_added":"31"}],"chrome_android":{"version_added":"34"},"edge":[{"version_added":"79"},{"prefix":"-webkit-","version_added":"79"}],"firefox":{"version_added":"34"},"firefox_android":{"version_added":"34"},"ie":{"version_added":false},"opera":[{"version_added":"21"},{"prefix":"-webkit-","version_added":"19"}],"opera_android":[{"version_added":"21"},{"prefix":"-webkit-","version_added":"19"}],"safari":[{"version_added":"9.1"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"9.3"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":{"version_added":"2.0"},"webview_android":[{"version_added":"37"},{"prefix":"-webkit-","version_added":"4.4"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"font-variant-numeric":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-variant-numeric","spec_url":"https://drafts.csswg.org/css-fonts/#font-variant-numeric-prop","support":{"chrome":{"version_added":"52"},"chrome_android":{"version_added":"52"},"edge":{"version_added":"79"},"firefox":{"version_added":"34"},"firefox_android":{"version_added":"34"},"ie":{"version_added":false},"opera":{"version_added":"39"},"opera_android":{"version_added":"41"},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"52"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"font-variant-position":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-variant-position","spec_url":"https://drafts.csswg.org/css-fonts/#font-variant-position-prop","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"34"},"firefox_android":{"version_added":"34"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"font-variant":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-variant","spec_url":"https://drafts.csswg.org/css-fonts/#font-variant-prop","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"11"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"css_fonts_shorthand":{"__compat":{"description":"CSS Fonts Module Level 3 shorthand","support":{"chrome":{"version_added":"52"},"chrome_android":{"version_added":"52"},"edge":{"version_added":"79"},"firefox":{"version_added":"34"},"firefox_android":{"version_added":"34"},"ie":{"version_added":false},"opera":{"version_added":"39"},"opera_android":{"version_added":"41"},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"52"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"greek_accented_characters":{"__compat":{"description":"Greek accented characters","support":{"chrome":{"version_added":false,"notes":"Some operating systems may correctly omit accents in all-uppercase Greek text."},"chrome_android":{"version_added":false,"notes":"Some operating systems may correctly omit accents in all-uppercase Greek text."},"edge":{"version_added":false,"notes":"Some operating systems may correctly omit accents in all-uppercase Greek text."},"firefox":{"version_added":false,"notes":"Some operating systems may correctly omit accents in all-uppercase Greek text."},"firefox_android":{"version_added":false,"notes":"Some operating systems may correctly omit accents in all-uppercase Greek text."},"ie":{"version_added":false},"opera":{"version_added":false,"notes":"Some operating systems may correctly omit accents in all-uppercase Greek text."},"opera_android":{"version_added":false,"notes":"Some operating systems may correctly omit accents in all-uppercase Greek text."},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false,"notes":"Some operating systems may correctly omit accents in all-uppercase Greek text."},"webview_android":{"version_added":false,"notes":"Some operating systems may correctly omit accents in all-uppercase Greek."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"turkic_is":{"__compat":{"description":"iİ and ıI","support":{"chrome":{"version_added":"31"},"chrome_android":{"version_added":"31"},"edge":{"version_added":"12"},"firefox":{"version_added":"14"},"firefox_android":{"version_added":"14"},"ie":{"version_added":"4"},"opera":{"version_added":"18"},"opera_android":{"version_added":"18"},"safari":{"version_added":"8"},"safari_ios":{"version_added":"8"},"samsunginternet_android":{"version_added":"2.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"uppercase_eszett":{"__compat":{"description":"ßSS","support":{"chrome":{"version_added":false,"notes":"Some operating systems may capitalize ß as SS."},"chrome_android":{"version_added":false,"notes":"Some operating systems may capitalize ß as SS."},"edge":{"version_added":false,"notes":"Some operating systems may capitalize ß as SS."},"firefox":{"version_added":false,"notes":"Some operating systems may capitalize ß as SS."},"firefox_android":{"version_added":false,"notes":"Some operating systems may capitalize ß as SS."},"ie":{"version_added":false},"opera":{"version_added":false,"notes":"Some operating systems may capitalize ß as SS."},"opera_android":{"version_added":false,"notes":"Some operating systems may capitalize ß as SS."},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false,"notes":"Some operating systems may capitalize ß as SS."},"webview_android":{"version_added":false,"notes":"Some operating systems may capitalize ß as SS."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"font-variation-settings":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-variation-settings","spec_url":"https://drafts.csswg.org/css-fonts/#font-rend-desc","support":{"chrome":{"version_added":"62"},"chrome_android":{"version_added":"62"},"edge":{"version_added":"17"},"firefox":{"version_added":"62"},"firefox_android":{"version_added":"62"},"ie":{"version_added":false},"opera":{"version_added":"49"},"opera_android":{"version_added":"46"},"safari":{"version_added":"11","notes":"Requires macOS 10.13 High Sierra or later."},"safari_ios":{"version_added":"11","notes":"Requires iOS 11 or later."},"samsunginternet_android":{"version_added":"8.0"},"webview_android":{"version_added":"62"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"font-weight":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-weight","spec_url":"https://drafts.csswg.org/css-fonts/#font-weight-prop","support":{"chrome":{"version_added":"2"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"3"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"number":{"__compat":{"description":"<number> syntax","support":{"chrome":{"version_added":"62"},"chrome_android":{"version_added":"62"},"edge":{"version_added":"17"},"firefox":{"version_added":"61"},"firefox_android":{"version_added":"61"},"ie":{"version_added":false},"opera":{"version_added":"49"},"opera_android":{"version_added":"46"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"8.0"},"webview_android":{"version_added":"62"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"font":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font","spec_url":"https://drafts.csswg.org/css-fonts/#font-prop","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"3"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"font_stretch_support":{"__compat":{"description":"Support for font-stretch values in shorthand","support":{"chrome":{"version_added":"60"},"chrome_android":{"version_added":"60"},"edge":{"version_added":"79"},"firefox":{"version_added":"43"},"firefox_android":{"version_added":"43"},"ie":{"version_added":false},"opera":{"version_added":"47"},"opera_android":{"version_added":"44"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"8.0"},"webview_android":{"version_added":"60"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"system_fonts":{"__compat":{"description":"System fonts","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"6"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"forced-color-adjust":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/forced-color-adjust","spec_url":"https://drafts.csswg.org/css-color-adjust/#forced-color-adjust-prop","support":{"chrome":[{"version_added":"89"},{"version_added":"79","flags":[{"type":"preference","name":"forced-colors","value_to_set":"enabled"}]}],"chrome_android":{"version_added":false},"edge":[{"version_added":"79"},{"version_added":"12","alternative_name":"-ms-high-contrast-adjust"}],"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":"10","alternative_name":"-ms-high-contrast-adjust"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"gap":{"flex_context":{"__compat":{"description":"Supported in Flex Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/gap","spec_url":"https://drafts.csswg.org/css-align/#gap-shorthand","support":{"chrome":{"version_added":"84"},"chrome_android":{"version_added":"84"},"edge":{"version_added":"84"},"firefox":{"version_added":"63"},"firefox_android":{"version_added":"63"},"ie":{"version_added":false},"opera":{"version_added":"70"},"opera_android":{"version_added":"60"},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"84"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid_context":{"__compat":{"description":"Supported in Grid Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/gap","spec_url":"https://drafts.csswg.org/css-align/#gap-shorthand","support":{"chrome":[{"version_added":"66"},{"version_added":"57","alternative_name":"grid-gap"}],"chrome_android":[{"version_added":"66"},{"version_added":"57","alternative_name":"grid-gap"}],"edge":[{"version_added":"16"},{"version_added":"16","alternative_name":"grid-gap"}],"firefox":[{"version_added":"61"},{"version_added":"52","alternative_name":"grid-gap"}],"firefox_android":[{"version_added":"61"},{"version_added":"52","alternative_name":"grid-gap"}],"ie":{"version_added":false},"opera":[{"version_added":"53"},{"version_added":"44","alternative_name":"grid-gap"}],"opera_android":[{"version_added":"47"},{"version_added":"43","alternative_name":"grid-gap"}],"safari":[{"version_added":"12"},{"version_added":"10.1","alternative_name":"grid-gap"}],"safari_ios":[{"version_added":"12"},{"version_added":"10.3","alternative_name":"grid-gap"}],"samsunginternet_android":[{"version_added":"9.0"},{"alternative_name":"grid-gap","version_added":"7.0"}],"webview_android":[{"version_added":"66"},{"version_added":"57","alternative_name":"grid-gap"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"calc_values":{"__compat":{"description":"calc() values","support":{"chrome":{"version_added":"66"},"chrome_android":{"version_added":"66"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"53"},"opera_android":{"version_added":"47"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"9.0"},"webview_android":{"version_added":"66"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"percentage_values":{"__compat":{"description":"<percentage> values","support":{"chrome":{"version_added":"66"},"chrome_android":{"version_added":"66"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"53"},"opera_android":{"version_added":"47"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"9.0"},"webview_android":{"version_added":"66"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"multicol_context":{"__compat":{"description":"Supported in Multi-column Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/gap","spec_url":"https://drafts.csswg.org/css-align/#gap-shorthand","support":{"chrome":{"version_added":"66"},"chrome_android":{"version_added":"66"},"edge":{"version_added":"16"},"firefox":{"version_added":"61"},"firefox_android":{"version_added":"61"},"ie":{"version_added":false},"opera":{"version_added":"53"},"opera_android":{"version_added":"47"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"9.0"},"webview_android":{"version_added":"66"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"grid-area":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/grid-area","spec_url":"https://drafts.csswg.org/css-grid/#propdef-grid-area","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid-auto-columns":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/grid-auto-columns","spec_url":"https://drafts.csswg.org/css-grid/#auto-tracks","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":[{"version_added":"16"},{"version_added":"12","version_removed":"79","alternative_name":"-ms-grid-columns"}],"firefox":[{"version_added":"70"},{"version_added":"52","version_removed":"70","partial_implementation":true,"notes":"Does not accept multiple track-size values. See bug 1339672."}],"firefox_android":[{"version_added":"79"},{"version_added":"52","version_removed":"79","partial_implementation":true,"notes":"Does not accept multiple track-size values. See bug 1339672."}],"ie":{"version_added":"10","alternative_name":"-ms-grid-columns"},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid-auto-flow":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/grid-auto-flow","spec_url":"https://drafts.csswg.org/css-grid/#grid-auto-flow-property","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid-auto-rows":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/grid-auto-rows","spec_url":"https://drafts.csswg.org/css-grid/#auto-tracks","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":[{"version_added":"16"},{"version_added":"12","version_removed":"79","alternative_name":"-ms-grid-rows"}],"firefox":[{"version_added":"70"},{"version_added":"52","version_removed":"70","partial_implementation":true,"notes":"Does not accept multiple track-size values. See bug 1339672."}],"firefox_android":[{"version_added":"79"},{"version_added":"52","version_removed":"79","partial_implementation":true,"notes":"Does not accept multiple track-size values. See bug 1339672."}],"ie":{"version_added":"10","alternative_name":"-ms-grid-rows"},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid-column-end":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/grid-column-end","spec_url":"https://drafts.csswg.org/css-grid/#line-placement","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid-column-start":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/grid-column-start","spec_url":"https://drafts.csswg.org/css-grid/#line-placement","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid-column":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/grid-column","spec_url":"https://drafts.csswg.org/css-grid/#placement-shorthands","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid-row-end":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/grid-row-end","spec_url":"https://drafts.csswg.org/css-grid/#line-placement","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid-row-start":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/grid-row-start","spec_url":"https://drafts.csswg.org/css-grid/#line-placement","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid-row":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/grid-row","spec_url":"https://drafts.csswg.org/css-grid/#placement-shorthands","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid-template-areas":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/grid-template-areas","spec_url":"https://drafts.csswg.org/css-grid/#grid-template-areas-property","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid-template-columns":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/grid-template-columns","spec_url":["https://drafts.csswg.org/css-grid/#track-sizing","https://drafts.csswg.org/css-grid/#subgrids","https://drafts.csswg.org/css-grid-3/#masonry-layout"],"support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":[{"version_added":"16"},{"alternative_name":"-ms-grid-columns","version_added":"12","version_removed":"79"}],"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":"10","alternative_name":"-ms-grid-columns"},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"animation":{"__compat":{"description":"Animation of tracks","support":{"chrome":{"version_added":false,"notes":"See bug 759665."},"chrome_android":{"version_added":false,"notes":"See bug 759665."},"edge":{"version_added":false,"notes":"See bug 759665."},"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":{"version_added":false,"notes":"See bug 759665."},"opera_android":{"version_added":false,"notes":"See bug 759665."},"safari":{"version_added":false,"notes":"See bug 204580."},"safari_ios":{"version_added":false,"notes":"See bug 204580."},"samsunginternet_android":{"version_added":false,"notes":"See bug 759665."},"webview_android":{"version_added":false,"notes":"See bug 759665."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"fit-content":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/fit-content","spec_url":"https://drafts.csswg.org/css-sizing-4/#sizing-values","description":"fit-content()","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"minmax":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/minmax()","spec_url":"https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-minmax","description":"minmax()","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"repeat":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/repeat()","spec_url":"https://drafts.csswg.org/css-grid/#repeat-notation","description":"repeat()","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":[{"version_added":"76"},{"version_added":"57","version_removed":"76","notes":"repeat(auto-fill, ...) and repeat(auto-fit, ...) only support one repeated column (see bug 1341507).","partial_implementation":true},{"version_added":"52","version_removed":"57","notes":"calc() doesn't work in repeat() (see bug 1350069).","partial_implementation":true}],"firefox_android":[{"version_added":"79"},{"version_added":"57","version_removed":"79","notes":"repeat(auto-fill, ...) and repeat(auto-fit, ...) only support one repeated column (see bug 1341507).","partial_implementation":true},{"version_added":"52","version_removed":"57","notes":"calc() doesn't work in repeat() (see bug 1350069).","partial_implementation":true}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"subgrid":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/CSS_Grid_Layout/Subgrid","spec_url":"https://drafts.csswg.org/css-grid/#subgrids","description":"subgrid","support":{"chrome":{"version_added":false,"notes":"See bug 618969."},"chrome_android":{"version_added":false,"notes":"See bug 618969."},"edge":{"version_added":false,"notes":"See bug 618969."},"firefox":[{"version_added":"71"},{"version_added":"69","notes":"Enabled by default in Firefox Nightly.","flags":[{"type":"preference","name":"layout.css.grid-template-subgrid-value.enabled","value_to_set":"true"}]}],"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false,"notes":"See bug 618969."},"opera_android":{"version_added":false,"notes":"See bug 618969."},"safari":{"version_added":false,"notes":"See bug 202115."},"safari_ios":{"version_added":false,"notes":"See bug 202115."},"samsunginternet_android":{"version_added":false,"notes":"See bug 618969."},"webview_android":{"version_added":false,"notes":"See bug 618969."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"masonry":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/CSS_Grid_Layout/Masonry_Layout","spec_url":"https://drafts.csswg.org/css-grid-3/#masonry-layout","description":"masonry","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"77","flags":[{"type":"preference","name":"layout.css.grid-template-masonry-value.enabled","value_to_set":"true"}]},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"grid-template-rows":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/grid-template-rows","spec_url":["https://drafts.csswg.org/css-grid/#track-sizing","https://drafts.csswg.org/css-grid/#subgrids","https://drafts.csswg.org/css-grid-3/#masonry-layout"],"support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":[{"version_added":"16"},{"alternative_name":"-ms-grid-rows","version_added":"12","version_removed":"79"}],"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":"10","alternative_name":"-ms-grid-rows"},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"animation":{"__compat":{"description":"Animation of tracks","support":{"chrome":{"version_added":false,"notes":"See bug 759665."},"chrome_android":{"version_added":false,"notes":"See bug 759665."},"edge":{"version_added":false,"notes":"See bug 759665."},"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":{"version_added":false,"notes":"See bug 759665."},"opera_android":{"version_added":false,"notes":"See bug 759665."},"safari":{"version_added":false,"notes":"See bug 204580."},"safari_ios":{"version_added":false,"notes":"See bug 204580."},"samsunginternet_android":{"version_added":false,"notes":"See bug 759665."},"webview_android":{"version_added":false,"notes":"See bug 759665."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"fit-content":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/fit-content","spec_url":"https://drafts.csswg.org/css-sizing-4/#sizing-values","description":"fit-content()","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"masonry":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/CSS_Grid_Layout/Masonry_Layout","spec_url":"https://drafts.csswg.org/css-grid-3/#masonry-layout","description":"masonry","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"77","flags":[{"type":"preference","name":"layout.css.grid-template-masonry-value.enabled","value_to_set":"true"}]},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"minmax":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/minmax()","spec_url":"https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-minmax","description":"minmax()","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"repeat":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/repeat()","spec_url":"https://drafts.csswg.org/css-grid/#repeat-notation","description":"repeat()","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":[{"version_added":"76"},{"version_added":"57","version_removed":"76","notes":"repeat(auto-fill, ...) and repeat(auto-fit, ...) only support one repeated column (see bug 1341507).","partial_implementation":true},{"version_added":"52","version_removed":"57","notes":"calc() doesn't work in repeat() (see bug 1350069).","partial_implementation":true}],"firefox_android":[{"version_added":"79"},{"version_added":"57","version_removed":"79","notes":"repeat(auto-fill, ...) and repeat(auto-fit, ...) only support one repeated column (see bug 1341507).","partial_implementation":true},{"version_added":"52","version_removed":"57","notes":"calc() doesn't work in repeat() (see bug 1350069).","partial_implementation":true}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"subgrid":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/CSS_Grid_Layout/Subgrid","spec_url":"https://drafts.csswg.org/css-grid/#subgrids","description":"subgrid","support":{"chrome":{"version_added":false,"notes":"See bug 618969."},"chrome_android":{"version_added":false,"notes":"See bug 618969."},"edge":{"version_added":false,"notes":"See bug 618969."},"firefox":[{"version_added":"71"},{"version_added":"69","notes":"Enabled by default in Firefox Nightly.","flags":[{"type":"preference","name":"layout.css.grid-template-subgrid-value.enabled","value_to_set":"true"}]}],"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false,"notes":"See bug 618969."},"opera_android":{"version_added":false,"notes":"See bug 618969."},"safari":{"version_added":false,"notes":"See bug 202115."},"safari_ios":{"version_added":false,"notes":"See bug 202115."},"samsunginternet_android":{"version_added":false,"notes":"See bug 618969."},"webview_android":{"version_added":false,"notes":"See bug 618969."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"grid-template":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/grid-template","spec_url":"https://drafts.csswg.org/css-grid/#explicit-grid-shorthand","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/grid","spec_url":"https://drafts.csswg.org/css-grid/#grid-shorthand","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0","notes":"This was added early so is out of sync with the equivalent Chromium version."},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"hanging-punctuation":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/hanging-punctuation","spec_url":"https://drafts.csswg.org/css-text/#hanging-punctuation-property","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"10","partial_implementation":true,"notes":"The force-end keyword is recognized but has no effect."},"safari_ios":{"version_added":"10","partial_implementation":true,"notes":"The force-end keyword is recognized but has no effect."},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"height":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/height","spec_url":"https://drafts.csswg.org/css-sizing/#preferred-size-properties","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"fit-content":{"__compat":{"description":"fit-content","support":{"chrome":{"version_added":"46"},"chrome_android":{"version_added":"46"},"edge":{"version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"33"},"opera_android":{"version_added":"33"},"safari":[{"version_added":"11"},{"version_added":"9","prefix":"-webkit-"}],"safari_ios":[{"version_added":"11"},{"version_added":"9","prefix":"-webkit-"}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"46"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"fit-content_function":{"__compat":{"description":"fit-content()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"firefox_android":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"max-content":{"__compat":{"description":"max-content","support":{"chrome":{"version_added":"46"},"chrome_android":{"version_added":"46"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"3","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"4","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"46"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"min-content":{"__compat":{"description":"min-content","support":{"chrome":{"version_added":"46"},"chrome_android":{"version_added":"46"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"3","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"4","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"46"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"stretch":{"__compat":{"description":"stretch","support":{"chrome":{"alternative_name":"-webkit-fill-available","version_added":"28"},"chrome_android":{"alternative_name":"-webkit-fill-available","version_added":"28"},"edge":{"alternative_name":"-webkit-fill-available","version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"alternative_name":"-webkit-fill-available","version_added":"15"},"opera_android":{"alternative_name":"-webkit-fill-available","version_added":"15"},"safari":{"alternative_name":"-webkit-fill-available","version_added":"9"},"safari_ios":{"alternative_name":"-webkit-fill-available","version_added":"9"},"samsunginternet_android":{"version_added":"5.0","alternative_name":"-webkit-fill-available"},"webview_android":{"alternative_name":"-webkit-fill-available","version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"hyphens":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/hyphens","spec_url":"https://drafts.csswg.org/css-text/#hyphens-property","support":{"chrome":[{"partial_implementation":true,"version_added":"55","notes":"auto value is only supported on macOS and Android and for languages the OS provides hyphenation for."},{"prefix":"-webkit-","version_added":"13","notes":"Only -webkit-hyphens: none is supported."}],"chrome_android":[{"version_added":"55"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"partial_implementation":true,"version_added":"79","notes":"auto value is only supported on macOS and Android and for languages the OS provides hyphenation for."},{"prefix":"-webkit-","version_added":"79","notes":"Only -webkit-hyphens: none is supported."},{"prefix":"-ms-","version_added":"12","version_removed":"79","partial_implementation":true,"notes":"Only works if the specified language is the same as the language of the underlying OS."}],"firefox":[{"version_added":"43"},{"prefix":"-moz-","version_added":"6","notes":"Automatic hyphenation only works for languages with hyphenation dictionaries that are integrated into Firefox."}],"firefox_android":[{"version_added":"43"},{"prefix":"-moz-","version_added":"6","notes":"Automatic hyphenation only works for languages with hyphenation dictionaries that are integrated into Firefox."}],"ie":{"prefix":"-ms-","version_added":"10","partial_implementation":true,"notes":"Only works if the specified language is the same as the language of the underlying OS."},"opera":{"version_added":"44","partial_implementation":true,"notes":"auto value is only supported on macOS and Android."},"opera_android":{"version_added":"43"},"safari":{"prefix":"-webkit-","version_added":"5.1"},"safari_ios":{"prefix":"-webkit-","version_added":"4.2"},"samsunginternet_android":[{"version_added":"6.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"55"},{"prefix":"-webkit-","version_added":"4"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"afrikaans":{"__compat":{"description":"Hyphenation dictionary for Afrikaans (af, af-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"bosnian":{"__compat":{"description":"Hyphenation dictionary for Bosnian, Serbian, and Serbo-Croatian (sh, sh-*, sr, sr-*, bs, bs-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"bulgarian":{"__compat":{"description":"Hyphenation dictionary for Bulgarian (bg, bg-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"catalan":{"__compat":{"description":"Hyphenation dictionary for Catalan (ca, ca-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"croatian":{"__compat":{"description":"Hyphenation dictionary for Croatian (hr, hr-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"czech":{"__compat":{"description":"Hyphenation dictionary for Czech (cs, cs-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"danish":{"__compat":{"description":"Hyphenation dictionary for Danish (da, da-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"dutch":{"__compat":{"description":"Hyphenation dictionary for Dutch (nl, nl-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"english":{"__compat":{"description":"Hyphenation dictionary for English (en, en-*)","support":{"chrome":{"version_added":"55"},"chrome_android":{"version_added":"55"},"edge":{"version_added":"12"},"firefox":{"version_added":"6","notes":"For English, Firefox uses an en-US dictionary"},"firefox_android":{"version_added":"6","notes":"For English, Firefox uses an en-US dictionary"},"ie":{"version_added":"10"},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"5.1","notes":"For English, Safari uses different en-GB and en-US dictionaries."},"safari_ios":{"version_added":"6","notes":"For English, Safari uses different en-GB and en-US dictionaries."},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"55"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"esperanto":{"__compat":{"description":"Hyphenation dictionary for Esperanto (eo, eo-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"estonian":{"__compat":{"description":"Hyphenation dictionary for Estonian (et, et-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"finish":{"__compat":{"description":"Hyphenation dictionary for Finnish (fi, fi-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"french":{"__compat":{"description":"Hyphenation dictionary for French (fr, fr-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"galician":{"__compat":{"description":"Hyphenation dictionary for Galician (gl, gl-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"9"},"firefox_android":{"version_added":"9"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"german_reformed_orthography":{"__compat":{"description":"Hyphenation dictionary for German, Reformed Orthography of 1996 (de, de-1996, de-DE, de-AT, de-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"german_swiss_orthography":{"__compat":{"description":"Hyphenation dictionary for German, Swiss Orthography (de-CH, de-CH-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"german_traditional_orthography":{"__compat":{"description":"Hyphenation dictionary for German, Traditional Orthography of 1901 (de-1901, de-AT-1901, de-DE-1901)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"hungarian":{"__compat":{"description":"Hyphenation dictionary for Hungarian (hu, hu-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"9"},"firefox_android":{"version_added":"9"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"icelandic":{"__compat":{"description":"Hyphenation dictionary for Icelandic (is, is-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"interlingua":{"__compat":{"description":"Hyphenation dictionary for Interlingua (ia, ia-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"italian":{"__compat":{"description":"Hyphenation dictionary for Italian (it, it-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"9"},"firefox_android":{"version_added":"9"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"kurmanji":{"__compat":{"description":"Hyphenation dictionary for Kurmanji (kmr, kmr-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"latin":{"__compat":{"description":"Hyphenation dictionary for Latin (la, la-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"lithuanian":{"__compat":{"description":"Hyphenation dictionary for Lithuanian (lt, lt-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"mongolian":{"__compat":{"description":"Hyphenation dictionary for Mongolian (mn, mn-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"norwegian_nn":{"__compat":{"description":"Hyphenation dictionary for Norwegian (Nynorsk) (nn, nn-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"norwegian_no":{"__compat":{"description":"Hyphenation dictionary for Norwegian (Bokmål) (no, no-*, nb, nb-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"polish":{"__compat":{"description":"Hyphenation dictionary for Polish (pl, pl-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"31"},"firefox_android":{"version_added":"31"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"portuguese":{"__compat":{"description":"Hyphenation dictionary for Portuguese (pt, pt-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"portuguese_brazillian":{"__compat":{"description":"Hyphenation dictionary for Brazilian Portuguese (pt-BR)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8","notes":"For Brazilian Portuguese, Firefox uses a Portuguese dictionary."},"firefox_android":{"version_added":"8","notes":"For Brazilian Portuguese, Firefox uses a Portuguese dictionary."},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"russian":{"__compat":{"description":"Hyphenation dictionary for Russian (ru, ru-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"slovenian":{"__compat":{"description":"Hyphenation dictionary for Slovenian (sl, sl-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"spanish":{"__compat":{"description":"Hyphenation dictionary for Spanish (es, es-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"swedish":{"__compat":{"description":"Hyphenation dictionary for Swedish (sv, sv-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"turkish":{"__compat":{"description":"Hyphenation dictionary for Turkish (tr, tr-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"9"},"firefox_android":{"version_added":"9"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ukrainian":{"__compat":{"description":"Hyphenation dictionary for Ukrainian (uk, uk-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"9"},"firefox_android":{"version_added":"9"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"upper_sorbian":{"__compat":{"description":"Hyphenation dictionary for Upper Sorbian (hsb, hsb-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"welsh":{"__compat":{"description":"Hyphenation dictionary for Welsh (cy, cy-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"image-orientation":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/image-orientation","spec_url":"https://drafts.csswg.org/css-images/#the-image-orientation","support":{"chrome":{"version_added":"81"},"chrome_android":{"version_added":"81"},"edge":{"version_added":"81"},"firefox":{"version_added":"26"},"firefox_android":{"version_added":"26"},"ie":{"version_added":false},"opera":{"version_added":"67"},"opera_android":{"version_added":false},"safari":{"version_added":"13.1"},"safari_ios":{"version_added":"13.4"},"samsunginternet_android":{"version_added":"13.0"},"webview_android":{"version_added":"81"}},"status":{"experimental":true,"standard_track":true,"deprecated":false}},"flip_and_angle":{"__compat":{"description":"flip & <angle>","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"26","version_removed":"63"},"firefox_android":{"version_added":"26","version_removed":"63"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}}},"image-rendering":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/image-rendering","spec_url":"https://drafts.csswg.org/css-images/#the-image-rendering","support":{"chrome":{"version_added":"13"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"3.6"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"6"},"safari_ios":{"version_added":"6"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"crisp-edges":{"__compat":{"support":{"chrome":{"alternative_name":"-webkit-optimize-contrast","version_added":"13"},"chrome_android":{"alternative_name":"-webkit-optimize-contrast","version_added":"18"},"edge":{"alternative_name":"-webkit-optimize-contrast","version_added":"79"},"firefox":[{"version_added":"65"},{"prefix":"-moz-","version_added":"3.6"}],"firefox_android":[{"version_added":"65"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"alternative_name":"-webkit-optimize-contrast","version_added":"15"},"opera_android":{"alternative_name":"-webkit-optimize-contrast","version_added":"14"},"safari":{"alternative_name":"-webkit-optimize-contrast","version_added":"6"},"safari_ios":{"alternative_name":"-webkit-optimize-contrast","version_added":"6"},"samsunginternet_android":{"alternative_name":"-webkit-optimize-contrast","version_added":"1.0"},"webview_android":{"alternative_name":"-webkit-optimize-contrast","version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"optimizeQuality":{"__compat":{"support":{"chrome":{"version_added":"28"},"chrome_android":{"version_added":"28"},"edge":{"version_added":"79"},"firefox":{"version_added":"3.6"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"7"},"safari_ios":{"version_added":"7"},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"optimizeSpeed":{"__compat":{"support":{"chrome":{"version_added":"28"},"chrome_android":{"version_added":"28"},"edge":{"version_added":"79"},"firefox":{"version_added":"3.6"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"7"},"safari_ios":{"version_added":"7"},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"pixelated":{"__compat":{"support":{"chrome":{"version_added":"41"},"chrome_android":{"version_added":"41"},"edge":{"version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"26"},"opera_android":{"version_added":"26"},"safari":{"version_added":"10"},"safari_ios":{"version_added":"10"},"samsunginternet_android":{"version_added":"4.0"},"webview_android":{"version_added":"41"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"ime-mode":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/ime-mode","spec_url":"https://drafts.csswg.org/css-ui/#input-method-editor","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":[{"version_added":"12","version_removed":"79"},{"prefix":"-ms-","version_added":"12","version_removed":"79"}],"firefox":{"version_added":"3"},"firefox_android":{"version_added":"4"},"ie":[{"version_added":"5"},{"prefix":"-ms-","version_added":"8"}],"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"initial-letter-align":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/initial-letter-align","spec_url":"https://drafts.csswg.org/css-inline/#aligning-initial-letter","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false,"notes":"See bug 1273021."},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"initial-letter":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/initial-letter","spec_url":"https://drafts.csswg.org/css-inline/#sizing-drop-initials","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false,"notes":"See bug 1223880."},"firefox_android":{"version_added":false,"notes":"See bug 1223880."},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"inline-size":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/inline-size","spec_url":"https://drafts.csswg.org/css-logical/#dimension-properties","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"fit-content":{"__compat":{"description":"fit-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"prefix":"-moz-","version_added":"41"},"firefox_android":{"prefix":"-moz-","version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"alternative_name":"-webkit-fill-available","version_added":"5.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"fit-content_function":{"__compat":{"description":"fit-content()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"firefox_android":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"max-content":{"__compat":{"description":"max-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"min-content":{"__compat":{"description":"min-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"inset-block-end":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/inset-block-end","spec_url":"https://drafts.csswg.org/css-logical/#position-properties","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":[{"version_added":"87"},{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"firefox":[{"version_added":"63"},{"alternative_name":"offset-block-end","version_added":"41","version_removed":"63"}],"firefox_android":[{"version_added":"63"},{"alternative_name":"offset-block-end","version_added":"41","version_removed":"63"}],"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"62"},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"inset-block-start":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/inset-block-start","spec_url":"https://drafts.csswg.org/css-logical/#position-properties","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":[{"version_added":"87"},{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"firefox":[{"version_added":"63"},{"alternative_name":"offset-block-start","version_added":"41","version_removed":"63"}],"firefox_android":[{"version_added":"63"},{"alternative_name":"offset-block-start","version_added":"41","version_removed":"63"}],"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"62"},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"inset-block":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/inset-block","spec_url":"https://drafts.csswg.org/css-logical/#propdef-inset-block","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":[{"version_added":"87"},{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"firefox":[{"version_added":"63"},{"alternative_name":"offset-block","version_added":"41","version_removed":"63"}],"firefox_android":[{"version_added":"63"},{"alternative_name":"offset-block","version_added":"41","version_removed":"63"}],"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"62"},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"inset-inline-end":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/inset-inline-end","spec_url":"https://drafts.csswg.org/css-logical/#position-properties","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":[{"version_added":"87"},{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"firefox":[{"version_added":"63"},{"alternative_name":"offset-inline-end","version_added":"41","version_removed":"63"}],"firefox_android":[{"version_added":"63"},{"alternative_name":"offset-inline-end","version_added":"41","version_removed":"63"}],"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"62"},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"inset-inline-start":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/inset-inline-start","spec_url":"https://drafts.csswg.org/css-logical/#position-properties","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":[{"version_added":"87"},{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"firefox":[{"version_added":"63"},{"alternative_name":"offset-inline-start","version_added":"41","version_removed":"63"}],"firefox_android":[{"version_added":"63"},{"alternative_name":"offset-inline-start","version_added":"41","version_removed":"63"}],"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"62"},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"inset-inline":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/inset-inline","spec_url":"https://drafts.csswg.org/css-logical/#propdef-inset-inline","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":[{"version_added":"87"},{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"firefox":[{"version_added":"63"},{"alternative_name":"offset-inline","version_added":"41","version_removed":"63"}],"firefox_android":[{"version_added":"63"},{"alternative_name":"offset-inline","version_added":"41","version_removed":"63"}],"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"62"},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"inset":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/inset","spec_url":"https://drafts.csswg.org/css-logical/#propdef-inset","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":[{"version_added":"87"},{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"62"},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"isolation":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/isolation","spec_url":"https://drafts.fxtf.org/compositing/#isolation","support":{"chrome":{"version_added":"41"},"chrome_android":{"version_added":"41"},"edge":{"version_added":"79"},"firefox":{"version_added":"36"},"firefox_android":{"version_added":"36"},"ie":{"version_added":false},"opera":{"version_added":"30"},"opera_android":{"version_added":"30"},"safari":{"version_added":"8"},"safari_ios":{"version_added":"8"},"samsunginternet_android":{"version_added":"4.0"},"webview_android":{"version_added":"41"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"justify-content":{"flex_context":{"__compat":{"description":"Supported in Flex Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/justify-content","spec_url":["https://drafts.csswg.org/css-align/#align-justify-content","https://drafts.csswg.org/css-flexbox/#justify-content-property"],"support":{"chrome":[{"version_added":"52"},{"version_added":"29","partial_implementation":true,"notes":"Older versions of the specification treat absolute positioned children as though they are a 0 by 0 flex item. Later specification versions take the children out of the flow and set their positions based on align and justify properties. Chrome implements the new behavior beginning with Chrome 52."},{"prefix":"-webkit-","version_added":"21"}],"chrome_android":[{"version_added":"52"},{"version_added":"29","partial_implementation":true,"notes":"Older versions of the specification treat absolute positioned children as though they are a 0 by 0 flex item. Later specification versions take the children out of the flow and set their positions based on align and justify properties. Chrome implements the new behavior beginning with Chrome 52."},{"prefix":"-webkit-","version_added":"25"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"20","notes":"Before Firefox 27, Firefox supported only single-line flexbox."},{"prefix":"-webkit-","version_added":"49"}],"firefox_android":[{"version_added":"20","notes":"Before Firefox 27, Firefox supported only single-line flexbox."},{"prefix":"-webkit-","version_added":"49"}],"ie":{"version_added":"11"},"opera":{"version_added":"12.1"},"opera_android":{"version_added":"12.1"},"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"6.1"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"6.1"}],"samsunginternet_android":[{"version_added":"6.0"},{"partial_implementation":true,"version_added":"2.0","notes":"Older versions of the specification treat absolute positioned children as though they are a 0 by 0 flex item. Later specification versions take the children out of the flow and set their positions based on align and justify properties. Chrome implements the new behavior beginning with Chrome 52."},{"prefix":"-webkit-","version_added":"1.5"}],"webview_android":[{"version_added":"52"},{"version_added":"4.4","partial_implementation":true,"notes":"Older versions of the specification treat absolute positioned children as though they are a 0 by 0 flex item. Later specification versions take the children out of the flow and set their positions based on align and justify properties. Chrome implements the new behavior beginning with Chrome 52."},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"baseline":{"__compat":{"description":"baseline","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":"45","version_removed":"60","notes":"justify-content no longer accepts baseline values"},"firefox_android":{"version_added":"45","version_removed":"60","notes":"justify-content no longer accepts baseline values"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"first_last_baseline":{"__compat":{"description":"first baseline and last baseline","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"52","version_removed":"60","notes":"justify-content no longer accepts baseline values"},"firefox_android":{"version_added":"52","version_removed":"60","notes":"justify-content no longer accepts baseline values"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"left_right":{"__compat":{"description":"left and right","support":{"chrome":{"version_added":"93"},"chrome_android":{"version_added":"93"},"edge":{"version_added":"93"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false,"notes":"This value is recognized, but has no effect."},"opera":{"version_added":false,"notes":"This value is recognized, but has no effect."},"opera_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"webview_android":{"version_added":"93"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"safe_unsafe":{"__compat":{"description":"safe and unsafe","support":{"chrome":{"version_added":false,"notes":"This value is recognized, but has no effect."},"chrome_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"edge":{"version_added":false,"notes":"This value is recognized, but has no effect."},"firefox":{"version_added":"63"},"firefox_android":{"version_added":"63"},"ie":{"version_added":false},"opera":{"version_added":false,"notes":"This value is recognized, but has no effect."},"opera_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"safari":{"version_added":false,"notes":"This value is recognized, but has no effect."},"safari_ios":{"version_added":false,"notes":"This value is recognized, but has no effect."},"samsunginternet_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"webview_android":{"version_added":false,"notes":"This value is recognized, but has no effect."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"space-evenly":{"__compat":{"description":"space-evenly","support":{"chrome":{"version_added":"60"},"chrome_android":{"version_added":"60"},"edge":{"version_added":"79"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"47"},"opera_android":{"version_added":"44"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"8.0"},"webview_android":{"version_added":"60"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"start_end":{"__compat":{"description":"start and end","support":{"chrome":{"version_added":"93"},"chrome_android":{"version_added":"93"},"edge":{"version_added":"93"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":false,"notes":"This value is recognized, but has no effect."},"opera_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"webview_android":{"version_added":"93"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"stretch":{"__compat":{"description":"stretch","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"grid_context":{"__compat":{"description":"Supported in Grid Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/justify-content","spec_url":["https://drafts.csswg.org/css-align/#align-justify-content","https://drafts.csswg.org/css-flexbox/#justify-content-property"],"support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"52"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.2"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"justify-items":{"flex_context":{"__compat":{"description":"Supported in Flex Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/justify-items","spec_url":"https://drafts.csswg.org/css-align/#justify-items-property","support":{"chrome":{"version_added":"52"},"chrome_android":{"version_added":"52"},"edge":{"version_added":"12"},"firefox":{"version_added":"20"},"firefox_android":{"version_added":"20"},"ie":{"version_added":"11"},"opera":{"version_added":"12.1"},"opera_android":{"version_added":"12.1"},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"52"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid_context":{"__compat":{"description":"Supported in Grid Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/justify-items","spec_url":"https://drafts.csswg.org/css-align/#justify-items-property","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"justify-self":{"flex_context":{"__compat":{"description":"Supported in Flex Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/justify-self","spec_url":"https://drafts.csswg.org/css-align/#justify-self-property","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid_context":{"__compat":{"description":"Supported in Grid Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/justify-self","spec_url":"https://drafts.csswg.org/css-align/#justify-self-property","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"prefix":"-ms-","version_added":"10","partial_implementation":true,"notes":"Internet Explorer 10 and 11 have the property -ms-grid-column-align, which acts in a similar way to justify-self."},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"justify-tracks":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/justify-tracks","spec_url":"https://drafts.csswg.org/css-grid-3/#tracks-alignment","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"77","flags":[{"type":"preference","name":"layout.css.grid-template-masonry-value.enabled","value_to_set":"true"}]},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"left":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/left","spec_url":"https://drafts.csswg.org/css-position/#insets","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"5.5"},"opera":{"version_added":"5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"letter-spacing":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/letter-spacing","spec_url":"https://drafts.csswg.org/css-text/#letter-spacing-property","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"svg":{"__compat":{"description":"SVG support","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"72"},"firefox_android":{"version_added":false},"ie":{"version_added":"9"},"opera":{"version_added":"7"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"line-break":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/line-break","spec_url":"https://drafts.csswg.org/css-text/#line-break-property","support":{"chrome":[{"version_added":"58"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"58"},{"prefix":"-webkit-","version_added":"18"}],"edge":{"version_added":"14"},"firefox":{"version_added":"69"},"firefox_android":{"version_added":false},"ie":[{"version_added":"5.5"},{"prefix":"-ms-","version_added":"8"}],"opera":[{"version_added":"45"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"11"},{"version_added":"3","prefix":"-webkit-"},{"version_added":"2","version_removed":"3","prefix":"-khtml-"}],"safari_ios":[{"version_added":"11"},{"version_added":"1","prefix":"-webkit-"}],"samsunginternet_android":[{"version_added":"7.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"58"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"line-height-step":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/line-height-step","spec_url":"https://drafts.csswg.org/css-rhythm/#line-height-step","support":{"chrome":{"version_added":"60","flags":[{"type":"runtime_flag","name":"--enable-blink-features=CSSSnapSize"}]},"chrome_android":{"version_added":"60","flags":[{"type":"runtime_flag","name":"--enable-blink-features=CSSSnapSize"}]},"edge":{"version_added":"79","flags":[{"type":"runtime_flag","name":"--enable-blink-features=CSSSnapSize"}]},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"47","flags":[{"type":"runtime_flag","name":"--enable-blink-features=CSSSnapSize"}]},"opera_android":{"version_added":"44","flags":[{"type":"runtime_flag","name":"--enable-blink-features=CSSSnapSize"}]},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"line-height":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/line-height","spec_url":"https://drafts.csswg.org/css-inline/#line-height-property","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"-moz-block-height":{"__compat":{"support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"3.6"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}}},"list-style-image":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/list-style-image","spec_url":"https://drafts.csswg.org/css-lists/#image-markers","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"Before Firefox 86, this property did not accept an <image> type, and required the URL of an image."},"firefox_android":{"version_added":"4","notes":"Before Firefox 86, this property did not accept an <image> type, and required the URL of an image."},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"list-style-position":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/list-style-position","spec_url":"https://drafts.csswg.org/css-lists/#list-style-position-property","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"list-style-type":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/list-style-type","spec_url":["https://drafts.csswg.org/css-lists/#text-markers","https://drafts.csswg.org/css-counter-styles/#extending-css2"],"support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"afar":{"__compat":{"description":"afar","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"amharic":{"__compat":{"description":"amaric","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"amharic-abegede":{"__compat":{"description":"amaric-abegede","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"arabic-indic":{"__compat":{"description":"arabic-indic","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"armenian":{"__compat":{"description":"armenian","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"6"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"asterisks":{"__compat":{"description":"asterisks","support":{"chrome":{"version_added":"13","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"4.4","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"bengali":{"__compat":{"description":"bengali","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"binary":{"__compat":{"description":"binary","support":{"chrome":{"version_added":"13","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"4.4","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"cambodian":{"__compat":{"description":"cambodian","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"33"},"firefox_android":{"version_added":"33"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"circle":{"__compat":{"description":"circle","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"cjk-decimal":{"__compat":{"description":"cjk-decimal","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"28"},"firefox_android":{"version_added":"28"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"cjk-earthly-branch":{"__compat":{"description":"cjk-earthly-branch","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"cjk-heavenly-stem":{"__compat":{"description":"cjk-heavenly-stem","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"cjk-ideographic":{"__compat":{"description":"cjk-ideographic","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":[{"version_added":"15"},{"version_added":"7","partial_implementation":true,"notes":"Until version 15, only decimal numbers display."}],"opera_android":[{"version_added":"14"},{"version_added":"10.1","partial_implementation":true,"notes":"Until version 15, only decimal numbers display."}],"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"decimal":{"__compat":{"description":"decimal","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"decimal-leading-zero":{"__compat":{"description":"decimal-leading-zero","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"8"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"devanagari":{"__compat":{"description":"devanagari","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"disc":{"__compat":{"description":"disc","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"disclosure-closed":{"__compat":{"description":"disclosure-closed","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"33"},"firefox_android":{"version_added":"33"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"disclosure-open":{"__compat":{"description":"disclosure-open","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"33"},"firefox_android":{"version_added":"33"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic":{"__compat":{"description":"ethiopic","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-abegede":{"__compat":{"description":"ethiopic-abegede","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-abegede-am-et":{"__compat":{"description":"ethiopic-abegede-am-et","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-abegede-gez":{"__compat":{"description":"ethiopic-abegede-gez","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-abegede-ti-er":{"__compat":{"description":"ethiopic-abegede-ti-er","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-abegede-ti-et":{"__compat":{"description":"ethiopic-abegede-ti-et","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-halehame":{"__compat":{"description":"ethiopic-halehame","support":{"chrome":{"version_added":"45"},"chrome_android":{"version_added":"45"},"edge":{"version_added":"79"},"firefox":{"version_added":"1","prefix":"-moz-"},"firefox_android":{"version_added":"4","prefix":"-moz-"},"ie":{"version_added":false},"opera":{"version_added":"32"},"opera_android":{"version_added":"32"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-halehame-aa-er":{"__compat":{"description":"ethiopic-halehame-aa-er","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-halehame-aa-et":{"__compat":{"description":"ethiopic-halehame-aa-et","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-halehame-am":{"__compat":{"description":"ethiopic-halehame-am","support":{"chrome":{"version_added":"45"},"chrome_android":{"version_added":"45"},"edge":{"version_added":"79"},"firefox":{"version_added":"1","prefix":"-moz-"},"firefox_android":{"version_added":"4","prefix":"-moz-"},"ie":{"version_added":false},"opera":{"version_added":"32"},"opera_android":{"version_added":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-halehame-am-et":{"__compat":{"description":"ethiopic-halehame-am-et","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-halehame-gez":{"__compat":{"description":"ethiopic-halehame-gez","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-halehame-om-et":{"__compat":{"description":"ethiopic-halehame-om-et","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-halehame-sid-et":{"__compat":{"description":"ethiopic-halehame-sid-et","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-halehame-so-et":{"__compat":{"description":"ethiopic-halehame-so-et","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-halehame-ti-er":{"__compat":{"description":"ethiopic-halehame-ti-er","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"1","prefix":"-moz-"},"firefox_android":{"version_added":"4","prefix":"-moz-"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"3"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-halehame-ti-et":{"__compat":{"description":"ethiopic-halehame-ti-et","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"1","prefix":"-moz-"},"firefox_android":{"version_added":"4","prefix":"-moz-"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"3"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-halehame-tig":{"__compat":{"description":"ethiopic-halehame-tig","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-numeric":{"__compat":{"description":"ethiopic-numeric","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"33","notes":"Before Firefox 38, Firefox added a dot as suffix of the number for ethiopic-numeric (for example, ፫. instead of ፫). The specification later defined the absence of a suffix, which Firefox 38 followed."},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33","notes":"Before Firefox 38, Firefox added a dot as suffix of the number for ethiopic-numeric (for example, ፫. instead of ፫). The specification later defined the absence of a suffix, which Firefox 38 followed."},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"footnotes":{"__compat":{"description":"footnotes","support":{"chrome":{"version_added":"13","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"4.4","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"georgian":{"__compat":{"description":"georgian","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"6"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"gujarati":{"__compat":{"description":"gujarati","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"gurmukhi":{"__compat":{"description":"gurmukhi","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"hangul":{"__compat":{"description":"hangul","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"1","prefix":"-moz-"},"firefox_android":{"version_added":"4","prefix":"-moz-"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"hangul-consonant":{"__compat":{"description":"hangul-consonant","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"1","prefix":"-moz-"},"firefox_android":{"version_added":"4","prefix":"-moz-"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"hebrew":{"__compat":{"description":"hebrew","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"hiragana":{"__compat":{"description":"hiragana","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"hiragana-iroha":{"__compat":{"description":"hiragana-iroha","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"japanese-formal":{"__compat":{"description":"japanese-formal","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"28"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"28"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"japanese-informal":{"__compat":{"description":"japanese-informal","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"28"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"28"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"kannada":{"__compat":{"description":"kannada","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"katakana":{"__compat":{"description":"katakana","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"katakana-iroha":{"__compat":{"description":"katakana-iroha","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"khmer":{"__compat":{"description":"khmer","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"korean-hangul-formal":{"__compat":{"description":"korean-hangul-formal","support":{"chrome":{"version_added":"45"},"chrome_android":{"version_added":"45"},"edge":{"version_added":"79"},"firefox":{"version_added":"28"},"firefox_android":{"version_added":"28"},"ie":{"version_added":false},"opera":{"version_added":"32"},"opera_android":{"version_added":"32"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"korean-hanja-formal":{"__compat":{"description":"korean-hanja-formal","support":{"chrome":{"version_added":"45"},"chrome_android":{"version_added":"45"},"edge":{"version_added":"79"},"firefox":{"version_added":"28"},"firefox_android":{"version_added":"28"},"ie":{"version_added":false},"opera":{"version_added":"32"},"opera_android":{"version_added":"32"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"korean-hanja-informal":{"__compat":{"description":"korean-hanja-informal","support":{"chrome":{"version_added":"45"},"chrome_android":{"version_added":"45"},"edge":{"version_added":"79"},"firefox":{"version_added":"28"},"firefox_android":{"version_added":"28"},"ie":{"version_added":false},"opera":{"version_added":"32"},"opera_android":{"version_added":"32"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"lao":{"__compat":{"description":"lao","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"lower-alpha":{"__compat":{"description":"lower-alpha","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"6"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"lower-armenian":{"__compat":{"description":"lower-armenian","support":{"chrome":{"version_added":"13"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"33"},"firefox_android":{"version_added":"33"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"lower-greek":{"__compat":{"description":"lower-greek","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"6"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"lower-hexadecimal":{"__compat":{"description":"lower-hexadecimal","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"lower-latin":{"__compat":{"description":"lower-latin","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"6"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"lower-norwegian":{"__compat":{"description":"lower-norwegian","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"lower-roman":{"__compat":{"description":"lower-roman","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"6"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"malayalam":{"__compat":{"description":"malayalam","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"mongolian":{"__compat":{"description":"mongolian","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"33"},"firefox_android":{"version_added":"33"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"myanmar":{"__compat":{"description":"myanmar","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"octal":{"__compat":{"description":"octal","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"1","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"oriya":{"__compat":{"description":"oriya","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"oromo":{"__compat":{"description":"oromo","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"1","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"persian":{"__compat":{"description":"persian","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"sidama":{"__compat":{"description":"sidama","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"1","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"simp-chinese-formal":{"__compat":{"description":"simp-chinese-formal","support":{"chrome":{"version_added":"45"},"chrome_android":{"version_added":"45"},"edge":{"version_added":"79"},"firefox":[{"version_added":"28"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"28"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"32"},"opera_android":{"version_added":"32"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"simp-chinese-informal":{"__compat":{"description":"simp-chinese-informal","support":{"chrome":{"version_added":"45"},"chrome_android":{"version_added":"45"},"edge":{"version_added":"79"},"firefox":[{"version_added":"28"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"28"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"32"},"opera_android":{"version_added":"32"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"somali":{"__compat":{"description":"somali","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"1","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"square":{"__compat":{"description":"square","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"string":{"__compat":{"description":"<string>","support":{"chrome":{"version_added":"79"},"chrome_android":{"version_added":"79"},"edge":{"version_added":"79"},"firefox":{"version_added":"39"},"firefox_android":{"version_added":"39"},"ie":{"version_added":false},"opera":{"version_added":"66"},"opera_android":{"version_added":false},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"12.0"},"webview_android":{"version_added":"79"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"symbols":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/symbols()","spec_url":"https://drafts.csswg.org/css-counter-styles/#symbols-function","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"35"},"firefox_android":{"version_added":"35"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"tamil":{"__compat":{"description":"tamil","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"telugu":{"__compat":{"description":"telugu","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"thai":{"__compat":{"description":"thai","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"tibetan":{"__compat":{"description":"tibetan","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"33"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"tigre":{"__compat":{"description":"tigre","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"1","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"tigrinya-er":{"__compat":{"description":"tigrinya-er","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"1","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"tigrinya-er-abegede":{"__compat":{"description":"tigrinya-er-abegede","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"1","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"tigrinya-et":{"__compat":{"description":"tigrinya-et","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"1","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"tigrinya-et-abegede":{"__compat":{"description":"tigrinya-et-abegede","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"1","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"trad-chinese-formal":{"__compat":{"description":"trad-chinese-formal","support":{"chrome":{"version_added":"45"},"chrome_android":{"version_added":"45"},"edge":{"version_added":"79"},"firefox":[{"version_added":"28"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"28"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"32"},"opera_android":{"version_added":"32"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"trad-chinese-informal":{"__compat":{"description":"trad-chinese-informal","support":{"chrome":{"version_added":"45"},"chrome_android":{"version_added":"45"},"edge":{"version_added":"79"},"firefox":[{"version_added":"28"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"28"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"32"},"opera_android":{"version_added":"32"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"upper-alpha":{"__compat":{"description":"upper-alpha","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"6"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"upper-armenian":{"__compat":{"description":"upper-armenian","support":{"chrome":{"version_added":"13"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"33"},"firefox_android":{"version_added":"33"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"upper-greek":{"__compat":{"description":"upper-greek","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"6"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"upper-hexadecimal":{"__compat":{"description":"upper-hexadecimal","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"upper-latin":{"__compat":{"description":"upper-latin","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"6"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"upper-norwegian":{"__compat":{"description":"upper-norwegian","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"upper-roman":{"__compat":{"description":"upper-roman","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"6"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"urdu":{"__compat":{"description":"urdu","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"prefix":"-moz-","version_added":"33"},"firefox_android":{"prefix":"-moz-","version_added":"33"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"list-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/list-style","spec_url":"https://drafts.csswg.org/css-lists/#list-style-property","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"symbols":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/symbols()","spec_url":"https://drafts.csswg.org/css-counter-styles/#symbols-function","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"35"},"firefox_android":{"version_added":"35"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"margin-block-end":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/margin-block-end","spec_url":"https://drafts.csswg.org/css-scroll-snap/#scroll-padding","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"margin-block-start":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/margin-block-start","spec_url":"https://drafts.csswg.org/css-logical/#margin-properties","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"margin-block":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/margin-block","spec_url":"https://drafts.csswg.org/css-logical/#propdef-margin-block","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]},"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"48","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"margin-bottom":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/margin-bottom","spec_url":"https://drafts.csswg.org/css-box/#margin-physical","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"3"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"auto":{"__compat":{"description":"auto","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12","notes":"The auto value is not supported in quirks mode."},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"6","notes":"The auto value is not supported in quirks mode."},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"margin-inline-end":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/margin-inline-end","spec_url":"https://drafts.csswg.org/css-logical/#margin-properties","support":{"chrome":[{"version_added":"69"},{"version_added":"2","alternative_name":"-webkit-margin-end"}],"chrome_android":[{"version_added":"69"},{"version_added":"18","alternative_name":"-webkit-margin-end"}],"edge":[{"version_added":"79"},{"version_added":"79","alternative_name":"-webkit-margin-end"}],"firefox":[{"version_added":"41"},{"version_added":"3","alternative_name":"-moz-margin-end"}],"firefox_android":[{"version_added":"41"},{"version_added":"4","alternative_name":"-moz-margin-end"}],"ie":{"version_added":false},"opera":[{"version_added":"56"},{"version_added":"15","alternative_name":"-webkit-margin-end"}],"opera_android":[{"version_added":"48"},{"version_added":"14","alternative_name":"-webkit-margin-end"}],"safari":[{"version_added":"12.1"},{"version_added":"3","alternative_name":"-webkit-margin-end"}],"safari_ios":[{"version_added":"12.2"},{"version_added":"3","alternative_name":"-webkit-margin-end"}],"samsunginternet_android":[{"version_added":"10.0"},{"alternative_name":"-webkit-margin-end","version_added":"1.0"}],"webview_android":[{"version_added":"69"},{"version_added":"2","alternative_name":"-webkit-margin-end"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"margin-inline-start":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/margin-inline-start","spec_url":"https://drafts.csswg.org/css-logical/#margin-properties","support":{"chrome":[{"version_added":"69"},{"version_added":"2","alternative_name":"-webkit-margin-start"}],"chrome_android":[{"version_added":"69"},{"version_added":"18","alternative_name":"-webkit-margin-start"}],"edge":[{"version_added":"79"},{"version_added":"79","alternative_name":"-webkit-margin-start"}],"firefox":[{"version_added":"41"},{"version_added":"3","alternative_name":"-moz-margin-start"}],"firefox_android":[{"version_added":"41"},{"version_added":"4","alternative_name":"-moz-margin-start"}],"ie":{"version_added":false},"opera":[{"version_added":"56"},{"version_added":"15","alternative_name":"-webkit-margin-start"}],"opera_android":[{"version_added":"48"},{"version_added":"14","alternative_name":"-webkit-margin-start"}],"safari":[{"version_added":"12.1"},{"version_added":"3","alternative_name":"-webkit-margin-start"}],"safari_ios":[{"version_added":"12.2"},{"version_added":"3","alternative_name":"-webkit-margin-start"}],"samsunginternet_android":[{"version_added":"10.0"},{"alternative_name":"-webkit-margin-start","version_added":"1.0"}],"webview_android":[{"version_added":"69"},{"version_added":"2","alternative_name":"-webkit-margin-start"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"margin-inline":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/margin-inline","spec_url":"https://drafts.csswg.org/css-logical/#propdef-margin-inline","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]},"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"48","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"margin-left":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/margin-left","spec_url":"https://drafts.csswg.org/css-box/#margin-physical","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"3"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"auto":{"__compat":{"description":"auto","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12","notes":"The auto value is not supported in quirks mode."},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"6","notes":"The auto value is not supported in quirks mode."},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"margin-right":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/margin-right","spec_url":"https://drafts.csswg.org/css-box/#margin-physical","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"3"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"auto":{"__compat":{"description":"auto","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12","notes":"The auto value is not supported in quirks mode."},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"6","notes":"The auto value is not supported in quirks mode."},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"margin-top":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/margin-top","spec_url":"https://drafts.csswg.org/css-box/#margin-physical","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"3"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"auto":{"__compat":{"description":"auto","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12","notes":"The auto value is not supported in quirks mode."},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"6","notes":"The auto value is not supported in quirks mode."},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"margin-trim":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/margin-trim","spec_url":"https://drafts.csswg.org/css-box-4/#margin-trim","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false,"notes":"See bug 1506241."},"firefox_android":{"version_added":false,"notes":"See bug 1506241."},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"margin":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/margin","spec_url":"https://drafts.csswg.org/css-box/#margin","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"3"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"auto":{"__compat":{"description":"auto","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12","notes":"The auto value is not supported in quirks mode."},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"6","notes":"The auto value is not supported in quirks mode."},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"mask-border-outset":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mask-border-outset","spec_url":"https://drafts.fxtf.org/css-masking/#the-mask-border-outset","support":{"chrome":{"alternative_name":"-webkit-mask-box-image-outset","version_added":"1"},"chrome_android":{"alternative_name":"-webkit-mask-box-image-outset","version_added":"18"},"edge":{"alternative_name":"-webkit-mask-box-image-outset","version_added":"79"},"firefox":{"version_added":false,"notes":"See bug 877294."},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"alternative_name":"-webkit-mask-box-image-outset","version_added":"15"},"opera_android":{"alternative_name":"-webkit-mask-box-image-outset","version_added":"14"},"safari":{"alternative_name":"-webkit-mask-box-image-outset","version_added":"3.1"},"safari_ios":{"alternative_name":"-webkit-mask-box-image-outset","version_added":"3"},"samsunginternet_android":{"alternative_name":"-webkit-mask-box-image-outset","version_added":"1.0"},"webview_android":{"alternative_name":"-webkit-mask-box-image-outset","version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"mask-border-repeat":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mask-border-repeat","spec_url":"https://drafts.fxtf.org/css-masking/#the-mask-border-repeat","support":{"chrome":{"alternative_name":"-webkit-mask-box-image-repeat","version_added":"1"},"chrome_android":{"alternative_name":"-webkit-mask-box-image-repeat","version_added":"18"},"edge":{"alternative_name":"-webkit-mask-box-image-repeat","version_added":"79"},"firefox":{"version_added":false,"notes":"See bug 877294."},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"alternative_name":"-webkit-mask-box-image-repeat","version_added":"15"},"opera_android":{"alternative_name":"-webkit-mask-box-image-repeat","version_added":"14"},"safari":{"alternative_name":"-webkit-mask-box-image-repeat","version_added":"3.1"},"safari_ios":{"alternative_name":"-webkit-mask-box-image-repeat","version_added":"3"},"samsunginternet_android":{"alternative_name":"-webkit-mask-box-image-repeat","version_added":"1.0"},"webview_android":{"alternative_name":"-webkit-mask-box-image-repeat","version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"mask-border-slice":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mask-border-slice","spec_url":"https://drafts.fxtf.org/css-masking/#the-mask-border-slice","support":{"chrome":{"alternative_name":"-webkit-mask-box-image-slice","version_added":"1"},"chrome_android":{"alternative_name":"-webkit-mask-box-image-slice","version_added":"18"},"edge":{"alternative_name":"-webkit-mask-box-image-slice","version_added":"79"},"firefox":{"version_added":false,"notes":"See bug 877294."},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"alternative_name":"-webkit-mask-box-image-slice","version_added":"15"},"opera_android":{"alternative_name":"-webkit-mask-box-image-slice","version_added":"14"},"safari":{"alternative_name":"-webkit-mask-box-image-slice","version_added":"3.1"},"safari_ios":{"alternative_name":"-webkit-mask-box-image-slice","version_added":"3"},"samsunginternet_android":{"alternative_name":"-webkit-mask-box-image-slice","version_added":"1.0"},"webview_android":{"alternative_name":"-webkit-mask-box-image-slice","version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"mask-border-source":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mask-border-source","spec_url":"https://drafts.fxtf.org/css-masking/#the-mask-border-source","support":{"chrome":{"alternative_name":"-webkit-mask-box-image-source","version_added":"1"},"chrome_android":{"alternative_name":"-webkit-mask-box-image-source","version_added":"18"},"edge":{"alternative_name":"-webkit-mask-box-image-source","version_added":"79"},"firefox":{"version_added":false,"notes":"See bug 877294."},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"alternative_name":"-webkit-mask-box-image-source","version_added":"15"},"opera_android":{"alternative_name":"-webkit-mask-box-image-source","version_added":"14"},"safari":{"alternative_name":"-webkit-mask-box-image-source","version_added":"3.1"},"safari_ios":{"alternative_name":"-webkit-mask-box-image-source","version_added":"3"},"samsunginternet_android":{"alternative_name":"-webkit-mask-box-image-source","version_added":"1.0"},"webview_android":{"alternative_name":"-webkit-mask-box-image-source","version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"mask-border-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mask-border-width","spec_url":"https://drafts.fxtf.org/css-masking/#the-mask-border-width","support":{"chrome":{"alternative_name":"-webkit-mask-box-image-width","version_added":"1"},"chrome_android":{"alternative_name":"-webkit-mask-box-image-width","version_added":"18"},"edge":{"alternative_name":"-webkit-mask-box-image-width","version_added":"79"},"firefox":{"version_added":false,"notes":"See bug 877294."},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"alternative_name":"-webkit-mask-box-image-width","version_added":"15"},"opera_android":{"alternative_name":"-webkit-mask-box-image-width","version_added":"14"},"safari":{"alternative_name":"-webkit-mask-box-image-width","version_added":"3.1"},"safari_ios":{"alternative_name":"-webkit-mask-box-image-width","version_added":"3"},"samsunginternet_android":{"alternative_name":"-webkit-mask-box-image-width","version_added":"1.0"},"webview_android":{"alternative_name":"-webkit-mask-box-image-width","version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"mask-border":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mask-border","spec_url":"https://drafts.fxtf.org/css-masking/#the-mask-border","support":{"chrome":{"alternative_name":"-webkit-mask-box-image","version_added":"1"},"chrome_android":{"alternative_name":"-webkit-mask-box-image","version_added":"18"},"edge":{"alternative_name":"-webkit-mask-box-image","version_added":"79"},"firefox":{"version_added":false,"notes":"See bug 877294."},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"alternative_name":"-webkit-mask-box-image","version_added":"15"},"opera_android":{"alternative_name":"-webkit-mask-box-image","version_added":"14"},"safari":{"alternative_name":"-webkit-mask-box-image","version_added":"3.1"},"safari_ios":{"alternative_name":"-webkit-mask-box-image","version_added":"3"},"samsunginternet_android":{"alternative_name":"-webkit-mask-box-image","version_added":"1.0"},"webview_android":{"alternative_name":"-webkit-mask-box-image","version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"mask-clip":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mask-clip","spec_url":"https://drafts.fxtf.org/css-masking/#the-mask-clip","support":{"chrome":{"prefix":"-webkit-","version_added":"1"},"chrome_android":{"prefix":"-webkit-","version_added":"18"},"edge":{"prefix":"-webkit-","version_added":"79"},"firefox":{"version_added":"53"},"firefox_android":{"version_added":"53"},"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":{"prefix":"-webkit-","version_added":"4"},"safari_ios":{"prefix":"-webkit-","version_added":"3.2"},"samsunginternet_android":{"prefix":"-webkit-","version_added":"1.0"},"webview_android":{"prefix":"-webkit-","version_added":"2"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"border":{"__compat":{"description":"border","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"4"},"safari_ios":{"version_added":"3.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"content":{"__compat":{"description":"content","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"4"},"safari_ios":{"version_added":"3.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"padding":{"__compat":{"description":"padding","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"4"},"safari_ios":{"version_added":"3.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"text":{"__compat":{"description":"text","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"4"},"safari_ios":{"version_added":"3.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}}},"mask-composite":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mask-composite","spec_url":"https://drafts.fxtf.org/css-masking/#the-mask-composite","support":{"chrome":{"version_added":false,"notes":"See also -webkit-mask-composite for a similar non-standard property that uses different keywords."},"chrome_android":{"version_added":false,"notes":"See also -webkit-mask-composite for a similar non-standard property that uses different keywords."},"edge":{"version_added":"18","version_removed":"79"},"firefox":{"version_added":"53"},"firefox_android":{"version_added":"53"},"ie":{"version_added":false},"opera":{"version_added":false,"notes":"See also -webkit-mask-composite for a similar non-standard property that uses different keywords."},"opera_android":{"version_added":false,"notes":"See also -webkit-mask-composite for a similar non-standard property that uses different keywords."},"safari":{"version_added":false,"notes":"See also -webkit-mask-composite for a similar non-standard property that uses different keywords."},"safari_ios":{"version_added":false,"notes":"See also -webkit-mask-composite for a similar non-standard property that uses different keywords."},"samsunginternet_android":{"version_added":false,"notes":"See also -webkit-mask-composite for a similar non-standard property that uses different keywords."},"webview_android":{"version_added":false,"notes":"See also -webkit-mask-composite for a similar non-standard property that uses different keywords."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"mask-image":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mask-image","spec_url":"https://drafts.fxtf.org/css-masking/#the-mask-image","support":{"chrome":{"prefix":"-webkit-","version_added":"1","notes":"From version 8, Chrome added support for gradient values. Initially, Chrome supported only -webkit- prefixed values for gradients (such as -webkit-linear-gradient()). Later, support for unprefixed values was added."},"chrome_android":{"prefix":"-webkit-","version_added":"18","notes":"From version 8, Chrome added support for gradient values. Initially, Chrome supported only -webkit- prefixed values for gradients (such as -webkit-linear-gradient()). Later, support for unprefixed values was added."},"edge":[{"prefix":"-webkit-","version_added":"79"},{"version_added":"16","version_removed":"79"}],"firefox":{"version_added":"53"},"firefox_android":{"version_added":"53"},"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":{"prefix":"-webkit-","version_added":"4","notes":"Initially, Safari supported only -webkit- prefixed values for gradients (such as -webkit-linear-gradient()). Later, support for unprefixed values was added."},"safari_ios":{"prefix":"-webkit-","version_added":"3.2","notes":"Initially, Safari supported only -webkit- prefixed values for gradients (such as -webkit-linear-gradient()). Later, support for unprefixed values was added."},"samsunginternet_android":{"version_added":"1.0","prefix":"-webkit-"},"webview_android":{"prefix":"-webkit-","version_added":"2","notes":"Initially, Android supported only -webkit- prefixed values for gradients (such as -webkit-linear-gradient()). Later, support for unprefixed values was added."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"multiple_mask_images":{"__compat":{"description":"Multiple mask images","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"18"},"firefox":{"version_added":"53"},"firefox_android":{"version_added":"53"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"4"},"safari_ios":{"version_added":"3.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"svg_masks":{"__compat":{"description":"SVG masks","support":{"chrome":{"version_added":"8"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"18"},"firefox":{"version_added":"53"},"firefox_android":{"version_added":"53"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"4"},"safari_ios":{"version_added":"3.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"mask-mode":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mask-mode","spec_url":"https://drafts.fxtf.org/css-masking/#the-mask-mode","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"53"},"firefox_android":{"version_added":"53"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"mask-origin":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mask-origin","spec_url":"https://drafts.fxtf.org/css-masking/#the-mask-origin","support":{"chrome":{"prefix":"-webkit-","version_added":"1","notes":"The margin-box value is unsupported."},"chrome_android":{"prefix":"-webkit-","version_added":"18","notes":"The margin-box value is unsupported."},"edge":{"prefix":"-webkit-","version_added":"79","notes":"The margin-box value is unsupported."},"firefox":{"version_added":"53"},"firefox_android":{"version_added":"53"},"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15","notes":"The margin-box value is unsupported."},"opera_android":{"prefix":"-webkit-","version_added":"14","notes":"The margin-box value is unsupported."},"safari":{"prefix":"-webkit-","version_added":"4","notes":"The margin-box value is unsupported."},"safari_ios":{"prefix":"-webkit-","version_added":"3.2","notes":"The margin-box value is unsupported."},"samsunginternet_android":{"prefix":"-webkit-","version_added":"1.0","notes":"The margin-box value is unsupported."},"webview_android":{"prefix":"-webkit-","version_added":"2","notes":"The margin-box value is unsupported."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"fill-box":{"__compat":{"description":"fill-box","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false,"notes":"See bug 137293."},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"non_standard_values":{"__compat":{"description":"Non-standard values content, padding, border","support":{"chrome":{"prefix":"-webkit-","version_added":"1"},"chrome_android":{"prefix":"-webkit-","version_added":"18"},"edge":{"prefix":"-webkit-","version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":{"prefix":"-webkit-","version_added":"4"},"safari_ios":{"prefix":"-webkit-","version_added":"3.2"},"samsunginternet_android":{"prefix":"-webkit-","version_added":"1.0"},"webview_android":{"prefix":"-webkit-","version_added":"2"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"stroke-box":{"__compat":{"description":"stroke-box","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false,"notes":"See bug 137293."},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"view-box":{"__compat":{"description":"view-box","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false,"notes":"See bug 137293."},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}}},"mask-position":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mask-position","spec_url":"https://drafts.fxtf.org/css-masking/#the-mask-position","support":{"chrome":{"prefix":"-webkit-","version_added":"1"},"chrome_android":{"prefix":"-webkit-","version_added":"18"},"edge":[{"prefix":"-webkit-","version_added":"79"},{"version_added":"18","version_removed":"79"}],"firefox":{"version_added":"53"},"firefox_android":{"version_added":"53"},"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":{"prefix":"-webkit-","version_added":"3.1"},"safari_ios":{"prefix":"-webkit-","version_added":"2"},"samsunginternet_android":{"prefix":"-webkit-","version_added":"1.0"},"webview_android":{"prefix":"-webkit-","version_added":"2"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"three_value_syntax":{"__compat":{"description":"Support for three-value syntax of position","support":{"chrome":{"prefix":"-webkit-","version_added":"1","version_removed":"68"},"chrome_android":{"prefix":"-webkit-","version_added":"18","version_removed":"68"},"edge":{"version_added":"18","version_removed":"79"},"firefox":{"version_added":"53","version_removed":"70"},"firefox_android":{"version_added":"53","version_removed":"79"},"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15","version_removed":"55"},"opera_android":{"prefix":"-webkit-","version_added":"14","version_removed":"48"},"safari":{"prefix":"-webkit-","version_added":"3.1"},"safari_ios":{"prefix":"-webkit-","version_added":"2"},"samsunginternet_android":{"prefix":"-webkit-","version_added":"1.0","version_removed":"10.0"},"webview_android":{"prefix":"-webkit-","version_added":"2","version_removed":"68"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"mask-repeat":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mask-repeat","spec_url":"https://drafts.fxtf.org/css-masking/#the-mask-repeat","support":{"chrome":{"prefix":"-webkit-","version_added":"1"},"chrome_android":{"prefix":"-webkit-","version_added":"18"},"edge":[{"prefix":"-webkit-","version_added":"79"},{"version_added":"18","version_removed":"79"}],"firefox":{"version_added":"53"},"firefox_android":{"version_added":"53"},"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":{"prefix":"-webkit-","version_added":"3.1"},"safari_ios":{"prefix":"-webkit-","version_added":"2"},"samsunginternet_android":{"prefix":"-webkit-","version_added":"1.0"},"webview_android":{"prefix":"-webkit-","version_added":"2"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"mask-size":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mask-size","spec_url":"https://drafts.fxtf.org/css-masking/#the-mask-size","support":{"chrome":{"prefix":"-webkit-","version_added":"4"},"chrome_android":{"prefix":"-webkit-","version_added":"18"},"edge":[{"prefix":"-webkit-","version_added":"79"},{"version_added":"18","version_removed":"79"}],"firefox":{"version_added":"53"},"firefox_android":{"version_added":"53"},"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":{"prefix":"-webkit-","version_added":"4"},"safari_ios":{"prefix":"-webkit-","version_added":"2"},"samsunginternet_android":{"prefix":"-webkit-","version_added":"1.0"},"webview_android":{"prefix":"-webkit-","version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"mask-type":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mask-type","spec_url":"https://drafts.fxtf.org/css-masking/#the-mask-type","support":{"chrome":{"version_added":"24"},"chrome_android":{"version_added":"25"},"edge":{"version_added":"79"},"firefox":{"version_added":"35"},"firefox_android":{"version_added":"35"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"7"},"safari_ios":{"version_added":"7"},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"mask":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mask","spec_url":"https://drafts.fxtf.org/css-masking/#the-mask","support":{"chrome":[{"version_added":"1","partial_implementation":true,"notes":"While the property is recognized, values applied to it don't have any effect."},{"prefix":"-webkit-","version_added":"1","notes":"The prefixed property can be used with SVG and HTML with a slightly different syntax, which allows setting the non-standard -webkit-mask-attachment property."}],"chrome_android":[{"version_added":"18","partial_implementation":true,"notes":"While the property is recognized, values applied to it don't have any effect."},{"prefix":"-webkit-","version_added":"18","notes":"The prefixed property can be used with SVG and HTML with a slightly different syntax, which allows setting the non-standard -webkit-mask-attachment property."}],"edge":[{"version_added":"79","partial_implementation":true,"notes":"While the property is recognized, values applied to it don't have any effect."},{"prefix":"-webkit-","version_added":"79","notes":"The prefixed property can be used with SVG and HTML with a slightly different syntax, which allows setting the non-standard -webkit-mask-attachment property."},{"version_added":"12","version_removed":"79"}],"firefox":{"version_added":"2","notes":"From Firefox 10, the default color space when handling masks is sRGB. Previously, the default and only supported color space was linear RGB. This changes the appearance of mask effects, but brings Firefox into compliance with the second edition of the SVG 1.1 specification."},"firefox_android":{"version_added":"4","notes":"From Firefox 10, the default color space when handling masks is sRGB. Previously, the default and only supported color space was linear RGB. This changes the appearance of mask effects, but brings Firefox into compliance with the second edition of the SVG 1.1 specification."},"ie":{"version_added":false},"opera":[{"version_added":"15","partial_implementation":true,"notes":"While the property is recognized, values applied to it don't have any effect."},{"prefix":"-webkit-","version_added":"15","notes":"The prefixed property can be used with SVG and HTML with a slightly different syntax, which allows setting the non-standard -webkit-mask-attachment property."}],"opera_android":[{"version_added":"14","partial_implementation":true,"notes":"While the property is recognized, values applied to it don't have any effect."},{"prefix":"-webkit-","version_added":"14","notes":"The prefixed property can be used with SVG and HTML with a slightly different syntax, which allows setting the non-standard -webkit-mask-attachment property."}],"safari":[{"version_added":"3.1","partial_implementation":true,"notes":"While the property is recognized, values applied to it don't have any effect."},{"prefix":"-webkit-","version_added":"3.1","notes":"The prefixed property can be used with SVG and HTML with a slightly different syntax, which allows setting the non-standard -webkit-mask-attachment property."}],"safari_ios":[{"version_added":"2","partial_implementation":true,"notes":"While the property is recognized, values applied to it don't have any effect."},{"prefix":"-webkit-","version_added":"2","notes":"The prefixed property can be used with SVG and HTML with a slightly different syntax, which allows setting the non-standard -webkit-mask-attachment property."}],"samsunginternet_android":[{"version_added":"1.0","partial_implementation":true,"notes":"While the property is recognized, values applied to it don't have any effect."},{"prefix":"-webkit-","version_added":"1.0","notes":"The prefixed property can be used with SVG and HTML with a slightly different syntax, which allows setting the non-standard -webkit-mask-attachment property."}],"webview_android":[{"version_added":"2","partial_implementation":true,"notes":"While the property is recognized, values applied to it don't have any effect."},{"prefix":"-webkit-","version_added":"2","notes":"The prefixed property can be used with SVG and HTML with a slightly different syntax, which allows setting the non-standard -webkit-mask-attachment property."}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"masonry-auto-flow":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/masonry-auto-flow","spec_url":"https://drafts.csswg.org/css-grid-3/#masonry-auto-flow","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"math-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/math-style","spec_url":"https://w3c.github.io/mathml-core/#the-math-style-property","support":{"chrome":{"version_added":"83","flags":[{"type":"preference","name":"Enable experimental Web Platform features"}]},"chrome_android":{"version_added":"83","flags":[{"type":"preference","name":"Enable experimental Web Platform features"}]},"edge":{"version_added":false},"firefox":{"version_added":"83","flags":[{"type":"preference","name":"layout.css.math-style.enabled","value_to_set":"true"}]},"firefox_android":{"version_added":"83","flags":[{"type":"preference","name":"layout.css.math-style.enabled","value_to_set":"true"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"max-block-size":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/max-block-size","spec_url":"https://drafts.csswg.org/css-logical/#propdef-max-block-size","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"fit-content":{"__compat":{"description":"fit-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"alternative_name":"-webkit-fill-available","version_added":"5.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"fit-content_function":{"__compat":{"description":"fit-content()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"firefox_android":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"max-content":{"__compat":{"description":"max-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"min-content":{"__compat":{"description":"min-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"max-height":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/max-height","spec_url":"https://drafts.csswg.org/css-sizing-4/#width-height-keywords","support":{"chrome":{"version_added":"18"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"CSS 2.1 leaves the behavior of max-height with table undefined. Firefox supports applying max-height to table elements."},"firefox_android":{"version_added":"4"},"ie":{"version_added":"7"},"opera":{"version_added":"7","notes":"CSS 2.1 leaves the behavior of max-height with table undefined. Opera supports applying max-height to table elements."},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"fit-content":{"__compat":{"description":"fit-content","support":{"chrome":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"25"}],"chrome_android":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"25"}],"edge":[{"version_added":"79"},{"prefix":"-webkit-","version_added":"79"}],"firefox":{"partial_implementation":true,"prefix":"-moz-","version_added":"3","notes":"Firefox implements the definitions given in CSS3 Basic Box. This defines available and not fit-available. Also, the definition of fit-content is simpler than in CSS3 Sizing."},"firefox_android":{"partial_implementation":true,"prefix":"-moz-","version_added":"4","notes":"Firefox implements the definitions given in CSS3 Basic Box. This defines available and not fit-available. Also, the definition of fit-content is simpler than in CSS3 Sizing."},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"7"},{"alternative_name":"intrinsic","version_added":"2"}],"safari_ios":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"7"},{"alternative_name":"intrinsic","version_added":"1"}],"samsunginternet_android":[{"version_added":"5.0"},{"prefix":"-webkit-","version_added":"1.5"}],"webview_android":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"fit-content_function":{"__compat":{"description":"fit-content()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"firefox_android":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"max-content":{"__compat":{"description":"max-content","support":{"chrome":{"version_added":"46"},"chrome_android":{"version_added":"46"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"3","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"4","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":[{"version_added":"11"},{"version_added":"9","prefix":"-webkit-"}],"safari_ios":[{"version_added":"11"},{"version_added":"9","prefix":"-webkit-"}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"46"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"min-content":{"__compat":{"description":"min-content","support":{"chrome":{"version_added":"46"},"chrome_android":{"version_added":"46"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"3","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"4","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":[{"version_added":"11"},{"version_added":"9","prefix":"-webkit-"}],"safari_ios":[{"version_added":"11"},{"version_added":"9","prefix":"-webkit-"}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"46"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"stretch":{"__compat":{"description":"stretch","support":{"chrome":{"alternative_name":"-webkit-fill-available","version_added":"28"},"chrome_android":{"alternative_name":"-webkit-fill-available","version_added":"28"},"edge":{"alternative_name":"-webkit-fill-available","version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"alternative_name":"-webkit-fill-available","version_added":"15"},"opera_android":{"alternative_name":"-webkit-fill-available","version_added":"15"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"1.5","alternative_name":"-webkit-fill-available"},"webview_android":{"alternative_name":"-webkit-fill-available","version_added":"4.4"}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}}},"max-inline-size":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/max-inline-size","spec_url":"https://drafts.csswg.org/css-logical/#propdef-max-inline-size","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"10.1"}],"safari_ios":[{"version_added":"12.2"},{"prefix":"-webkit-","version_added":"10.3"}],"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"fit-content":{"__compat":{"description":"fit-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"prefix":"-moz-","version_added":"41"},"firefox_android":{"prefix":"-moz-","version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"alternative_name":"-webkit-fill-available","version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"fit-content_function":{"__compat":{"description":"fit-content()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"firefox_android":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"max-content":{"__compat":{"description":"max-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"min-content":{"__compat":{"description":"min-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"max-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/max-width","spec_url":"https://drafts.csswg.org/css-sizing-4/#width-height-keywords","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"CSS 2.1 leaves the behavior of max-width with table undefined. Firefox supports applying max-width to table elements."},"firefox_android":{"version_added":"4","notes":"CSS 2.1 leaves the behavior of max-width with table undefined. Firefox supports applying max-width to table elements."},"ie":{"version_added":"7"},"opera":{"version_added":"4","notes":"CSS 2.1 leaves the behavior of max-width with table undefined. Opera supports applying max-width to table elements."},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"fit-content":{"__compat":{"description":"fit-content","support":{"chrome":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"25"}],"chrome_android":{"version_added":"46"},"edge":[{"version_added":"79"},{"prefix":"-webkit-","version_added":"79"}],"firefox":{"partial_implementation":true,"prefix":"-moz-","version_added":"3","notes":"Firefox implements the definitions given in CSS3 Basic Box. This defines available and not fit-available. Also, the definition of fit-content is simpler than in CSS3 Sizing."},"firefox_android":{"partial_implementation":true,"prefix":"-moz-","version_added":"4","notes":"Firefox implements the definitions given in CSS3 Basic Box. This defines available and not fit-available. Also, the definition of fit-content is simpler than in CSS3 Sizing."},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"7"},{"alternative_name":"intrinsic","version_added":"2"}],"safari_ios":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"7"},{"alternative_name":"intrinsic","version_added":"1"}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"fit-content_function":{"__compat":{"description":"fit-content()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"firefox_android":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"max-content":{"__compat":{"description":"max-content","support":{"chrome":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"22"}],"chrome_android":{"version_added":"46"},"edge":[{"version_added":"79"},{"prefix":"-webkit-","version_added":"79"}],"firefox":[{"version_added":"66"},{"version_added":"3","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"4","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"7"},{"alternative_name":"intrinsic","version_added":"2"}],"safari_ios":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"7"},{"alternative_name":"intrinsic","version_added":"1"}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"min-content":{"__compat":{"description":"min-content","support":{"chrome":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"25"}],"chrome_android":{"version_added":"46"},"edge":[{"version_added":"79"},{"prefix":"-webkit-","version_added":"79"}],"firefox":[{"version_added":"66"},{"version_added":"3","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"4","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"7"},{"alternative_name":"intrinsic","version_added":"2"}],"safari_ios":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"7"},{"alternative_name":"intrinsic","version_added":"1"}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"46"}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"stretch":{"__compat":{"description":"stretch","support":{"chrome":{"alternative_name":"-webkit-fill-available","version_added":"22"},"chrome_android":{"alternative_name":"-webkit-fill-available","version_added":"25"},"edge":{"alternative_name":"-webkit-fill-available","version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"alternative_name":"-webkit-fill-available","version_added":"15"},"opera_android":{"alternative_name":"-webkit-fill-available","version_added":"14"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"1.5","alternative_name":"-webkit-fill-available"},"webview_android":{"alternative_name":"-webkit-fill-available","version_added":"4.4"}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}}},"min-block-size":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/min-block-size","spec_url":"https://drafts.csswg.org/css-logical/#propdef-min-block-size","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"fit-content":{"__compat":{"description":"fit-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"alternative_name":"-webkit-fill-available","version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"fit-content_function":{"__compat":{"description":"fit-content()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"firefox_android":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"max-content":{"__compat":{"description":"max-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"min-content":{"__compat":{"description":"min-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"min-height":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/min-height","spec_url":"https://drafts.csswg.org/css-sizing/#width-height-keywords","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3","notes":"CSS 2.1 leaves the behavior of min-height with table undefined. Firefox supports applying min-height to table elements."},"firefox_android":{"version_added":"4","notes":"CSS 2.1 leaves the behavior of min-height with table undefined. Firefox supports applying min-height to table elements."},"ie":{"version_added":"7","notes":"In Internet Explorer 10 and 11, a min-height declaration on a column-direction flex container doesn't apply to the container's flex items. See Flexbug #3 for more info."},"opera":{"version_added":"4","notes":"CSS 2.1 leaves the behavior of min-height with table undefined. Opera supports applying min-height to table elements."},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"auto":{"__compat":{"description":"auto","support":{"chrome":{"version_added":"21"},"chrome_android":{"version_added":"25"},"edge":{"version_added":"79"},"firefox":{"version_added":"16","version_removed":"22","notes":"Firefox 18 and later used auto as the initial value for min-height."},"firefox_android":{"version_added":"16","version_removed":"22","notes":"Firefox 18 and later used auto as the initial value for min-height."},"ie":{"version_added":false},"opera":{"version_added":"12.1"},"opera_android":{"version_added":"14"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"fit-content":{"__compat":{"description":"fit-content","support":{"chrome":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"25"}],"chrome_android":{"version_added":"46"},"edge":[{"version_added":"79"},{"prefix":"-webkit-","version_added":"79"}],"firefox":{"prefix":"-moz-","version_added":"3","notes":"Firefox implements the definitions given in CSS3 Basic Box. This defines available and not fit-available. Also, the definition of fit-content is simpler than in CSS3 Sizing."},"firefox_android":{"prefix":"-moz-","version_added":"4","notes":"Firefox implements the definitions given in CSS3 Basic Box. This defines available and not fit-available. Also, the definition of fit-content is simpler than in CSS3 Sizing."},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"6.1"},{"alternative_name":"intrinsic","version_added":"2"}],"safari_ios":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"6.1"},{"alternative_name":"intrinsic","version_added":"1"}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"fit-content_function":{"__compat":{"description":"fit-content()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"firefox_android":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"max-content":{"__compat":{"description":"max-content","support":{"chrome":{"version_added":"46"},"chrome_android":{"version_added":"46"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"3","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"4","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":[{"version_added":"11"},{"version_added":"9","prefix":"-webkit-"}],"safari_ios":[{"version_added":"11"},{"version_added":"9","prefix":"-webkit-"}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"46"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"min-content":{"__compat":{"description":"min-content","support":{"chrome":{"version_added":"46"},"chrome_android":{"version_added":"46"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"3","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"4","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":[{"version_added":"11"},{"version_added":"9","prefix":"-webkit-"}],"safari_ios":[{"version_added":"11"},{"version_added":"9","prefix":"-webkit-"}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"46"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"stretch":{"__compat":{"description":"stretch","support":{"chrome":{"alternative_name":"-webkit-fill-available","version_added":"28"},"chrome_android":{"alternative_name":"-webkit-fill-available","version_added":"28"},"edge":{"alternative_name":"-webkit-fill-available","version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"alternative_name":"-webkit-fill-available","version_added":"15"},"opera_android":{"alternative_name":"-webkit-fill-available","version_added":"15"},"safari":{"alternative_name":"-webkit-fill-available","version_added":"9"},"safari_ios":{"alternative_name":"-webkit-fill-available","version_added":"9"},"samsunginternet_android":{"alternative_name":"-webkit-fill-available","version_added":"1.5"},"webview_android":{"alternative_name":"-webkit-fill-available","version_added":"4.4"}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}}},"min-inline-size":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/min-inline-size","spec_url":"https://drafts.csswg.org/css-logical/#propdef-min-inline-size","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"fit-content":{"__compat":{"description":"fit-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"prefix":"-moz-","version_added":"41"},"firefox_android":{"prefix":"-moz-","version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"alternative_name":"-webkit-fill-available","version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"fit-content_function":{"__compat":{"description":"fit-content()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"firefox_android":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"max-content":{"__compat":{"description":"max-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"min-content":{"__compat":{"description":"min-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"min-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/min-width","spec_url":"https://drafts.csswg.org/css-sizing/#min-size-properties","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"CSS 2.1 leaves the behavior of min-width with table undefined. Firefox supports applying min-width to table elements."},"firefox_android":{"version_added":"4","notes":"CSS 2.1 leaves the behavior of min-width with table undefined. Firefox supports applying min-width to table elements."},"ie":{"version_added":"7"},"opera":{"version_added":"4","notes":"CSS 2.1 leaves the behavior of min-width with table undefined. Opera supports applying min-width to table elements."},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"auto":{"__compat":{"description":"auto","support":{"chrome":{"version_added":"21","notes":"Chrome uses auto as the initial value for min-width."},"chrome_android":{"version_added":"25","notes":"Chrome uses auto as the initial value for min-width."},"edge":{"version_added":"12","notes":"Edge uses auto as the initial value for min-width."},"firefox":[{"version_added":"34"},{"version_added":"16","version_removed":"22","notes":"Firefox 18 and later (until the value was removed), used auto as the initial value for min-width."}],"firefox_android":[{"version_added":"34"},{"version_added":"16","version_removed":"22","notes":"Firefox 18 and later (until the value was removed), used auto as the initial value for min-width."}],"ie":{"version_added":false},"opera":{"version_added":"12.1","notes":"Opera uses auto as the initial value for min-width."},"opera_android":{"version_added":"14","notes":"Opera uses auto as the initial value for min-width."},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"1.5","notes":"Samsung Internet uses auto as the initial value for min-width."},"webview_android":{"version_added":"37","notes":"Chrome uses auto as the initial value for min-width."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"fit-content":{"__compat":{"description":"fit-content","support":{"chrome":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"25"}],"chrome_android":{"version_added":"46"},"edge":[{"version_added":"79"},{"prefix":"-webkit-","version_added":"79"}],"firefox":{"prefix":"-moz-","version_added":"3","notes":"Firefox implements the definitions given in CSS3 Basic Box. This defines available and not fit-available. Also, the definition of fit-content is simpler than in CSS3 Sizing."},"firefox_android":{"prefix":"-moz-","version_added":"4","notes":"Firefox implements the definitions given in CSS3 Basic Box. This defines available and not fit-available. Also, the definition of fit-content is simpler than in CSS3 Sizing."},"ie":{"version_added":false},"opera":[{"version_added":"33"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":{"version_added":"33"},"safari":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":{"alternative_name":"-webkit-fill-available","version_added":"5.0"},"webview_android":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"fit-content_function":{"__compat":{"description":"fit-content()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"firefox_android":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"max-content":{"__compat":{"description":"max-content","support":{"chrome":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"25"}],"chrome_android":{"version_added":"46"},"edge":[{"version_added":"79"},{"prefix":"-webkit-","version_added":"79"}],"firefox":[{"version_added":"66"},{"version_added":"3","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"4","prefix":"-moz-"}],"ie":{"version_added":false},"opera":[{"version_added":"33"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":{"version_added":"33"},"safari":[{"version_added":"11"},{"alternative_name":"intrinsic","version_added":"2"}],"safari_ios":[{"version_added":"11"},{"alternative_name":"intrinsic","version_added":"1"}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"min-content":{"__compat":{"description":"min-content","support":{"chrome":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"25"},{"alternative_name":"min-intrinsic","version_added":"25","version_removed":"48"}],"chrome_android":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"25"},{"alternative_name":"min-intrinsic","version_added":"25","version_removed":"48"}],"edge":[{"version_added":"79"},{"prefix":"-webkit-","version_added":"79"}],"firefox":[{"version_added":"66"},{"version_added":"3","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"4","prefix":"-moz-"}],"ie":{"version_added":false},"opera":[{"version_added":"33"},{"prefix":"-webkit-","version_added":"≤15"},{"alternative_name":"min-intrinsic","version_added":"≤15","version_removed":"35"}],"opera_android":[{"version_added":"33"},{"prefix":"-webkit-","version_added":"≤14"},{"alternative_name":"min-intrinsic","version_added":"≤14","version_removed":"35"}],"safari":[{"version_added":"11"},{"alternative_name":"min-intrinsic","version_added":"2"}],"safari_ios":[{"version_added":"11"},{"alternative_name":"min-intrinsic","version_added":"1"}],"samsunginternet_android":[{"version_added":"5.0"},{"prefix":"-webkit-","version_added":"1.5"},{"alternative_name":"min-intrinsic","version_added":"1.5","version_removed":"5.0"}],"webview_android":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"≤37"},{"alternative_name":"min-intrinsic","version_added":"≤37","version_removed":"48"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"stretch":{"__compat":{"description":"stretch","support":{"chrome":{"alternative_name":"-webkit-fill-available","version_added":"22"},"chrome_android":{"alternative_name":"-webkit-fill-available","version_added":"25"},"edge":{"alternative_name":"-webkit-fill-available","version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"alternative_name":"-webkit-fill-available","version_added":"15"},"opera_android":{"alternative_name":"-webkit-fill-available","version_added":"14"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"1.5","alternative_name":"-webkit-fill-available"},"webview_android":{"alternative_name":"-webkit-fill-available","version_added":"4.4"}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}}},"mix-blend-mode":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mix-blend-mode","spec_url":"https://drafts.fxtf.org/compositing/#mix-blend-mode","support":{"chrome":{"version_added":"41"},"chrome_android":{"version_added":"41"},"edge":{"version_added":"79"},"firefox":{"version_added":"32"},"firefox_android":{"version_added":"32"},"ie":{"version_added":false},"opera":{"version_added":"28"},"opera_android":{"version_added":"28"},"safari":{"version_added":"8"},"safari_ios":{"version_added":"8"},"samsunginternet_android":{"version_added":"4.0"},"webview_android":{"version_added":"41"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"svg":{"__compat":{"description":"On SVG elements","support":{"chrome":{"version_added":"41"},"chrome_android":{"version_added":false},"edge":{"version_added":"79"},"firefox":{"version_added":"32"},"firefox_android":{"version_added":"32"},"ie":{"version_added":false},"opera":{"version_added":"28"},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"object-fit":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/object-fit","spec_url":"https://drafts.csswg.org/css-images/#the-object-fit","support":{"chrome":{"version_added":"32"},"chrome_android":{"version_added":"32"},"edge":[{"version_added":"79"},{"version_added":"16","version_removed":"79","partial_implementation":true,"notes":"Only supported for <img> elements."}],"firefox":{"version_added":"36"},"firefox_android":{"version_added":"36"},"ie":{"version_added":false},"opera":[{"version_added":"19"},{"prefix":"-o-","version_added":"11.6","version_removed":"15"}],"opera_android":[{"version_added":"19"},{"prefix":"-o-","version_added":"12","version_removed":"14"}],"safari":{"version_added":"10"},"safari_ios":{"version_added":"10"},"samsunginternet_android":{"version_added":"2.0"},"webview_android":{"version_added":"4.4.3"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"object-position":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/object-position","spec_url":"https://drafts.csswg.org/css-images/#the-object-position","support":{"chrome":{"version_added":"32"},"chrome_android":{"version_added":"32"},"edge":[{"version_added":"79"},{"version_added":"16","version_removed":"79","partial_implementation":true,"notes":"Only supported for <img> elements."}],"firefox":{"version_added":"36"},"firefox_android":{"version_added":"36"},"ie":{"version_added":false},"opera":[{"version_added":"19"},{"prefix":"-o-","version_added":"11.6","version_removed":"15"}],"opera_android":[{"version_added":"19"},{"prefix":"-o-","version_added":"12","version_removed":"14"}],"safari":{"version_added":"10"},"safari_ios":{"version_added":"10"},"samsunginternet_android":{"version_added":"2.0"},"webview_android":{"version_added":"4.4.3"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"three_value_syntax":{"__compat":{"description":"Support for three-value syntax of position","support":{"chrome":{"version_added":"32","version_removed":"68"},"chrome_android":{"version_added":"32","version_removed":"68"},"edge":{"version_added":"16","version_removed":"79"},"firefox":{"version_added":"36","version_removed":"70"},"firefox_android":{"version_added":"36","version_removed":"79"},"ie":{"version_added":false},"opera":[{"version_added":"19","version_removed":"55"},{"prefix":"-o-","version_added":"11.6","version_removed":"15"}],"opera_android":[{"version_added":"19","version_removed":"48"},{"prefix":"-o-","version_added":"12","version_removed":"14"}],"safari":{"version_added":"10","version_removed":"13.1"},"safari_ios":{"version_added":"10","version_removed":"13.4"},"samsunginternet_android":{"version_added":"2.0","version_removed":"10.0"},"webview_android":{"version_added":"4.4.3","version_removed":"68"}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}}},"offset-anchor":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/offset-anchor","spec_url":"https://drafts.fxtf.org/motion/#offset-anchor-property","support":{"chrome":{"version_added":"79"},"chrome_android":{"version_added":"79"},"edge":{"version_added":"79"},"firefox":[{"version_added":"72"},{"version_added":"70","version_removed":"72","flags":[{"type":"preference","name":"layout.css.motion-path.enabled","value_to_set":"true"}]}],"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"12.0"},"webview_android":{"version_added":"79"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"offset-distance":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/offset-distance","spec_url":"https://drafts.fxtf.org/motion/#offset-distance-property","support":{"chrome":[{"version_added":"55"},{"alternative_name":"motion-distance","version_added":"46"}],"chrome_android":[{"version_added":"55"},{"alternative_name":"motion-distance","version_added":"46"}],"edge":[{"version_added":"79"},{"alternative_name":"motion-distance","version_added":"79"}],"firefox":[{"version_added":"72"},{"version_added":"69","version_removed":"72","flags":[{"type":"preference","name":"layout.css.motion-path.enabled","value_to_set":"true"}]}],"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":[{"version_added":"42"},{"alternative_name":"motion-distance","version_added":"33"}],"opera_android":[{"version_added":"42"},{"alternative_name":"motion-distance","version_added":"33"}],"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":[{"version_added":"6.0"},{"alternative_name":"motion-distance","version_added":"5.0"}],"webview_android":[{"version_added":"55"},{"alternative_name":"motion-distance","version_added":"46"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"offset-path":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/offset-path","spec_url":"https://drafts.fxtf.org/motion/#offset-path-property","support":{"chrome":[{"version_added":"55"},{"alternative_name":"motion-path","version_added":"46"}],"chrome_android":[{"version_added":"55"},{"alternative_name":"motion-path","version_added":"46"}],"edge":[{"version_added":"79"},{"alternative_name":"motion-path","version_added":"79"}],"firefox":[{"version_added":"72"},{"version_added":"63","version_removed":"72","flags":[{"type":"preference","name":"layout.css.motion-path.enabled","value_to_set":"true"}]}],"firefox_android":{"version_added":"63","flags":[{"type":"preference","name":"layout.css.motion-path.enabled","value_to_set":"true"}]},"ie":{"version_added":false},"opera":[{"version_added":"45"},{"alternative_name":"motion-path","version_added":"32"}],"opera_android":[{"version_added":"43"},{"alternative_name":"motion-path","version_added":"32"}],"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":[{"version_added":"6.0","notes":"path() is the only value type supported."},{"alternative_name":"motion-path","version_added":"5.0"}],"webview_android":[{"version_added":"55"},{"alternative_name":"motion-path","version_added":"46"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"path-support":{"__compat":{"description":"Supports the path() function as a value","support":{"chrome":{"version_added":"64"},"chrome_android":{"version_added":"64"},"edge":{"version_added":"79"},"firefox":{"version_added":"63"},"firefox_android":{"version_added":"63"},"ie":{"version_added":false},"opera":{"version_added":"51"},"opera_android":{"version_added":"47"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"9.0"},"webview_android":{"version_added":"64"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ray-support":{"__compat":{"description":"Supports the ray() function as a value","support":{"chrome":{"version_added":"64"},"chrome_android":{"version_added":"64"},"edge":{"version_added":"79"},"firefox":{"version_added":"71","flags":[{"type":"preference","name":"layout.css.motion-path.enabled","value_to_set":"true"}]},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"51"},"opera_android":{"version_added":"47"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"9.0"},"webview_android":{"version_added":"64"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"offset-position":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/offset-position","spec_url":"https://drafts.fxtf.org/motion/#offset-position-property","support":{"chrome":{"version_added":false,"notes":"See bug 638055."},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false,"notes":"See bug 1559232."},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"offset-rotate":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/offset-rotate","spec_url":"https://drafts.fxtf.org/motion/#offset-rotate-property","support":{"chrome":[{"version_added":"56"},{"alternative_name":"offset-rotation","version_added":"55"},{"alternative_name":"motion-rotation","version_added":"46"}],"chrome_android":[{"version_added":"56"},{"alternative_name":"offset-rotation","version_added":"55"},{"alternative_name":"motion-rotation","version_added":"46"}],"edge":[{"version_added":"79"},{"alternative_name":"offset-rotation","version_added":"79"},{"alternative_name":"motion-rotation","version_added":"79"}],"firefox":[{"version_added":"72"},{"version_added":"69","version_removed":"72","flags":[{"type":"preference","name":"layout.css.motion-path.enabled","value_to_set":"true"}]}],"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":[{"version_added":"43"},{"alternative_name":"offset-rotation","version_added":"42"},{"alternative_name":"motion-rotation","version_added":"33"}],"opera_android":[{"version_added":"43"},{"alternative_name":"offset-rotation","version_added":"42"},{"alternative_name":"motion-rotation","version_added":"33"}],"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":[{"version_added":"6.0"},{"alternative_name":"offset-rotation","version_added":"6.0"},{"alternative_name":"motion-rotation","version_added":"5.0"}],"webview_android":[{"version_added":"56"},{"alternative_name":"offset-rotation","version_added":"55"},{"alternative_name":"motion-rotation","version_added":"46"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"offset":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/offset","spec_url":"https://drafts.fxtf.org/motion/#offset-shorthand","support":{"chrome":[{"version_added":"55"},{"alternative_name":"motion","version_added":"46"}],"chrome_android":[{"version_added":"55"},{"alternative_name":"motion","version_added":"46"}],"edge":[{"version_added":"79"},{"alternative_name":"motion","version_added":"79"}],"firefox":[{"version_added":"72"},{"version_added":"71","version_removed":"72","flags":[{"type":"preference","name":"layout.css.motion-path.enabled","value_to_set":"true"}]}],"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":[{"version_added":"42"},{"alternative_name":"motion","version_added":"33"}],"opera_android":[{"version_added":"42"},{"alternative_name":"motion","version_added":"33"}],"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":[{"version_added":"6.0"},{"alternative_name":"motion","version_added":"5.0"}],"webview_android":[{"version_added":"55"},{"alternative_name":"motion","version_added":"46"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"opacity":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/opacity","spec_url":"https://drafts.csswg.org/css-color/#transparency","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":[{"version_added":"1"},{"prefix":"-moz-","version_added":"1","version_removed":"3.5"}],"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"9"},"opera_android":{"version_added":"10.1"},"safari":[{"version_added":"2"},{"version_added":"1.1","version_removed":"2","prefix":"-khtml-"}],"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"percentages":{"__compat":{"description":"Support for percentage opacity values","support":{"chrome":{"version_added":"78"},"chrome_android":{"version_added":"78"},"edge":{"version_added":"79"},"firefox":{"version_added":"70"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"12.0"},"webview_android":{"version_added":"78"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"order":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/order","spec_url":"https://drafts.csswg.org/css-display/#order-property","support":{"chrome":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"21"}],"chrome_android":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"25"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"20","notes":"Since Firefox 28, multi-line flexbox is supported."},{"prefix":"-webkit-","version_added":"49"}],"firefox_android":[{"version_added":"20","notes":"Since Firefox 28, multi-line flexbox is supported."},{"prefix":"-webkit-","version_added":"49"}],"ie":[{"version_added":"11"},{"prefix":"-ms-","version_added":"10"}],"opera":{"version_added":"12.1"},"opera_android":{"version_added":"12.1"},"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":[{"version_added":"2.0"},{"prefix":"-webkit-","version_added":"1.5"}],"webview_android":[{"version_added":"4.4"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"orphans":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/orphans","spec_url":"https://drafts.csswg.org/css-break/#widows-orphans","support":{"chrome":{"version_added":"25"},"chrome_android":{"version_added":"25"},"edge":{"version_added":"12"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":"8"},"opera":{"version_added":"9.2"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"outline-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/outline-color","spec_url":"https://drafts.csswg.org/css-ui/#outline-color","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":[{"version_added":"1.5"},{"prefix":"-moz-","version_added":"1","version_removed":"3.6"}],"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"invert":{"__compat":{"description":"invert","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"79"},"firefox":{"version_removed":"3","version_added":"1"},"firefox_android":{"version_added":false},"ie":{"version_added":"8"},"opera":{"version_added":"7","version_removed":"15"},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"outline-offset":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/outline-offset","spec_url":"https://drafts.csswg.org/css-ui/#outline-offset","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"15"},"firefox":{"version_added":"1.5","notes":"Before Firefox 88, an outline does not follow the shape of border-radius."},"firefox_android":{"version_added":"4","notes":"Before Firefox 88, an outline does not follow the shape of border-radius."},"ie":{"version_added":false},"opera":{"version_added":"9.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"outline-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/outline-style","spec_url":"https://drafts.csswg.org/css-ui/#outline-style","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":[{"version_added":"1.5","notes":"Before Firefox 88, an outline does not follow the shape of border-radius."},{"prefix":"-moz-","version_added":"1","version_removed":"3.6"}],"firefox_android":{"version_added":"4","notes":"Before Firefox 88, an outline does not follow the shape of border-radius."},"ie":{"version_added":"8"},"opera":{"version_added":"7"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"auto":{"__compat":{"description":"auto","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"1.5"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"outline-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/outline-width","spec_url":"https://drafts.csswg.org/css-ui/#outline-width","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":[{"version_added":"1.5","notes":"Before Firefox 88, an outline does not follow the shape of border-radius."},{"prefix":"-moz-","version_added":"1","version_removed":"3.6"}],"firefox_android":{"version_added":"4","notes":"Before Firefox 88, an outline does not follow the shape of border-radius."},"ie":{"version_added":"8"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"outline":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/outline","spec_url":"https://drafts.csswg.org/css-ui/#outline","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":[{"version_added":"1.5","notes":"Before Firefox 88, outline does not follow the shape of border-radius."},{"prefix":"-moz-","version_added":"1","version_removed":"3.6"}],"firefox_android":{"version_added":"4","notes":"Before Firefox 88, outline does not follow the shape of border-radius."},"ie":{"version_added":"8"},"opera":{"version_added":"7"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"overflow-anchor":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/overflow-anchor","spec_url":"https://drafts.csswg.org/css-scroll-anchoring/#exclusion-api","support":{"chrome":{"version_added":"56"},"chrome_android":{"version_added":"56"},"edge":{"version_added":"79"},"firefox":{"version_added":"66"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"43"},"opera_android":{"version_added":"43"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"56"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"overflow-block":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/overflow-block","spec_url":"https://drafts.csswg.org/css-overflow/#logical","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"69"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"overflow-clip-box-block":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Mozilla/Gecko/Chrome/CSS/overflow-clip-box-block","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"59"},"firefox_android":{"version_added":"59"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"overflow-clip-box-inline":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Mozilla/Gecko/Chrome/CSS/overflow-clip-box-inline","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"59"},"firefox_android":{"version_added":"59"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"overflow-clip-box":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Mozilla/Gecko/Chrome/CSS/overflow-clip-box","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"29"},"firefox_android":{"version_added":"29"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":false}},"shorthand":{"__compat":{"description":"Two values; property as shorthand","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"59"},"firefox_android":{"version_added":"59"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}}},"overflow-clip-margin":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/overflow-clip-margin","spec_url":"https://drafts.csswg.org/css-overflow/#overflow-clip-margin","support":{"chrome":{"version_added":"90"},"chrome_android":{"version_added":"90"},"edge":{"version_added":"90"},"firefox":{"version_added":false,"notes":"See bug 1661582."},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"76"},"opera_android":{"version_added":"64"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"15.0"},"webview_android":{"version_added":"90"}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"overflow-inline":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/overflow-inline","spec_url":"https://drafts.csswg.org/css-overflow/#logical","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"69"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"overflow-wrap":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/overflow-wrap","spec_url":"https://drafts.csswg.org/css-text/#overflow-wrap-property","support":{"chrome":[{"version_added":"23"},{"alternative_name":"word-wrap","version_added":"1"}],"chrome_android":[{"version_added":"25"},{"alternative_name":"word-wrap","version_added":"18"}],"edge":[{"version_added":"18"},{"alternative_name":"word-wrap","version_added":"12"}],"firefox":[{"version_added":"49"},{"alternative_name":"word-wrap","version_added":"3.5"}],"firefox_android":[{"version_added":"49"},{"alternative_name":"word-wrap","version_added":"4"}],"ie":{"alternative_name":"word-wrap","version_added":"5.5"},"opera":[{"version_added":"12.1"},{"alternative_name":"word-wrap","version_added":"10.5"}],"opera_android":[{"version_added":"12.1"},{"alternative_name":"word-wrap","version_added":"11"}],"safari":[{"version_added":"7"},{"alternative_name":"word-wrap","version_added":"1"}],"safari_ios":[{"version_added":"7"},{"alternative_name":"word-wrap","version_added":"1"}],"samsunginternet_android":[{"version_added":"1.5"},{"alternative_name":"word-wrap","version_added":"1.0"}],"webview_android":[{"version_added":"4.4"},{"alternative_name":"word-wrap","version_added":"1"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"anywhere":{"__compat":{"description":"anywhere","support":{"chrome":{"version_added":"80"},"chrome_android":{"version_added":"80"},"edge":{"version_added":"80"},"firefox":{"version_added":"65"},"firefox_android":{"version_added":"65"},"ie":{"version_added":false},"opera":{"version_added":"67"},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"13.0"},"webview_android":{"version_added":"80"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"break-word":{"__compat":{"description":"break-word","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3.5"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"5.5"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"11"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"overflow-x":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/overflow-x","spec_url":"https://drafts.csswg.org/css-overflow/#overflow-properties","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3.5"},"firefox_android":{"version_added":"4"},"ie":[{"version_added":"5"},{"prefix":"-ms-","version_added":"8"}],"opera":{"version_added":"9.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"clip":{"__compat":{"description":"clip value","support":{"chrome":{"version_added":"90"},"chrome_android":{"version_added":"90"},"edge":{"version_added":"90"},"firefox":[{"version_added":"81"},{"alternative_name":"-moz-hidden-unscrollable","version_added":"3.5","version_removed":"81"}],"firefox_android":[{"version_added":"81"},{"alternative_name":"-moz-hidden-unscrollable","version_added":"4","version_removed":"81"}],"ie":{"version_added":false},"opera":{"version_added":"76"},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"15.0"},"webview_android":{"version_added":"90"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"overflow-y":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/overflow-y","spec_url":"https://drafts.csswg.org/css-overflow/#overflow-properties","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3.5"},"firefox_android":{"version_added":"4"},"ie":[{"version_added":"5"},{"prefix":"-ms-","version_added":"8"}],"opera":{"version_added":"9.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"clip":{"__compat":{"description":"clip value","support":{"chrome":{"version_added":"90"},"chrome_android":{"version_added":"90"},"edge":{"version_added":"90"},"firefox":[{"version_added":"81"},{"alternative_name":"-moz-hidden-unscrollable","version_added":"3.5","version_removed":"81"}],"firefox_android":[{"version_added":"81"},{"alternative_name":"-moz-hidden-unscrollable","version_added":"4","version_removed":"81"}],"ie":{"version_added":false},"opera":{"version_added":"76"},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"15.0"},"webview_android":{"version_added":"90"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"overflow":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/overflow","spec_url":"https://drafts.csswg.org/css-overflow/#propdef-overflow","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"After Firefox 3.6, the overflow property is correctly applied to table group elements (<thead>, <tbody>, <tfoot>)."},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4","notes":"From version 4 to 6, Internet Explorer enlarges an element with overflow: visible (default value) to fit the content inside it. height and width behave like min-height and min-width, respectively."},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"clip":{"__compat":{"description":"clip value","support":{"chrome":{"version_added":"90"},"chrome_android":{"version_added":"90"},"edge":{"version_added":"90"},"firefox":[{"version_added":"81"},{"alternative_name":"-moz-hidden-unscrollable","version_added":"1.5","version_removed":"81"}],"firefox_android":[{"version_added":"81"},{"alternative_name":"-moz-hidden-unscrollable","version_added":"4","version_removed":"81"}],"ie":{"version_added":false},"opera":{"version_added":"76"},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"15.0"},"webview_android":{"version_added":"90"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"multiple_keywords":{"__compat":{"description":"Multiple keyword syntax for overflow-x and overflow-y","support":{"chrome":{"version_added":"68"},"chrome_android":{"version_added":"68"},"edge":{"version_added":"79"},"firefox":{"version_added":"61"},"firefox_android":{"version_added":"61"},"ie":{"version_added":false},"opera":{"version_added":"55"},"opera_android":{"version_added":"48"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"68"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"overscroll-behavior-block":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/overscroll-behavior-block","spec_url":"https://drafts.csswg.org/css-overscroll/#overscroll-behavior-longhands-logical","support":{"chrome":{"version_added":"77"},"chrome_android":{"version_added":"77"},"edge":{"version_added":"79"},"firefox":{"version_added":"73"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"64"},"opera_android":{"version_added":"55"},"safari":{"version_added":false,"notes":"See bug 176454."},"safari_ios":{"version_added":false,"notes":"See bug 176454."},"samsunginternet_android":{"version_added":"12.0"},"webview_android":{"version_added":"77"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"overscroll-behavior-inline":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/overscroll-behavior-inline","spec_url":"https://drafts.csswg.org/css-overscroll/#overscroll-behavior-longhands-logical","support":{"chrome":{"version_added":"77"},"chrome_android":{"version_added":"77"},"edge":{"version_added":"79"},"firefox":{"version_added":"73"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"64"},"opera_android":{"version_added":"55"},"safari":{"version_added":false,"notes":"See bug 176454."},"safari_ios":{"version_added":false,"notes":"See bug 176454."},"samsunginternet_android":{"version_added":"12.0"},"webview_android":{"version_added":"77"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"overscroll-behavior-x":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/overscroll-behavior-x","spec_url":"https://drafts.csswg.org/css-overscroll/#overscroll-behavior-longhands-physical","support":{"chrome":{"version_added":"63"},"chrome_android":{"version_added":"63"},"edge":{"version_added":"18","partial_implementation":true,"notes":"Currently the none value incorrectly behaves as contain (allowing for the elastic bounce effect)."},"firefox":{"version_added":"59"},"firefox_android":{"version_added":"59"},"ie":{"version_added":false},"opera":{"version_added":"50"},"opera_android":{"version_added":"46"},"safari":{"version_added":false,"notes":"See bug 176454."},"safari_ios":{"version_added":false,"notes":"See bug 176454."},"samsunginternet_android":{"version_added":"8.0"},"webview_android":{"version_added":"63"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"overscroll-behavior-y":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/overscroll-behavior-y","spec_url":"https://drafts.csswg.org/css-overscroll/#overscroll-behavior-longhands-physical","support":{"chrome":{"version_added":"63"},"chrome_android":{"version_added":"63"},"edge":{"version_added":"18","partial_implementation":true,"notes":"Currently the none value incorrectly behaves as contain (allowing for the elastic bounce effect)."},"firefox":{"version_added":"59"},"firefox_android":{"version_added":"59"},"ie":{"version_added":false},"opera":{"version_added":"50"},"opera_android":{"version_added":"46"},"safari":{"version_added":false,"notes":"See bug 176454."},"safari_ios":{"version_added":false,"notes":"See bug 176454."},"samsunginternet_android":{"version_added":"8.0"},"webview_android":{"version_added":"63"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"overscroll-behavior":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/overscroll-behavior","spec_url":"https://drafts.csswg.org/css-overscroll/#overscroll-behavior-properties","support":{"chrome":{"version_added":"63"},"chrome_android":{"version_added":"63"},"edge":{"version_added":"18","partial_implementation":true,"notes":"Currently the none value incorrectly behaves as contain (allowing for the elastic bounce effect)."},"firefox":{"version_added":"59"},"firefox_android":{"version_added":"59"},"ie":{"version_added":false},"opera":{"version_added":"50"},"opera_android":{"version_added":"46"},"safari":{"version_added":false,"notes":"See bug 176454."},"safari_ios":{"version_added":false,"notes":"See bug 176454."},"samsunginternet_android":{"version_added":"8.0"},"webview_android":{"version_added":"63"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"padding-block-end":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/padding-block-end","spec_url":"https://drafts.csswg.org/css-logical/#padding-properties","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"padding-block-start":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/padding-block-start","spec_url":"https://drafts.csswg.org/css-logical/#padding-properties","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"padding-block":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/padding-block","spec_url":"https://drafts.csswg.org/css-logical/#propdef-padding-block","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]},"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"48","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"padding-bottom":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/padding-bottom","spec_url":"https://drafts.csswg.org/css-box/#padding-physical","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"padding-inline-end":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/padding-inline-end","spec_url":"https://drafts.csswg.org/css-logical/#padding-properties","support":{"chrome":[{"version_added":"69"},{"version_added":"2","alternative_name":"-webkit-padding-end"}],"chrome_android":[{"version_added":"69"},{"version_added":"18","alternative_name":"-webkit-padding-end"}],"edge":[{"version_added":"79"},{"version_added":"79","alternative_name":"-webkit-padding-end"}],"firefox":[{"version_added":"41"},{"version_added":"3","alternative_name":"-moz-padding-end"}],"firefox_android":[{"version_added":"41"},{"version_added":"4","alternative_name":"-moz-padding-end"}],"ie":{"version_added":false},"opera":[{"version_added":"56"},{"version_added":"15","alternative_name":"-webkit-padding-end"}],"opera_android":[{"version_added":"48"},{"version_added":"14","alternative_name":"-webkit-padding-end"}],"safari":[{"version_added":"12.1"},{"version_added":"3","alternative_name":"-webkit-padding-end"}],"safari_ios":[{"version_added":"12.2"},{"version_added":"3","alternative_name":"-webkit-padding-end"}],"samsunginternet_android":[{"version_added":"10.0"},{"alternative_name":"-webkit-padding-end","version_added":"1.0"}],"webview_android":[{"version_added":"69"},{"version_added":"2","alternative_name":"-webkit-padding-end"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"padding-inline-start":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/padding-inline-start","spec_url":"https://drafts.csswg.org/css-logical/#padding-properties","support":{"chrome":[{"version_added":"69"},{"version_added":"2","alternative_name":"-webkit-padding-start"}],"chrome_android":[{"version_added":"69"},{"version_added":"18","alternative_name":"-webkit-padding-start"}],"edge":[{"version_added":"79"},{"version_added":"79","alternative_name":"-webkit-padding-start"}],"firefox":[{"version_added":"41"},{"version_added":"3","alternative_name":"-moz-padding-start"}],"firefox_android":[{"version_added":"41"},{"version_added":"4","alternative_name":"-moz-padding-start"}],"ie":{"version_added":false},"opera":[{"version_added":"56"},{"version_added":"15","alternative_name":"-webkit-padding-start"}],"opera_android":[{"version_added":"48"},{"version_added":"14","alternative_name":"-webkit-padding-start"}],"safari":[{"version_added":"12.1"},{"version_added":"3","alternative_name":"-webkit-padding-start"}],"safari_ios":[{"version_added":"12.2"},{"version_added":"3","alternative_name":"-webkit-padding-start"}],"samsunginternet_android":[{"version_added":"10.0"},{"alternative_name":"-webkit-padding-start","version_added":"1.0"}],"webview_android":[{"version_added":"69"},{"version_added":"2","alternative_name":"-webkit-padding-start"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"padding-inline":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/padding-inline","spec_url":"https://drafts.csswg.org/css-logical/#propdef-padding-inline","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":[{"version_added":"87"},{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"48","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"padding-left":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/padding-left","spec_url":"https://drafts.csswg.org/css-box/#padding-physical","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"padding-right":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/padding-right","spec_url":"https://drafts.csswg.org/css-box/#padding-physical","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"padding-top":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/padding-top","spec_url":"https://drafts.csswg.org/css-box/#padding-physical","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"padding":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/padding","spec_url":"https://drafts.csswg.org/css-box/#padding-shorthand","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"page-break-after":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/page-break-after","spec_url":["https://drafts.csswg.org/css-logical/#page","https://drafts.csswg.org/css-page/#page-break-after"],"support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"The values avoid, left, and right are unsupported."},"firefox_android":{"version_added":"4","notes":"The values avoid, left, and right are unsupported."},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"page-break-before":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/page-break-before","spec_url":["https://drafts.csswg.org/css-logical/#page","https://drafts.csswg.org/css-page/#page-break-before"],"support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"The values avoid, left, and right are unsupported."},"firefox_android":{"version_added":"4","notes":"The values avoid, left, and right are unsupported."},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"page-break-inside":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/page-break-inside","spec_url":"https://drafts.csswg.org/css-page/#page-break-inside","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"19","notes":"Until Firefox 25, page-break-inside: avoid did not work with the height of a block."},"firefox_android":{"version_added":"19","notes":"Until Firefox 25, page-break-inside: avoid did not work with the height of a block."},"ie":{"version_added":"8"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"page":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/page","spec_url":"https://drafts.csswg.org/css-page/#using-named-pages","support":{"chrome":{"version_added":"85"},"chrome_android":{"version_added":"85"},"edge":{"version_added":"85"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"71"},"opera_android":{"version_added":"60"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"85"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"paint-order":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/paint-order","spec_url":"https://svgwg.org/svg2-draft/painting.html#PaintOrder","support":{"chrome":{"version_added":"35"},"chrome_android":{"version_added":"35"},"edge":{"version_added":"17"},"firefox":{"version_added":"60"},"firefox_android":{"version_added":"60"},"ie":{"version_added":false},"opera":{"version_added":"22"},"opera_android":{"version_added":"22"},"safari":{"version_added":"8"},"safari_ios":{"version_added":"8"},"samsunginternet_android":{"version_added":"3.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"perspective-origin":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/perspective-origin","spec_url":"https://drafts.csswg.org/css-transforms-2/#perspective-origin-property","support":{"chrome":[{"version_added":"36"},{"prefix":"-webkit-","version_added":"12"}],"chrome_android":[{"version_added":"36"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"prefix":"-moz-","version_added":"10"},{"version_added":"49","prefix":"-webkit-"}],"firefox_android":[{"version_added":"16"},{"prefix":"-moz-","version_added":"10"},{"version_added":"49","prefix":"-webkit-"}],"ie":{"version_added":"10"},"opera":[{"version_added":"23"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"24"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"4"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"2"}],"samsunginternet_android":[{"version_added":"3.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"37"},{"prefix":"-webkit-","version_added":"3"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"three_value_syntax":{"__compat":{"description":"Support for three-value syntax of position","support":{"chrome":[{"version_added":"36","version_removed":"68"},{"prefix":"-webkit-","version_added":"12"}],"chrome_android":[{"version_added":"36","version_removed":"68"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12","version_removed":"79"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16","version_removed":"70"},{"prefix":"-moz-","version_added":"10"},{"version_added":"49","prefix":"-webkit-"}],"firefox_android":[{"version_added":"16"},{"prefix":"-moz-","version_added":"10"},{"version_added":"49","prefix":"-webkit-"}],"ie":{"version_added":"10"},"opera":{"prefix":"-webkit-","version_added":"15","version_removed":"55"},"opera_android":{"prefix":"-webkit-","version_added":"14","version_removed":"48"},"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"4"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"2"}],"samsunginternet_android":[{"version_added":"3.0","version_removed":"10.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"37","version_removed":"68"},{"prefix":"-webkit-","version_added":"3"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"perspective":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/perspective","spec_url":"https://drafts.csswg.org/css-transforms-2/#perspective-property","support":{"chrome":[{"version_added":"36"},{"prefix":"-webkit-","version_added":"12"}],"chrome_android":[{"version_added":"36"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"prefix":"-moz-","version_added":"10"},{"version_added":"49","prefix":"-webkit-"}],"firefox_android":[{"version_added":"16"},{"prefix":"-moz-","version_added":"10"},{"version_added":"49","prefix":"-webkit-"}],"ie":{"version_added":"10"},"opera":[{"version_added":"23"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"24"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"4"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"2"}],"samsunginternet_android":[{"version_added":"3.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"37"},{"prefix":"-webkit-","version_added":"3"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"place-content":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/place-content","spec_url":"https://drafts.csswg.org/css-align/#place-content","support":{"chrome":{"version_added":"59"},"chrome_android":{"version_added":"59"},"edge":{"version_added":"79"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":"46"},"opera_android":{"version_added":"43"},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"59"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"flex_context":{"__compat":{"description":"Supported in Flex Layout","support":{"chrome":{"version_added":"59"},"chrome_android":{"version_added":"59"},"edge":{"version_added":"79"},"firefox":{"version_added":"45","notes":"Starting with version 60, you can only specify a single value if it is valid for both align-content and justify-content."},"firefox_android":{"version_added":"45","notes":"Starting with version 60, you can only specify a single value if it is valid for both align-content and justify-content."},"ie":{"version_added":false},"opera":{"version_added":"46"},"opera_android":{"version_added":"43"},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"59"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid_context":{"__compat":{"description":"Supported in Grid Layout","support":{"chrome":{"version_added":"59"},"chrome_android":{"version_added":"59"},"edge":{"version_added":"79"},"firefox":{"version_added":"53","notes":"Starting with version 60, you can only specify a single value if it is valid for both align-content and justify-content."},"firefox_android":{"version_added":"53","notes":"Starting with version 60, you can only specify a single value if it is valid for both align-content and justify-content."},"ie":{"version_added":false},"opera":{"version_added":"46"},"opera_android":{"version_added":"43"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"59"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"place-items":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/place-items","spec_url":"https://drafts.csswg.org/css-align/#place-items-property","support":{"chrome":{"version_added":"59"},"chrome_android":{"version_added":"59"},"edge":{"version_added":"79"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":"46"},"opera_android":{"version_added":"43"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"59"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"flex_context":{"__compat":{"description":"Supported in Flex Layout","support":{"chrome":{"version_added":"59"},"chrome_android":{"version_added":"59"},"edge":{"version_added":"79"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":"46"},"opera_android":{"version_added":"43"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"59"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid_context":{"__compat":{"description":"Supported in Grid Layout","support":{"chrome":{"version_added":"59"},"chrome_android":{"version_added":"59"},"edge":{"version_added":"79"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":"46"},"opera_android":{"version_added":"43"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"59"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"place-self":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/place-self","spec_url":"https://drafts.csswg.org/css-align/#place-self-property","support":{"chrome":{"version_added":"59"},"chrome_android":{"version_added":"59"},"edge":{"version_added":"79"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":"46"},"opera_android":{"version_added":"43"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"59"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"flex_context":{"__compat":{"description":"Supported in Flex Layout","support":{"chrome":{"version_added":"59"},"chrome_android":{"version_added":"59"},"edge":{"version_added":"79"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":"46"},"opera_android":{"version_added":"43"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"59"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid_context":{"__compat":{"description":"Supported in Grid Layout","support":{"chrome":{"version_added":"59"},"chrome_android":{"version_added":"59"},"edge":{"version_added":"79"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":"46"},"opera_android":{"version_added":"43"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"59"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"pointer-events":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/pointer-events","spec_url":"https://svgwg.org/svg2-draft/interact.html#PointerEventsProperty","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1.5"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"11"},"opera":{"version_added":"9"},"opera_android":{"version_added":"14"},"safari":{"version_added":"4"},"safari_ios":{"version_added":"3.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"html_elements":{"__compat":{"description":"Applies to HTML elements","support":{"chrome":{"version_added":"2"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3.6"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"11"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"4"},"safari_ios":{"version_added":"3.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}}},"position":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/position","spec_url":"https://drafts.csswg.org/css-position/#position-property","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":["Before Firefox 57, absolute positioning did not work correctly when applied to elements inside tables that have border-collapse applied to them (bug 1379306).","Before Firefox 30, absolute positioning of table rows and row groups was not supported (bug 63895)."]},"firefox_android":{"version_added":"4","notes":["Before Firefox 57, absolute positioning did not work correctly when applied to elements inside tables that have border-collapse applied to them (bug 1379306).","Before Firefox 30, absolute positioning of table rows and row groups was not supported (bug 63895)."]},"ie":{"version_added":"4"},"opera":{"version_added":"4"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"absolutely_positioned_flex_children":{"__compat":{"description":"Absolutely-positioned flex children","support":{"chrome":{"version_added":"52"},"chrome_android":{"version_added":"52"},"edge":{"version_added":"12"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":"10"},"opera":{"version_added":"39"},"opera_android":{"version_added":"41"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"52"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"fixed":{"__compat":{"description":"fixed","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"Before Firefox 44, position: fixed didn't create a stacking context in most cases. Firefox and the specification have been modified to mimic Chrome and Safari's long-time behavior."},"firefox_android":{"version_added":"4","notes":"Before Firefox 44, position: fixed didn't create a stacking context in most cases. Firefox and the specification have been modified to mimic Chrome and Safari's long-time behavior."},"ie":{"version_added":"7","notes":"In Internet Explorer, fixed positioning doesn't work if the document is in quirks mode."},"opera":{"version_added":"4"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"position_sticky_table_elements":{"__compat":{"description":"Table elements as sticky positioning containers","support":{"chrome":{"version_added":"56"},"chrome_android":{"version_added":"56"},"edge":{"version_added":"16"},"firefox":{"version_added":"59"},"firefox_android":{"version_added":"59"},"ie":{"version_added":false},"opera":{"version_added":"43"},"opera_android":{"version_added":"43"},"safari":{"version_added":"8"},"safari_ios":{"version_added":"8"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"56"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"sticky":{"__compat":{"description":"sticky","support":{"chrome":{"version_added":"56"},"chrome_android":{"version_added":"56"},"edge":{"version_added":"16"},"firefox":{"version_added":"32"},"firefox_android":{"version_added":"32"},"ie":{"version_added":false},"opera":{"version_added":"43"},"opera_android":{"version_added":"43"},"safari":[{"version_added":"13"},{"prefix":"-webkit-","version_added":"6.1"}],"safari_ios":[{"version_added":"13"},{"prefix":"-webkit-","version_added":"6.1"}],"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"56"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"quotes":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/quotes","spec_url":"https://drafts.csswg.org/css-content/#quotes","support":{"chrome":{"version_added":"11"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1.5"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"4"},"opera_android":{"version_added":"14"},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"quotes_auto":{"__compat":{"description":"auto keyword","support":{"chrome":{"version_added":"87"},"chrome_android":{"version_added":"87"},"edge":{"version_added":"87"},"firefox":{"version_added":"70"},"firefox_android":{"version_added":false},"ie":{"version_added":false,"notes":"This value is not supported, but the default browser behavior is to choose appropriate quotes for the user's language setting"},"opera":{"version_added":"73"},"opera_android":{"version_added":false,"notes":"This value is not supported, but the default browser behavior is to choose appropriate quotes for the user's language setting"},"safari":{"version_added":false,"notes":"This value is not supported, but the default browser behavior is to choose appropriate quotes for the user's language setting"},"safari_ios":{"version_added":false,"notes":"This value is not supported, but the default browser behavior is to choose appropriate quotes for the user's language setting"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"resize":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/resize","spec_url":"https://drafts.csswg.org/css-ui/#resize","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"4"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":"12.1"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"block_level_support":{"__compat":{"description":"Support on block level, replaced, table cell, or inline block elements","support":{"chrome":{"version_added":"4"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"5","notes":"resize doesn't have any effect on <iframe>. See bug 680823)"},"firefox_android":{"version_added":"5","notes":"resize doesn't have any effect on <iframe>. See bug 680823)"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"4"},"safari_ios":{"version_added":"3.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"flow_relative_support":{"__compat":{"description":"Support for flow-relative values block and inline","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"63"},"firefox_android":{"version_added":"63"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"right":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/right","spec_url":"https://drafts.csswg.org/css-position/#insets","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"5.5"},"opera":{"version_added":"5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"rotate":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/rotate","spec_url":"https://drafts.csswg.org/css-transforms-2/#individual-transforms","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"72"},{"version_added":"60","version_removed":"72","flags":[{"type":"preference","name":"layout.css.individual-transform.enabled","value_to_set":"true"}]}],"firefox_android":{"version_added":"60","flags":[{"type":"preference","name":"layout.css.individual-transform.enabled","value_to_set":"true"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"x_y_z_angle":{"__compat":{"description":"x, y, or z axis name plus angle value","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"72"},{"version_added":"65","version_removed":"72","flags":[{"type":"preference","name":"layout.css.individual-transform.enabled","value_to_set":"true"}]}],"firefox_android":{"version_added":"65","flags":[{"type":"preference","name":"layout.css.individual-transform.enabled","value_to_set":"true"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"row-gap":{"flex_context":{"__compat":{"description":"Supported in Flex Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/row-gap","spec_url":"https://drafts.csswg.org/css-align/#column-row-gap","support":{"chrome":{"version_added":"84"},"chrome_android":{"version_added":"84"},"edge":{"version_added":"84"},"firefox":{"version_added":"63"},"firefox_android":{"version_added":"63"},"ie":{"version_added":false},"opera":{"version_added":"70"},"opera_android":{"version_added":"60"},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"84"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid_context":{"__compat":{"description":"Supported in Grid Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/row-gap","spec_url":"https://drafts.csswg.org/css-align/#column-row-gap","support":{"chrome":[{"version_added":"66"},{"version_added":"57","alternative_name":"grid-row-gap"}],"chrome_android":[{"version_added":"66"},{"version_added":"57","alternative_name":"grid-row-gap"}],"edge":[{"version_added":"16"},{"version_added":"16","alternative_name":"grid-row-gap"}],"firefox":[{"version_added":"61"},{"version_added":"52","alternative_name":"grid-row-gap"}],"firefox_android":[{"version_added":"61"},{"version_added":"52","alternative_name":"grid-row-gap"}],"ie":{"version_added":false},"opera":[{"version_added":"53"},{"version_added":"44","alternative_name":"grid-row-gap"}],"opera_android":[{"version_added":"47"},{"version_added":"43","alternative_name":"grid-row-gap"}],"safari":[{"version_added":"12"},{"version_added":"10.1","alternative_name":"grid-row-gap"}],"safari_ios":[{"version_added":"12"},{"version_added":"10.3","alternative_name":"grid-row-gap"}],"samsunginternet_android":[{"version_added":"9.0"},{"alternative_name":"grid-row-gap","version_added":"6.0"}],"webview_android":[{"version_added":"66"},{"version_added":"57","alternative_name":"grid-row-gap"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"ruby-align":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/ruby-align","spec_url":"https://drafts.csswg.org/css-ruby/#ruby-align-property","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"38"},"firefox_android":{"version_added":"38"},"ie":{"version_added":false,"notes":"Internet Explorer 9 and later supports an earlier draft of CSS Ruby with non-standard values for this property: auto, left, center, right, distribute-letter, distribute-space, and line-edge."},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"ruby-position":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/ruby-position","spec_url":"https://drafts.csswg.org/css-ruby/#rubypos","support":{"chrome":[{"version_added":"84"},{"version_added":"1","prefix":"-webkit-","notes":"Implemented as a non-standard, prefixed, version of ruby-position, -webkit-ruby-position: it has two properties: before and after (both equivalent, for ltr and rtl scripts to the standard over value used with ruby-align: start)."}],"chrome_android":[{"version_added":"84"},{"version_added":"18","prefix":"-webkit-","notes":"Implemented as a non-standard, prefixed, version of ruby-position, -webkit-ruby-position: it has two properties: before and after (both equivalent, for ltr and rtl scripts to the standard over value used with ruby-align: start)."}],"edge":[{"version_added":"84"},{"version_added":"79","prefix":"-webkit-","notes":"Implemented as a non-standard, prefixed, version of ruby-position, -webkit-ruby-position: it has two properties: before and after (both equivalent, for ltr and rtl scripts to the standard over value used with ruby-align: start)."},{"version_added":"12","version_removed":"79"}],"firefox":{"version_added":"38"},"firefox_android":{"version_added":"38"},"ie":{"version_added":false,"notes":"Internet Explorer 9 and later support an old draft values: inline (equivalent of having display: inline on the ruby), and above (synonym of the modern over)."},"opera":[{"version_added":"70"},{"version_added":"15","prefix":"-webkit-","notes":"Implemented as a non-standard, prefixed, version of ruby-position, -webkit-ruby-position: it has two properties: before and after (both equivalent, for ltr and rtl scripts to the standard over value used with ruby-align: start)."}],"opera_android":[{"version_added":"60"},{"version_added":"14","prefix":"-webkit-","notes":"Implemented as a non-standard, prefixed, version of ruby-position, -webkit-ruby-position: it has two properties: before and after (both equivalent, for ltr and rtl scripts to the standard over value used with ruby-align: start)."}],"safari":{"version_added":"6.1","prefix":"-webkit-","notes":"Implemented as a non-standard, prefixed, version of ruby-position, -webkit-ruby-position: it has two properties: before and after (both equivalent, for ltr and rtl scripts to the standard over value used with ruby-align: start)."},"safari_ios":{"version_added":"6.1","prefix":"-webkit-","notes":"Implemented as a non-standard, prefixed, version of ruby-position, -webkit-ruby-position: it has two properties: before and after (both equivalent, for ltr and rtl scripts to the standard over value used with ruby-align: start)."},"samsunginternet_android":{"version_added":"14.0"},"webview_android":[{"version_added":"84"},{"version_added":"1","prefix":"-webkit-","notes":"Implemented as a non-standard, prefixed, version of ruby-position, -webkit-ruby-position: it has two properties: before and after (both equivalent, for ltr and rtl scripts to the standard over value used with ruby-align: start)."}]},"status":{"experimental":true,"standard_track":true,"deprecated":false}},"alternate":{"__compat":{"description":"alternate","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"88"},"firefox_android":{"version_added":"88"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"inter-character":{"__compat":{"description":"inter-character","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false,"notes":"See bug 1055672."},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}}},"scale":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scale","spec_url":"https://drafts.csswg.org/css-transforms-2/#individual-transforms","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"72"},{"version_added":"60","version_removed":"72","flags":[{"type":"preference","name":"layout.css.individual-transform.enabled","value_to_set":"true"}]}],"firefox_android":{"version_added":"60","flags":[{"type":"preference","name":"layout.css.individual-transform.enabled","value_to_set":"true"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-behavior":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-behavior","spec_url":"https://drafts.csswg.org/css-overflow/#smooth-scrolling","support":{"chrome":{"version_added":"61"},"chrome_android":{"version_added":"61"},"edge":{"version_added":"79"},"firefox":{"version_added":"36"},"firefox_android":{"version_added":"36"},"ie":{"version_added":false},"opera":{"version_added":"48"},"opera_android":{"version_added":"45"},"safari":{"version_added":"14","flags":[{"type":"preference","name":"CSSOM View Smooth Scrolling"}]},"safari_ios":{"version_added":"14","flags":[{"type":"preference","name":"CSSOM View Smooth Scrolling"}]},"samsunginternet_android":{"version_added":"8.0"},"webview_android":{"version_added":"61"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-margin-block-end":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-margin-block-end","spec_url":"https://drafts.csswg.org/css-scroll-snap/#margin-longhands-logical","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-margin-block-start":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-margin-block-start","spec_url":"https://drafts.csswg.org/css-scroll-snap/#margin-longhands-logical","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-margin-block":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-margin-block","spec_url":"https://drafts.csswg.org/css-scroll-snap/#propdef-scroll-margin-block","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-margin-bottom":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-margin-bottom","spec_url":"https://drafts.csswg.org/css-scroll-snap/#margin-longhands-physical","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":[{"version_added":"14.1"},{"version_added":"11","alternative_name":"scroll-snap-margin-bottom","partial_implementation":true,"notes":"Before version 14.1, scroll margin is not applied for scrolls to fragment target or scrollIntoView(), see bug 189265."}],"safari_ios":[{"version_added":"14.5"},{"version_added":"11","alternative_name":"scroll-snap-margin-bottom","partial_implementation":true,"notes":"Before version 14.5, scroll margin is not applied for scrolls to fragment target or scrollIntoView(), see bug 189265."}],"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-margin-inline-end":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-margin-inline-end","spec_url":"https://drafts.csswg.org/css-scroll-snap/#margin-longhands-logical","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-margin-inline-start":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-margin-inline-start","spec_url":"https://drafts.csswg.org/css-scroll-snap/#margin-longhands-logical","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-margin-inline":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-margin-inline","spec_url":"https://drafts.csswg.org/css-scroll-snap/#propdef-scroll-margin-inline","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-margin-left":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-margin-left","spec_url":"https://drafts.csswg.org/css-scroll-snap/#margin-longhands-physical","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":[{"version_added":"14.1"},{"version_added":"11","alternative_name":"scroll-snap-margin-left","partial_implementation":true,"notes":"Before version 14.1, scroll margin is not applied for scrolls to fragment target or scrollIntoView(), see bug 189265."}],"safari_ios":[{"version_added":"14.5"},{"version_added":"11","alternative_name":"scroll-snap-margin-left","partial_implementation":true,"notes":"Before version 14.5, scroll margin is not applied for scrolls to fragment target or scrollIntoView(), see bug 189265."}],"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-margin-right":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-margin-right","spec_url":"https://drafts.csswg.org/css-scroll-snap/#margin-longhands-physical","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":[{"version_added":"14.1"},{"version_added":"11","alternative_name":"scroll-snap-margin-right","partial_implementation":true,"notes":"Before version 14.1, scroll margin is not applied for scrolls to fragment target or scrollIntoView(), see bug 189265."}],"safari_ios":[{"version_added":"14.5"},{"version_added":"11","alternative_name":"scroll-snap-margin-right","partial_implementation":true,"notes":"Before version 14.5, scroll margin is not applied for scrolls to fragment target or scrollIntoView(), see bug 189265."}],"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-margin-top":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-margin-top","spec_url":"https://drafts.csswg.org/css-scroll-snap/#margin-longhands-physical","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":[{"version_added":"14.1"},{"version_added":"11","alternative_name":"scroll-snap-margin-top","partial_implementation":true,"notes":"Before version 14.1, scroll margin is not applied for scrolls to fragment target or scrollIntoView(), see bug 189265."}],"safari_ios":[{"version_added":"14.5"},{"version_added":"11","alternative_name":"scroll-snap-margin-top","partial_implementation":true,"notes":"Before version 14.5, scroll margin is not applied for scrolls to fragment target or scrollIntoView(), see bug 189265."}],"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-margin":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-margin","spec_url":"https://drafts.csswg.org/css-scroll-snap/#scroll-margin","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":[{"version_added":"90"},{"version_added":"68","version_removed":"90","partial_implementation":true,"notes":"The scroll-margin property can cause an element's visibility to be incorrectly calculated for element.focus(). See bug 1708303."}],"firefox_android":[{"version_added":"90"},{"version_added":"68","version_removed":"90","partial_implementation":true,"notes":"The scroll-margin property can cause an element's visibility to be incorrectly calculated for element.focus(). See bug 1708303."}],"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":[{"version_added":"14.1"},{"version_added":"11","alternative_name":"scroll-snap-margin","partial_implementation":true,"notes":"Before version 14.1, scroll margin is not applied for scrolls to fragment target or scrollIntoView(), see bug 189265."}],"safari_ios":[{"version_added":"14.5"},{"version_added":"11","alternative_name":"scroll-snap-margin","partial_implementation":true,"notes":"Before version 14.5, scroll margin is not applied for scrolls to fragment target or scrollIntoView(), see bug 189265."}],"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-padding-block-end":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-padding-block-end","spec_url":"https://drafts.csswg.org/css-scroll-snap/#padding-longhands-logical","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"15"},"safari_ios":{"version_added":"15"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-padding-block-start":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-padding-block-start","spec_url":"https://drafts.csswg.org/css-scroll-snap/#padding-longhands-logical","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"15"},"safari_ios":{"version_added":"15"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-padding-block":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-padding-block","spec_url":"https://drafts.csswg.org/css-scroll-snap/#propdef-scroll-padding-block","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"15"},"safari_ios":{"version_added":"15"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-padding-bottom":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-padding-bottom","spec_url":"https://drafts.csswg.org/css-scroll-snap/#padding-longhands-physical","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"11","partial_implementation":true,"notes":"Scroll padding is not applied for scrolls to fragment target or scrollIntoView(), see bug 179379."},"safari_ios":{"version_added":"11","partial_implementation":true,"notes":"Scroll padding is not applied for scrolls to fragment target or scrollIntoView(), see bug 179379."},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-padding-inline-end":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-padding-inline-end","spec_url":"https://drafts.csswg.org/css-scroll-snap/#padding-longhands-logical","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"15"},"safari_ios":{"version_added":"15"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-padding-inline-start":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-padding-inline-start","spec_url":"https://drafts.csswg.org/css-scroll-snap/#padding-longhands-logical","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"15"},"safari_ios":{"version_added":"15"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-padding-inline":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-padding-inline","spec_url":"https://drafts.csswg.org/css-scroll-snap/#propdef-scroll-padding-inline","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"15"},"safari_ios":{"version_added":"15"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-padding-left":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-padding-left","spec_url":"https://drafts.csswg.org/css-scroll-snap/#padding-longhands-physical","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"11","partial_implementation":true,"notes":"Scroll padding is not applied for scrolls to fragment target or scrollIntoView(), see bug 179379."},"safari_ios":{"version_added":"11","partial_implementation":true,"notes":"Scroll padding is not applied for scrolls to fragment target or scrollIntoView(), see bug 179379."},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-padding-right":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-padding-right","spec_url":"https://drafts.csswg.org/css-scroll-snap/#padding-longhands-physical","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"11","partial_implementation":true,"notes":"Scroll padding is not applied for scrolls to fragment target or scrollIntoView(), see bug 179379."},"safari_ios":{"version_added":"11","partial_implementation":true,"notes":"Scroll padding is not applied for scrolls to fragment target or scrollIntoView(), see bug 179379."},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-padding-top":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-padding-top","spec_url":"https://drafts.csswg.org/css-scroll-snap/#padding-longhands-physical","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"11","partial_implementation":true,"notes":"Scroll padding is not applied for scrolls to fragment target or scrollIntoView(), see bug 179379."},"safari_ios":{"version_added":"11","partial_implementation":true,"notes":"Scroll padding is not applied for scrolls to fragment target or scrollIntoView(), see bug 179379."},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-padding":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-padding","spec_url":"https://drafts.csswg.org/css-scroll-snap/#scroll-padding","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"11","partial_implementation":true,"notes":"Scroll padding is not applied for scrolls to fragment target or scrollIntoView(), see bug 179379."},"safari_ios":{"version_added":"11","partial_implementation":true,"notes":"Scroll padding is not applied for scrolls to fragment target or scrollIntoView(), see bug 179379."},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-snap-align":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-snap-align","spec_url":"https://drafts.csswg.org/css-scroll-snap/#scroll-snap-align","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-snap-coordinate":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-snap-coordinate","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_removed":"68","version_added":"39"},"firefox_android":{"version_removed":"68","version_added":"39"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"scroll-snap-destination":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-snap-destination","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_removed":"68","version_added":"39"},"firefox_android":{"version_removed":"68","version_added":"39"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"scroll-snap-points-x":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-snap-points-x","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_removed":"68","version_added":"39"},"firefox_android":{"version_removed":"68","version_added":"39"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"9","prefix":"-webkit-"},"safari_ios":{"version_added":"9","prefix":"-webkit-"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"scroll-snap-points-y":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-snap-points-y","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_removed":"68","version_added":"39"},"firefox_android":{"version_removed":"68","version_added":"39"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"9","prefix":"-webkit-"},"safari_ios":{"version_added":"9","prefix":"-webkit-"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"scroll-snap-stop":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-snap-stop","spec_url":"https://drafts.csswg.org/css-scroll-snap/#scroll-snap-stop","support":{"chrome":{"version_added":"75"},"chrome_android":{"version_added":"75"},"edge":{"version_added":"79"},"firefox":{"version_added":false,"notes":"See bug 1312165."},"firefox_android":{"version_added":false,"notes":"See bug 1312165."},"ie":{"version_added":false},"opera":{"version_added":"62"},"opera_android":{"version_added":"54"},"safari":{"version_added":"15"},"safari_ios":{"version_added":"15"},"samsunginternet_android":{"version_added":"11.0"},"webview_android":{"version_added":"75"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-snap-type-x":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-snap-type-x","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_removed":"68","version_added":"39"},"firefox_android":{"version_removed":"68","version_added":"39"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"scroll-snap-type-y":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-snap-type-y","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_removed":"68","version_added":"39"},"firefox_android":{"version_removed":"68","version_added":"39"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"scroll-snap-type":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-snap-type","spec_url":"https://drafts.csswg.org/css-scroll-snap/#scroll-snap-type","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":[{"version_added":"79"},{"version_added":"12","version_removed":"79","prefix":"-ms-","notes":"Edge supports an earlier draft of CSS Scroll Snap without axis values."}],"firefox":[{"version_added":"68"},{"version_added":"39","version_removed":"68","notes":"An earlier draft of CSS Scroll Snap without axis values."}],"firefox_android":[{"version_added":"68"},{"version_added":"39","version_removed":"68","notes":"An earlier draft of CSS Scroll Snap without axis values."}],"ie":{"version_added":"10","prefix":"-ms-","notes":"Internet Explorer supports an earlier draft of CSS Scroll Snap without axis values."},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":[{"version_added":"11"},{"version_added":"9","prefix":"-webkit-","notes":"Older Safari versions support an earlier draft of CSS Scroll Snap without axis values."}],"safari_ios":[{"version_added":"11"},{"version_added":"9","prefix":"-webkit-","notes":"Older Safari versions support an earlier draft of CSS Scroll Snap without axis values."}],"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scrollbar-3dlight-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scrollbar-3dlight-color","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":[{"version_added":"5"},{"version_added":"8","prefix":"-ms-"}],"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"deprecated":true,"experimental":false,"standard_track":false}}},"scrollbar-arrow-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scrollbar-arrow-color","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":[{"version_added":"5"},{"version_added":"8","prefix":"-ms-"}],"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"deprecated":true,"experimental":false,"standard_track":false}}},"scrollbar-base-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scrollbar-base-color","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":[{"version_added":"5"},{"version_added":"8","prefix":"-ms-"}],"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"deprecated":true,"experimental":false,"standard_track":false}}},"scrollbar-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scrollbar-color","spec_url":"https://drafts.csswg.org/css-scrollbars/#scrollbar-color","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"64","notes":"On macOS, you need to set the General > Show scroll bars setting in System Preferences to \"Always\" for this property to have any effect."},"firefox_android":{"version_added":"64"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scrollbar-darkshadow-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scrollbar-darkshadow-color","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":[{"version_added":"5"},{"version_added":"8","prefix":"-ms-"}],"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"deprecated":true,"experimental":false,"standard_track":false}}},"scrollbar-face-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scrollbar-face-color","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":[{"version_added":"5"},{"version_added":"8","prefix":"-ms-"}],"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"deprecated":true,"experimental":false,"standard_track":false}}},"scrollbar-gutter":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scrollbar-gutter","spec_url":"https://drafts.csswg.org/css-overflow/#scrollbar-gutter-property","support":{"chrome":{"version_added":"88","flags":[{"type":"preference","name":"Enable experimental Web Platform features"}]},"chrome_android":{"version_added":"88","flags":[{"type":"preference","name":"Enable experimental Web Platform features"}]},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"scrollbar-highlight-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scrollbar-highlight-color","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":[{"version_added":"5"},{"version_added":"8","prefix":"-ms-"}],"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"deprecated":true,"experimental":false,"standard_track":false}}},"scrollbar-shadow-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scrollbar-shadow-color","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":[{"version_added":"5"},{"version_added":"8","prefix":"-ms-"}],"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"deprecated":true,"experimental":false,"standard_track":false}}},"scrollbar-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scrollbar-width","spec_url":"https://drafts.csswg.org/css-scrollbars/#scrollbar-width","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"64"},"firefox_android":{"version_added":"64"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"shape-image-threshold":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/shape-image-threshold","spec_url":"https://drafts.csswg.org/css-shapes/#shape-image-threshold-property","support":{"chrome":{"version_added":"37"},"chrome_android":{"version_added":"37"},"edge":{"version_added":"79"},"firefox":{"version_added":"62"},"firefox_android":{"version_added":"62"},"ie":{"version_added":false},"opera":{"version_added":"24"},"opera_android":{"version_added":"24"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"3.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"percentages":{"__compat":{"description":"Support for percentage opacity values","support":{"chrome":{"version_added":"78"},"chrome_android":{"version_added":"78"},"edge":{"version_added":"79"},"firefox":{"version_added":"70"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"12.0"},"webview_android":{"version_added":"78"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"shape-margin":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/shape-margin","spec_url":"https://drafts.csswg.org/css-shapes/#shape-margin-property","support":{"chrome":{"version_added":"37"},"chrome_android":{"version_added":"37"},"edge":{"version_added":"79"},"firefox":{"version_added":"62"},"firefox_android":{"version_added":"62"},"ie":{"version_added":false},"opera":{"version_added":"24"},"opera_android":{"version_added":"24"},"safari":[{"version_added":"10.1"},{"prefix":"-webkit-","version_added":"10.1"}],"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"3.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"shape-outside":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/shape-outside","spec_url":"https://drafts.csswg.org/css-shapes/#shape-outside-property","support":{"chrome":{"version_added":"37"},"chrome_android":{"version_added":"37"},"edge":{"version_added":"79"},"firefox":{"version_added":"62"},"firefox_android":{"version_added":"62"},"ie":{"version_added":false},"opera":{"version_added":"24"},"opera_android":{"version_added":"24"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"3.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"circle":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/basic-shape#circle()","description":"circle()","support":{"chrome":{"version_added":"37"},"chrome_android":{"version_added":"37"},"edge":{"version_added":"79"},"firefox":{"version_added":"62"},"firefox_android":{"version_added":"62"},"ie":{"version_added":false},"opera":{"version_added":"24"},"opera_android":{"version_added":"24"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"3.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"gradient":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/gradient","spec_url":"https://drafts.csswg.org/css-images/#gradients","description":"<gradient>","support":{"chrome":{"version_added":"37"},"chrome_android":{"version_added":"37"},"edge":{"version_added":"79"},"firefox":{"version_added":"62"},"firefox_android":{"version_added":"62"},"ie":{"version_added":false},"opera":{"version_added":"24"},"opera_android":{"version_added":"24"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"3.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"image":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/image","spec_url":"https://drafts.csswg.org/css-images/#image-values","description":"<image>","support":{"chrome":{"version_added":"37"},"chrome_android":{"version_added":"37"},"edge":{"version_added":"79"},"firefox":{"version_added":"62"},"firefox_android":{"version_added":"62"},"ie":{"version_added":false},"opera":{"version_added":"24"},"opera_android":{"version_added":"24"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"3.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"inset":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/basic-shape#inset()","description":"inset()","support":{"chrome":{"version_added":"37"},"chrome_android":{"version_added":"37"},"edge":{"version_added":"79"},"firefox":{"version_added":"62"},"firefox_android":{"version_added":"62"},"ie":{"version_added":false},"opera":{"version_added":"24"},"opera_android":{"version_added":"24"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"3.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"polygon":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/basic-shape#polygon()","description":"polygon()","support":{"chrome":{"version_added":"37"},"chrome_android":{"version_added":"37"},"edge":{"version_added":"79"},"firefox":{"version_added":"62"},"firefox_android":{"version_added":"62"},"ie":{"version_added":false},"opera":{"version_added":"24"},"opera_android":{"version_added":"24"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"3.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"three_value_syntax":{"__compat":{"description":"Support for three-value syntax of position in circle and ellipse","support":{"chrome":{"version_added":"37","version_removed":"68"},"chrome_android":{"version_added":"37","version_removed":"68"},"edge":{"version_added":false},"firefox":{"version_added":"62","version_removed":"70"},"firefox_android":{"version_added":"62","version_removed":"79"},"ie":{"version_added":false},"opera":{"version_added":"24","version_removed":"55"},"opera_android":{"version_added":"24","version_removed":"48"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"3.0","version_removed":"10.0"},"webview_android":{"version_added":"37","version_removed":"68"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"tab-size":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/tab-size","spec_url":"https://drafts.csswg.org/css-text/#tab-size-property","support":{"chrome":{"version_added":"21","notes":"This property is not yet animatable."},"chrome_android":{"version_added":"25","notes":"This property is not yet animatable."},"edge":{"version_added":"79","notes":"This property is not yet animatable."},"firefox":[{"version_added":"91"},{"prefix":"-moz-","version_added":"4","notes":"Before Firefox 53, this property was not animatable."}],"firefox_android":[{"version_added":"91"},{"prefix":"-moz-","version_added":"4","notes":"Before Firefox 53, this property was not animatable."}],"ie":{"version_added":false},"opera":[{"version_added":"15"},{"prefix":"-o-","version_added":"10.5","version_removed":"15"}],"opera_android":[{"version_added":"14"},{"prefix":"-o-","version_added":"11","version_removed":"14"}],"safari":{"version_added":"7"},"safari_ios":{"version_added":"7"},"samsunginternet_android":{"version_added":"1.5","notes":"This property is not yet animatable."},"webview_android":{"version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"length":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/length","spec_url":"https://drafts.csswg.org/css-values/#lengths","description":"<length>","support":{"chrome":{"version_added":"42"},"chrome_android":{"version_added":"42"},"edge":{"version_added":"79"},"firefox":{"version_added":"53"},"firefox_android":{"version_added":"53"},"ie":{"version_added":false},"opera":{"version_added":"29"},"opera_android":{"version_added":"29"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"4.0"},"webview_android":{"version_added":"56"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"table-layout":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/table-layout","spec_url":"https://drafts.csswg.org/css2/#width-layout","support":{"chrome":{"version_added":"14"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"5"},"opera":{"version_added":"7"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"3"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1.5"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"text-align-last":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-align-last","spec_url":"https://drafts.csswg.org/css-text/#text-align-last-property","support":{"chrome":{"version_added":"47"},"chrome_android":{"version_added":"47"},"edge":{"version_added":"12"},"firefox":[{"version_added":"49"},{"prefix":"-moz-","version_added":"12","version_removed":"53"}],"firefox_android":[{"version_added":"49"},{"prefix":"-moz-","version_added":"14","version_removed":"53"}],"ie":{"version_added":"5.5","partial_implementation":true,"notes":["IE only supports text-align-last when text-align is set to justify.","The start and end values are not supported."]},"opera":{"version_added":"34"},"opera_android":{"version_added":"34"},"safari":{"version_added":false,"notes":"See WebKit bug 76173."},"safari_ios":{"version_added":false,"notes":"See WebKit bug 76173."},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"47"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"text-align":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-align","spec_url":["https://drafts.csswg.org/css-logical/#text-align","https://drafts.csswg.org/css-text/#text-align-property"],"support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"3"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"block_alignment_values":{"__compat":{"description":"Prefixed center, left, and right values for block alignment","support":{"chrome":{"prefix":"-webkit-","version_added":"1"},"chrome_android":{"prefix":"-webkit-","version_added":"18"},"edge":{"prefix":"-webkit-","version_added":"79"},"firefox":{"prefix":"-moz-","version_added":"1"},"firefox_android":{"prefix":"-moz-","version_added":"4"},"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":[{"prefix":"-webkit-","version_added":"1.3"},{"prefix":"-khtml-","version_added":"1"}],"safari_ios":[{"prefix":"-webkit-","version_added":"1"},{"prefix":"-khtml-","version_added":"1"}],"samsunginternet_android":{"version_added":"1.0","prefix":"-webkit-"},"webview_android":{"prefix":"-webkit-","version_added":"37"}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"flow_relative_values_start_and_end":{"__compat":{"description":"Flow-relative values start and end","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3.1"},"safari_ios":{"version_added":"2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"justify-all":{"__compat":{"description":"justify-all","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"match-parent":{"__compat":{"description":"match-parent","support":{"chrome":{"version_added":"16"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"40"},"firefox_android":{"version_added":"40"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"string":{"__compat":{"description":"Character-based alignment in a table column (<string> value)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}}},"text-combine-upright":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-combine-upright","spec_url":"https://drafts.csswg.org/css-writing-modes/#text-combine-upright","support":{"chrome":[{"version_added":"48"},{"partial_implementation":true,"alternative_name":"-webkit-text-combine","version_added":"9","notes":"This property was initially named -webkit-text-combine according to a 2011 version of the CSS3 Writing Modes specification, supporting the values none and horizontal without digits."}],"chrome_android":[{"version_added":"48"},{"partial_implementation":true,"alternative_name":"-webkit-text-combine","version_added":"18","notes":"This property was initially named -webkit-text-combine according to a 2011 version of the CSS3 Writing Modes specification, supporting the values none and horizontal without digits."}],"edge":[{"version_added":"15","version_removed":"79"},{"version_added":"12","version_removed":"79","alternative_name":"-ms-text-combine-horizontal"}],"firefox":{"version_added":"48","notes":"Before version 81, Firefox implemented the property as animatable. This was corrected to spec in 81."},"firefox_android":{"version_added":"48","notes":"Before version 81, Firefox implemented the property as animatable. This was corrected to spec in 81."},"ie":{"version_added":"11","alternative_name":"-ms-text-combine-horizontal"},"opera":[{"version_added":"35"},{"partial_implementation":true,"alternative_name":"-webkit-text-combine","version_added":"15","notes":"This property was initially named -webkit-text-combine according to a 2011 version of the CSS3 Writing Modes specification, supporting the values none and horizontal without digits."}],"opera_android":[{"version_added":"35"},{"partial_implementation":true,"alternative_name":"-webkit-text-combine","version_added":"14","notes":"This property was initially named -webkit-text-combine according to a 2011 version of the CSS3 Writing Modes specification, supporting the values none and horizontal without digits."}],"safari":{"partial_implementation":true,"alternative_name":"-webkit-text-combine","version_added":"5.1","notes":"This property was initially named -webkit-text-combine according to a 2011 version of the CSS3 Writing Modes specification, supporting the values none and horizontal without digits."},"safari_ios":{"partial_implementation":true,"alternative_name":"-webkit-text-combine","version_added":"5","notes":"This property was initially named -webkit-text-combine according to a 2011 version of the CSS3 Writing Modes specification, supporting the values none and horizontal without digits."},"samsunginternet_android":[{"version_added":"5.0"},{"partial_implementation":true,"alternative_name":"-webkit-text-combine","version_added":"1.0","notes":"This property was initially named -webkit-text-combine according to a 2011 version of the CSS3 Writing Modes specification, supporting the values none and horizontal without digits."}],"webview_android":[{"version_added":"48"},{"partial_implementation":true,"alternative_name":"-webkit-text-combine","version_added":"≤37","notes":"This property was initially named -webkit-text-combine according to a 2011 version of the CSS3 Writing Modes specification, supporting the values none and horizontal without digits."}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"digits":{"__compat":{"description":"digits","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":[{"version_added":"15","version_removed":"79"},{"version_added":"12","version_removed":"79","alternative_name":"-ms-text-combine-horizontal"}],"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":"11","alternative_name":"-ms-text-combine-horizontal"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"text-decoration-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-decoration-color","spec_url":"https://drafts.csswg.org/css-text-decor/#text-decoration-color-property","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":[{"version_added":"36"},{"prefix":"-moz-","version_added":"6","version_removed":"39"}],"firefox_android":[{"version_added":"36"},{"prefix":"-moz-","version_added":"6","version_removed":"39"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"8"}],"safari_ios":[{"version_added":"12.2"},{"prefix":"-webkit-","version_added":"8"}],"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"text-decoration-line":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-decoration-line","spec_url":"https://drafts.csswg.org/css-text-decor/#text-decoration-line-property","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":[{"version_added":"36"},{"prefix":"-moz-","version_added":"6","version_removed":"39"}],"firefox_android":[{"version_added":"36"},{"prefix":"-moz-","version_added":"6","version_removed":"39"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"8"}],"safari_ios":[{"version_added":"12.2"},{"prefix":"-webkit-","version_added":"8"}],"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"blink":{"__compat":{"description":"blink","support":{"chrome":{"version_added":"57","notes":"The blink value does not have any effect."},"chrome_android":{"version_added":"57","notes":"The blink value does not have any effect."},"edge":{"version_added":"79","notes":"The blink value does not have any effect."},"firefox":{"version_added":"26","notes":"The blink value does not have any effect."},"firefox_android":{"version_added":"26","notes":"The blink value does not have any effect."},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"8"},"safari_ios":{"version_added":"8"},"samsunginternet_android":{"version_added":"7.0","notes":"The blink value does not have any effect."},"webview_android":{"version_added":"57","notes":"The blink value does not have any effect."}},"status":{"experimental":false,"standard_track":true,"deprecated":true}}}},"text-decoration-skip-ink":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-decoration-skip-ink","spec_url":"https://drafts.csswg.org/css-text-decor-4/#text-decoration-skip-ink-property","support":{"chrome":{"version_added":"64"},"chrome_android":{"version_added":"64"},"edge":{"version_added":"79"},"firefox":{"version_added":"70"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"50"},"opera_android":{"version_added":"46"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"9.0"},"webview_android":{"version_added":"64"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"all":{"__compat":{"description":"all","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"75"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"text-decoration-skip":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-decoration-skip","spec_url":"https://drafts.csswg.org/css-text-decor-4/#text-decoration-skipping","support":{"chrome":{"version_added":"57","version_removed":"64","notes":"Only supports the deprecated ink value."},"chrome_android":{"version_added":"57","version_removed":"64","notes":"Only supports the deprecated ink value."},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"44","version_removed":"50","notes":"Only supports the deprecated ink value."},"opera_android":{"version_added":"43","version_removed":"46","notes":"Only supports the deprecated ink value."},"safari":[{"version_added":"12.1","notes":"Supports only none, auto, and objects values."},{"version_added":"7","prefix":"-webkit-","notes":"Supports only none, auto, and objects values."}],"safari_ios":[{"version_added":"12.2","notes":"Supports only none, auto, and objects values."},{"version_added":"7","prefix":"-webkit-","notes":"Supports only none, auto, and objects values."}],"samsunginternet_android":{"version_added":"7.0","version_removed":"9.0","notes":"Only supports the deprecated ink value."},"webview_android":{"version_added":"57","version_removed":"64","notes":"Only supports the deprecated ink value."}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"text-decoration-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-decoration-style","spec_url":"https://drafts.csswg.org/css-text-decor/#text-decoration-style-property","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":[{"version_added":"36"},{"prefix":"-moz-","version_added":"6","version_removed":"39"}],"firefox_android":[{"version_added":"36"},{"prefix":"-moz-","version_added":"6","version_removed":"39"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"8"}],"safari_ios":[{"version_added":"12.2"},{"prefix":"-webkit-","version_added":"8"}],"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"wavy":{"__compat":{"description":"wavy","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":"6"},"firefox_android":{"version_added":"6"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"8"},"safari_ios":{"version_added":"8"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"text-decoration-thickness":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-decoration-thickness","spec_url":"https://drafts.csswg.org/css-text-decor-4/#text-decoration-width-property","support":{"chrome":[{"version_added":"89"},{"version_added":"87","version_removed":"89","partial_implementation":true,"notes":"The text-decoration-thickness property does not work unless either text-underline-offset is set to something other than auto or text-decoration-color is set to something other than currentColor. See Chromium bug 1154537."}],"chrome_android":[{"version_added":"89"},{"version_added":"87","version_removed":"89","partial_implementation":true,"notes":"The text-decoration-thickness property does not work unless either text-underline-offset is set to something other than auto or text-decoration-color is set to something other than currentColor. See Chromium bug 1154537."}],"edge":[{"version_added":"89"},{"version_added":"87","version_removed":"89","partial_implementation":true,"notes":"The text-decoration-thickness property does not work unless either text-underline-offset is set to something other than auto or text-decoration-color is set to something other than currentColor. See Chromium bug 1154537."}],"firefox":[{"version_added":"70"},{"version_added":"69","alternative_name":"text-decoration-width","flags":[{"type":"preference","name":"layout.css.text-decoration-width.enabled","value_to_set":"true"}]}],"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":[{"version_added":"75"},{"version_added":"73","version_removed":"75","partial_implementation":true,"notes":"The text-decoration-thickness property does not work unless either text-underline-offset is set to something other than auto or text-decoration-color is set to something other than currentColor. See Chromium bug 1154537."}],"opera_android":{"version_added":"63"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"15.0"},"webview_android":[{"version_added":"89"},{"version_added":"87","version_removed":"89","partial_implementation":true,"notes":"The text-decoration-thickness property does not work unless either text-underline-offset is set to something other than auto or text-decoration-color is set to something other than currentColor. See Chromium bug 1154537."}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"percentage":{"__compat":{"description":"percentage values","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"74"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"text-decoration":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-decoration","spec_url":"https://drafts.csswg.org/css-text-decor/#text-decoration-property","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"3"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"blink":{"__compat":{"description":"blink","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1","version_removed":"23"},"firefox_android":{"version_added":"4","version_removed":"23"},"ie":{"version_added":false},"opera":{"version_added":"4","version_removed":"15"},"opera_android":{"version_added":"10.1","version_removed":"14"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":true}}},"shorthand":{"__compat":{"description":"Shorthand","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":[{"version_added":"36"},{"partial_implementation":true,"version_added":"6"}],"firefox_android":[{"version_added":"36"},{"partial_implementation":true,"version_added":"6"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"prefix":"-webkit-","version_added":"8"},"safari_ios":{"prefix":"-webkit-","version_added":"8"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"text-decoration-thickness":{"__compat":{"description":"text-decoration-thickness included in shorthand","support":{"chrome":{"version_added":"87"},"chrome_android":{"version_added":"87"},"edge":{"version_added":"87"},"firefox":{"version_added":"70"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"73"},"opera_android":{"version_added":"62"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":true,"standard_track":false,"deprecated":false}}}},"text-emphasis-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-emphasis-color","spec_url":"https://drafts.csswg.org/css-text-decor/#text-emphasis-color-property","support":{"chrome":{"prefix":"-webkit-","version_added":"25"},"chrome_android":{"prefix":"-webkit-","version_added":"25"},"edge":{"prefix":"-webkit-","version_added":"79"},"firefox":{"version_added":"46"},"firefox_android":{"version_added":"46"},"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":[{"version_added":"7"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"7"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":{"prefix":"-webkit-","version_added":"1.5"},"webview_android":{"prefix":"-webkit-","version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"text-emphasis-position":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-emphasis-position","spec_url":"https://drafts.csswg.org/css-text-decor/#text-emphasis-position-property","support":{"chrome":{"prefix":"-webkit-","version_added":"25"},"chrome_android":{"prefix":"-webkit-","version_added":"25"},"edge":{"prefix":"-webkit-","version_added":"79"},"firefox":{"version_added":"46"},"firefox_android":{"version_added":"46"},"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":[{"version_added":"7"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"7"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":{"prefix":"-webkit-","version_added":"1.5"},"webview_android":{"prefix":"-webkit-","version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"left_and_right":{"__compat":{"description":"left and right","support":{"chrome":{"version_added":"62"},"chrome_android":{"version_added":"62"},"edge":{"version_added":"79"},"firefox":{"version_added":"46"},"firefox_android":{"version_added":"46"},"ie":{"version_added":false},"opera":{"version_added":"49"},"opera_android":{"version_added":"46"},"safari":{"version_added":"8"},"safari_ios":{"version_added":"8"},"samsunginternet_android":{"version_added":"8.0"},"webview_android":{"version_added":"62"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"text-emphasis-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-emphasis-style","spec_url":"https://drafts.csswg.org/css-text-decor/#text-emphasis-style-property","support":{"chrome":{"prefix":"-webkit-","version_added":"25"},"chrome_android":{"prefix":"-webkit-","version_added":"25"},"edge":{"prefix":"-webkit-","version_added":"79"},"firefox":{"version_added":"46"},"firefox_android":{"version_added":"46"},"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":[{"version_added":"7"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"7"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":{"prefix":"-webkit-","version_added":"1.5"},"webview_android":{"prefix":"-webkit-","version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"text-emphasis":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-emphasis","spec_url":"https://drafts.csswg.org/css-text-decor/#text-emphasis-property","support":{"chrome":{"prefix":"-webkit-","version_added":"25"},"chrome_android":{"prefix":"-webkit-","version_added":"25"},"edge":{"prefix":"-webkit-","version_added":"79"},"firefox":{"version_added":"46"},"firefox_android":{"version_added":"46"},"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":[{"version_added":"7"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"7"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":{"prefix":"-webkit-","version_added":"1.5"},"webview_android":{"prefix":"-webkit-","version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"text-indent":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-indent","spec_url":"https://drafts.csswg.org/css-text/#text-indent-property","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"3"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"each-line":{"__compat":{"description":"each-line","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"preview"},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"hanging":{"__compat":{"description":"hanging","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"preview"},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"text-justify":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-justify","spec_url":"https://drafts.csswg.org/css-text/#text-justify-property","support":{"chrome":{"version_added":"32","flags":[{"type":"preference","name":"Enable Experimental Web Platform Features","value_to_set":"true"}],"notes":"inter-word and distribute (deprecated) values are supported, but distribute behavior is buggy."},"chrome_android":{"version_added":"32","flags":[{"type":"preference","name":"Enable Experimental Web Platform Features","value_to_set":"true"}],"notes":"inter-word and distribute (deprecated) values are supported, but distribute behavior is buggy."},"edge":{"version_added":"12","notes":"Standard values inter-character and none are supported. The deprecated distribute value is also supported."},"firefox":{"version_added":"55"},"firefox_android":{"version_added":"55"},"ie":{"version_added":"11","notes":"Standard values inter-character and none are supported. The deprecated distribute value is also supported."},"opera":{"version_added":"19","flags":[{"type":"preference","name":"Enable Experimental Web Platform Features","value_to_set":"true"}],"notes":"inter-word and distribute (deprecated) values are supported, but distribute behavior is buggy."},"opera_android":{"version_added":"19","flags":[{"type":"preference","name":"Enable Experimental Web Platform Features","value_to_set":"true"}],"notes":"inter-word and distribute (deprecated) values are supported, but distribute behavior is buggy."},"safari":{"version_added":false,"notes":"See bug 9945."},"safari_ios":{"version_added":false,"notes":"See bug 9945."},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"text-orientation":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-orientation","spec_url":"https://drafts.csswg.org/css-writing-modes/#text-orientation","support":{"chrome":[{"version_added":"48"},{"prefix":"-webkit-","version_added":"11"}],"chrome_android":[{"version_added":"48"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"79"},{"prefix":"-webkit-","version_added":"79"}],"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":[{"version_added":"35"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"35"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"14"},{"prefix":"-webkit-","version_added":"5.1"}],"safari_ios":[{"version_added":"14"},{"prefix":"-webkit-","version_added":"5"}],"samsunginternet_android":[{"version_added":"5.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"48"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"sideways":{"__compat":{"description":"sideways","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"44","notes":"sideways-right has become an alias of sideways."},"firefox_android":{"version_added":"44","notes":"sideways-right has become an alias of sideways."},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"text-overflow":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-overflow","spec_url":"https://drafts.csswg.org/css-overflow/#text-overflow","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"7","notes":"Until Firefox 10, handling of text-overflow on blocks with inline overflow on both horizontal sides was incorrect. Before Firefox 10, if only one value was specified (such as text-overflow: ellipsis;), text was ellipsed on both sides of the block, instead of only the end edge based on the block's text direction."},"firefox_android":{"version_added":"7","notes":"Until Firefox 10, handling of text-overflow on blocks with inline overflow on both horizontal sides was incorrect. Before Firefox 10, if only one value was specified (such as text-overflow: ellipsis;), text was ellipsed on both sides of the block, instead of only the end edge based on the block's text direction."},"ie":[{"version_added":"6"},{"prefix":"-ms-","version_added":"8"}],"opera":[{"version_added":"11"},{"prefix":"-o-","version_added":"9","version_removed":"15"}],"opera_android":[{"version_added":"11"},{"prefix":"-o-","version_added":"10.1","version_removed":"14"}],"safari":{"version_added":"1.3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"fade_function":{"__compat":{"description":"fade()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"fade_value":{"__compat":{"description":"fade","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"string":{"__compat":{"description":"<string>","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"9"},"firefox_android":{"version_added":"9"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"two_value_syntax":{"__compat":{"description":"Two-value syntax","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"9"},"firefox_android":{"version_added":"9"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"text-rendering":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-rendering","spec_url":"https://svgwg.org/svg2-draft/painting.html#TextRenderingProperty","support":{"chrome":{"version_added":"4","notes":["This property is only supported on Windows and Linux.","Initial versions had bugs on Windows and Linux that broke font substitition, small-caps, letter-spacing or caused text to overlap."]},"chrome_android":{"version_added":"18","notes":["This property is only supported on Windows and Linux.","Initial versions had bugs on Windows and Linux that broke font substitition, small-caps, letter-spacing or caused text to overlap."]},"edge":{"version_added":"79","notes":["This property is only supported on Windows and Linux.","Initial versions had bugs on Windows and Linux that broke font substitition, small-caps, letter-spacing or caused text to overlap."]},"firefox":{"version_added":"1","notes":["This property is only supported on Windows and Linux.","The optimizeSpeed option has no effect on Firefox 4 because the standard code for text rendering is already fast and there is not a faster code path at this time. See bug 595688 for details."]},"firefox_android":{"version_added":"46"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","notes":"This property is only supported on Windows and Linux. Samsung Internet is not on Windows or Linux."},"webview_android":{"version_added":"3","notes":"From version 3 to 4.3, there is a serious bug where text-rendering: optimizeLegibility causes custom web fonts to not render. This was fixed in version 4.4."}},"status":{"experimental":false,"standard_track":false,"deprecated":false}},"auto":{"__compat":{"description":"auto","support":{"chrome":{"version_added":"4","notes":"Chrome treats auto as optimizeSpeed."},"chrome_android":{"version_added":"18","notes":"Chrome treats auto as optimizeSpeed."},"edge":{"version_added":"79","notes":"Edge treats auto as optimizeSpeed."},"firefox":{"version_added":"1","notes":"If the font size is 20 pixels or higher, Firefox treats auto as optimizeLegibility. For smaller text, Firefox treats auto as optimizeSpeed. The 20-pixel threshold can be changed with the browser.display.auto_quality_min_font_size preference."},"firefox_android":{"version_added":"46","notes":"If the font size is 20 pixels or higher, Firefox treats auto as optimizeLegibility. For smaller text, Firefox treats auto as optimizeSpeed. The 20-pixel threshold can be changed with the browser.display.auto_quality_min_font_size preference."},"ie":{"version_added":false},"opera":{"version_added":"15","notes":"Opera treats auto as optimizeSpeed."},"opera_android":{"version_added":"14","notes":"Opera treats auto as optimizeSpeed."},"safari":{"version_added":"5","notes":"Safari treats auto as optimizeSpeed. See WebKit bug 41363."},"safari_ios":{"version_added":"4.2","notes":"Safari treats auto as optimizeSpeed. See WebKit bug 41363."},"samsunginternet_android":{"version_added":"1.0","notes":"Samsung Internet treats auto as optimizeSpeed."},"webview_android":{"version_added":"≤37","notes":"WebView treats auto as optimizeSpeed."}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"geometricPrecision":{"__compat":{"description":"geometricPrecision","support":{"chrome":{"version_added":"13","notes":"Supports true geometric precision without rounding up or down to the nearest supported font size in the operating system."},"chrome_android":{"version_added":"18","notes":"Supports true geometric precision without rounding up or down to the nearest supported font size in the operating system."},"edge":{"version_added":"79","notes":"Supports true geometric precision without rounding up or down to the nearest supported font size in the operating system."},"firefox":{"version_added":"1","notes":"Firefox treats geometricPrecision the same as optimizeLegibility."},"firefox_android":{"version_added":"46","notes":"Firefox treats geometricPrecision the same as optimizeLegibility."},"ie":{"version_added":false},"opera":{"version_added":"15","notes":"Supports true geometric precision without rounding up or down to the nearest supported font size in the operating system."},"opera_android":{"version_added":"14","notes":"Supports true geometric precision without rounding up or down to the nearest supported font size in the operating system."},"safari":{"version_added":"6"},"safari_ios":{"version_added":"6"},"samsunginternet_android":{"version_added":"1.0","notes":"Supports true geometric precision without rounding up or down to the nearest supported font size in the operating system."},"webview_android":{"version_added":"37","notes":"Supports true geometric precision without rounding up or down to the nearest supported font size in the operating system."}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}}},"text-shadow":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-shadow","spec_url":"https://drafts.csswg.org/css-text-decor/#text-shadow-property","support":{"chrome":{"version_added":"2"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3.5","notes":["Firefox versions before 57 have a bug whereby transitions will not work when transitioning from a text-shadow with a color specified to a text-shadow without a color specified (bug 726550).","From Firefox 4, the blur radius is capped at 300 for performance reasons.","Firefox theoretically supports infinite text-shadows (don't try it).","If the <color> value is unspecified, then Firefox uses the value of the element's color property."]},"firefox_android":{"version_added":"4","notes":["Firefox versions before 57 have a bug whereby transitions will not work when transitioning from a text-shadow with a color specified to a text-shadow without a color specified (bug 726550).","From Firefox 4, the blur radius is capped at 300 for performance reasons.","Firefox theoretically supports infinite text-shadows (don't try it).","If the <color> value is unspecified, then Firefox uses the value of the element's color property."]},"ie":{"version_added":"10"},"opera":{"version_added":"9.5","notes":["Opera supports a maximum of 6-9 text-shadows for performance reasons. The blur radius is limited to 100px.","Opera 9.5 to 10.1 adheres to the old, reverse painting order (in CSS2, the first specified shadow is on the bottom)."]},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.1","notes":["In Safari, any shadows that do not explicitly specify a color are transparent.","Safari 1.1 to 3.2 only supports one text-shadow (displays the first shadow of a comma-separated list and ignores the rest). Safari 4.0 (WebKit 528) and later support multiple text-shadows."]},"safari_ios":{"version_added":"1","notes":["In Safari, any shadows that do not explicitly specify a color are transparent.","Safari iOS 1 and 2 only support one text-shadow (displays the first shadow of a comma-separated list and ignores the rest). Safari iOS 3 (WebKit 528) and later support multiple text-shadows."]},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"text-size-adjust":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-size-adjust","spec_url":"https://drafts.csswg.org/css-size-adjust/#adjustment-control","support":{"chrome":[{"version_added":"54"},{"prefix":"-webkit-","version_added":false,"notes":"Instead of ignoring the -webkit-text-size-adjust property, a bug prevents desktop Chrome users from zooming in or out. The bug was fixed after Chrome 26."}],"chrome_android":{"version_added":"54"},"edge":[{"version_added":"79"},{"prefix":"-webkit-","version_added":"12","version_removed":"79"}],"firefox":{"version_added":false},"firefox_android":[{"prefix":"-webkit-","version_added":"49"},{"prefix":"-moz-","version_added":"14"},{"prefix":"-webkit-","version_added":"44","flags":[{"type":"preference","name":"layout.css.prefixes.webkit","value_to_set":"true"}]}],"ie":{"version_added":false},"opera":{"version_added":"41"},"opera_android":{"version_added":"41"},"safari":[{"version_added":false},{"prefix":"-webkit-","version_added":false,"notes":"Instead of ignoring the -webkit-text-size-adjust property, a bug prevents desktop Safari users from zooming in or out. The bug was fixed after Safari 5."}],"safari_ios":{"prefix":"-webkit-","version_added":"1"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"54"}},"status":{"experimental":true,"standard_track":true,"deprecated":false}},"percentages":{"__compat":{"description":"<percentage>","support":{"chrome":{"version_added":"54"},"chrome_android":{"version_added":"54"},"edge":{"version_added":"12"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"41"},"opera_android":{"version_added":"41"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"54"}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}}},"text-transform":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-transform","spec_url":"https://drafts.csswg.org/css-text/#text-transform","support":{"chrome":{"version_added":"1","notes":"The text-transform property does not work for ::first-line pseudo-elements (nor for the one-colon syntax). See Chromium bug 129669."},"chrome_android":{"version_added":"18","notes":"The text-transform property does not work for ::first-line pseudo-elements (nor for the one-colon syntax). See Chromium bug 129669."},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"7","notes":"Since Opera 15, the text-transform property does not work for ::first-line pseudo-elements (nor for the one-colon syntax). See Chromium bug 129669."},"opera_android":{"version_added":"11"},"safari":{"version_added":"1","notes":"The text-transform property does not work for ::first-line pseudo-elements (also not for the old one-colon syntax). See WebKit bug 3409."},"safari_ios":{"version_added":"1","notes":"The text-transform property does not work for ::first-line pseudo-elements (also not for the old one-colon syntax). See WebKit bug 3409."},"samsunginternet_android":{"version_added":"1.0","notes":"The text-transform property does not work for ::first-line pseudo-elements (nor for the one-colon syntax). See Chromium bug 129669."},"webview_android":{"version_added":"1","notes":"The text-transform property does not work for ::first-line pseudo-elements (nor for the one-colon syntax). See Chromium bug 129669."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"capitalize":{"__compat":{"support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"Before Firefox 14, some punctuation characters could interfere with correct capitalization. See bug 731536."},"firefox_android":{"version_added":"4","notes":"Before Firefox 14, some punctuation characters could interfere with correct capitalization. See bug 731536."},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"11"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"dutch_ij_digraph":{"__compat":{"description":"Dutch IJ digraph","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"14"},"firefox_android":{"version_added":"14"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"full-size-kana":{"__compat":{"description":"full-size-kana","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"64"},"firefox_android":{"version_added":"64"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"full-width":{"__compat":{"description":"full-width","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"19"},"firefox_android":{"version_added":"19"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"greek_accented_characters":{"__compat":{"description":"Greek accented letters","support":{"chrome":{"version_added":"34"},"chrome_android":{"version_added":"34"},"edge":{"version_added":"79"},"firefox":{"version_added":"15"},"firefox_android":{"version_added":"15"},"ie":{"version_added":false},"opera":{"version_added":"21"},"opera_android":{"version_added":"21"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"2.0"},"webview_android":{"version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"lowercase_sigma":{"__compat":{"description":"Σσ or word-final ς","support":{"chrome":{"version_added":"30"},"chrome_android":{"version_added":"30"},"edge":{"version_added":"12"},"firefox":{"version_added":"14"},"firefox_android":{"version_added":"14"},"ie":{"version_added":"4"},"opera":{"version_added":"17"},"opera_android":{"version_added":"18"},"safari":{"version_added":"6"},"safari_ios":{"version_added":"6"},"samsunginternet_android":{"version_added":"2.0"},"webview_android":{"version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"turkic_is":{"__compat":{"description":"iİ and ıI","support":{"chrome":{"version_added":"31"},"chrome_android":{"version_added":"31"},"edge":{"version_added":"12"},"firefox":{"version_added":"14"},"firefox_android":{"version_added":"14"},"ie":{"version_added":"4"},"opera":{"version_added":"18"},"opera_android":{"version_added":"18"},"safari":{"version_added":"8"},"safari_ios":{"version_added":"8"},"samsunginternet_android":{"version_added":"2.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"uppercase_eszett":{"__compat":{"description":"ßSS","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"18"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":"7"},"opera_android":{"version_added":"11"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"text-underline-offset":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-underline-offset","spec_url":"https://drafts.csswg.org/css-text-decor-4/#underline-offset","support":{"chrome":{"version_added":"87"},"chrome_android":{"version_added":"87"},"edge":{"version_added":"87"},"firefox":[{"version_added":"70"},{"version_added":"69","flags":[{"type":"preference","name":"layout.css.text-underline-offset.enabled","value_to_set":"true"}]}],"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"73"},"opera_android":{"version_added":false},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"percentage":{"__compat":{"description":"percentage values","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"74"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"text-underline-position":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-underline-position","spec_url":"https://drafts.csswg.org/css-text-decor/#text-underline-position-property","support":{"chrome":{"version_added":"33"},"chrome_android":{"version_added":"33"},"edge":{"version_added":"12"},"firefox":{"version_added":"74"},"firefox_android":{"version_added":false},"ie":{"version_added":"6"},"opera":{"version_added":"20"},"opera_android":{"version_added":"20"},"safari":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"9"}],"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"2.0"},"webview_android":{"version_added":"4.4.3"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"above_below":{"__compat":{"description":"above and below","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"79"},"firefox":{"version_added":"74"},"firefox_android":{"version_added":false},"ie":{"version_added":"6"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":false,"deprecated":false}}},"auto-pos":{"__compat":{"description":"auto-pos","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":"6"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":false,"deprecated":false}}},"from-font":{"__compat":{"description":"from-font","support":{"chrome":{"version_added":"87"},"chrome_android":{"version_added":"87"},"edge":{"version_added":"87"},"firefox":{"version_added":"74"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"left_right":{"__compat":{"description":"left and right","support":{"chrome":{"version_added":"71"},"chrome_android":{"version_added":"71"},"edge":{"version_added":"79"},"firefox":{"version_added":"74"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"58"},"opera_android":{"version_added":"50"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"71"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"under":{"__compat":{"description":"under","support":{"chrome":{"version_added":"33"},"chrome_android":{"version_added":"33"},"edge":{"version_added":"79"},"firefox":{"version_added":"74"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"20"},"opera_android":{"version_added":"20"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"2.0"},"webview_android":{"version_added":"4.4.3"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"top":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/top","spec_url":"https://drafts.csswg.org/css-position/#insets","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"5","notes":"In Internet Explorer versions before 7, when both top and bottom are specified, the element position is overconstrained and the top property has precedence; the computed value of bottom is set to -top, while its specified value is ignored."},"opera":{"version_added":"6"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"touch-action":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/touch-action","spec_url":["https://compat.spec.whatwg.org/#touch-action","https://w3c.github.io/pointerevents/#the-touch-action-css-property"],"support":{"chrome":{"version_added":"36"},"chrome_android":{"version_added":"36"},"edge":{"version_added":"12"},"firefox":{"version_added":"52","notes":"Not applicable to Firefox platforms that support neither pointer nor touch events."},"firefox_android":{"version_added":"52"},"ie":[{"version_added":"11"},{"version_added":"10","prefix":"-ms-"}],"opera":{"version_added":"23"},"opera_android":{"version_added":"24"},"safari":{"version_added":"13"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":"3.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"axis-pan":{"__compat":{"description":"pan-x and pan-y","support":{"chrome":{"version_added":"36"},"chrome_android":{"version_added":"36"},"edge":{"version_added":"12"},"firefox":{"version_added":"52","notes":"Not applicable to Firefox platforms that support neither pointer nor touch events."},"firefox_android":{"version_added":"52"},"ie":[{"version_added":"11"},{"version_added":"10","prefix":"-ms-"}],"opera":{"version_added":"23"},"opera_android":{"version_added":"24"},"safari":{"version_added":"13"},"safari_ios":{"version_added":"13"},"samsunginternet_android":{"version_added":"3.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"double-tap-zoom":{"__compat":{"support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":[{"version_added":"11"},{"version_added":"10","prefix":"-ms-"}],"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"manipulation":{"__compat":{"support":{"chrome":{"version_added":"36"},"chrome_android":{"version_added":"36"},"edge":{"version_added":"12"},"firefox":{"version_added":"52","notes":"Not applicable to Firefox platforms that support neither pointer nor touch events."},"firefox_android":{"version_added":"52"},"ie":[{"version_added":"11"},{"version_added":"10","prefix":"-ms-"}],"opera":{"version_added":"23"},"opera_android":{"version_added":"24"},"safari":{"version_added":"13"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":"3.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"none":{"__compat":{"support":{"chrome":{"version_added":"36"},"chrome_android":{"version_added":"36"},"edge":{"version_added":"12"},"firefox":{"version_added":"52","notes":"Not applicable to Firefox platforms that support neither pointer nor touch events."},"firefox_android":{"version_added":"52"},"ie":[{"version_added":"11"},{"version_added":"10","prefix":"-ms-"}],"opera":{"version_added":"23"},"opera_android":{"version_added":"24"},"safari":{"version_added":"13"},"safari_ios":{"version_added":"13"},"samsunginternet_android":{"version_added":"3.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"pinch-zoom":{"__compat":{"support":{"chrome":{"version_added":"56"},"chrome_android":{"version_added":"56"},"edge":{"version_added":"12"},"firefox":{"version_added":"85","notes":"Not applicable to Firefox platforms that support neither pointer nor touch events."},"firefox_android":{"version_added":"85","notes":"Not applicable to Firefox platforms that support neither pointer nor touch events."},"ie":[{"version_added":"11"},{"version_added":"10","prefix":"-ms-"}],"opera":{"version_added":"43"},"opera_android":{"version_added":"43"},"safari":{"version_added":"13"},"safari_ios":{"version_added":"13"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"56"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"unidirectional-pan":{"__compat":{"description":"pan-up, pan-down, pan-left and pan-right","support":{"chrome":{"version_added":"55"},"chrome_android":{"version_added":"55"},"edge":{"version_added":"79"},"firefox":{"version_added":false,"notes":"See bug 1285685."},"firefox_android":{"version_added":false,"notes":"See bug 1285685."},"ie":{"version_added":false},"opera":{"version_added":"42"},"opera_android":{"version_added":"42"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"55"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"transform-box":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/transform-box","spec_url":"https://drafts.csswg.org/css-transforms/#transform-box","support":{"chrome":{"version_added":"64"},"chrome_android":{"version_added":"64"},"edge":{"version_added":"79"},"firefox":{"version_added":"55"},"firefox_android":{"version_added":"55"},"ie":{"version_added":false},"opera":{"version_added":"51"},"opera_android":{"version_added":"47"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"9.0"},"webview_android":{"version_added":"64"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"transform-origin":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/transform-origin","spec_url":"https://drafts.csswg.org/css-transforms/#transform-origin-property","support":{"chrome":[{"version_added":"36"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"36"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"prefix":"-moz-","version_added":"3.5"},{"prefix":"-webkit-","version_added":"49"}],"firefox_android":[{"version_added":"16"},{"prefix":"-moz-","version_added":"4"},{"prefix":"-webkit-","version_added":"49"}],"ie":[{"version_added":"10"},{"prefix":"-ms-","version_added":"9"}],"opera":[{"version_added":"23"},{"prefix":"-webkit-","version_added":"15"},{"version_added":"12.1","version_removed":"15"},{"prefix":"-o-","version_added":"10.5","version_removed":"15"}],"opera_android":[{"version_added":"24"},{"prefix":"-webkit-","version_added":"15"},{"version_added":"12.1","version_removed":"15"},{"prefix":"-o-","version_added":"11","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"2"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"1"}],"samsunginternet_android":[{"version_added":"3.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"37"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"support_in_svg":{"__compat":{"description":"Support in SVG","support":{"chrome":{"version_added":"19"},"chrome_android":{"version_added":"25"},"edge":{"version_added":"17"},"firefox":{"version_added":"43","notes":"Keywords and percentages refer to the canvas instead of the object itself. See bug 1209061."},"firefox_android":{"version_added":"43","notes":"Keywords and percentages refer to the canvas instead of the object itself. See bug 1209061."},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"6","notes":"Only supported for transformations applied using the CSS transform property (e.g. .className { transform: rotate(45deg); transform-origin: center; }). It has no effect on transformations applied using the transform SVG attribute (e.g. <rect style="transform-origin: center;" transform="rotate(45)" />)."},"safari_ios":{"version_added":"6","notes":"Only supported for transformations applied using the CSS transform property (e.g. .className { transform: rotate(45deg); transform-origin: center; }). It has no effect on transformations applied using the transform SVG attribute (e.g. <rect style="transform-origin: center;" transform="rotate(45)" />)."},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"three_value_syntax":{"__compat":{"description":"Three-value syntax","matches":{"regex_value":"^([\\d\\w%-]+|calc\\(.+\\))\\s+([\\d\\w%-]+|calc\\(.+\\))\\s+([\\d\\w-]+|calc\\(.+\\))$"},"support":{"chrome":{"version_added":"12"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"10"},"firefox_android":{"version_added":"10"},"ie":{"version_added":"9"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"3.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"transform-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/transform-style","spec_url":"https://drafts.csswg.org/css-transforms-2/#transform-style-property","support":{"chrome":[{"version_added":"36"},{"prefix":"-webkit-","version_added":"12"}],"chrome_android":[{"version_added":"36"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"prefix":"-moz-","version_added":"10"},{"prefix":"-webkit-","version_added":"49"}],"firefox_android":[{"version_added":"16"},{"prefix":"-moz-","version_added":"10"},{"prefix":"-webkit-","version_added":"49"}],"ie":{"version_added":false},"opera":[{"version_added":"23"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"24"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"4"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"2"}],"samsunginternet_android":[{"version_added":"3.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"37"},{"prefix":"-webkit-","version_added":"3"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"transform":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/transform","spec_url":["https://drafts.csswg.org/css-transforms-2/#transform-functions","https://drafts.csswg.org/css-transforms/#transform-property"],"support":{"chrome":[{"version_added":"36"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"36"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"prefix":"-webkit-","version_added":"49"}],"firefox_android":[{"version_added":"16"},{"prefix":"-webkit-","version_added":"49"}],"ie":[{"version_added":"10","notes":"Internet Explorer does not support the global values initial and unset."},{"prefix":"-webkit-","version_added":"11"},{"prefix":"-ms-","version_added":"9","notes":"Internet Explorer 5.5 or later supports a proprietary Matrix Filter which can be used to achieve a similar effect."}],"opera":[{"version_added":"23"},{"prefix":"-webkit-","version_added":"15"},{"version_added":"12.1","version_removed":"15"},{"prefix":"-o-","version_added":"10.5","version_removed":"15"}],"opera_android":[{"version_added":"24"},{"prefix":"-webkit-","version_added":"14"},{"version_added":"12.1","version_removed":"14"},{"prefix":"-o-","version_added":"11","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3.1"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3.2"}],"samsunginternet_android":[{"version_added":"3.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"37"},{"prefix":"-webkit-","version_added":"2","notes":"Android 2.3 has a bug where input forms will \"jump\" when typing, if any container element has a -webkit-transform."}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"3d":{"__compat":{"description":"3D support","matches":{"keywords":["matrix3d","translate3d","translateZ","scale3d","scaleZ","rotate3d","rotateX","rotateY","rotateZ","perspective"]},"support":{"chrome":{"version_added":"12"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"16"},"firefox_android":{"version_added":"16"},"ie":{"version_added":"10","notes":"Internet Explorer 9.0 or earlier has no support for 3D transforms. Mixing 3D and 2D transform functions, such as -ms-transform:rotate(10deg) translateZ(0);, will prevent the entire property from being applied."},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"4"},"safari_ios":{"version_added":"3.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"prefix":"-webkit-","version_added":"3"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"transition-delay":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/transition-delay","spec_url":"https://drafts.csswg.org/css-transitions/#transition-delay-property","support":{"chrome":[{"version_added":"26"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"26"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"prefix":"-moz-","version_added":"4"},{"version_added":"49","prefix":"-webkit-"}],"firefox_android":[{"version_added":"16"},{"prefix":"-moz-","version_added":"4"},{"version_added":"49","prefix":"-webkit-"}],"ie":[{"version_added":"10"},{"prefix":"-ms-","version_added":"10"}],"opera":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"15"},{"prefix":"-o-","version_added":"11.6","version_removed":"15"}],"opera_android":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"14"},{"prefix":"-o-","version_added":"12","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"4"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"2"}],"samsunginternet_android":[{"version_added":"1.5"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"≤37"},{"prefix":"-webkit-","version_added":"2"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"transition-duration":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/transition-duration","spec_url":"https://drafts.csswg.org/css-transitions/#transition-duration-property","support":{"chrome":[{"version_added":"26"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"26"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"prefix":"-moz-","version_added":"4"},{"version_added":"49","prefix":"-webkit-"}],"firefox_android":[{"version_added":"16"},{"prefix":"-moz-","version_added":"4"},{"version_added":"49","prefix":"-webkit-"}],"ie":[{"version_added":"10"},{"prefix":"-ms-","version_added":"10"}],"opera":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"15"},{"prefix":"-o-","version_added":"10","version_removed":"15"}],"opera_android":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"14"},{"prefix":"-o-","version_added":"10.1","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3.1"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"2"}],"samsunginternet_android":[{"version_added":"1.5"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"≤37"},{"prefix":"-webkit-","version_added":"2"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"transition-property":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/transition-property","spec_url":"https://drafts.csswg.org/css-transitions/#transition-property-property","support":{"chrome":[{"version_added":"26"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"26"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"prefix":"-moz-","version_added":"4"},{"version_added":"49","prefix":"-webkit-"}],"firefox_android":[{"version_added":"16"},{"prefix":"-moz-","version_added":"4"},{"version_added":"49","prefix":"-webkit-"}],"ie":[{"version_added":"10"},{"prefix":"-ms-","version_added":"10"}],"opera":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"15"},{"prefix":"-o-","version_added":"11.6","version_removed":"15"}],"opera_android":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"14"},{"prefix":"-o-","version_added":"12","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3.1"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"2"}],"samsunginternet_android":[{"version_added":"1.5"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"≤37"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"IDENT_value":{"__compat":{"description":"IDENT value","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"16"},"firefox_android":{"version_added":"16"},"ie":{"version_added":"10"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"4"},"safari_ios":{"version_added":"3"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"transition-timing-function":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/transition-timing-function","spec_url":"https://drafts.csswg.org/css-transitions/#transition-timing-function-property","support":{"chrome":[{"version_added":"26"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"26"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"prefix":"-moz-","version_added":"4"},{"version_added":"49","prefix":"-webkit-"}],"firefox_android":[{"version_added":"16"},{"prefix":"-moz-","version_added":"4"},{"version_added":"49","prefix":"-webkit-"}],"ie":[{"version_added":"10"},{"prefix":"-ms-","version_added":"10"}],"opera":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"15"},{"prefix":"-o-","version_added":"11.6","version_removed":"15"}],"opera_android":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"14"},{"prefix":"-o-","version_added":"12","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3.1"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"2"}],"samsunginternet_android":[{"version_added":"1.5"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"≤37"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"jump":{"__compat":{"description":"jump- keywords for steps()","support":{"chrome":{"version_added":"77"},"chrome_android":{"version_added":"77"},"edge":{"version_added":"79"},"firefox":{"version_added":"65"},"firefox_android":{"version_added":"65"},"ie":{"version_added":false},"opera":{"version_added":"64"},"opera_android":{"version_added":"55"},"safari":{"version_added":"14"},"safari_ios":{"version_added":"14"},"samsunginternet_android":{"version_added":"12.0"},"webview_android":{"version_added":"77"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"transition":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/transition","spec_url":"https://drafts.csswg.org/css-transitions/#transition-shorthand-property","support":{"chrome":[{"version_added":"26"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"26"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16","notes":["Before Firefox 57, transitions do not work when transitioning from a text-shadow with a color specified to a text-shadow without a color specified (see bug 726550).","Before Firefox 57, cancelling a filling animation (for example, with animation-fill-mode: forwards set) can trigger a transition set on the same element, although only once (see bug 1192592 and these test cases for more information).","Before Firefox 57, the background-position property can't be transitioned between two values containing different numbers of <position> values, for example background-position: 10px 10px; and background-position: 20px 20px, 30px 30px; (see bug 1390446)."]},{"prefix":"-moz-","version_added":"4"},{"version_added":"49","prefix":"-webkit-"}],"firefox_android":[{"version_added":"16","notes":["Before Firefox 57, transitions do not work when transitioning from a text-shadow with a color specified to a text-shadow without a color specified (see bug 726550).","Before Firefox 57, cancelling a filling animation (for example, with animation-fill-mode: forwards set) can trigger a transition set on the same element, although only once (see bug 1192592 and these test cases for more information).","Before Firefox 57, the background-position property can't be transitioned between two values containing different numbers of <position> values, for example background-position: 10px 10px; and background-position: 20px 20px, 30px 30px; (see bug 1390446)."]},{"prefix":"-moz-","version_added":"4"},{"version_added":"49","prefix":"-webkit-"}],"ie":[{"version_added":"10"},{"prefix":"-ms-","version_added":"10"}],"opera":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"15"},{"prefix":"-o-","version_added":"10.1","version_removed":"15"}],"opera_android":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"14"},{"prefix":"-o-","version_added":"10.1","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3.1"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"2"}],"samsunginternet_android":[{"version_added":"1.5"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"≤37"},{"prefix":"-webkit-","version_added":"2"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"gradients":{"__compat":{"description":"Gradients","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":"10"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"translate":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/translate","spec_url":"https://drafts.csswg.org/css-transforms-2/#individual-transforms","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"72"},{"version_added":"60","version_removed":"72","flags":[{"type":"preference","name":"layout.css.individual-transform.enabled","value_to_set":"true"}]}],"firefox_android":{"version_added":"60","flags":[{"type":"preference","name":"layout.css.individual-transform.enabled","value_to_set":"true"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"unicode-bidi":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/unicode-bidi","spec_url":"https://drafts.csswg.org/css-writing-modes/#unicode-bidi","support":{"chrome":{"version_added":"2"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"5.5"},"opera":{"version_added":"9.2"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1.3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"isolate":{"__compat":{"description":"isolate","support":{"chrome":[{"version_added":"48"},{"prefix":"-webkit-","version_added":"16","notes":["Avoiding using -webkit-isolate. It can lock up older versions of Safari (up to version 9) and Chrome (up to version 47).","Since Chrome 19, the syntax from a previous version of the specification, where the isolate keyword could be used together with bidi-override, is allowed."]}],"chrome_android":{"version_added":"48"},"edge":[{"version_added":"79"},{"prefix":"-webkit-","version_added":"79"}],"firefox":[{"version_added":"50"},{"prefix":"-moz-","version_added":"10","version_removed":"54","notes":"From Firefox 10 to Firefox 16 (inclusive), the isolate keyword could be used together with bidi-override, which was the syntax from a previous version of the specification. From Firefox 17, only one value is allowed. Use isolate-override instead the previous isolate bidi-override."}],"firefox_android":[{"version_added":"50"},{"prefix":"-moz-","version_added":"10","version_removed":"54","notes":"From Firefox 10 to Firefox 16 (inclusive), the isolate keyword could be used together with bidi-override, which was the syntax from a previous version of the specification. From Firefox 17, only one value is allowed. Use isolate-override instead the previous isolate bidi-override."}],"ie":{"version_added":false},"opera":[{"version_added":"35"},{"prefix":"-webkit-","version_added":"15","notes":["Avoiding using -webkit-isolate. It can lock up older versions of Opera (up to version 34).","The syntax from a previous version of the specification, where the isolate keyword could be used together with bidi-override, is allowed."]}],"opera_android":{"version_added":"35"},"safari":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"6","notes":"Avoiding using -webkit-isolate. It can lock up older versions of Safari (up to version 9) and Chrome (up to version 47)."}],"safari_ios":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"6","notes":"Avoiding using -webkit-isolate. It can lock up older versions of Safari (up to version 9) and Chrome (up to version 47)."}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"48"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"isolate-override":{"__compat":{"description":"isolate-override","support":{"chrome":{"version_added":"48"},"chrome_android":{"version_added":"48"},"edge":{"version_added":"79"},"firefox":[{"version_added":"50"},{"prefix":"-moz-","version_added":"17","version_removed":"54"}],"firefox_android":[{"version_added":"50"},{"prefix":"-moz-","version_added":"17","version_removed":"54"}],"ie":{"version_added":false},"opera":{"version_added":"35"},"opera_android":{"version_added":"35"},"safari":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"48"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"plaintext":{"__compat":{"description":"plaintext","support":{"chrome":{"version_added":"48"},"chrome_android":{"version_added":"48"},"edge":{"version_added":"79"},"firefox":[{"version_added":"50"},{"prefix":"-moz-","version_added":"10","version_removed":"54","notes":["Before Firefox 50, the plaintext value was ignored for vertical writing modes (bug 1302734).","Before Firefox 15, plaintext didn't do anything to an inline element. The specification changed and the implementation was changed in Firefox 15."]}],"firefox_android":[{"version_added":"50"},{"prefix":"-moz-","version_added":"10","version_removed":"54","notes":["Before Firefox 50, the plaintext value was ignored for vertical writing modes (bug 1302734).","Before Firefox 15, plaintext didn't do anything to an inline element. The specification changed and the implementation was changed in Firefox 15."]}],"ie":{"version_added":false},"opera":{"version_added":"35"},"opera_android":{"version_added":"35"},"safari":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"6"}],"safari_ios":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"6"}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"48"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"user-modify":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/user-modify","support":{"chrome":{"prefix":"-webkit-","version_added":"1"},"chrome_android":{"prefix":"-webkit-","version_added":"18"},"edge":{"prefix":"-webkit-","version_added":"12"},"firefox":{"prefix":"-moz-","version_added":"1","partial_implementation":true,"notes":["While the CSS property is parsed and accepted, it does not have any effect.","Scheduled for removal (see bug 1388910)."]},"firefox_android":{"prefix":"-moz-","version_added":"4","partial_implementation":true,"notes":["While the CSS property is parsed and accepted, it does not have any effect.","Scheduled for removal (see bug 1388910)."]},"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":[{"version_added":"3","prefix":"-webkit-"},{"version_added":"2","version_removed":"3","prefix":"-khtml-"}],"safari_ios":{"version_added":"5","prefix":"-webkit-"},"samsunginternet_android":{"version_added":"1.0","prefix":"-webkit-"},"webview_android":{"prefix":"-webkit-","version_added":"37"}},"status":{"experimental":false,"standard_track":false,"deprecated":true}},"read-write-plaintext-only":{"__compat":{"support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}}},"user-select":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/user-select","spec_url":"https://drafts.csswg.org/css-ui/#content-selection","support":{"chrome":[{"version_added":"54"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"54"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"79"},{"prefix":"-ms-","version_added":"12","version_removed":"79"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"69"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"79"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-moz-","version_added":"4"}],"ie":{"prefix":"-ms-","version_added":"10"},"opera":[{"version_added":"41"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"41"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"3","prefix":"-webkit-"},{"version_added":"2","version_removed":"3","prefix":"-khtml-"}],"safari_ios":{"prefix":"-webkit-","version_added":"3"},"samsunginternet_android":[{"version_added":"6.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"54"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"all":{"__compat":{"support":{"chrome":{"version_added":"53"},"chrome_android":{"version_added":"53"},"edge":{"version_added":"79"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":"40"},"opera_android":{"version_added":"41"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"53"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"auto":{"__compat":{"support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"10"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"2"},"safari_ios":{"version_added":"3"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"contain":{"__compat":{"support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"alternative_name":"element","version_added":"12","version_removed":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"alternative_name":"element","version_added":"10"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"none":{"__compat":{"support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":[{"version_added":"21"},{"version_added":"1","version_removed":"65","prefix":"-moz-"}],"firefox_android":[{"version_added":"21"},{"version_added":"4","version_removed":"65","prefix":"-moz-"}],"ie":{"version_added":"10"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"2"},"safari_ios":{"version_added":"3"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"text":{"__compat":{"support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"10"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"2","partial_implementation":true,"notes":"Allows typing in the <html> container."},"safari_ios":{"version_added":"3","partial_implementation":true,"notes":"Allows typing in the <html> container."},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"vertical-align":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/vertical-align","spec_url":"https://drafts.csswg.org/css2/#propdef-vertical-align","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"4"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"visibility":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/visibility","spec_url":"https://drafts.csswg.org/css2/#visibility","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4","notes":["Internet Explorer doesn't support visibility: initial.","Up to Internet Explorer 7, descendants of hidden elements will still be invisible even if they have visibility set to visible."]},"opera":{"version_added":"4"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"collapse":{"__compat":{"description":"collapse","support":{"chrome":{"version_added":"1","notes":["Chrome treats visibility: collapse like hidden, leaving a white gap.","Chrome supports the collapse value only on <tr>, <thead>, <tbody>, and <tfoot>, but not on <col> and <colgroup> elements."]},"chrome_android":{"version_added":"18","notes":["Chrome treats visibility: collapse like hidden, leaving a white gap.","Chrome supports the collapse value only on <tr>, <thead>, <tbody>, and <tfoot>, but not on <col> and <colgroup> elements."]},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":["Firefox doesn't hide borders when hiding <col> and <colgroup> elements if border-collapse: collapse is set.","Prior to Firefox 88, collapse is not supported on ruby annotations."]},"firefox_android":{"version_added":"4","notes":["Firefox doesn't hide borders when hiding <col> and <colgroup> elements if border-collapse: collapse is set.","Prior to Firefox 88, collapse is not supported on ruby annotations."]},"ie":{"version_added":"10"},"opera":{"version_added":"4","notes":["Opera treats visibility: collapse like hidden, leaving a white gap.","Opera supports the collapse value only on <tr>, <thead>, <tbody>, and <tfoot>, but not on <col> and <colgroup> elements."]},"opera_android":{"version_added":"10.1","notes":["Opera treats visibility: collapse like hidden, leaving a white gap.","Opera supports the collapse value only on <tr>, <thead>, <tbody>, and <tfoot>, but not on <col> and <colgroup> elements."]},"safari":{"version_added":"1.3","notes":["Safari treats visibility: collapse like hidden, leaving a white gap.","Safari supports the collapse value only on <tr>, <thead>, <tbody>, and <tfoot>, but not on <col> and <colgroup> elements."]},"safari_ios":{"version_added":"1","notes":["Safari treats visibility: collapse like hidden, leaving a white gap.","Safari supports the collapse value only on <tr>, <thead>, <tbody>, and <tfoot>, but not on <col> and <colgroup> elements."]},"samsunginternet_android":{"version_added":"1.0","notes":["Samsung Internet treats visibility: collapse like hidden, leaving a white gap.","Samsung Internet supports the collapse value only on <tr>, <thead>, <tbody>, and <tfoot>, but not on <col> and <colgroup> elements."]},"webview_android":{"version_added":"≤37","notes":["WebView treats visibility: collapse like hidden, leaving a white gap.","WebView supports the collapse value only on <tr>, <thead>, <tbody>, and <tfoot>, but not on <col> and <colgroup> elements."]}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"white-space":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/white-space","spec_url":"https://drafts.csswg.org/css-text/#white-space-property","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"5.5"},"opera":{"version_added":"4"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"break-spaces":{"__compat":{"description":"break-spaces","support":{"chrome":{"version_added":"76"},"chrome_android":{"version_added":"76"},"edge":{"version_added":"79"},"firefox":{"version_added":"69"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"62"},"opera_android":{"version_added":"54"},"safari":{"version_added":"13.1"},"safari_ios":{"version_added":"13.4"},"samsunginternet_android":{"version_added":"12.0"},"webview_android":{"version_added":"76"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"nowrap":{"__compat":{"description":"nowrap","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"5.5"},"opera":{"version_added":"4"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"pre":{"__compat":{"description":"pre","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"6"},"opera":{"version_added":"4"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"pre-line":{"__compat":{"description":"pre-line","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3.5"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"9.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"pre-wrap":{"__compat":{"description":"pre-wrap","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":[{"version_added":"3"},{"prefix":"-moz-","version_added":"1","version_removed":"3.6"}],"firefox_android":{"version_added":"4"},"ie":{"version_added":"8","notes":"From Internet Explorer 5.5 to 7, word-wrap: break-word; can be used for line breaks in pre elements."},"opera":{"version_added":"8"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"svg_support":{"__compat":{"description":"Support in SVG","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"79"},"firefox":{"version_added":"36"},"firefox_android":{"version_added":"36"},"ie":{"version_added":"10"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"textarea_support":{"__compat":{"description":"Support on <textarea>","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"36"},"firefox_android":{"version_added":"36"},"ie":{"version_added":"5.5"},"opera":{"version_added":"4"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"widows":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/widows","spec_url":["https://drafts.csswg.org/css-break/#widows-orphans","https://drafts.csswg.org/css-multicol/#filling-columns"],"support":{"chrome":{"version_added":"25"},"chrome_android":{"version_added":"25"},"edge":{"version_added":"12"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":"8"},"opera":{"version_added":"9.2"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/width","spec_url":"https://drafts.csswg.org/css-sizing-4/#width-height-keywords","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"animatable":{"__compat":{"description":"Animatable","support":{"chrome":{"version_added":"26"},"chrome_android":{"version_added":"26"},"edge":{"version_added":"12"},"firefox":{"version_added":"16"},"firefox_android":{"version_added":"16"},"ie":{"version_added":"11"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"6.1"},"safari_ios":{"version_added":"6.1"},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"fill":{"__compat":{"description":"fill","support":{"chrome":{"version_added":"46"},"chrome_android":{"version_added":"46"},"edge":{"version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"33"},"opera_android":{"version_added":"33"},"safari":{"version_added":"12"},"safari_ios":{"version_added":"12"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"46"}},"status":{"experimental":true,"standard_track":false,"deprecated":false}}},"fit-content":{"__compat":{"spec_url":"https://drafts.csswg.org/css-sizing-4/#valdef-width-fit-content","description":"fit-content","support":{"chrome":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"22"},{"alternative_name":"intrinsic","version_added":"1","version_removed":"48"}],"chrome_android":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"25"},{"alternative_name":"intrinsic","version_added":"18","version_removed":"48"}],"edge":[{"version_added":"79"},{"prefix":"-webkit-","version_added":"79"}],"firefox":{"prefix":"-moz-","version_added":"3"},"firefox_android":{"prefix":"-moz-","version_added":"4"},"ie":{"version_added":false},"opera":[{"version_added":"33"},{"prefix":"-webkit-","version_added":"15"},{"alternative_name":"intrinsic","version_added":"15","version_removed":"35"}],"opera_android":[{"version_added":"33"},{"prefix":"-webkit-","version_added":"14"},{"alternative_name":"intrinsic","version_added":"14","version_removed":"35"}],"safari":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"7"},{"alternative_name":"intrinsic","version_added":"2"}],"safari_ios":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"7"},{"alternative_name":"intrinsic","version_added":"1"}],"samsunginternet_android":[{"version_added":"5.0"},{"prefix":"-webkit-","version_added":"1.5"},{"alternative_name":"intrinsic","version_added":"1.0","version_removed":"5.0"}],"webview_android":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"≤37"},{"alternative_name":"intrinsic","version_added":"1","version_removed":"48"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"fit-content_function":{"__compat":{"description":"fit-content()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"firefox_android":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"max-content":{"__compat":{"spec_url":"https://drafts.csswg.org/css-sizing-3/#valdef-width-max-content","description":"max-content","support":{"chrome":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"22"}],"chrome_android":{"version_added":"46"},"edge":[{"version_added":"79"},{"prefix":"-webkit-","version_added":"79"}],"firefox":[{"version_added":"66"},{"version_added":"3","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"4","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":[{"version_added":"11"},{"alternative_name":"intrinsic","version_added":"2"}],"safari_ios":[{"version_added":"11"},{"alternative_name":"intrinsic","version_added":"1"}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"46"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"min-content":{"__compat":{"spec_url":"https://drafts.csswg.org/css-sizing-3/#valdef-width-min-content","description":"min-content","support":{"chrome":[{"version_added":"46"},{"alternative_name":"min-intrinsic","version_added":"1","version_removed":"48"}],"chrome_android":[{"version_added":"46"},{"alternative_name":"min-intrinsic","version_added":"18","version_removed":"48"}],"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"3","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"4","prefix":"-moz-"}],"ie":{"version_added":false},"opera":[{"version_added":"33"},{"alternative_name":"min-intrinsic","version_added":"15","version_removed":"35"}],"opera_android":[{"version_added":"33"},{"alternative_name":"min-intrinsic","version_added":"14","version_removed":"35"}],"safari":[{"version_added":"11"},{"alternative_name":"min-intrinsic","version_added":"2"}],"safari_ios":[{"version_added":"11"},{"alternative_name":"min-intrinsic","version_added":"1"}],"samsunginternet_android":[{"version_added":"5.0"},{"alternative_name":"min-intrinsic","version_added":"1.0","version_removed":"5.0"}],"webview_android":[{"version_added":"46"},{"alternative_name":"min-intrinsic","version_added":"1","version_removed":"48"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"stretch":{"__compat":{"description":"stretch","support":{"chrome":{"alternative_name":"-webkit-fill-available","version_added":"22"},"chrome_android":{"alternative_name":"-webkit-fill-available","version_added":"25"},"edge":{"alternative_name":"-webkit-fill-available","version_added":"79"},"firefox":{"alternative_name":"-moz-available","version_added":"3"},"firefox_android":{"alternative_name":"-moz-available","version_added":"4"},"ie":{"version_added":false},"opera":{"alternative_name":"-webkit-fill-available","version_added":"15"},"opera_android":{"alternative_name":"-webkit-fill-available","version_added":"14"},"safari":{"alternative_name":"-webkit-fill-available","version_added":"6.1"},"safari_ios":{"alternative_name":"-webkit-fill-available","version_added":"6.1"},"samsunginternet_android":{"version_added":"5.0","alternative_name":"-webkit-fill-available"},"webview_android":{"alternative_name":"-webkit-fill-available","version_added":"4.4"}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}}},"will-change":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/will-change","spec_url":"https://drafts.csswg.org/css-will-change/#will-change","support":{"chrome":{"version_added":"36"},"chrome_android":{"version_added":"36"},"edge":{"version_added":"79"},"firefox":{"version_added":"36"},"firefox_android":{"version_added":"36"},"ie":{"version_added":false},"opera":{"version_added":"24"},"opera_android":{"version_added":"24"},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":"3.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"word-break":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/word-break","spec_url":"https://drafts.csswg.org/css-text/#word-break-property","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"15"},"firefox_android":{"version_added":"15"},"ie":[{"version_added":"5.5","notes":"No version of Internet Explorer supports the initial value."},{"prefix":"-ms-","version_added":"8","notes":"Don't use -ms-word-break, which is a synonym for word-break."}],"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"break-word":{"__compat":{"description":"break-word","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"67"},"firefox_android":{"version_added":"67"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"keep-all":{"__compat":{"description":"keep-all","support":{"chrome":{"version_added":"44"},"chrome_android":{"version_added":"44"},"edge":{"version_added":"12"},"firefox":{"version_added":"15"},"firefox_android":{"version_added":"15"},"ie":{"version_added":"5.5"},"opera":{"version_added":"31"},"opera_android":{"version_added":"32"},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":"4.0"},"webview_android":{"version_added":"44"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"word-spacing":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/word-spacing","spec_url":"https://drafts.csswg.org/css-text/#word-spacing-property","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"6"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"percentages":{"__compat":{"description":"<percentage> values","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"6.1"},"safari_ios":{"version_added":"6.1"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"svg":{"__compat":{"description":"SVG support","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"72"},"firefox_android":{"version_added":false},"ie":{"version_added":"9"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"writing-mode":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/writing-mode","spec_url":"https://drafts.csswg.org/css-writing-modes/#block-flow","support":{"chrome":[{"version_added":"48"},{"prefix":"-webkit-","version_added":"8"}],"chrome_android":[{"version_added":"48"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":{"version_added":"41","notes":"Firefox 42 added support for bidirectional and RTL scripts in vertical modes."},"firefox_android":{"version_added":"41","notes":"Firefox 42 added support for bidirectional and RTL scripts in vertical modes."},"ie":[{"version_added":"9","notes":"Internet Explorer's implementation differs from the specification."},{"prefix":"-ms-","version_added":"9","notes":"Internet Explorer's implementation differs from the specification."}],"opera":[{"version_added":"35"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"35"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"10.1"},{"prefix":"-webkit-","version_added":"5.1"}],"safari_ios":[{"version_added":"10.3"},{"prefix":"-webkit-","version_added":"5"}],"samsunginternet_android":[{"version_added":"5.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"48"},{"prefix":"-webkit-","version_added":"3"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"horizontal_vertical_values":{"__compat":{"description":"horizontal-tb, vertical-lr, and vertical-rl","support":{"chrome":{"version_added":"48"},"chrome_android":{"version_added":"48"},"edge":{"version_added":"79"},"firefox":{"version_added":"43"},"firefox_android":{"version_added":"43"},"ie":{"version_added":false},"opera":{"version_added":"35"},"opera_android":{"version_added":"35"},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"48"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"sideways_values":{"__compat":{"description":"sideways-lr and sideways-rl","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"43"},"firefox_android":{"version_added":"43"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"svg_values":{"__compat":{"description":"lr, lr-tb, rl, rl-tb, tb, and tb-rl","support":{"chrome":{"version_added":"48"},"chrome_android":{"version_added":"48"},"edge":{"version_added":"12"},"firefox":{"version_added":"43"},"firefox_android":{"version_added":"43"},"ie":{"version_added":"9"},"opera":{"version_added":"35"},"opera_android":{"version_added":"35"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"48"}},"status":{"experimental":false,"standard_track":true,"deprecated":true}}}},"z-index":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/z-index","spec_url":"https://drafts.csswg.org/css2/#z-index","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"4"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"negative_values":{"__compat":{"description":"Negative values","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"4"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"zoom":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/zoom","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":false,"notes":"See bug 390936."},"firefox_android":{"version_added":false,"notes":"See bug 390936."},"ie":{"version_added":"5.5"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3.1"},"safari_ios":{"version_added":"3"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}},"reset":{"__compat":{"description":"The reset value","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/zoom#Values","support":{"chrome":{"version_added":"1","version_removed":"59"},"chrome_android":{"version_added":"18","version_removed":"59"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"46"},"opera_android":{"version_added":"14","version_removed":"43"},"safari":{"version_added":"3.1"},"safari_ios":{"version_added":"3"},"samsunginternet_android":{"version_added":"1.0","version_removed":"7.0"},"webview_android":{"version_added":"≤37","version_removed":"59"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}}}} \ No newline at end of file +{"-moz-binding":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-moz-binding","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1","version_removed":"67","notes":["XBL is deprecated and being removed. See bug 1397874.","Available only in chrome and UA style sheets."]},"firefox_android":{"version_added":"4","version_removed":"67","notes":"Available only in chrome and UA style sheets."},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"-moz-context-properties":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-moz-context-properties","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"55","flags":[{"type":"preference","name":"svg.context-properties.content.enabled","value_to_set":"true"}],"notes":"With the preference set to false, the property still works with SVGs via chrome:// or resource:// URLs"},"firefox_android":{"version_added":"55","flags":[{"type":"preference","name":"svg.context-properties.content.enabled","value_to_set":"true"}],"notes":"With the preference set to false, the property still works with SVGs via chrome:// or resource:// URLs"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":false,"deprecated":false}}},"-moz-float-edge":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-moz-float-edge","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"-moz-force-broken-image-icon":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-moz-force-broken-image-icon","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"-moz-image-region":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-moz-image-region","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-moz-orient":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-moz-orient","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"6"},"firefox_android":{"version_added":"6"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":false}},"auto":{"__compat":{"description":"auto value","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"21","version_removed":"40","notes":"The auto value was equivalent to horizontal."},"firefox_android":{"version_added":"21","version_removed":"40","notes":"The auto value was equivalent to horizontal."},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"inline_and_block":{"__compat":{"description":"inline and block values","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"40"},"firefox_android":{"version_added":"40"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}}},"-moz-outline-radius-bottomleft":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-moz-outline-radius-bottomleft","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1","version_removed":"88","notes":"From Firefox 88, outline now follows the shape created by border-radius automatically, so this property is no longer needed (see bug 1694146)."},"firefox_android":{"version_added":"4","version_removed":"88","notes":"From Firefox 88, outline now follows the shape created by border-radius automatically, so this property is no longer needed (see bug 1694146)."},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"-moz-outline-radius-bottomright":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-moz-outline-radius-bottomright","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1","version_removed":"88","notes":"From Firefox 88, outline now follows the shape created by border-radius automatically, so this property is no longer needed (see bug 1694146)."},"firefox_android":{"version_added":"4","version_removed":"88","notes":"From Firefox 88, outline now follows the shape created by border-radius automatically, so this property is no longer needed (see bug 1694146)."},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"-moz-outline-radius-topleft":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-moz-outline-radius-topleft","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1","version_removed":"88","notes":"From Firefox 88, outline now follows the shape created by border-radius automatically, so this property is no longer needed (see bug 1694146)."},"firefox_android":{"version_added":"4","version_removed":"88","notes":"From Firefox 88, outline now follows the shape created by border-radius automatically, so this property is no longer needed (see bug 1694146)."},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"-moz-outline-radius-topright":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-moz-outline-radius-topright","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1","version_removed":"88","notes":"From Firefox 88, outline now follows the shape created by border-radius automatically, so this property is no longer needed (see bug 1694146)."},"firefox_android":{"version_added":"4","version_removed":"88","notes":"From Firefox 88, outline now follows the shape created by border-radius automatically, so this property is no longer needed (see bug 1694146)."},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"-moz-outline-radius":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-moz-outline-radius","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1","version_removed":"88","notes":"From Firefox 88, outline now follows the shape created by border-radius automatically, so this property is no longer needed (see bug 1694146)."},"firefox_android":{"version_added":"4","version_removed":"88","notes":"From Firefox 88, outline now follows the shape created by border-radius automatically, so this property is no longer needed (see bug 1694146)."},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"-moz-user-focus":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-moz-user-focus","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-moz-user-input":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-moz-user-input","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}},"auto":{"__compat":{"support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"disabled":{"__compat":{"support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1","version_removed":"60"},"firefox_android":{"version_added":"4","version_removed":"60"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"enabled":{"__compat":{"support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1","version_removed":"60"},"firefox_android":{"version_added":"4","version_removed":"60"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"none":{"__compat":{"support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}}},"-ms-grid-column-align":{"__compat":{"support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"16"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":"10"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"-ms-grid-column-span":{"__compat":{"support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"16"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":"10"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"-ms-grid-column":{"__compat":{"support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"16"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":"10"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"-ms-grid-row-align":{"__compat":{"support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"16"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":"10"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"-ms-grid-row-span":{"__compat":{"support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"16"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":"10"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"-ms-grid-row":{"__compat":{"support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"16"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":"10"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"-ms-scrollbar-track-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-ms-scrollbar-track-color","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":[{"version_added":"5","alternative_name":"scrollbar-track-color"},{"version_added":"8"}],"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"deprecated":true,"experimental":false,"standard_track":false}}},"-webkit-border-before":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-border-before","support":{"chrome":{"version_added":"8"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"15"},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-box-reflect":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-box-reflect","support":{"chrome":{"version_added":"4"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"4"},"safari_ios":{"version_added":"3.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2.2"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-line-clamp":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-line-clamp","spec_url":"https://drafts.csswg.org/css-overflow/#webkit-line-clamp","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"17"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"-webkit-mask-attachment":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-mask-attachment","support":{"chrome":{"version_added":"1","version_removed":"24"},"chrome_android":{"version_added":"18","version_removed":"25"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"4","version_removed":"7"},"safari_ios":{"version_added":"3.2","version_removed":"7"},"samsunginternet_android":{"version_added":"1.0","version_removed":"1.5"},"webview_android":{"version_added":"2","version_removed":"4.4"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-mask-box-image":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-mask-box-image","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"18"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3.1"},"safari_ios":{"version_added":"2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-mask-composite":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-mask-composite","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"18"},"firefox":{"version_added":"53"},"firefox_android":{"version_added":"53"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3.1"},"safari_ios":{"version_added":"2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-mask-position-x":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-mask-position-x","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"18"},"firefox":{"version_added":"49"},"firefox_android":{"version_added":"49"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3.1"},"safari_ios":{"version_added":"2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-mask-position-y":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-mask-position-y","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"18"},"firefox":{"version_added":"49"},"firefox_android":{"version_added":"49"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3.1"},"safari_ios":{"version_added":"2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-mask-repeat-x":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-mask-repeat-x","support":{"chrome":{"version_added":"3"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"18"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5","version_removed":"15"},"safari_ios":{"version_added":"5","version_removed":"15"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-mask-repeat-y":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-mask-repeat-y","support":{"chrome":{"version_added":"3"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"18"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"15"},"safari":{"version_added":"5","version_removed":"15"},"safari_ios":{"version_added":"5","version_removed":"15"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-overflow-scrolling":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-overflow-scrolling","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":"5","version_removed":"13"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-print-color-adjust":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-print-color-adjust","support":{"chrome":{"version_added":"17","notes":["Chrome does not print backgrounds of the <body> element. If this property is set to exact for the <body> element, it will apply only to its descendants.","Before Chrome 26, if background images are clipped (for example, when using background-image sprites) and -webkit-print-color-adjust is set to exact, then backgrounds will appear distorted when printed. Solid backgrounds and background images that are not clipped (i.e., backgrounds that have narrower and shorter than the element to which they are applied) are printed correctly. See Chromium bug 131054."]},"chrome_android":{"version_added":"18","notes":["Chrome does not print backgrounds of the <body> element. If this property is set to exact for the <body> element, it will apply only to its descendants.","Before Chrome 26, if background images are clipped (for example, when using background-image sprites) and -webkit-print-color-adjust is set to exact, then backgrounds will appear distorted when printed. Solid backgrounds and background images that are not clipped (i.e., backgrounds that have narrower and shorter than the element to which they are applied) are printed correctly. See Chromium bug 131054."]},"edge":{"version_added":"79","notes":"Edge does not print backgrounds of the <body> element. If this property is set to exact for the <body> element, it will apply only to its descendants."},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","notes":"Opera does not print backgrounds of the <body> element. If this property is set to exact for the <body> element, it will apply only to its descendants."},"opera_android":{"version_added":"15","notes":"Opera does not print backgrounds of the <body> element. If this property is set to exact for the <body> element, it will apply only to its descendants."},"safari":{"version_added":"6","notes":"Safari does not print backgrounds of the <body> element. If this property is set to exact for the <body> element, it will apply only to its descendants."},"safari_ios":{"version_added":"6","notes":"Safari does not print backgrounds of the <body> element. If this property is set to exact for the <body> element, it will apply only to its descendants."},"samsunginternet_android":{"version_added":"1.0","notes":["Samsung Internet does not print backgrounds of the <body> element. If this property is set to exact for the <body> element, it will apply only to its descendants.","Before Chrome 26, if background images are clipped (for example, when using background-image sprites) and -webkit-print-color-adjust is set to exact, then backgrounds will appear distorted when printed. Solid backgrounds and background images that are not clipped (i.e., backgrounds that have narrower and shorter than the element to which they are applied) are printed correctly. See Chromium bug 131054."]},"webview_android":{"version_added":"37","notes":"WebView does not print backgrounds of the <body> element. If this property is set to exact for the <body> element, it will apply only to its descendants."}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-tap-highlight-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-tap-highlight-color","support":{"chrome":{"version_added":"16"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":false},"safari_ios":{"version_added":"4"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-text-fill-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-text-fill-color","spec_url":"https://compat.spec.whatwg.org/#the-webkit-text-fill-color","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"49"},"firefox_android":{"version_added":"49"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"15"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-text-security":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-text-security","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-text-stroke-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-text-stroke-color","spec_url":"https://compat.spec.whatwg.org/#the-webkit-text-stroke-color","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"15"},"firefox":{"version_added":"49"},"firefox_android":{"version_added":"49"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"15"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-text-stroke-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-text-stroke-width","spec_url":"https://compat.spec.whatwg.org/#the-webkit-text-stroke-width","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"15"},"firefox":{"version_added":"49"},"firefox_android":{"version_added":"49"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"15"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"38"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-text-stroke":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-text-stroke","spec_url":"https://compat.spec.whatwg.org/#the-webkit-text-stroke","support":{"chrome":{"version_added":"4"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"15"},"firefox":{"version_added":"49"},"firefox_android":{"version_added":"49"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"4"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"-webkit-touch-callout":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-webkit-touch-callout","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":"2"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"accent-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/accent-color","spec_url":"https://drafts.csswg.org/css-ui/#widget-accent","support":{"chrome":[{"version_added":"93"},{"version_added":"91","version_removed":"93","flags":[{"name":"#enable-experimental-web-platform-features","type":"preference","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"93"},{"version_added":"91","version_removed":"93","flags":[{"name":"#enable-experimental-web-platform-features","type":"preference","value_to_set":"enabled"}]}],"edge":[{"version_added":"93"},{"version_added":"91","version_removed":"93","flags":[{"name":"#enable-experimental-web-platform-features","type":"preference","value_to_set":"enabled"}]}],"firefox":[{"version_added":"92"},{"version_added":"90","version_removed":"92","flags":[{"type":"preference","name":"layout.css.accent-color.enabled","value_to_set":"enabled"}],"notes":"Enabled by default in Firefox Nightly."}],"firefox_android":{"version_added":"92"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":"93"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"align-content":{"flex_context":{"__compat":{"description":"Supported in Flex Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/align-content","spec_url":["https://drafts.csswg.org/css-align/#align-justify-content","https://drafts.csswg.org/css-flexbox/#align-content-property"],"support":{"chrome":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"21"}],"chrome_android":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"25"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"28"},{"prefix":"-webkit-","version_added":"49"}],"firefox_android":[{"version_added":"28"},{"prefix":"-webkit-","version_added":"49"}],"ie":{"version_added":"11"},"opera":{"version_added":"12.1"},"opera_android":{"version_added":"12.1"},"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":[{"version_added":"2.0"},{"prefix":"-webkit-","version_added":"1.5"}],"webview_android":[{"version_added":"4.4"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"baseline":{"__compat":{"description":"baseline","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"first_last_baseline":{"__compat":{"description":"first baseline and last baseline","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"left_right":{"__compat":{"description":"left and right","support":{"chrome":{"version_added":"93"},"chrome_android":{"version_added":"93"},"edge":{"version_added":"93"},"firefox":{"version_added":"52","version_removed":"60","notes":"align-content no longer accepts the left and right values"},"firefox_android":{"version_added":"52","version_removed":"60","notes":"align-content no longer accepts the left and right values"},"ie":{"version_added":false},"opera":{"version_added":false,"notes":"This value is recognized, but has no effect."},"opera_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"webview_android":{"version_added":"93"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"safe_unsafe":{"__compat":{"description":"safe and unsafe","support":{"chrome":{"version_added":false,"notes":"This value is recognized, but has no effect."},"chrome_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"edge":{"version_added":false,"notes":"This value is recognized, but has no effect."},"firefox":{"version_added":"63"},"firefox_android":{"version_added":"63"},"ie":{"version_added":false},"opera":{"version_added":false,"notes":"This value is recognized, but has no effect."},"opera_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"safari":{"version_added":false,"notes":"This value is recognized, but has no effect."},"safari_ios":{"version_added":false,"notes":"This value is recognized, but has no effect."},"samsunginternet_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"webview_android":{"version_added":false,"notes":"This value is recognized, but has no effect."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"space-evenly":{"__compat":{"description":"space-evenly","support":{"chrome":{"version_added":"60"},"chrome_android":{"version_added":"60"},"edge":{"version_added":"79"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"47"},"opera_android":{"version_added":"44"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"8.0"},"webview_android":{"version_added":"60"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"start_end":{"__compat":{"description":"start and end","support":{"chrome":{"version_added":"93"},"chrome_android":{"version_added":"93"},"edge":{"version_added":"93"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":false,"notes":"This value is recognized, but has no effect."},"opera_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"webview_android":{"version_added":"93"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"stretch":{"__compat":{"description":"stretch","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"grid_context":{"__compat":{"description":"Supported in Grid Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/align-content","spec_url":["https://drafts.csswg.org/css-align/#align-justify-content","https://drafts.csswg.org/css-flexbox/#align-content-property"],"support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"52"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.2"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"align-items":{"flex_context":{"__compat":{"description":"Supported in Flex Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/align-items","spec_url":["https://drafts.csswg.org/css-align/#align-items-property","https://drafts.csswg.org/css-flexbox/#align-items-property"],"support":{"chrome":[{"version_added":"52"},{"version_added":"29","partial_implementation":true,"notes":"Older versions of the specification treat absolute positioned children as though they are a 0 by 0 flex item. Later specification versions take the children out of the flow and set their positions based on align and justify properties. Chrome implements the new behavior beginning with Chrome 52."},{"prefix":"-webkit-","version_added":"21"}],"chrome_android":[{"version_added":"52"},{"version_added":"29","partial_implementation":true,"notes":"Older versions of the specification treat absolute positioned children as though they are a 0 by 0 flex item. Later specification versions take the children out of the flow and set their positions based on align and justify properties. Chrome implements the new behavior beginning with Chrome 52."},{"prefix":"-webkit-","version_added":"25"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"20","notes":"Multi-line flexbox has been supported since Firefox 28."},{"prefix":"-webkit-","version_added":"49"}],"firefox_android":[{"version_added":"20","notes":"Multi-line flexbox has been supported since Firefox 28."},{"prefix":"-webkit-","version_added":"49"}],"ie":{"version_added":"11","notes":"In Internet Explorer 10 and 11, if column flex items have align-items: center; set on them and their content is too large, then they will overflow the bounds of their container. See Flexbug #2."},"opera":{"version_added":"12.1"},"opera_android":{"version_added":"12.1"},"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":[{"version_added":"6.0"},{"version_added":"2.0","partial_implementation":true,"notes":"Older versions of the specification treat absolute positioned children as though they are a 0 by 0 flex item. Later specification versions take the children out of the flow and set their positions based on align and justify properties. Chrome implements the new behavior beginning with Samsung Internet 6.0."},{"prefix":"-webkit-","version_added":"1.5"}],"webview_android":[{"version_added":"52"},{"version_added":"4.4","partial_implementation":true,"notes":"Older versions of the specification treat absolute positioned children as though they are a 0 by 0 flex item. Later specification versions take the children out of the flow and set their positions based on align and justify properties. Chrome implements the new behavior beginning with Chrome 52."},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"first_last_baseline":{"__compat":{"description":"first baseline and last baseline","support":{"chrome":{"version_added":"59"},"chrome_android":{"version_added":"59"},"edge":{"version_added":"79"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":"46"},"opera_android":{"version_added":"43"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"59"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"left_right":{"__compat":{"description":"left and right","support":{"chrome":{"version_added":"93"},"chrome_android":{"version_added":"93"},"edge":{"version_added":"93"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":"93"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"safe_unsafe":{"__compat":{"description":"safe and unsafe","support":{"chrome":{"version_added":false,"notes":"This value is recognized, but has no effect."},"chrome_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"edge":{"version_added":false,"notes":"This value is recognized, but has no effect."},"firefox":{"version_added":"63"},"firefox_android":{"version_added":"63"},"ie":{"version_added":false},"opera":{"version_added":false,"notes":"This value is recognized, but has no effect."},"opera_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"safari":{"version_added":false,"notes":"This value is recognized, but has no effect."},"safari_ios":{"version_added":false,"notes":"This value is recognized, but has no effect."},"samsunginternet_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"webview_android":{"version_added":false,"notes":"This value is recognized, but has no effect."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"start_end":{"__compat":{"description":"start and end","support":{"chrome":{"version_added":"93"},"chrome_android":{"version_added":"93"},"edge":{"version_added":"93"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":"93"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"grid_context":{"__compat":{"description":"Supported in Grid Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/align-items","spec_url":["https://drafts.csswg.org/css-align/#align-items-property","https://drafts.csswg.org/css-flexbox/#align-items-property"],"support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"52"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.2"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"start_end":{"__compat":{"description":"start and end","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}}},"align-self":{"flex_context":{"__compat":{"description":"Supported in Flex Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/align-self","spec_url":["https://drafts.csswg.org/css-align/#align-self-property","https://drafts.csswg.org/css-flexbox/#align-items-property"],"support":{"chrome":[{"version_added":"36"},{"version_added":"29","partial_implementation":true,"notes":"Older versions of the specification treat absolute positioned children as though they are a 0 by 0 flex item. Later specification versions take the children out of the flow and set their positions based on align and justify properties. Chrome implements the new behavior beginning with Chrome 52."},{"prefix":"-webkit-","version_added":"21"}],"chrome_android":[{"version_added":"36"},{"version_added":"29","partial_implementation":true,"notes":"Older versions of the specification treat absolute positioned children as though they are a 0 by 0 flex item. Later specification versions take the children out of the flow and set their positions based on align and justify properties. Chrome implements the new behavior beginning with Chrome 52."},{"prefix":"-webkit-","version_added":"25"}],"edge":{"version_added":"12"},"firefox":[{"version_added":"20","notes":"Before Firefox 27, only single-line flexbox is supported."},{"prefix":"-webkit-","version_added":"49"}],"firefox_android":[{"version_added":"20","notes":"Before Firefox 27, only single-line flexbox is supported."},{"prefix":"-webkit-","version_added":"49"}],"ie":{"version_added":"11"},"opera":{"version_added":"12.1"},"opera_android":{"version_added":"12.1"},"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"6.1"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"6.1"}],"samsunginternet_android":[{"version_added":"3.0"},{"version_added":"2.0","partial_implementation":true,"notes":"Older versions of the specification treat absolute positioned children as though they are a 0 by 0 flex item. Later specification versions take the children out of the flow and set their positions based on align and justify properties. Chrome implements the new behavior beginning with Samsung Internet 6.0."},{"prefix":"-webkit-","version_added":"1.5"}],"webview_android":[{"version_added":"37"},{"version_added":"4.4","partial_implementation":true,"notes":"Older versions of the specification treat absolute positioned children as though they are a 0 by 0 flex item. Later specification versions take the children out of the flow and set their positions based on align and justify properties. Chrome implements the new behavior beginning with Chrome 52."},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"baseline":{"__compat":{"description":"baseline","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"first_last_baseline":{"__compat":{"description":"first baseline and last baseline","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"11.1"},"safari_ios":{"version_added":"11.3"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"left_right":{"__compat":{"description":"left and right","support":{"chrome":{"version_added":"93"},"chrome_android":{"version_added":"93"},"edge":{"version_added":"93"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":"93"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"safe_unsafe":{"__compat":{"description":"safe and unsafe","support":{"chrome":{"version_added":false,"notes":"This value is recognized, but has no effect."},"chrome_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"edge":{"version_added":false,"notes":"This value is recognized, but has no effect."},"firefox":{"version_added":"63"},"firefox_android":{"version_added":"63"},"ie":{"version_added":false},"opera":{"version_added":false,"notes":"This value is recognized, but has no effect."},"opera_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"safari":{"version_added":false,"notes":"This value is recognized, but has no effect."},"safari_ios":{"version_added":false,"notes":"This value is recognized, but has no effect."},"samsunginternet_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"webview_android":{"version_added":false,"notes":"This value is recognized, but has no effect."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"start_end":{"__compat":{"description":"start and end","support":{"chrome":{"version_added":"93"},"chrome_android":{"version_added":"93"},"edge":{"version_added":"93"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"93"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"stretch":{"__compat":{"description":"stretch","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"grid_context":{"__compat":{"description":"Supported in Grid Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/align-self","spec_url":["https://drafts.csswg.org/css-align/#align-self-property","https://drafts.csswg.org/css-flexbox/#align-items-property"],"support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"52"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"prefix":"-ms-","version_added":"10","partial_implementation":true,"notes":"Internet Explorer 10 and 11 have the property -ms-grid-row-align, which acts in a similar way to align-self."},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.2"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"align-tracks":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/align-tracks","spec_url":"https://drafts.csswg.org/css-grid-3/#tracks-alignment","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"77","flags":[{"type":"preference","name":"layout.css.grid-template-masonry-value.enabled","value_to_set":"true"}]},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"all":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/all","spec_url":"https://drafts.csswg.org/css-cascade/#all-shorthand","support":{"chrome":{"version_added":"37"},"chrome_android":{"version_added":"37"},"edge":{"version_added":"79"},"firefox":{"version_added":"27"},"firefox_android":{"version_added":"27"},"ie":{"version_added":false},"opera":{"version_added":"24"},"opera_android":{"version_added":"24"},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":"3.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"revert":{"__compat":{"description":"revert","support":{"chrome":{"version_added":"84"},"chrome_android":{"version_added":"84"},"edge":{"version_added":"84"},"firefox":{"version_added":"67"},"firefox_android":{"version_added":"67"},"ie":{"version_added":false},"opera":{"version_added":"70"},"opera_android":{"version_added":false},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"84"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"alt":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/alt","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"8"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"8"}],"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"animation-delay":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/animation-delay","spec_url":"https://drafts.csswg.org/css-animations/#animation-delay","support":{"chrome":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"3"}],"chrome_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"18"}],"edge":{"version_added":"12"},"firefox":[{"version_added":"16","notes":"Before Firefox 57, Firefox does not repaint elements outside the viewport that are animated into the viewport with a delay. This bug affects only some platforms, such as Windows."},{"version_added":"49","prefix":"-webkit-"},{"prefix":"-moz-","version_added":"5"}],"firefox_android":[{"version_added":"16"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-moz-","version_added":"5"}],"ie":{"version_added":"10"},"opera":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"15"},{"version_added":"12.1","version_removed":"15"},{"prefix":"-o-","version_added":"12","version_removed":"15"}],"opera_android":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"14"},{"version_added":"12.1","version_removed":"14"},{"prefix":"-o-","version_added":"12","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"4"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3.2"}],"samsunginternet_android":[{"version_added":"4.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"animation-direction":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/animation-direction","spec_url":"https://drafts.csswg.org/css-animations/#animation-direction","support":{"chrome":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"3"}],"chrome_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"version_added":"49","prefix":"-webkit-"},{"prefix":"-moz-","version_added":"5"}],"firefox_android":[{"version_added":"16"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-moz-","version_added":"5"}],"ie":{"version_added":"10"},"opera":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"15"},{"version_added":"12.1","version_removed":"15"},{"prefix":"-o-","version_added":"12","version_removed":"15"}],"opera_android":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"14"},{"version_added":"12.1","version_removed":"14"},{"prefix":"-o-","version_added":"12","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"4"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3.2"}],"samsunginternet_android":[{"version_added":"4.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"alternate-reverse":{"__compat":{"description":"alternate-reverse","support":{"chrome":{"version_added":"19"},"chrome_android":{"version_added":"25"},"edge":{"version_added":"12"},"firefox":{"version_added":"16"},"firefox_android":{"version_added":"16"},"ie":{"version_added":"10"},"opera":{"version_added":"12.1"},"opera_android":{"version_added":"12.1"},"safari":{"version_added":"6"},"safari_ios":{"version_added":"6"},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"reverse":{"__compat":{"description":"reverse","support":{"chrome":{"version_added":"19"},"chrome_android":{"version_added":"25"},"edge":{"version_added":"12"},"firefox":{"version_added":"16"},"firefox_android":{"version_added":"16"},"ie":{"version_added":"10"},"opera":{"version_added":"12.1"},"opera_android":{"version_added":"12.1"},"safari":{"version_added":"6"},"safari_ios":{"version_added":"6"},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"animation-duration":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/animation-duration","spec_url":"https://drafts.csswg.org/css-animations/#animation-duration","support":{"chrome":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"3"}],"chrome_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"version_added":"49","prefix":"-webkit-"},{"prefix":"-moz-","version_added":"5"}],"firefox_android":[{"version_added":"16"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-moz-","version_added":"5"}],"ie":{"version_added":"10","partial_implementation":true,"notes":"Once the element has loaded, changing the value of this property has no effect."},"opera":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"15"},{"version_added":"12.1","version_removed":"15"},{"prefix":"-o-","version_added":"12","version_removed":"15"}],"opera_android":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"14"},{"version_added":"12.1","version_removed":"14"},{"prefix":"-o-","version_added":"12","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"4"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"4.2"}],"samsunginternet_android":[{"version_added":"4.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"2"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"animation-fill-mode":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/animation-fill-mode","spec_url":"https://drafts.csswg.org/css-animations/#animation-fill-mode","support":{"chrome":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"3"}],"chrome_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"version_added":"49","prefix":"-webkit-"},{"prefix":"-moz-","version_added":"5"}],"firefox_android":[{"version_added":"16"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-moz-","version_added":"5"}],"ie":{"version_added":"10"},"opera":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"15"},{"version_added":"12.1","version_removed":"15"},{"prefix":"-o-","version_added":"12","version_removed":"15"}],"opera_android":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"14"},{"version_added":"12.1","version_removed":"14"},{"prefix":"-o-","version_added":"12","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"5"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"4"}],"samsunginternet_android":[{"version_added":"4.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"animation-iteration-count":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/animation-iteration-count","spec_url":"https://drafts.csswg.org/css-animations/#animation-iteration-count","support":{"chrome":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"3"}],"chrome_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"version_added":"49","prefix":"-webkit-"},{"prefix":"-moz-","version_added":"5"}],"firefox_android":[{"version_added":"16"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-moz-","version_added":"5"}],"ie":{"version_added":"10"},"opera":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"15"},{"version_added":"12.1","version_removed":"15"},{"prefix":"-o-","version_added":"12","version_removed":"15"}],"opera_android":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"14"},{"version_added":"12.1","version_removed":"14"},{"prefix":"-o-","version_added":"12","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"4"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3.2"}],"samsunginternet_android":[{"version_added":"4.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"animation-name":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/animation-name","spec_url":"https://drafts.csswg.org/css-animations/#animation-name","support":{"chrome":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"3"}],"chrome_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-moz-","version_added":"5"}],"firefox_android":[{"version_added":"16"},{"version_added":"49","prefix":"-webkit-"},{"prefix":"-moz-","version_added":"5"}],"ie":{"version_added":"10"},"opera":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"15"},{"version_added":"12.1","version_removed":"15"},{"prefix":"-o-","version_added":"12","version_removed":"15"}],"opera_android":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"14"},{"version_added":"12.1","version_removed":"14"},{"prefix":"-o-","version_added":"12","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"4"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3.2"}],"samsunginternet_android":[{"version_added":"4.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"animation-play-state":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/animation-play-state","spec_url":"https://drafts.csswg.org/css-animations/#animation-play-state","support":{"chrome":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"3"}],"chrome_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-moz-","version_added":"5"}],"firefox_android":[{"version_added":"16"},{"version_added":"49","prefix":"-webkit-"},{"prefix":"-moz-","version_added":"5"}],"ie":{"version_added":"10"},"opera":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"15"},{"version_added":"12.1","version_removed":"15"},{"prefix":"-o-","version_added":"12","version_removed":"15"}],"opera_android":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"14"},{"version_added":"12.1","version_removed":"14"},{"prefix":"-o-","version_added":"12","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"4"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"2"}],"samsunginternet_android":[{"version_added":"4.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"animation-timing-function":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/animation-timing-function","spec_url":"https://drafts.csswg.org/css-animations/#animation-timing-function","support":{"chrome":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"3"}],"chrome_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-moz-","version_added":"5"}],"firefox_android":[{"version_added":"16"},{"version_added":"49","prefix":"-webkit-"},{"prefix":"-moz-","version_added":"5"}],"ie":{"version_added":"10"},"opera":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"15"},{"version_added":"12.1","version_removed":"15"},{"prefix":"-o-","version_added":"12","version_removed":"15"}],"opera_android":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"14"},{"version_added":"12.1","version_removed":"14"},{"prefix":"-o-","version_added":"12","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"4"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3.2"}],"samsunginternet_android":[{"version_added":"4.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"jump":{"__compat":{"description":"jump- keywords for steps()","support":{"chrome":{"version_added":"77"},"chrome_android":{"version_added":"77"},"edge":{"version_added":"79"},"firefox":{"version_added":"65"},"firefox_android":{"version_added":"65"},"ie":{"version_added":false},"opera":{"version_added":"64"},"opera_android":{"version_added":"55"},"safari":{"version_added":"14"},"safari_ios":{"version_added":"14"},"samsunginternet_android":{"version_added":"12.0"},"webview_android":{"version_added":"77"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"animation":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/animation","spec_url":"https://drafts.csswg.org/css-animations/#animation","support":{"chrome":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"3"}],"chrome_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-moz-","version_added":"5"}],"firefox_android":[{"version_added":"16"},{"version_added":"49","prefix":"-webkit-"},{"prefix":"-moz-","version_added":"5"}],"ie":{"version_added":"10"},"opera":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"15"},{"version_added":"12.1","version_removed":"15"},{"prefix":"-o-","version_added":"12","version_removed":"15"}],"opera_android":[{"version_added":"30"},{"prefix":"-webkit-","version_added":"14"},{"version_added":"12.1","version_removed":"14"},{"prefix":"-o-","version_added":"12","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"4"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3.2"}],"samsunginternet_android":[{"version_added":"4.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"2","notes":"The animation-fill-mode property is not supported in Android browsers below 2.3."}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"appearance":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/appearance","spec_url":"https://drafts.csswg.org/css-ui/#appearance-switching","support":{"chrome":[{"version_added":"84"},{"version_added":"1","partial_implementation":true,"prefix":"-webkit-"}],"chrome_android":[{"version_added":"84"},{"version_added":"18","partial_implementation":true,"prefix":"-webkit-"}],"edge":[{"version_added":"84"},{"version_added":"12","partial_implementation":true,"prefix":"-webkit-"}],"firefox":[{"version_added":"80"},{"version_added":"1","partial_implementation":true,"prefix":"-moz-"},{"version_added":"64","partial_implementation":true,"prefix":"-webkit-"}],"firefox_android":[{"version_added":"80"},{"version_added":"4","partial_implementation":true,"prefix":"-moz-"},{"version_added":"64","partial_implementation":true,"prefix":"-webkit-"}],"ie":{"version_added":false},"opera":[{"version_added":"70"},{"version_added":"15","partial_implementation":true,"prefix":"-webkit-"}],"opera_android":[{"version_added":"60"},{"version_added":"14","partial_implementation":true,"prefix":"-webkit-"}],"safari":{"version_added":"3","partial_implementation":true,"prefix":"-webkit-"},"safari_ios":{"version_added":"1","partial_implementation":true,"prefix":"-webkit-"},"samsunginternet_android":[{"version_added":"14.0"},{"version_added":"1.0","partial_implementation":true,"prefix":"-webkit-"}],"webview_android":[{"version_added":"84"},{"version_added":"1","partial_implementation":true,"prefix":"-webkit-"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"auto":{"__compat":{"support":{"chrome":{"version_added":"83"},"chrome_android":{"version_added":"83"},"edge":{"version_added":"83"},"firefox":{"version_added":"80"},"firefox_android":{"version_added":"80"},"ie":{"version_added":false},"opera":{"version_added":"69"},"opera_android":{"version_added":"59"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"13.0"},"webview_android":{"version_added":"83"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"compat-auto":{"__compat":{"description":"<compat-auto> (compatibility values searchfield, textarea, push-button, slider-horizontal, checkbox, radio, square-button, menulist, listbox, meter, progress-bar, button)","support":{"chrome":[{"version_added":"83"},{"version_added":"1","partial_implementation":true}],"chrome_android":[{"version_added":"83"},{"version_added":"18","partial_implementation":true}],"edge":[{"version_added":"83"},{"version_added":"12","partial_implementation":true}],"firefox":[{"version_added":"80"},{"version_added":"1","partial_implementation":true}],"firefox_android":[{"version_added":"80"},{"version_added":"4","partial_implementation":true}],"ie":{"version_added":false},"opera":[{"version_added":"69"},{"version_added":"15","partial_implementation":true}],"opera_android":[{"version_added":"59"},{"version_added":"14","partial_implementation":true}],"safari":{"version_added":"3","partial_implementation":true},"safari_ios":{"version_added":"1","partial_implementation":true},"samsunginternet_android":{"version_added":"1.0","partial_implementation":true},"webview_android":[{"version_added":"83"},{"version_added":"1","partial_implementation":true}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"menulist-button":{"__compat":{"description":"menulist-button","support":{"chrome":[{"version_added":"83"},{"version_added":"1","partial_implementation":true}],"chrome_android":[{"version_added":"83"},{"version_added":"18","partial_implementation":true}],"edge":[{"version_added":"83"},{"version_added":"12","partial_implementation":true}],"firefox":[{"version_added":"80"},{"version_added":"1","partial_implementation":true,"notes":"See bug 1481615."}],"firefox_android":[{"version_added":"80"},{"version_added":"4","partial_implementation":true,"notes":"See bug 1481615."}],"ie":{"version_added":false},"opera":[{"version_added":"69"},{"version_added":"15","partial_implementation":true}],"opera_android":[{"version_added":"59"},{"version_added":"14","partial_implementation":true}],"safari":{"version_added":"3","partial_implementation":true},"safari_ios":{"version_added":"1","partial_implementation":true},"samsunginternet_android":[{"version_added":"13.0"},{"version_added":"1.0","partial_implementation":true}],"webview_android":[{"version_added":"83"},{"version_added":"1","partial_implementation":true}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"none":{"__compat":{"support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":[{"version_added":"54"},{"version_added":"1","partial_implementation":true,"notes":"Doesn't work with <input type=\"checkbox\"> and <input type=\"radio\">."}],"firefox_android":[{"version_added":"54"},{"version_added":"4","partial_implementation":true,"notes":"Doesn't work with <input type=\"checkbox\"> and <input type=\"radio\">."}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"3"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"textfield":{"__compat":{"support":{"chrome":[{"version_added":"83"},{"version_added":"1","partial_implementation":true}],"chrome_android":[{"version_added":"83"},{"version_added":"18","partial_implementation":true}],"edge":[{"version_added":"83"},{"version_added":"12","partial_implementation":true}],"firefox":[{"version_added":"80"},{"version_added":"1","partial_implementation":true}],"firefox_android":[{"version_added":"80"},{"version_added":"4","partial_implementation":true}],"ie":{"version_added":false},"opera":[{"version_added":"69"},{"version_added":"15","partial_implementation":true}],"opera_android":[{"version_added":"59"},{"version_added":"14","partial_implementation":true}],"safari":{"version_added":"3","partial_implementation":true},"safari_ios":{"version_added":"1","partial_implementation":true},"samsunginternet_android":[{"version_added":"13.0"},{"version_added":"1.0","partial_implementation":true}],"webview_android":[{"version_added":"83"},{"version_added":"1","partial_implementation":true}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"aspect-ratio":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/aspect-ratio","spec_url":"https://drafts.csswg.org/css-sizing-4/#aspect-ratio","support":{"chrome":[{"version_added":"88"},{"version_added":"84","flags":[{"type":"preference","name":"#enable-experimental-web-platform-features","value_to_set":"Enabled"}]}],"chrome_android":{"version_added":"88"},"edge":{"version_added":"88"},"firefox":[{"version_added":"89"},{"version_added":"83","partial_implementation":true,"notes":"Firefox 83 implements aspect-ratio for flex items.","flags":[{"type":"preference","name":"layout.css.aspect-ratio.enabled","value_to_set":"true"}]},{"version_added":"81","partial_implementation":true,"notes":"Firefox 81 implements aspect-ratio for blocks and replaced elements.","flags":[{"type":"preference","name":"layout.css.aspect-ratio.enabled","value_to_set":"true"}]}],"firefox_android":{"version_added":"89"},"ie":{"version_added":false},"opera":{"version_added":"74"},"opera_android":{"version_added":"63"},"safari":{"version_added":"15"},"safari_ios":{"version_added":"15"},"samsunginternet_android":{"version_added":"15.0"},"webview_android":{"version_added":"88"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"backdrop-filter":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/backdrop-filter","spec_url":"https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty","support":{"chrome":[{"version_added":"76"},{"version_added":"47","flags":[{"type":"preference","name":"Enable Experimental Web Platform Features"}]}],"chrome_android":[{"version_added":"76"},{"version_added":"47","flags":[{"type":"preference","name":"Enable Experimental Web Platform Features"}]}],"edge":{"version_added":"17"},"firefox":{"version_added":"70","flags":[{"type":"preference","name":"layout.css.backdrop-filter.enabled","value_to_set":"true"},{"type":"preference","name":"gfx.webrender.all","value_to_set":"true"}]},"firefox_android":{"version_added":false,"notes":"See bug 1178765."},"ie":{"version_added":false},"opera":[{"version_added":"63"},{"version_added":"34","flags":[{"type":"preference","name":"Enable Experimental Web Platform Features"}]}],"opera_android":[{"version_added":"54"},{"version_added":"34","flags":[{"type":"preference","name":"Enable Experimental Web Platform Features"}]}],"safari":{"prefix":"-webkit-","version_added":"9"},"safari_ios":{"prefix":"-webkit-","version_added":"9"},"samsunginternet_android":{"version_added":"12.0"},"webview_android":{"version_added":"76"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"backface-visibility":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/backface-visibility","spec_url":"https://drafts.csswg.org/css-transforms-2/#backface-visibility-property","support":{"chrome":[{"version_added":"36"},{"prefix":"-webkit-","version_added":"12"}],"chrome_android":[{"version_added":"36"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"prefix":"-moz-","version_added":"10"},{"version_added":"49","prefix":"-webkit-"}],"firefox_android":[{"version_added":"16"},{"prefix":"-moz-","version_added":"10"},{"version_added":"49","prefix":"-webkit-"}],"ie":{"version_added":"10"},"opera":[{"version_added":"23"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"24"},{"prefix":"-webkit-","version_added":"14"}],"safari":{"prefix":"-webkit-","version_added":"5.1"},"safari_ios":{"prefix":"-webkit-","version_added":"5"},"samsunginternet_android":[{"version_added":"3.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"37"},{"prefix":"-webkit-","version_added":"3"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"background-attachment":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/background-attachment","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-background-attachment","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"3.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"fixed":{"__compat":{"description":"fixed","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"25"},"firefox_android":{"version_added":"25"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"14"},"safari":[{"version_added":"14","partial_implementation":true,"notes":"local is recognized but has no effect due to a bug."},{"version_added":"3.1","version_removed":"14"}],"safari_ios":{"version_added":"5","partial_implementation":true,"notes":"local is recognized but has no effect."},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"local":{"__compat":{"description":"local","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"25"},"firefox_android":{"version_added":"25"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"14"},"safari":[{"version_added":"13","partial_implementation":true,"notes":"local is recognized but has no effect due to a bug."},{"version_added":"5","version_removed":"13"}],"safari_ios":[{"version_added":"13","partial_implementation":true,"notes":"local is recognized but has no effect due to a bug."},{"version_added":"4.2","version_removed":"13","notes":"If -webkit-overflow-scrolling: touch is set, then local has no effect."}],"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"multiple_backgrounds":{"__compat":{"description":"Multiple backgrounds","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3.6"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"11"},"safari":{"version_added":"1.3"},"safari_ios":{"version_added":"3.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"background-blend-mode":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/background-blend-mode","spec_url":"https://drafts.fxtf.org/compositing/#background-blend-mode","support":{"chrome":{"version_added":"35"},"chrome_android":{"version_added":"35"},"edge":{"version_added":"79"},"firefox":{"version_added":"30"},"firefox_android":{"version_added":"30"},"ie":{"version_added":false},"opera":{"version_added":"22"},"opera_android":{"version_added":"22"},"safari":{"version_added":"8"},"safari_ios":{"version_added":"8"},"samsunginternet_android":{"version_added":"3.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"background-clip":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/background-clip","spec_url":"https://drafts.csswg.org/css-backgrounds/#background-clip","support":{"chrome":[{"version_added":"1"},{"version_added":"1","version_removed":"64","prefix":"-webkit-","notes":"Chrome accepts alternate synonyms to its values: padding, border, and content."}],"chrome_android":[{"version_added":"18"},{"version_added":"18","version_removed":"64","prefix":"-webkit-","notes":"Chrome accepts alternate synonyms to its values: padding, border, and content."}],"edge":{"version_added":"12"},"firefox":[{"version_added":"4"},{"version_added":"49","prefix":"-webkit-"},{"version_added":"1","version_removed":"4","partial_implementation":true,"prefix":"-moz-","notes":"Used the -moz-background-clip: padding | border syntax."}],"firefox_android":[{"version_added":"14"},{"version_added":"49","prefix":"-webkit-"}],"ie":{"version_added":"9","notes":"In IE 7 and IE 8 of Internet Explorer, this property always behaved like background-clip: padding when overflow was hidden, auto, or scroll."},"opera":[{"version_added":"10.5"},{"version_added":"15","version_removed":"51","prefix":"-webkit-","notes":"Opera accepts alternate synonyms to its values: padding, border, and content."}],"opera_android":[{"version_added":"11"},{"version_added":"14","version_removed":"47","prefix":"-webkit-","notes":"Opera accepts alternate synonyms to its values: padding, border, and content."}],"safari":{"version_added":"3","prefix":"-webkit-","notes":"Safari accepts alternate synonyms to its values: padding, border, and content."},"safari_ios":{"version_added":"1","prefix":"-webkit-","notes":"Safari accepts alternate synonyms to its values: padding, border, and content."},"samsunginternet_android":[{"version_added":"1.0"},{"prefix":"-webkit-","version_added":"1.0","version_removed":"9.0","notes":"Chrome accepts alternate synonyms to its values: padding, border, and content."}],"webview_android":[{"version_added":"4"},{"version_added":"≤37","version_removed":"64","prefix":"-webkit-","notes":"WebView accepts alternate synonyms to its values: padding, border, and content."}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"content-box":{"__compat":{"description":"content-box","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"4"},"firefox_android":{"version_added":"14"},"ie":{"version_added":"9","notes":"In IE 7 and IE 9 of Internet Explorer, it always behaved like background-clip: padding if overflow: hidden | auto | scroll"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"11"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"text":{"__compat":{"description":"text","support":{"chrome":{"version_added":"3","partial_implementation":true,"notes":["This value is supported with the prefixed version of the property only.","According to the WebKit blog, text decorations or shadows are not included in the clipping."]},"chrome_android":{"version_added":"18","partial_implementation":true,"notes":["This value is supported with the prefixed version of the property only.","According to the WebKit blog, text decorations or shadows are not included in the clipping."]},"edge":[{"version_added":"15"},{"version_added":"12","partial_implementation":true,"notes":"Before Edge 15, this value was supported with the prefixed version of the property only."}],"firefox":{"version_added":"49"},"firefox_android":{"version_added":"49"},"ie":{"version_added":false},"opera":{"version_added":"15","partial_implementation":true,"notes":["This value is supported with the prefixed version of the property only.","According to the WebKit blog, text decorations or shadows are not included in the clipping."]},"opera_android":{"version_added":"14","partial_implementation":true,"notes":["This value is supported with the prefixed version of the property only.","According to the WebKit blog, text decorations or shadows are not included in the clipping."]},"safari":{"version_added":"4","partial_implementation":true,"notes":["This value is supported with the prefixed version of the property only.","According to the WebKit blog, text decorations or shadows are not included in the clipping."]},"safari_ios":{"version_added":"3.2","partial_implementation":true,"notes":["This value is supported with the prefixed version of the property only.","According to the WebKit blog, text decorations or shadows are not included in the clipping."]},"samsunginternet_android":{"version_added":"1.0","partial_implementation":true,"notes":["This value is supported with the prefixed version of the property only.","According to the WebKit blog, text decorations or shadows are not included in the clipping."]},"webview_android":{"version_added":"≤37","partial_implementation":true,"notes":["This value is supported with the prefixed version of the property only.","According to the WebKit blog, text decorations or shadows are not included in the clipping."]}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"background-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/background-color","spec_url":"https://drafts.csswg.org/css-backgrounds/#background-color","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4","notes":"In Internet Explorer 8 and 9, there is a bug where a computed background-color of transparent causes click events to not get fired on overlaid elements."},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"background-image":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/background-image","spec_url":"https://drafts.csswg.org/css-backgrounds/#background-image","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"If the browser.display.use_document_colors user preference in about:config is set to false, background images will not be displayed."},"firefox_android":{"version_added":"4","notes":"If the browser.display.use_document_colors user preference in about:config is set to false, background images will not be displayed."},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"element":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/element()","spec_url":"https://drafts.csswg.org/css-images-4/#element-notation","description":"element()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"4","prefix":"-moz-"},"firefox_android":{"version_added":"4","prefix":"-moz-"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"gradients":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/gradient","spec_url":"https://drafts.csswg.org/css-images-4/#gradients","description":"Gradients","support":{"chrome":{"version_added":"1","notes":"Some versions support only experimental gradients prefixed with -webkit."},"chrome_android":{"version_added":"18","notes":"Some versions support only experimental gradients prefixed with -webkit."},"edge":{"version_added":"12"},"firefox":{"version_added":"3.6","notes":"Some versions support only experimental gradients prefixed with -moz."},"firefox_android":{"version_added":"4","notes":"Some versions support only experimental gradients prefixed with -moz."},"ie":{"version_added":"10"},"opera":{"version_added":"11","notes":"Some versions support only experimental gradients prefixed with -o."},"opera_android":{"version_added":"14","notes":"Some versions support only experimental gradients prefixed with -webkit."},"safari":{"version_added":"4","notes":"Some versions support only experimental gradients prefixed with -webkit."},"safari_ios":{"version_added":"3.2","notes":"Some versions support only experimental gradients prefixed with -webkit."},"samsunginternet_android":{"version_added":"1.0","notes":"Some versions support only experimental gradients prefixed with -webkit."},"webview_android":{"version_added":"≤37","notes":"Some versions support only experimental gradients prefixed with -webkit."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"image-rect":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/-moz-image-rect","description":"image-rect()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"prefix":"-moz-","version_added":"4"},"firefox_android":{"prefix":"-moz-","version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"image-set":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/image-set()","spec_url":"https://drafts.csswg.org/css-images-4/#image-set-notation","description":"image-set()","support":{"chrome":{"prefix":"-webkit-","version_added":"21"},"chrome_android":{"prefix":"-webkit-","version_added":"25"},"edge":{"prefix":"-webkit-","version_added":"79"},"firefox":{"version_added":false,"notes":"See bug 1107646."},"firefox_android":{"version_added":false,"notes":"See bug 1107646."},"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":{"prefix":"-webkit-","version_added":"6","partial_implementation":true,"notes":"Support for url images only and x is the only supported resolution unit. See bug 160934."},"safari_ios":{"prefix":"-webkit-","version_added":"6","partial_implementation":true,"notes":"Support for url images only and x is the only supported resolution unit. See bug 160934."},"samsunginternet_android":{"prefix":"-webkit-","version_added":"1.5"},"webview_android":{"prefix":"-webkit-","version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"multiple_backgrounds":{"__compat":{"description":"Multiple backgrounds","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3.6"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"svg_images":{"__compat":{"description":"SVG images","support":{"chrome":{"version_added":"8"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"4"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"9.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5","notes":"Support of SVG in CSS background is incomplete."},"safari_ios":{"version_added":"5","notes":"Support of SVG in CSS background is incomplete."},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"background-origin":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/background-origin","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-background-origin","support":{"chrome":[{"version_added":"1"},{"version_added":"1","version_removed":"64","prefix":"-webkit-","notes":"Chrome accepts alternate synonyms to its values: padding, border, and content."}],"chrome_android":[{"version_added":"18"},{"version_added":"18","version_removed":"64","prefix":"-webkit-","notes":"Chrome accepts alternate synonyms to its values: padding, border, and content."}],"edge":{"version_added":"12"},"firefox":[{"version_added":"4"},{"version_added":"49","prefix":"-webkit-"},{"version_added":"1","version_removed":"4","partial_implementation":true,"prefix":"-moz-","notes":"Used the -moz-background-clip: padding | border syntax."}],"firefox_android":[{"version_added":"14"},{"version_added":"49","prefix":"-webkit-"}],"ie":{"version_added":"9","notes":"In IE 7 and before, Internet explorer was behaving as if background-origin: border-box was set. In Internet Explorer 8, as if background-origin: padding-box, the regular default value, was set."},"opera":[{"version_added":"10.5"},{"version_added":"15","version_removed":"51","prefix":"-webkit-","notes":"Opera accepts alternate synonyms to its values: padding, border, and content."}],"opera_android":[{"version_added":"11"},{"version_added":"14","version_removed":"47","prefix":"-webkit-","notes":"Opera accepts alternate synonyms to its values: padding, border, and content."}],"safari":[{"version_added":"3"},{"version_added":"3","prefix":"-webkit-","notes":"Webkit accepts alternate synonyms to its values: padding, border, and content."}],"safari_ios":[{"version_added":"1"},{"version_added":"1","prefix":"-webkit-","notes":"Webkit accepts alternate synonyms to its values: padding, border, and content."}],"samsunginternet_android":[{"version_added":"1.0"},{"prefix":"-webkit-","version_added":"1.0","version_removed":"9.0","notes":"Chrome accepts alternate synonyms to its values: padding, border, and content."}],"webview_android":[{"version_added":"4"},{"version_added":"4","version_removed":"64","prefix":"-webkit-","notes":"WebView accepts alternate synonyms to its values: padding, border, and content."}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"content-box":{"__compat":{"description":"content-box","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"4"},"firefox_android":{"version_added":"14"},"ie":{"version_added":"9","notes":"In IE 7 and IE 9 of Internet Explorer, it always behaved like background-clip: padding if overflow: hidden | auto | scroll."},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"11"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"background-position-x":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/background-position-x","spec_url":"https://drafts.csswg.org/css-backgrounds-4/#background-position-longhands","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"49"},"firefox_android":{"version_added":"49"},"ie":{"version_added":"6"},"opera":{"version_added":"15"},"opera_android":{"version_added":"18"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"two_value_syntax":{"__compat":{"description":"Two-value syntax (support for offsets from any edge)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"79"},"firefox":{"version_added":"49"},"firefox_android":{"version_added":"49"},"ie":{"version_added":"9"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"background-position-y":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/background-position-y","spec_url":"https://drafts.csswg.org/css-backgrounds-4/#background-position-longhands","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"49"},"firefox_android":{"version_added":"49"},"ie":{"version_added":"6"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"2_value_syntax":{"__compat":{"description":"Two-value syntax (support for offsets from any edge)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"79"},"firefox":{"version_added":"49"},"firefox_android":{"version_added":"49"},"ie":{"version_added":"9"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"background-position":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/background-position","spec_url":"https://drafts.csswg.org/css-backgrounds/#background-position","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"multiple_backgrounds":{"__compat":{"description":"Multiple backgrounds","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3.6"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"4_value_syntax":{"__compat":{"description":"Four-value syntax (support for offsets from any edge)","support":{"chrome":{"version_added":"25"},"chrome_android":{"version_added":"25"},"edge":{"version_added":"12"},"firefox":{"version_added":"13"},"firefox_android":{"version_added":"14"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"7"},"safari_ios":{"version_added":"7"},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"background-repeat":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/background-repeat","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-background-repeat","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"multiple_backgrounds":{"__compat":{"description":"Multiple backgrounds","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3.6"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"11"},"safari":{"version_added":"1.3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"round_space":{"__compat":{"description":"round and space keywords","support":{"chrome":{"version_added":"30"},"chrome_android":{"version_added":"30"},"edge":{"version_added":"12"},"firefox":{"version_added":"49"},"firefox_android":{"version_added":"49"},"ie":{"version_added":"9"},"opera":[{"version_added":"17"},{"version_added":"10.5","version_removed":"15"}],"opera_android":{"version_added":"18"},"safari":{"version_added":"7"},"safari_ios":{"version_added":"8"},"samsunginternet_android":{"version_added":"2.0"},"webview_android":{"version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"2-value":{"__compat":{"description":"Two-value syntax (different values for x & y directions)","support":{"chrome":{"version_added":"3"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"13"},"firefox_android":{"version_added":"14"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"11"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"background-size":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/background-size","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-background-size","support":{"chrome":[{"version_added":"3"},{"version_added":"1","prefix":"-webkit-","notes":"WebKit-based browsers originally implemented an older draft of CSS3 background-size in which an omitted second value is treated as duplicating the first value; this draft does not include the contain or cover keywords."}],"chrome_android":[{"version_added":"18"},{"version_added":"18","prefix":"-webkit-","notes":"WebKit-based browsers originally implemented an older draft of CSS3 background-size in which an omitted second value is treated as duplicating the first value; this draft does not include the contain or cover keywords."}],"edge":{"version_added":"12"},"firefox":[{"version_added":"4"},{"version_added":"49","prefix":"-webkit-"},{"prefix":"-moz-","version_added":"3.6","version_removed":"4"}],"firefox_android":[{"version_added":"4"},{"version_added":"49","prefix":"-webkit-"}],"ie":{"version_added":"9"},"opera":[{"version_added":"10"},{"version_added":"15","prefix":"-webkit-","notes":"WebKit-based browsers originally implemented an older draft of CSS3 background-size in which an omitted second value is treated as duplicating the first value; this draft does not include the contain or cover keywords."},{"version_added":"9.5","version_removed":"15","prefix":"-o-","notes":"Opera 9.5's computation of the background positioning area is incorrect for fixed backgrounds. Opera 9.5 also interprets the two-value form as a horizontal scaling factor and, from appearances, a vertical clipping dimension. This has been fixed in Opera 10."}],"opera_android":[{"version_added":"10.1"},{"version_added":"14","prefix":"-webkit-","notes":"WebKit-based browsers originally implemented an older draft of CSS3 background-size in which an omitted second value is treated as duplicating the first value; this draft does not include the contain or cover keywords."},{"version_added":"10.1","version_removed":"14","prefix":"-o-","notes":"Opera 9.5's computation of the background positioning area is incorrect for fixed backgrounds. Opera 9.5 also interprets the two-value form as a horizontal scaling factor and, from appearances, a vertical clipping dimension. This has been fixed in Opera 10."}],"safari":[{"version_added":"5"},{"prefix":"-webkit-","version_added":"3","notes":"WebKit-based browsers originally implemented an older draft of CSS3 background-size in which an omitted second value is treated as duplicating the first value; this draft does not include the contain or cover keywords."}],"safari_ios":[{"version_added":"4.2"},{"prefix":"-webkit-","version_added":"1","notes":"WebKit-based browsers originally implemented an older draft of CSS3 background-size in which an omitted second value is treated as duplicating the first value; this draft does not include the contain or cover keywords."}],"samsunginternet_android":[{"version_added":"1.0"},{"prefix":"-webkit-","version_added":"1.0","notes":"WebKit-based browsers originally implemented an older draft of CSS3 background-size in which an omitted second value is treated as duplicating the first value; this draft does not include the contain or cover keywords."}],"webview_android":[{"version_added":"2.2"},{"version_added":"≤37","prefix":"-webkit-","notes":"WebKit-based browsers originally implemented an older draft of CSS3 background-size in which an omitted second value is treated as duplicating the first value; this draft does not include the contain or cover keywords."}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"contain_and_cover":{"__compat":{"description":"contain and cover","support":{"chrome":{"version_added":"3"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3.6"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"10"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"background":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/background","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-background","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"SVG_image_as_background":{"__compat":{"description":"SVG image as background","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"4"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"9.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"3.1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"background-clip":{"__compat":{"description":"Values of background-clip longhand","support":{"chrome":{"version_added":"21"},"chrome_android":{"version_added":"25"},"edge":{"version_added":"12"},"firefox":{"version_added":"22"},"firefox_android":{"version_added":"22"},"ie":{"version_added":"9"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"4"},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"3"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"background-origin":{"__compat":{"description":"Values of background-origin longhand","support":{"chrome":{"version_added":"21"},"chrome_android":{"version_added":"25"},"edge":{"version_added":"12"},"firefox":{"version_added":"22"},"firefox_android":{"version_added":"22"},"ie":{"version_added":"9"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"4"},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"3"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"background-size":{"__compat":{"description":"Values of background-size longhand","support":{"chrome":{"version_added":"21"},"chrome_android":{"version_added":"25"},"edge":{"version_added":"12"},"firefox":{"version_added":"9"},"firefox_android":{"version_added":"18"},"ie":{"version_added":"9"},"opera":{"version_added":"21"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"4"},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"3"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"multiple_backgrounds":{"__compat":{"description":"Multiple backgrounds","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3.6"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"block-size":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/block-size","spec_url":"https://drafts.csswg.org/css-logical/#dimension-properties","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"fit-content":{"__compat":{"description":"fit-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"prefix":"-moz-","version_added":"41"},"firefox_android":{"prefix":"-moz-","version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"alternative_name":"-webkit-fill-available","version_added":"5.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"fit-content_function":{"__compat":{"description":"fit-content()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"firefox_android":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"max-content":{"__compat":{"description":"max-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"min-content":{"__compat":{"description":"min-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"border-block-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-block-color","spec_url":"https://drafts.csswg.org/css-logical/#propdef-border-block-color","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":[{"version_added":"87"},{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":false},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-block-end-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-block-end-color","spec_url":"https://drafts.csswg.org/css-logical/#border-color","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-block-end-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-block-end-style","spec_url":"https://drafts.csswg.org/css-logical/#border-style","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-block-end-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-block-end-width","spec_url":"https://drafts.csswg.org/css-logical/#border-width","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-block-end":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-block-end","spec_url":"https://drafts.csswg.org/css-logical/#border-shorthands","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-block-start-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-block-start-color","spec_url":"https://drafts.csswg.org/css-logical/#border-color","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-block-start-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-block-start-style","spec_url":"https://drafts.csswg.org/css-logical/#border-style","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-block-start-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-block-start-width","spec_url":"https://drafts.csswg.org/css-logical/#border-width","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-block-start":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-block-start","spec_url":"https://drafts.csswg.org/css-logical/#border-shorthands","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-block-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-block-style","spec_url":"https://drafts.csswg.org/css-logical/#propdef-border-block-style","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":[{"version_added":"87"},{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"48","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-block-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-block-width","spec_url":"https://drafts.csswg.org/css-logical/#propdef-border-block-width","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":[{"version_added":"87"},{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":[{"version_added":false},{"version_added":"48","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-block":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-block","spec_url":"https://drafts.csswg.org/css-logical/#propdef-border-block","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":[{"version_added":"87"},{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"48","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-bottom-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-bottom-color","spec_url":"https://drafts.csswg.org/css-backgrounds/#border-color","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"Firefox also supports the non-standard -moz-border-bottom-colors CSS property that sets the bottom border to multiple colors."},"firefox_android":{"version_added":"4","notes":"Firefox also supports the non-standard -moz-border-bottom-colors CSS property that sets the bottom border to multiple colors."},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-bottom-left-radius":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-bottom-left-radius","spec_url":"https://drafts.csswg.org/css-backgrounds/#border-radius","support":{"chrome":[{"version_added":"4"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"18"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"4","notes":"Prior to Firefox 50, border styles of rounded corners were always rendered as if border-style was solid. This has been fixed in Firefox 50."},{"prefix":"-webkit-","version_added":"49"},{"alternative_name":"-moz-border-radius-bottomleft","version_added":"1","version_removed":"12"}],"firefox_android":[{"version_added":"4","notes":"Prior to Firefox 50, border styles of rounded corners were always rendered as if border-style was solid. This has been fixed in Firefox 50."},{"prefix":"-webkit-","version_added":"49"},{"alternative_name":"-moz-border-radius-bottomleft","version_added":"4","version_removed":"14"}],"ie":{"version_added":"9"},"opera":[{"version_added":"10.5"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"5"},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"4.2"},{"prefix":"-webkit-","version_added":"1"}],"samsunginternet_android":[{"version_added":"1.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"≤37"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"elliptical_corners":{"__compat":{"description":"Elliptical corners","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3.5"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"percentages":{"__compat":{"description":"Percentages","support":{"chrome":{"version_added":"4"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":[{"version_added":"4"},{"version_added":"1","notes":"Before Firefox 4, the <percentage> was relative to the width of the box even when specifying the radius for a height. This implied that -moz-border-radius-bottomleft was always drawing an arc of circle, and never an ellipse, when followed by a single value."}],"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"border-bottom-right-radius":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-bottom-right-radius","spec_url":"https://drafts.csswg.org/css-backgrounds/#border-radius","support":{"chrome":[{"version_added":"4"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"18"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"4","notes":"Prior to Firefox 50, border styles of rounded corners were always rendered as if border-style was solid. This has been fixed in Firefox 50."},{"prefix":"-webkit-","version_added":"49"},{"alternative_name":"-moz-border-radius-bottomright","version_added":"1","version_removed":"12"}],"firefox_android":[{"version_added":"4","notes":"Prior to Firefox 50, border styles of rounded corners were always rendered as if border-style was solid. This has been fixed in Firefox 50."},{"prefix":"-webkit-","version_added":"49"},{"alternative_name":"-moz-border-radius-bottomright","version_added":"4","version_removed":"14"}],"ie":{"version_added":"9"},"opera":[{"version_added":"10.5"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"5"},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"4.2"},{"prefix":"-webkit-","version_added":"1"}],"samsunginternet_android":[{"version_added":"1.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"≤37"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"elliptical_corners":{"__compat":{"description":"Elliptical corners","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3.5"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"percentages":{"__compat":{"description":"Percentages","support":{"chrome":{"version_added":"4"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":[{"version_added":"4"},{"version_added":"1","notes":"Before Firefox 4, the <percentage> was relative to the width of the box even when specifying the radius for a height. This implied that -moz-border-radius-bottomright was always drawing an arc of circle, and never an ellipse, when followed by a single value."}],"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"border-bottom-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-bottom-style","spec_url":"https://drafts.csswg.org/css-backgrounds/#border-style","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"Prior to Firefox 50, border styles of rounded corners (with border-radius) were always rendered as if border-bottom-style was solid. This has been fixed in Firefox 50."},"firefox_android":{"version_added":"4","notes":"Prior to Firefox 50, border styles of rounded corners (with border-radius) were always rendered as if border-bottom-style was solid. This has been fixed in Firefox 50."},"ie":{"version_added":"5.5"},"opera":{"version_added":"9.2"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-bottom-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-bottom-width","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-border-width","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2.2"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-bottom":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-bottom","spec_url":"https://drafts.csswg.org/css-backgrounds/#border-shorthands","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-collapse":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-collapse","spec_url":"https://drafts.csswg.org/css2/#propdef-border-collapse","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"5"},"opera":{"version_added":"4"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"3"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2.2"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-color","spec_url":["https://drafts.csswg.org/css-logical/#logical-shorthand-keyword","https://drafts.csswg.org/css-backgrounds/#border-color"],"support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"Firefox also supports the following non-standard CSS properties to set the border sides to multiple colors: -moz-border-top-colors, -moz-border-right-colors, -moz-border-bottom-colors, -moz-border-left-colors"},"firefox_android":{"version_added":"4","notes":"Firefox also supports the following non-standard CSS properties to set the border sides to multiple colors: -moz-border-top-colors, -moz-border-right-colors, -moz-border-bottom-colors, -moz-border-left-colors"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-end-end-radius":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-end-end-radius","spec_url":"https://drafts.csswg.org/css-logical/#border-radius-properties","support":{"chrome":{"version_added":"89"},"chrome_android":{"version_added":"89"},"edge":{"version_added":"89"},"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":{"version_added":"75"},"opera_android":{"version_added":false},"safari":{"version_added":"15"},"safari_ios":{"version_added":"15"},"samsunginternet_android":{"version_added":"15.0"},"webview_android":{"version_added":"89"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-end-start-radius":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-end-start-radius","spec_url":"https://drafts.csswg.org/css-logical/#border-radius-properties","support":{"chrome":{"version_added":"89"},"chrome_android":{"version_added":"89"},"edge":{"version_added":"89"},"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":{"version_added":"75"},"opera_android":{"version_added":false},"safari":{"version_added":"15"},"safari_ios":{"version_added":"15"},"samsunginternet_android":{"version_added":"15.0"},"webview_android":{"version_added":"89"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-image-outset":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-image-outset","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-border-image-outset","support":{"chrome":{"version_added":"15"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"15"},"firefox_android":{"version_added":"15"},"ie":{"version_added":"11"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"6"},"safari_ios":{"version_added":"6"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-image-repeat":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-image-repeat","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-border-image-repeat","support":{"chrome":{"version_added":"15"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"15"},"firefox_android":{"version_added":"15"},"ie":{"version_added":"11"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"6"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"round":{"__compat":{"description":"round","support":{"chrome":{"version_added":"30"},"chrome_android":{"version_added":"30"},"edge":{"version_added":"12"},"firefox":{"version_added":"15"},"firefox_android":{"version_added":"15"},"ie":{"version_added":"11"},"opera":{"version_added":"17"},"opera_android":{"version_added":"18"},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":"2.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"space":{"__compat":{"description":"space","support":{"chrome":{"version_added":"56"},"chrome_android":{"version_added":"56"},"edge":{"version_added":"12"},"firefox":{"version_added":"50"},"firefox_android":{"version_added":"50"},"ie":{"version_added":"11"},"opera":{"version_added":"43"},"opera_android":{"version_added":"43"},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"56"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"border-image-slice":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-image-slice","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-border-image-slice","support":{"chrome":{"version_added":"15"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"15","notes":["Small SVGs are incorrectly stretched, because percentages in border-image-slice are computed to integers instead of floats (bug 1284797).","Until Firefox 47, SVGs without viewport were not sliced correctly (bug 619500).","From Firefox 48 until Firefox 49, SVGs without viewport are displayed the same as SVGs with viewport, but if the slices are not exactly 50%, they are incorrectly stretched (bug 1264809).","Until Firefox 57, an issue persisted for SVGs without viewport when e10s was disabled (bug 1290782)."]},"firefox_android":{"version_added":"15","notes":["Small SVGs are incorrectly stretched, because percentages in border-image-slice are computed to integers instead of floats (bug 1284797).","Until Firefox 47, SVGs without viewport were not sliced correctly (bug 619500).","From Firefox 48 until Firefox 49, SVGs without viewport are displayed the same as SVGs with viewport, but if the slices are not exactly 50%, they are incorrectly stretched (bug 1264809).","Until Firefox 57, an issue persisted for SVGs without viewport when e10s was disabled (bug 1290782)."]},"ie":{"version_added":"11"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"6"},"safari_ios":{"version_added":"6"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":[{"version_added":"≤37"},{"prefix":"-webkit-","version_added":"4"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-image-source":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-image-source","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-border-image-source","support":{"chrome":{"version_added":"15"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"15"},"firefox_android":{"version_added":"15"},"ie":{"version_added":"11"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"6"},"safari_ios":{"version_added":"6"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-image-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-image-width","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-border-image-width","support":{"chrome":{"version_added":"15"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"13"},"firefox_android":{"version_added":"14"},"ie":{"version_added":"11"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"6"},"safari_ios":{"version_added":"6"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-image":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-image","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-border-image","support":{"chrome":[{"version_added":"16"},{"prefix":"-webkit-","version_added":"7"}],"chrome_android":[{"version_added":"18"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"15","notes":["Small SVGs are incorrectly stretched, because percentages in border-image-slice are computed to integers instead of floats (bug 1284797).","Until Firefox 47, SVGs without viewport were not sliced correctly (bug 619500).","From Firefox 48 until Firefox 49, SVGs without viewport are displayed the same as SVGs with viewport, but if the slices are not exactly 50%, they are incorrectly stretched (bug 1264809).","Until Firefox 57, an issue persisted for SVGs without viewport when e10s was disabled (bug 1290782)."]},{"version_added":"3.5","prefix":"-moz-","notes":"An earlier version of the specification was implemented, prefixed, in Firefox versions prior to 15."}],"firefox_android":[{"version_added":"15","notes":["Small SVGs are incorrectly stretched, because percentages in border-image-slice are computed to integers instead of floats (bug 1284797).","Until Firefox 47, SVGs without viewport were not sliced correctly (bug 619500).","From Firefox 48 until Firefox 49, SVGs without viewport are displayed the same as SVGs with viewport, but if the slices are not exactly 50%, they are incorrectly stretched (bug 1264809).","Until Firefox 57, an issue persisted for SVGs without viewport when e10s was disabled (bug 1290782)."]},{"version_added":"4","prefix":"-moz-","notes":"An earlier version of the specification was implemented, prefixed, in Firefox versions prior to 15."}],"ie":{"version_added":"11"},"opera":[{"version_added":"11"},{"version_added":"10.5","version_removed":"15","prefix":"-o-"}],"opera_android":[{"version_added":"11"},{"version_added":"11","version_removed":"14","prefix":"-o-"}],"safari":[{"version_added":"6"},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"6"},{"prefix":"-webkit-","version_added":"3.2"}],"samsunginternet_android":[{"version_added":"1.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"≤37"},{"prefix":"-webkit-","version_added":"2"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"fill":{"__compat":{"support":{"chrome":{"version_added":"16"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"15"},"firefox_android":{"version_added":"15"},"ie":{"version_added":"11"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"6"},"safari_ios":{"version_added":"6"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"gradient":{"__compat":{"description":"<gradient>","support":{"chrome":{"version_added":"7"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"29"},"firefox_android":{"version_added":"29"},"ie":{"version_added":"11"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"4"},"safari_ios":{"version_added":"3.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"optional_border_image_slice":{"__compat":{"description":"optional <border-image-slice>","support":{"chrome":{"version_added":"16"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"15"},"firefox_android":{"version_added":"15"},"ie":{"version_added":"11"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"6"},"safari_ios":{"version_added":"6"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"border-inline-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-inline-color","spec_url":"https://drafts.csswg.org/css-logical/#propdef-border-inline-color","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":[{"version_added":"87"},{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"48","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-inline-end-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-inline-end-color","spec_url":"https://drafts.csswg.org/css-logical/#border-color","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":[{"version_added":"41"},{"version_added":"3","alternative_name":"-moz-border-end-color"}],"firefox_android":[{"version_added":"41"},{"version_added":"4","alternative_name":"-moz-border-end-color"}],"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-inline-end-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-inline-end-style","spec_url":"https://drafts.csswg.org/css-logical/#border-style","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":[{"version_added":"41"},{"version_added":"3","alternative_name":"-moz-border-end-style"}],"firefox_android":[{"version_added":"41"},{"version_added":"4","alternative_name":"-moz-border-end-style"}],"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-inline-end-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-inline-end-width","spec_url":"https://drafts.csswg.org/css-logical/#border-width","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":[{"version_added":"41"},{"version_added":"3","alternative_name":"-moz-border-end-width"}],"firefox_android":[{"version_added":"41"},{"version_added":"4","alternative_name":"-moz-border-end-width"}],"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-inline-end":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-inline-end","spec_url":"https://drafts.csswg.org/css-logical/#border-shorthands","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-inline-start-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-inline-start-color","spec_url":"https://drafts.csswg.org/css-logical/#border-color","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":[{"version_added":"41"},{"version_added":"3","alternative_name":"-moz-border-start-color"}],"firefox_android":[{"version_added":"41"},{"version_added":"4","alternative_name":"-moz-border-start-color"}],"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-inline-start-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-inline-start-style","spec_url":"https://drafts.csswg.org/css-logical/#border-style","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":[{"version_added":"41"},{"version_added":"3","alternative_name":"-moz-border-start-style"}],"firefox_android":[{"version_added":"41"},{"version_added":"4","alternative_name":"-moz-border-start-style"}],"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-inline-start-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-inline-start-width","spec_url":"https://drafts.csswg.org/css-logical/#border-width","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-inline-start":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-inline-start","spec_url":"https://drafts.csswg.org/css-logical/#border-shorthands","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-inline-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-inline-style","spec_url":"https://drafts.csswg.org/css-logical/#propdef-border-inline-style","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":[{"version_added":"87"},{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"48","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-inline-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-inline-width","spec_url":"https://drafts.csswg.org/css-logical/#propdef-border-inline-width","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":[{"version_added":"87"},{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"48","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-inline":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-inline","spec_url":"https://drafts.csswg.org/css-logical/#propdef-border-inline","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":[{"version_added":"87"},{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"48","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-left-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-left-color","spec_url":"https://drafts.csswg.org/css-backgrounds/#border-color","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"Firefox also supports the non-standard -moz-border-left-colors CSS property that sets the bottom border to multiple colors."},"firefox_android":{"version_added":"4","notes":"Firefox also supports the non-standard -moz-border-left-colors CSS property that sets the bottom border to multiple colors."},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-left-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-left-style","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-border-style","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"Prior to Firefox 50, border styles of rounded corners (with border-radius) were always rendered as if border-bottom-style was solid. This has been fixed in Firefox 50."},"firefox_android":{"version_added":"14","notes":"Prior to Firefox 50, border styles of rounded corners (with border-radius) were always rendered as if border-bottom-style was solid. This has been fixed in Firefox 50."},"ie":{"version_added":"5.5"},"opera":{"version_added":"9.2"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2.2"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-left-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-left-width","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-border-width","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2.2"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-left":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-left","spec_url":"https://drafts.csswg.org/css-backgrounds/#border-shorthands","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-radius":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-radius","spec_url":"https://drafts.csswg.org/css-backgrounds/#border-radius","support":{"chrome":[{"version_added":"4","notes":"Chrome ignores border-radius on <select> elements unless -webkit-appearance is overridden to an appropriate value."},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":{"version_added":"18"},"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"4","notes":["Prior to Firefox 50, border styles of rounded corners (with border-radius) were always rendered as if border-style was solid. This has been fixed in Firefox 50.","To conform to the CSS3 standard, Firefox 4 changes the handling of <percentage> values to match the specification. You can specify an ellipse as a border on an arbitrary sized element with border-radius: 50%;. Firefox 4 also makes rounded corners clip content and images if overflow: visible is not set."]},{"prefix":"-moz-","version_added":"1","version_removed":"12"}],"firefox_android":[{"version_added":"4","notes":"Prior to Firefox 50, border styles of rounded corners (with border-radius) were always rendered as if border-style was solid. This has been fixed in Firefox 50."},{"prefix":"-moz-","version_added":"4","version_removed":"14"}],"ie":{"version_added":"9"},"opera":{"version_added":"10.5","notes":"Before Opera 11.60, replaced elements with border-radius do not have rounded corners."},"opera_android":{"version_added":"11"},"safari":[{"version_added":"5","notes":"Safari ignores border-radius on <select> elements unless -webkit-appearance is overridden to an appropriate value."},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"4.2","notes":"Safari ignores border-radius on <select> elements unless -webkit-appearance is overridden to an appropriate value."},{"prefix":"-webkit-","version_added":"1"}],"samsunginternet_android":{"version_added":"1.0"},"webview_android":[{"version_added":"≤37"},{"version_added":"2","prefix":"-webkit-"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"elliptical_borders":{"__compat":{"description":"Elliptical borders","support":{"chrome":{"version_added":"1","notes":"Before Chrome 4, the slash / notation is unsupported. If two values are specified, then an elliptical border is drawn on all four corners. -webkit-border-radius: 40px 10px; is equivalent to border-radius: 40px / 10px;."},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"4"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"11"},"safari":{"version_added":"3","notes":"Before Safari 5, the slash / notation is unsupported. If two values are specified, then an elliptical border is drawn on all four corners. -webkit-border-radius: 40px 10px; is equivalent to border-radius: 40px / 10px;."},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"percentages":{"__compat":{"description":"Percentages","support":{"chrome":{"version_added":"8"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"4","notes":"<percentage> values are implemented in a non-standard way prior to Firefox 4. Both horizontal and vertical radii were relative to the width of the border box."},"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"11.5","notes":"The implementation of <percentage> values was buggy in Opera prior to 11.50."},"opera_android":{"version_added":"11.5"},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"6"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"4_values_for_4_corners":{"__compat":{"description":"4 values for 4 corners","support":{"chrome":{"version_added":"4"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"4"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"11"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"border-right-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-right-color","spec_url":"https://drafts.csswg.org/css-backgrounds/#border-color","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"Firefox also supports the non-standard -moz-border-right-colors CSS property that sets the right border to multiple colors."},"firefox_android":{"version_added":"4","notes":"Firefox also supports the non-standard -moz-border-right-colors CSS property that sets the right border to multiple colors."},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-right-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-right-style","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-border-style","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"Prior to Firefox 50, border styles of rounded corners (with border-radius) were always rendered as if border-bottom-style was solid. This has been fixed in Firefox 50."},"firefox_android":{"version_added":"14","notes":"Prior to Firefox 50, border styles of rounded corners (with border-radius) were always rendered as if border-bottom-style was solid. This has been fixed in Firefox 50."},"ie":{"version_added":"5.5"},"opera":{"version_added":"9.2"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-right-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-right-width","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-border-width","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2.2"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-right":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-right","spec_url":"https://drafts.csswg.org/css-backgrounds/#border-shorthands","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"14"},"ie":{"version_added":"5.5"},"opera":{"version_added":"9.2"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-spacing":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-spacing","spec_url":"https://drafts.csswg.org/css2/#separated-borders","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"4"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-start-end-radius":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-start-end-radius","spec_url":"https://drafts.csswg.org/css-logical/#border-radius-properties","support":{"chrome":{"version_added":"89"},"chrome_android":{"version_added":"89"},"edge":{"version_added":"89"},"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":{"version_added":"75"},"opera_android":{"version_added":false},"safari":{"version_added":"15"},"safari_ios":{"version_added":"15"},"samsunginternet_android":{"version_added":"15.0"},"webview_android":{"version_added":"89"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-start-start-radius":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-start-start-radius","spec_url":"https://drafts.csswg.org/css-logical/#border-radius-properties","support":{"chrome":{"version_added":"89"},"chrome_android":{"version_added":"89"},"edge":{"version_added":"89"},"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":{"version_added":"75"},"opera_android":{"version_added":false},"safari":{"version_added":"15"},"safari_ios":{"version_added":"15"},"samsunginternet_android":{"version_added":"15.0"},"webview_android":{"version_added":"89"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-style","spec_url":"https://drafts.csswg.org/css-backgrounds/#border-style","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"Prior to Firefox 50, border styles of rounded corners were always rendered as if border-style was solid. This has been fixed in Firefox 50."},"firefox_android":{"version_added":"4","notes":"Prior to Firefox 50, border styles of rounded corners were always rendered as if border-style was solid. This has been fixed in Firefox 50."},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"3"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"3"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-top-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-top-color","spec_url":"https://drafts.csswg.org/css-backgrounds/#border-color","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"Firefox also supports the non-standard -moz-border-top-colors CSS property that sets the top border to multiple colors."},"firefox_android":{"version_added":"4","notes":"Firefox also supports the non-standard -moz-border-top-colors CSS property that sets the top border to multiple colors."},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-top-left-radius":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-top-left-radius","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-border-radius","support":{"chrome":[{"version_added":"4"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"18"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"4","notes":"Prior to Firefox 50, border styles of rounded corners were always rendered as if border-style was solid. This has been fixed in Firefox 50."},{"prefix":"-webkit-","version_added":"49"},{"alternative_name":"-moz-border-radius-topleft","version_added":"1","version_removed":"12"}],"firefox_android":[{"version_added":"4","notes":"Prior to Firefox 50, border styles of rounded corners were always rendered as if border-style was solid. This has been fixed in Firefox 50."},{"prefix":"-webkit-","version_added":"49"},{"alternative_name":"-moz-border-radius-topleft","version_added":"4","version_removed":"14"}],"ie":{"version_added":"9"},"opera":[{"version_added":"10.5"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"5"},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"4.2"},{"prefix":"-webkit-","version_added":"1"}],"samsunginternet_android":[{"version_added":"1.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"≤37"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"elliptical_corners":{"__compat":{"description":"Elliptical corners","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3.5"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"percentages":{"__compat":{"description":"Percentages","support":{"chrome":{"version_added":"4"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":[{"version_added":"4"},{"version_added":"1","notes":"Before Firefox 4, the <percentage> was relative to the width of the box even when specifying the radius for a height. This implied that -moz-border-radius-topleft was always drawing an arc of circle, and never an ellipse, when followed by a single value."}],"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"border-top-right-radius":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-top-right-radius","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-border-radius","support":{"chrome":[{"version_added":"4"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"18"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"4","notes":"Prior to Firefox 50, border styles of rounded corners were always rendered as if border-style was solid. This has been fixed in Firefox 50."},{"prefix":"-webkit-","version_added":"49"},{"alternative_name":"-moz-border-radius-topright","version_added":"1","version_removed":"12"}],"firefox_android":[{"version_added":"4","notes":"Prior to Firefox 50, border styles of rounded corners were always rendered as if border-style was solid. This has been fixed in Firefox 50."},{"prefix":"-webkit-","version_added":"49"},{"alternative_name":"-moz-border-radius-topright","version_added":"4","version_removed":"14"}],"ie":{"version_added":"9"},"opera":[{"version_added":"10.5"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"5"},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"4.2"},{"prefix":"-webkit-","version_added":"1"}],"samsunginternet_android":[{"version_added":"1.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"≤37"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"elliptical_corners":{"__compat":{"description":"Elliptical corners","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3.5"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"percentages":{"__compat":{"description":"Percentages","support":{"chrome":{"version_added":"4"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":[{"version_added":"4"},{"version_added":"1","notes":"Before Firefox 4, the <percentage> was relative to the width of the box even when specifying the radius for a height. This implied that -moz-border-radius-topright was always drawing an arc of circle, and never an ellipse, when followed by a single value."}],"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"border-top-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-top-style","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-border-style","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"Prior to Firefox 50, border styles of rounded corners (with border-radius) were always rendered as if border-top-style was solid. This has been fixed in Firefox 50."},"firefox_android":{"version_added":"4","notes":"Prior to Firefox 50, border styles of rounded corners (with border-radius) were always rendered as if border-top-style was solid. This has been fixed in Firefox 50."},"ie":{"version_added":"5.5"},"opera":{"version_added":"9.2"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-top-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-top-width","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-border-width","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2.2"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-top":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-top","spec_url":"https://drafts.csswg.org/css-backgrounds/#border-shorthands","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border-width","spec_url":"https://drafts.csswg.org/css-backgrounds/#the-border-width","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"3"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"border":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/border","spec_url":"https://drafts.csswg.org/css-backgrounds/#propdef-border","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"bottom":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/bottom","spec_url":"https://drafts.csswg.org/css-position/#insets","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"5","notes":"In Internet Explorer versions before 7, when both top and bottom are specified, the element position is overconstrained and the top property has precedence; the computed value of bottom is set to -top, while its specified value is ignored."},"opera":{"version_added":"6"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"box-align":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/box-align","support":{"chrome":{"version_added":"1","prefix":"-webkit-"},"chrome_android":{"version_added":"18","prefix":"-webkit-"},"edge":{"version_added":"12","prefix":"-webkit-"},"firefox":[{"version_added":"1","prefix":"-moz-"},{"version_added":"49","prefix":"-webkit-"}],"firefox_android":[{"version_added":"4","prefix":"-moz-"},{"version_added":"49","prefix":"-webkit-"}],"ie":{"version_added":false},"opera":{"version_added":"15","prefix":"-webkit-"},"opera_android":{"version_added":"14","prefix":"-webkit-"},"safari":[{"version_added":"3","prefix":"-webkit-"},{"version_added":"1.1","version_removed":"3","prefix":"-khtml-"}],"safari_ios":{"version_added":"1","prefix":"-webkit-"},"samsunginternet_android":{"version_added":"1.0","prefix":"-webkit-"},"webview_android":{"version_added":"≤37","prefix":"-webkit-"}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"box-decoration-break":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/box-decoration-break","spec_url":"https://drafts.csswg.org/css-break/#break-decoration","support":{"chrome":{"prefix":"-webkit-","version_added":"22","notes":"This property is only supported for inline elements."},"chrome_android":{"prefix":"-webkit-","version_added":"18","notes":"This property is only supported for inline elements."},"edge":{"prefix":"-webkit-","version_added":"79","notes":"This property is only supported for inline elements."},"firefox":[{"version_added":"32"},{"alternative_name":"-moz-background-inline-policy","version_added":"1","version_removed":"32"}],"firefox_android":[{"version_added":"32"},{"alternative_name":"-moz-background-inline-policy","version_added":"4","version_removed":"32"}],"ie":{"version_added":false},"opera":[{"prefix":"-webkit-","version_added":"15"},{"version_added":"11.5","version_removed":"15"}],"opera_android":[{"prefix":"-webkit-","version_added":"14"},{"version_added":"11.5","version_removed":"14"}],"safari":{"version_added":"7","prefix":"-webkit-","notes":"This property is only supported for inline elements."},"safari_ios":{"version_added":"7","prefix":"-webkit-","notes":"This property is only supported for inline elements."},"samsunginternet_android":{"version_added":"1.0","prefix":"-webkit-","notes":"This property is only supported for inline elements."},"webview_android":{"prefix":"-webkit-","version_added":"≤37","notes":"This property is only supported for inline elements."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"box-direction":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/box-direction","support":{"chrome":{"prefix":"-webkit-","version_added":"1"},"chrome_android":{"prefix":"-webkit-","version_added":"18"},"edge":{"prefix":"-webkit-","version_added":"12"},"firefox":[{"prefix":"-moz-","version_added":"1"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-webkit-","version_added":"48","flags":[{"type":"preference","name":"layout.css.prefixes.webkit","value_to_set":"true"}]}],"firefox_android":[{"prefix":"-moz-","version_added":"4"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-webkit-","version_added":"48","flags":[{"type":"preference","name":"layout.css.prefixes.webkit","value_to_set":"true"}]}],"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":[{"prefix":"-webkit-","version_added":"3"},{"prefix":"-khtml-","version_added":"1.1","version_removed":"3"}],"safari_ios":{"prefix":"-webkit-","version_added":"1"},"samsunginternet_android":{"version_added":"1.0","prefix":"-webkit-"},"webview_android":{"prefix":"-webkit-","version_added":"≤37"}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"box-flex-group":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/box-flex-group","support":{"chrome":{"version_added":"1","version_removed":"67","prefix":"-webkit-"},"chrome_android":{"version_added":"18","version_removed":"67","prefix":"-webkit-"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"54","prefix":"-webkit-"},"opera_android":{"version_added":"14","version_removed":"48","prefix":"-webkit-"},"safari":[{"version_added":"3","prefix":"-webkit-"},{"version_added":"1.1","version_removed":"3","prefix":"-khtml-"}],"safari_ios":{"version_added":"1","prefix":"-webkit-"},"samsunginternet_android":{"version_added":"1.0","version_removed":"9.0","prefix":"-webkit-"},"webview_android":{"version_added":"≤37","version_removed":"67","prefix":"-webkit-"}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"box-flex":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/box-flex","support":{"chrome":{"prefix":"-webkit-","version_added":"1"},"chrome_android":{"prefix":"-webkit-","version_added":"18"},"edge":{"prefix":"-webkit-","version_added":"12"},"firefox":[{"prefix":"-moz-","version_added":"1"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-webkit-","version_added":"48","flags":[{"type":"preference","name":"layout.css.prefixes.webkit","value_to_set":"true"}]}],"firefox_android":[{"prefix":"-moz-","version_added":"4"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-webkit-","version_added":"48","flags":[{"type":"preference","name":"layout.css.prefixes.webkit","value_to_set":"true"}]}],"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":[{"prefix":"-webkit-","version_added":"3"},{"prefix":"-khtml-","version_added":"1.1","version_removed":"3"}],"safari_ios":{"prefix":"-webkit-","version_added":"1"},"samsunginternet_android":{"version_added":"1.0","prefix":"-webkit-"},"webview_android":{"prefix":"-webkit-","version_added":"≤37"}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"box-lines":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/box-lines","support":{"chrome":{"version_added":"1","version_removed":"67","prefix":"-webkit-"},"chrome_android":{"version_added":"18","version_removed":"67","prefix":"-webkit-"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"54","prefix":"-webkit-"},"opera_android":{"version_added":"14","version_removed":"48","prefix":"-webkit-"},"safari":[{"version_added":"3","prefix":"-webkit-"},{"version_added":"1.1","version_removed":"3","prefix":"-khtml-"}],"safari_ios":{"version_added":"1","prefix":"-webkit-"},"samsunginternet_android":{"version_added":"1.0","version_removed":"9.0","prefix":"-webkit-"},"webview_android":{"version_added":"≤37","version_removed":"67","prefix":"-webkit-"}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"box-ordinal-group":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/box-ordinal-group","support":{"chrome":{"version_added":"1","prefix":"-webkit-"},"chrome_android":{"version_added":"18","prefix":"-webkit-"},"edge":{"version_added":"12","prefix":"-webkit-"},"firefox":[{"prefix":"-moz-","version_added":"1"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-webkit-","version_added":"48","flags":[{"type":"preference","name":"layout.css.prefixes.webkit","value_to_set":"true"}]}],"firefox_android":[{"prefix":"-moz-","version_added":"4"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-webkit-","version_added":"48","flags":[{"type":"preference","name":"layout.css.prefixes.webkit","value_to_set":"true"}]}],"ie":{"version_added":false},"opera":{"version_added":"15","prefix":"-webkit-"},"opera_android":{"version_added":"14","prefix":"-webkit-"},"safari":[{"version_added":"3","prefix":"-webkit-"},{"version_added":"1.1","version_removed":"3","prefix":"-khtml-"}],"safari_ios":{"version_added":"1","prefix":"-webkit-"},"samsunginternet_android":{"version_added":"1.0","prefix":"-webkit-"},"webview_android":{"version_added":"≤37","prefix":"-webkit-"}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"box-orient":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/box-orient","support":{"chrome":{"prefix":"-webkit-","version_added":"1"},"chrome_android":{"prefix":"-webkit-","version_added":"18"},"edge":{"prefix":"-webkit-","version_added":"12"},"firefox":[{"prefix":"-moz-","version_added":"1"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-webkit-","version_added":"48","flags":[{"type":"preference","name":"layout.css.prefixes.webkit","value_to_set":"true"}]}],"firefox_android":[{"prefix":"-moz-","version_added":"4"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-webkit-","version_added":"48","flags":[{"type":"preference","name":"layout.css.prefixes.webkit","value_to_set":"true"}]}],"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":[{"prefix":"-webkit-","version_added":"3"},{"prefix":"-khtml-","version_added":"1.1","version_removed":"3"}],"safari_ios":{"prefix":"-webkit-","version_added":"1"},"samsunginternet_android":{"prefix":"-webkit-","version_added":"1.0"},"webview_android":{"prefix":"-webkit-","version_added":"≤37"}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"box-pack":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/box-pack","support":{"chrome":{"prefix":"-webkit-","version_added":"1"},"chrome_android":{"prefix":"-webkit-","version_added":"18"},"edge":{"prefix":"-webkit-","version_added":"12"},"firefox":[{"prefix":"-moz-","version_added":"1"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-webkit-","version_added":"48","flags":[{"type":"preference","name":"layout.css.prefixes.webkit","value_to_set":"true"}]}],"firefox_android":[{"prefix":"-moz-","version_added":"4"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-webkit-","version_added":"48","flags":[{"type":"preference","name":"layout.css.prefixes.webkit","value_to_set":"true"}]}],"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":[{"prefix":"-webkit-","version_added":"3"},{"prefix":"-khtml-","version_added":"1.1","version_removed":"3"}],"safari_ios":{"prefix":"-webkit-","version_added":"1"},"samsunginternet_android":{"prefix":"-webkit-","version_added":"1.0"},"webview_android":{"prefix":"-webkit-","version_added":"≤37"}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"box-shadow":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/box-shadow","spec_url":"https://drafts.csswg.org/css-backgrounds/#box-shadow","support":{"chrome":[{"version_added":"10","notes":"Shadows affect layout in this browser. For example, if you cast an outer shadow to a box with a width of 100%, then you'll see a scrollbar."},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"18","notes":"Shadows affect layout in this browser. For example, if you cast an outer shadow to a box with a width of 100%, then you'll see a scrollbar."},{"prefix":"-webkit-","version_added":"18"}],"edge":{"version_added":"12"},"firefox":[{"version_added":"4","notes":"Shadows affect layout in this browser. For example, if you cast an outer shadow to a box with a width of 100%, then you'll see a scrollbar."},{"prefix":"-moz-","version_added":"3.5","version_removed":"13"},{"prefix":"-webkit-","version_added":"49"}],"firefox_android":[{"version_added":"4","notes":"Shadows affect layout in this browser. For example, if you cast an outer shadow to a box with a width of 100%, then you'll see a scrollbar."},{"prefix":"-moz-","version_added":"4","version_removed":"14"},{"prefix":"-webkit-","version_added":"49"}],"ie":{"version_added":"9","notes":["To use box-shadow in Internet Explorer 9 or later, you must set border-collapse to separate.","Since version 5.5, Internet Explorer supports Microsoft's DropShadow and Shadow Filter. You can use this proprietary extension to cast a drop shadow (though the syntax and the effect are different from CSS3)"]},"opera":{"version_added":"10.5","notes":"Shadows affect layout in this browser. For example, if you cast an outer shadow to a box with a width of 100%, then you'll see a scrollbar."},"opera_android":[{"version_added":"14","notes":"Shadows affect layout in this browser. For example, if you cast an outer shadow to a box with a width of 100%, then you'll see a scrollbar."},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"5.1","notes":"Shadows affect layout in this browser. For example, if you cast an outer shadow to a box with a width of 100%, then you'll see a scrollbar."},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"5","notes":"Shadows affect layout in this browser. For example, if you cast an outer shadow to a box with a width of 100%, then you'll see a scrollbar."},{"prefix":"-webkit-","version_added":"1"}],"samsunginternet_android":[{"version_added":"1.0","notes":"Shadows affect layout in this browser. For example, if you cast an outer shadow to a box with a width of 100%, then you'll see a scrollbar."},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"≤37","notes":"Shadows affect layout in this browser. For example, if you cast an outer shadow to a box with a width of 100%, then you'll see a scrollbar."},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"inset":{"__compat":{"description":"inset","support":{"chrome":[{"version_added":"10"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"18"},{"prefix":"-webkit-","version_added":"18"}],"edge":{"version_added":"12"},"firefox":[{"version_added":"4"},{"prefix":"-moz-","version_added":"3.5","version_removed":"13"}],"firefox_android":[{"version_added":"4"},{"prefix":"-moz-","version_added":"4","version_removed":"14"}],"ie":{"version_added":"9","partial_implementation":true,"notes":["To use box-shadow in Internet Explorer 9 or later, you must set border-collapse to separate.","inset must be the last keyword in the declaration."]},"opera":{"version_added":"10.5"},"opera_android":[{"version_added":"14"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"5.1"},{"prefix":"-webkit-","version_added":"5"}],"safari_ios":[{"version_added":"5"},{"prefix":"-webkit-","version_added":"4.2"}],"samsunginternet_android":[{"version_added":"1.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"≤37"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"multiple_shadows":{"__compat":{"description":"Multiple shadows","support":{"chrome":[{"version_added":"10"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"18"},{"prefix":"-webkit-","version_added":"18"}],"edge":{"version_added":"12"},"firefox":[{"version_added":"4"},{"prefix":"-moz-","version_added":"3.5","version_removed":"13"}],"firefox_android":[{"version_added":"4"},{"prefix":"-moz-","version_added":"4","version_removed":"14"}],"ie":{"version_added":"9","notes":"To use box-shadow in Internet Explorer 9 or later, you must set border-collapse to separate."},"opera":{"version_added":"10.5"},"opera_android":[{"version_added":"14"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"5.1"},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"5"},{"prefix":"-webkit-","version_added":"1"}],"samsunginternet_android":[{"version_added":"1.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"≤37"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"spread_radius":{"__compat":{"description":"Spread radius","support":{"chrome":[{"version_added":"10"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"18"},{"prefix":"-webkit-","version_added":"18"}],"edge":{"version_added":"12"},"firefox":[{"version_added":"4"},{"prefix":"-moz-","version_added":"3.5","version_removed":"13"}],"firefox_android":[{"version_added":"4"},{"prefix":"-moz-","version_added":"4","version_removed":"14"}],"ie":{"version_added":"9","notes":"To use box-shadow in Internet Explorer 9 or later, you must set border-collapse to separate."},"opera":{"version_added":"10.5"},"opera_android":[{"version_added":"14"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"5.1"},{"prefix":"-webkit-","version_added":"5"}],"safari_ios":[{"version_added":"5"},{"prefix":"-webkit-","version_added":"4.2"}],"samsunginternet_android":[{"version_added":"1.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"≤37"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"box-sizing":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/box-sizing","spec_url":"https://drafts.csswg.org/css-sizing/#box-sizing","support":{"chrome":[{"version_added":"10","notes":"box-sizing is not respected when the height is calculated from window.getComputedStyle()."},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"18","notes":"box-sizing is not respected when the height is calculated from window.getComputedStyle()."},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"29"},{"prefix":"-moz-","version_added":"1","notes":"Before Firefox 23, box-sizing is not respected when the height is calculated from window.getComputedStyle()."},{"prefix":"-webkit-","version_added":"49"}],"firefox_android":[{"version_added":"29"},{"prefix":"-moz-","version_added":"4","notes":"Before Firefox 23, box-sizing is not respected when the height is calculated from window.getComputedStyle()."},{"prefix":"-webkit-","version_added":"49"}],"ie":{"version_added":"8","notes":"box-sizing is not respected when the height is calculated from window.getComputedStyle()."},"opera":{"version_added":"7"},"opera_android":[{"version_added":"14","notes":"box-sizing is not respected when the height is calculated from window.getComputedStyle()."},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"5.1"},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"6"},{"prefix":"-webkit-","version_added":"1"}],"samsunginternet_android":[{"version_added":"1.0","notes":"box-sizing is not respected when the height is calculated from window.getComputedStyle()."},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"4","notes":"box-sizing is not respected when the height is calculated from window.getComputedStyle()."},{"prefix":"-webkit-","version_added":"2"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"padding-box":{"__compat":{"description":"padding-box","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1","version_removed":"50"},"firefox_android":{"version_added":"4","version_removed":"50"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}}},"break-after":{"multicol_context":{"__compat":{"description":"Supported in Multi-column Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/break-after","spec_url":["https://drafts.csswg.org/css-break/#break-between","https://drafts.csswg.org/css-regions/#region-flow-break","https://drafts.csswg.org/css-multicol/#break-before-break-after-break-inside"],"support":{"chrome":{"version_added":"50"},"chrome_android":{"version_added":"50"},"edge":{"version_added":"12"},"firefox":{"version_added":"65","partial_implementation":true,"notes":"Only supported in print mode. See bug 1675322."},"firefox_android":{"version_added":"65","partial_implementation":true,"notes":"Only supported in print mode. See bug 1675322."},"ie":{"version_added":"10"},"opera":[{"version_added":"37"},{"version_added":"11.1","version_removed":"12.1"}],"opera_android":[{"version_added":"37"},{"version_added":"11.1","version_removed":"12.1"}],"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"50"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"always":{"__compat":{"description":"always","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"65","partial_implementation":true,"notes":"Only supported in print mode. See bug 1675322."},"firefox_android":{"version_added":"65","partial_implementation":true,"notes":"Only supported in print mode. See bug 1675322."},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"avoid-column":{"__compat":{"description":"avoid-column","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"column":{"__compat":{"description":"column","support":{"chrome":{"version_added":"50"},"chrome_android":{"version_added":"50"},"edge":{"version_added":"12"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":"10"},"opera":[{"version_added":"11.1","version_removed":"15"},{"version_added":"37"}],"opera_android":[{"version_added":"11.1","version_removed":"14"},{"version_added":"37"}],"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"50"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"paged_context":{"__compat":{"description":"Supported in Paged Media","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/break-after","spec_url":["https://drafts.csswg.org/css-break/#break-between","https://drafts.csswg.org/css-regions/#region-flow-break","https://drafts.csswg.org/css-multicol/#break-before-break-after-break-inside"],"support":{"chrome":{"version_added":"50"},"chrome_android":{"version_added":"50"},"edge":{"version_added":"12"},"firefox":{"version_added":"65"},"firefox_android":{"version_added":"65"},"ie":{"version_added":"10"},"opera":[{"version_added":"37"},{"version_added":"11.1","version_removed":"12.1"}],"opera_android":[{"version_added":"37"},{"version_added":"11.1","version_removed":"12.1"}],"safari":{"version_added":"10"},"safari_ios":{"version_added":"10"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"50"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"always":{"__compat":{"description":"always","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"79"},"firefox":{"version_added":"65"},"firefox_android":{"version_added":"65"},"ie":{"version_added":"10"},"opera":{"version_added":"11.1","version_removed":"12.1"},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":"50"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"page":{"__compat":{"description":"page and avoid-page","support":{"chrome":{"version_added":"50"},"chrome_android":{"version_added":"50"},"edge":{"version_added":"12"},"firefox":{"version_added":"65"},"firefox_android":{"version_added":"65"},"ie":{"version_added":"10"},"opera":{"version_added":"11.1","version_removed":"12.1"},"opera_android":{"version_added":"37"},"safari":{"version_added":"10"},"safari_ios":{"version_added":"10"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"50"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"recto":{"__compat":{"description":"recto and verso","support":{"chrome":{"version_added":false,"notes":"See bug 538475."},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}}},"break-before":{"multicol_context":{"__compat":{"description":"Supported in Multi-column Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/break-before","spec_url":["https://drafts.csswg.org/css-break/#break-between","https://drafts.csswg.org/css-regions/#region-flow-break","https://drafts.csswg.org/css-multicol/#break-before-break-after-break-inside"],"support":{"chrome":{"version_added":"50"},"chrome_android":{"version_added":"50"},"edge":{"version_added":"12"},"firefox":{"version_added":"65","partial_implementation":true,"notes":"Only supported in print mode. See bug 1675322."},"firefox_android":{"version_added":"65","partial_implementation":true,"notes":"Only supported in print mode. See bug 1675322."},"ie":{"version_added":"10"},"opera":[{"version_added":"37"},{"version_added":"11.1","version_removed":"12.1"}],"opera_android":[{"version_added":"37"},{"version_added":"11.1","version_removed":"12.1"}],"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"50"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"always":{"__compat":{"description":"always","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"65","partial_implementation":true,"notes":"Only supported in print mode. See bug 1675322."},"firefox_android":{"version_added":"65","partial_implementation":true,"notes":"Only supported in print mode. See bug 1675322."},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"avoid-column":{"__compat":{"description":"avoid-column","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":"10"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"column":{"__compat":{"description":"column","support":{"chrome":{"version_added":"51"},"chrome_android":{"version_added":"51"},"edge":{"version_added":"12"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":"10"},"opera":{"version_added":"38"},"opera_android":{"version_added":"41"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"51"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"paged_context":{"__compat":{"description":"Supported in Paged Media","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/break-before","spec_url":["https://drafts.csswg.org/css-break/#break-between","https://drafts.csswg.org/css-regions/#region-flow-break","https://drafts.csswg.org/css-multicol/#break-before-break-after-break-inside"],"support":{"chrome":{"version_added":"50"},"chrome_android":{"version_added":"50"},"edge":{"version_added":"12"},"firefox":{"version_added":"65"},"firefox_android":{"version_added":"65"},"ie":{"version_added":"10"},"opera":[{"version_added":"37"},{"version_added":"11.1","version_removed":"12.1"}],"opera_android":[{"version_added":"37"},{"version_added":"11.1","version_removed":"12.1"}],"safari":{"version_added":"10"},"safari_ios":{"version_added":"10"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"50"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"always":{"__compat":{"description":"always","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"79"},"firefox":{"version_added":"65"},"firefox_android":{"version_added":"65"},"ie":{"version_added":"10"},"opera":{"version_added":"11.1","version_removed":"12.1"},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":"50"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"page":{"__compat":{"description":"page and avoid-page","support":{"chrome":{"version_added":"50"},"chrome_android":{"version_added":"50"},"edge":{"version_added":"12"},"firefox":{"version_added":"65"},"firefox_android":{"version_added":"65"},"ie":{"version_added":"10"},"opera":{"version_added":"11.1","version_removed":"12.1"},"opera_android":{"version_added":"37"},"safari":{"version_added":"10"},"safari_ios":{"version_added":"10"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"50"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"recto":{"__compat":{"description":"recto and verso","support":{"chrome":{"version_added":false,"notes":"See bug 538475."},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}}},"break-inside":{"multicol_context":{"__compat":{"description":"Supported in Multi-column Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/break-inside","spec_url":["https://drafts.csswg.org/css-break/#break-within","https://drafts.csswg.org/css-regions/#region-flow-break","https://drafts.csswg.org/css-multicol/#break-before-break-after-break-inside"],"support":{"chrome":{"version_added":"50"},"chrome_android":{"version_added":"50"},"edge":{"version_added":"12"},"firefox":{"version_added":"65"},"firefox_android":{"version_added":"65"},"ie":{"version_added":"10"},"opera":[{"version_added":"37"},{"version_added":"11.1","version_removed":"12.1"}],"opera_android":[{"version_added":"37"},{"version_added":"11.1","version_removed":"12.1"}],"safari":{"version_added":"10"},"safari_ios":{"version_added":"10"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"50"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"column":{"__compat":{"description":"column and avoid-column","support":{"chrome":{"version_added":"50"},"chrome_android":{"version_added":"50"},"edge":{"version_added":"12"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":"10"},"opera":{"version_added":"37"},"opera_android":{"version_added":"37"},"safari":{"version_added":"10"},"safari_ios":{"version_added":"10"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"50"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"paged_context":{"__compat":{"description":"Supported in Paged Media","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/break-inside","spec_url":["https://drafts.csswg.org/css-break/#break-within","https://drafts.csswg.org/css-regions/#region-flow-break","https://drafts.csswg.org/css-multicol/#break-before-break-after-break-inside"],"support":{"chrome":{"version_added":"50"},"chrome_android":{"version_added":"50"},"edge":{"version_added":"12"},"firefox":{"version_added":"65"},"firefox_android":{"version_added":"65"},"ie":{"version_added":"10"},"opera":[{"version_added":"37"},{"version_added":"11.1","version_removed":"12.1"}],"opera_android":[{"version_added":"37"},{"version_added":"11.1","version_removed":"12.1"}],"safari":{"version_added":"10"},"safari_ios":{"version_added":"10"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"50"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"page":{"__compat":{"description":"page and avoid-page","support":{"chrome":{"version_added":"51"},"chrome_android":{"version_added":"51"},"edge":{"version_added":"12"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":[{"version_added":"38"},{"version_added":"11.1","version_removed":"12.1"}],"opera_android":[{"version_added":"41"},{"version_added":"11.1","version_removed":"12.1"}],"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"51"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}}},"caption-side":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/caption-side","spec_url":"https://drafts.csswg.org/css-logical/#caption-side","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"4"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"non_standard_values":{"__compat":{"description":"Non-standard values left, right, top-outside, and bottom-outside","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1","version_removed":"87"},"firefox_android":{"version_added":"4","version_removed":"87"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"writing-mode_relative_values":{"__compat":{"description":"top and bottom are relative to the writing-mode value","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"42"},"firefox_android":{"version_added":"42"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"caret-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/caret-color","spec_url":"https://drafts.csswg.org/css-ui/#caret-color","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":"53"},"firefox_android":{"version_added":"53"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"11.1"},"safari_ios":{"version_added":"11.3","partial_implementation":true,"notes":"While the property is recognized, implementation is incomplete. The color of the caret does not always match the color set for the element in focus. See bug 177489."},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"clear":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/clear","spec_url":"https://drafts.csswg.org/css-logical/#float-clear","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"flow_relative_values":{"__compat":{"description":"Flow-relative values inline-start and inline-end","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"55"},"firefox_android":{"version_added":"55"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"clip-path":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/clip-path","spec_url":["https://drafts.fxtf.org/css-masking/#the-clip-path","https://drafts.csswg.org/css-shapes/#supported-basic-shapes"],"support":{"chrome":[{"version_added":"55"},{"prefix":"-webkit-","version_added":"23"}],"chrome_android":[{"version_added":"55"},{"prefix":"-webkit-","version_added":"25"}],"edge":{"version_added":"12","notes":"Edge only supports clip paths defined by url()."},"firefox":{"version_added":"3.5"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"10","notes":"Internet Explorer only supports clip paths defined by url()."},"opera":[{"version_added":"42"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"42"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9.1"},{"prefix":"-webkit-","version_added":"6.1"}],"safari_ios":[{"version_added":"9.3"},{"prefix":"-webkit-","version_added":"6.1"}],"samsunginternet_android":[{"version_added":"6.0"},{"prefix":"-webkit-","version_added":"1.5"}],"webview_android":[{"version_added":"55"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"animations":{"__compat":{"description":"Animations","support":{"chrome":{"version_added":"55"},"chrome_android":{"version_added":"55"},"edge":{"version_added":"79"},"firefox":{"version_added":"49"},"firefox_android":{"version_added":"49"},"ie":{"version_added":false},"opera":{"version_added":"42"},"opera_android":{"version_added":"42"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"55"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"basic_shape":{"__compat":{"description":"<basic-shape>","support":{"chrome":{"version_added":"23"},"chrome_android":{"version_added":"25"},"edge":{"version_added":"79"},"firefox":{"version_added":"54"},"firefox_android":{"version_added":"54"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"6.1"},"safari_ios":{"version_added":"6.1"},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"fill_and_stroke_box":{"__compat":{"description":"fill-box and stroke-box","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"51","notes":"This value was supported before Firefox 51, but as an alias to border-box."},"firefox_android":{"version_added":"51","notes":"This value was supported before Firefox 51, but as an alias to border-box."},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"html":{"__compat":{"description":"On HTML elements","support":{"chrome":{"version_added":"23"},"chrome_android":{"version_added":"25"},"edge":{"version_added":"79"},"firefox":{"version_added":"3.5"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"6.1"},"safari_ios":{"version_added":"6.1"},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"path":{"__compat":{"description":"path()","support":{"chrome":{"version_added":"88"},"chrome_android":{"version_added":"88"},"edge":{"version_added":"88"},"firefox":[{"version_added":"71"},{"version_added":"63","flags":[{"type":"preference","name":"layout.css.clip-path-path.enabled","value_to_set":"true"}]}],"firefox_android":{"version_added":"63","flags":[{"type":"preference","name":"layout.css.clip-path-path.enabled","value_to_set":"true"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":[{"version_added":"13.1"},{"prefix":"-webkit-","version_added":"10"}],"safari_ios":[{"version_added":"13"},{"prefix":"-webkit-","version_added":"10"}],"samsunginternet_android":{"version_added":"15.0"},"webview_android":{"version_added":"88"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"svg":{"__compat":{"description":"On SVG elements","support":{"chrome":{"version_added":"23"},"chrome_android":{"version_added":"25"},"edge":{"version_added":"12"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":"10"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"6.1"},"safari_ios":{"version_added":"6.1"},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"clip":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/clip","spec_url":"https://drafts.fxtf.org/css-masking/#clip-property","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4","notes":"Before Internet Explorer 7, Internet Explorer incorrectly interprets clip: auto as clip: rect(auto, auto, auto, auto)."},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1","notes":"Safari incorrectly interprets clip: auto as clip: rect(auto, auto, auto, auto)."},"safari_ios":{"version_added":"1","notes":"Safari incorrectly interprets clip: auto as clip: rect(auto, auto, auto, auto)."},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":true}}},"color-adjust":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/color-adjust","spec_url":"https://drafts.csswg.org/css-color-adjust/#perf","support":{"chrome":{"version_added":"49","alternative_name":"-webkit-print-color-adjust"},"chrome_android":{"version_added":"49","alternative_name":"-webkit-print-color-adjust"},"edge":{"version_added":"79","alternative_name":"-webkit-print-color-adjust"},"firefox":{"version_added":"48"},"firefox_android":{"version_added":"48"},"ie":{"version_added":false},"opera":{"version_added":"15","alternative_name":"-webkit-print-color-adjust"},"opera_android":{"version_added":"36","alternative_name":"-webkit-print-color-adjust"},"safari":{"version_added":"6","alternative_name":"-webkit-print-color-adjust"},"safari_ios":{"version_added":"6","alternative_name":"-webkit-print-color-adjust"},"samsunginternet_android":{"version_added":"5.0","alternative_name":"-webkit-print-color-adjust"},"webview_android":{"version_added":"49","alternative_name":"-webkit-print-color-adjust"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"color-scheme":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/color-scheme","spec_url":"https://drafts.csswg.org/css-color-adjust/#color-scheme-prop","support":{"chrome":{"version_added":"81"},"chrome_android":{"version_added":"81"},"edge":{"version_added":"81"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"68"},"opera_android":{"version_added":"58"},"safari":{"version_added":"13"},"safari_ios":{"version_added":"13"},"samsunginternet_android":{"version_added":"13.0"},"webview_android":{"version_added":"81"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"only_dark":{"__compat":{"description":"only dark keyword","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"13"},"safari_ios":{"version_added":"13"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"only_light":{"__compat":{"description":"only light keyword","support":{"chrome":{"version_added":"81","version_removed":"85"},"chrome_android":{"version_added":"81","version_removed":"85"},"edge":{"version_added":"81","version_removed":"85"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"68","version_removed":"71"},"opera_android":{"version_added":"58","version_removed":"60"},"safari":{"version_added":"13"},"safari_ios":{"version_added":"13"},"samsunginternet_android":{"version_added":"13.0","version_removed":"14.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/color","spec_url":"https://drafts.csswg.org/css-color/#the-color-property","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"3"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"column-count":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/column-count","spec_url":"https://drafts.csswg.org/css-multicol/#cc","support":{"chrome":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"52"},{"prefix":"-moz-","version_added":"1.5","version_removed":"74","notes":"Prior to version 37, multiple columns didn't work with display: table-caption elements."}],"firefox_android":[{"version_added":"52"},{"prefix":"-moz-","version_added":"4","notes":"Prior to version 37, multiple columns didn't work with display: table-caption elements."}],"ie":{"version_added":"10"},"opera":[{"version_added":"11.1"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"11.1"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"1"}],"samsunginternet_android":[{"version_added":"5.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"column-fill":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/column-fill","spec_url":"https://drafts.csswg.org/css-multicol/#cf","support":{"chrome":{"version_added":"50"},"chrome_android":{"version_added":"50"},"edge":{"version_added":"12"},"firefox":[{"version_added":"52"},{"prefix":"-moz-","version_added":"13","version_removed":"74"}],"firefox_android":[{"version_added":"52"},{"prefix":"-moz-","version_added":"14"}],"ie":{"version_added":"10"},"opera":{"version_added":"37"},"opera_android":{"version_added":"37"},"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"8"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"8"}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"50"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"balance-all":{"__compat":{"description":"balance-all","support":{"chrome":{"version_added":false,"notes":"See bug 909596."},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}}},"column-gap":{"flex_context":{"__compat":{"description":"Supported in Flex Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/column-gap","spec_url":["https://drafts.csswg.org/css-align/#column-row-gap","https://drafts.csswg.org/css-grid/#gutters","https://drafts.csswg.org/css-multicol/#column-gap"],"support":{"chrome":{"version_added":"84"},"chrome_android":{"version_added":"84"},"edge":{"version_added":"84"},"firefox":{"version_added":"63"},"firefox_android":{"version_added":"63"},"ie":{"version_added":false},"opera":{"version_added":"70"},"opera_android":{"version_added":false},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"84"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid_context":{"__compat":{"description":"Supported in Grid Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/column-gap","spec_url":["https://drafts.csswg.org/css-align/#column-row-gap","https://drafts.csswg.org/css-grid/#gutters","https://drafts.csswg.org/css-multicol/#column-gap"],"support":{"chrome":[{"version_added":"66"},{"version_added":"57","alternative_name":"grid-column-gap"}],"chrome_android":[{"version_added":"66"},{"version_added":"57","alternative_name":"grid-column-gap"}],"edge":[{"version_added":"16"},{"version_added":"16","alternative_name":"grid-column-gap"}],"firefox":[{"version_added":"61"},{"version_added":"52","alternative_name":"grid-column-gap"}],"firefox_android":[{"version_added":"61"},{"version_added":"52","alternative_name":"grid-column-gap"}],"ie":{"version_added":false},"opera":[{"version_added":"53"},{"version_added":"44","alternative_name":"grid-column-gap"}],"opera_android":[{"version_added":"47"},{"version_added":"43","alternative_name":"grid-column-gap"}],"safari":[{"version_added":"12"},{"version_added":"10.1","alternative_name":"grid-column-gap"}],"safari_ios":[{"version_added":"12"},{"version_added":"10.3","alternative_name":"grid-column-gap"}],"samsunginternet_android":[{"version_added":"9.0"},{"alternative_name":"grid-column-gap","version_added":"6.0"}],"webview_android":[{"version_added":"66"},{"version_added":"57","alternative_name":"grid-column-gap"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"multicol_context":{"__compat":{"description":"Supported in Multi-column Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/column-gap","spec_url":["https://drafts.csswg.org/css-align/#column-row-gap","https://drafts.csswg.org/css-grid/#gutters","https://drafts.csswg.org/css-multicol/#column-gap"],"support":{"chrome":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"52"},{"prefix":"-moz-","version_added":"1.5","version_removed":"74","notes":"Before Firefox 3, the default value for the normal keyword was 0 and not 1em."}],"firefox_android":[{"version_added":"52"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":"10"},"opera":[{"version_added":"37"},{"prefix":"-webkit-","version_added":"15"},{"version_added":"11.1","version_removed":"15"}],"opera_android":[{"version_added":"37"},{"prefix":"-webkit-","version_added":"14"},{"version_added":"11.1","version_removed":"14"}],"safari":[{"version_added":"10"},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"10"},{"prefix":"-webkit-","version_added":"3"}],"samsunginternet_android":[{"version_added":"5.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"calc_values":{"__compat":{"description":"calc() values","support":{"chrome":{"version_added":"66"},"chrome_android":{"version_added":"66"},"edge":{"version_added":"16"},"firefox":{"version_added":"61"},"firefox_android":{"version_added":"61"},"ie":{"version_added":false},"opera":{"version_added":"53"},"opera_android":{"version_added":"47"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"9.0"},"webview_android":{"version_added":"66"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"percentage_values":{"__compat":{"description":"<percentage> values","support":{"chrome":{"version_added":"66"},"chrome_android":{"version_added":"66"},"edge":{"version_added":"16"},"firefox":{"version_added":"61"},"firefox_android":{"version_added":"61"},"ie":{"version_added":false},"opera":{"version_added":"53"},"opera_android":{"version_added":"47"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"9.0"},"webview_android":{"version_added":"66"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}}},"column-rule-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/column-rule-color","spec_url":"https://drafts.csswg.org/css-multicol/#crc","support":{"chrome":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"52"},{"prefix":"-moz-","version_added":"3.5","version_removed":"74"}],"firefox_android":[{"version_added":"52"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":"10"},"opera":[{"version_added":"11.1"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"11.1"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"1"}],"samsunginternet_android":[{"version_added":"5.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"column-rule-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/column-rule-style","spec_url":"https://drafts.csswg.org/css-multicol/#crs","support":{"chrome":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"52"},{"prefix":"-moz-","version_added":"3.5","version_removed":"74"}],"firefox_android":[{"version_added":"52"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":"10"},"opera":[{"version_added":"11.1"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"11.1"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"1"}],"samsunginternet_android":[{"version_added":"5.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"column-rule-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/column-rule-width","spec_url":"https://drafts.csswg.org/css-multicol/#crw","support":{"chrome":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"52"},{"prefix":"-moz-","version_added":"3.5","version_removed":"74"}],"firefox_android":[{"version_added":"52"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":"10"},"opera":[{"version_added":"11.1"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"11.1"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"1"}],"samsunginternet_android":[{"version_added":"5.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"column-rule":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/column-rule","spec_url":"https://drafts.csswg.org/css-multicol/#column-rule","support":{"chrome":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"52"},{"prefix":"-moz-","version_added":"3.5","version_removed":"74","notes":"Before Firefox 3, the default value for the normal keyword was 0 and not 1em."}],"firefox_android":[{"version_added":"52"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":"10"},"opera":[{"version_added":"11.1"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"11.1"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"1"}],"samsunginternet_android":[{"version_added":"5.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"column-span":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/column-span","spec_url":"https://drafts.csswg.org/css-multicol/#column-span","support":{"chrome":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"6"}],"chrome_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"71"},{"version_added":"65","flags":[{"type":"preference","name":"layout.css.column-span.enabled","value_to_set":"true"}]}],"firefox_android":{"version_added":"65","flags":[{"type":"preference","name":"layout.css.column-span.enabled","value_to_set":"true"}]},"ie":{"version_added":"10"},"opera":[{"version_added":"11.1"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"11.1"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"5.1"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"5"}],"samsunginternet_android":[{"version_added":"5.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"column-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/column-width","spec_url":["https://drafts.csswg.org/css-sizing/#column-sizing","https://drafts.csswg.org/css-multicol/#cw"],"support":{"chrome":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"50"},{"prefix":"-moz-","version_added":"1.5","version_removed":"74","notes":"Prior to version 37, multiple columns didn't work with display: table-caption elements."}],"firefox_android":[{"version_added":"50"},{"prefix":"-moz-","version_added":"4","notes":"Prior to version 37, multiple columns didn't work with display: table-caption elements."}],"ie":{"version_added":"10"},"opera":[{"version_added":"11.1"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"11.1"},{"prefix":"-webkit-","version_added":"15"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"1"}],"samsunginternet_android":[{"version_added":"5.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"intrinsic_sizes":{"__compat":{"description":"Intrinsic sizes","support":{"chrome":{"version_added":false,"notes":"See bug 964183."},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}}},"columns":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/columns","spec_url":"https://drafts.csswg.org/css-multicol/#columns","support":{"chrome":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"50"}],"chrome_android":{"version_added":"50"},"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"52"},{"prefix":"-moz-","version_added":"9","version_removed":"74","notes":"Prior to version 37, multiple columns didn't work with display: table-caption elements."}],"firefox_android":[{"version_added":"52"},{"prefix":"-moz-","version_added":"22","notes":"Prior to version 37, multiple columns didn't work with display: table-caption elements."}],"ie":{"version_added":"10"},"opera":[{"version_added":"11.1"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"11.1"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3.2"}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":[{"version_added":"50"},{"prefix":"-webkit-","version_added":"2"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"contain-intrinsic-size":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/contain-intrinsic-size","spec_url":"https://drafts.csswg.org/css-sizing-4/#propdef-contain-intrinsic-size","support":{"chrome":{"version_added":"83"},"chrome_android":{"version_added":"83"},"edge":{"version_added":"83"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"69"},"opera_android":{"version_added":"59"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"13.0"},"webview_android":{"version_added":"83"}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"contain":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/contain","spec_url":"https://drafts.csswg.org/css-contain/#contain-property","support":{"chrome":{"version_added":"52"},"chrome_android":{"version_added":"52"},"edge":{"version_added":"79"},"firefox":[{"version_added":"69","notes":"Firefox does not support the style value."},{"version_added":"41","flags":[{"type":"preference","name":"layout.css.contain.enabled","value_to_set":"true"}]}],"firefox_android":{"version_added":"41","flags":[{"type":"preference","name":"layout.css.contain.enabled","value_to_set":"true"}],"notes":"Firefox does not support the style value."},"ie":{"version_added":false},"opera":{"version_added":"40"},"opera_android":{"version_added":"41"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"52"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"content-visibility":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/content-visibility","spec_url":"https://drafts.csswg.org/css-contain/#content-visibility","support":{"chrome":{"version_added":"85"},"chrome_android":{"version_added":"85"},"edge":{"version_added":"85"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"71"},"opera_android":{"version_added":"60"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"85"}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"content":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/content","spec_url":"https://drafts.csswg.org/css-content/#content-property","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"4"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"alt_text":{"__compat":{"description":"Alternative text after /","support":{"chrome":{"version_added":"77"},"chrome_android":{"version_added":"77"},"edge":{"version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"64"},"opera_android":{"version_added":"55"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"12.0"},"webview_android":{"version_added":"77"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"element_replacement":{"__compat":{"description":"Element replacement","support":{"chrome":{"version_added":"28"},"chrome_android":{"version_added":"28"},"edge":{"version_added":"79"},"firefox":{"version_added":"63"},"firefox_android":{"version_added":"63"},"ie":{"version_added":false},"opera":{"version_added":"7"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"none_applies_to_elements":{"__compat":{"description":"content: none for elements","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.element-content-none.enabled"}]},"firefox_android":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.element-content-none.enabled"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"url":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/url()","spec_url":"https://drafts.csswg.org/css-values/#urls","description":"url()","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"counter-increment":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/counter-increment","spec_url":"https://drafts.csswg.org/css-lists/#increment-set","support":{"chrome":{"version_added":"2"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"25"},"ie":{"version_added":"8"},"opera":{"version_added":"9.2"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"counter-reset":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/counter-reset","spec_url":"https://drafts.csswg.org/css-lists/#counter-reset","support":{"chrome":{"version_added":"2"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"25"},"ie":{"version_added":"8"},"opera":{"version_added":"9.2"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"counter-set":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/counter-set","spec_url":"https://drafts.csswg.org/css-lists/#propdef-counter-set","support":{"chrome":{"version_added":"85"},"chrome_android":{"version_added":"85"},"edge":{"version_added":"85"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"71"},"opera_android":{"version_added":"60"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"85"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"cursor":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/cursor","spec_url":"https://drafts.csswg.org/css-ui/#cursor","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"Starting in Firefox 67, the maximum size allowed for custom cursors is 32x32 pixels due to cursors being misused by certain malicious sites."},"firefox_android":{"version_added":false},"ie":{"version_added":"4","notes":"In Internet Explorer 11, when cursor is applied to an element and this element is underneath an open <select> menu and the user hovers over a <select> menu item that's on top of said element, the cursor for said element will be displayed rather than the <select>'s normal cursor. See bug 817822."},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"alias":{"__compat":{"description":"alias","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1.5"},"firefox_android":{"version_added":false},"ie":{"version_added":"10"},"opera":{"version_added":"10.6"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"all-scroll":{"__compat":{"description":"all-scroll","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1.5"},"firefox_android":{"version_added":false},"ie":{"version_added":"6"},"opera":{"version_added":"10.6"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"auto":{"__compat":{"description":"auto","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":false},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"bidirectional_resize":{"__compat":{"description":"Bidirectional resize cursors (ew-resize, nesw-resize, ns-resize, and nwse-resize)","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1.5"},"firefox_android":{"version_added":false},"ie":{"version_added":"10"},"opera":{"version_added":"10.6"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"cell":{"__compat":{"description":"cell","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1.5"},"firefox_android":{"version_added":false},"ie":{"version_added":"10"},"opera":{"version_added":"10.6"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"col-resize":{"__compat":{"description":"col-resize","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1.5"},"firefox_android":{"version_added":false},"ie":{"version_added":"6"},"opera":{"version_added":"10.6"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"context-menu":{"__compat":{"description":"context-menu","support":{"chrome":{"version_added":"1","notes":"This cursor is only supported on macOS and Linux."},"chrome_android":{"version_added":"18","notes":"This cursor is only supported on macOS and Linux."},"edge":{"version_added":"12"},"firefox":{"version_added":"1.5","notes":"This cursor is only supported on macOS and Linux."},"firefox_android":{"version_added":false},"ie":{"version_added":"10"},"opera":{"version_added":"10.6"},"opera_android":{"version_added":"14","notes":"This cursor is only supported on macOS and Linux."},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0","notes":"This cursor is only supported on macOS and Linux."},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"copy":{"__compat":{"description":"copy","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1.5"},"firefox_android":{"version_added":false},"ie":{"version_added":"10"},"opera":{"version_added":"10.6"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"crosshair":{"__compat":{"description":"crosshair","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":false},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"default":{"__compat":{"description":"default","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":false},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grab":{"__compat":{"description":"Grab cursors (grab and grabbing)","support":{"chrome":[{"version_added":"68","notes":"Chrome also continues to support the prefixed versions."},{"prefix":"-webkit-","version_added":"1","notes":"Chrome 22 added Windows support."}],"chrome_android":[{"version_added":"68","notes":"Chrome also continues to support the prefixed versions."},{"prefix":"-webkit-","version_added":"18","notes":"Chrome 22 added Windows support."}],"edge":{"version_added":"14"},"firefox":[{"version_added":"27"},{"prefix":"-moz-","version_added":"1.5"}],"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":[{"version_added":"55","notes":"Opera also continues to support the prefixed versions."},{"prefix":"-webkit-","version_added":"15","notes":"Opera 22 added Windows support."}],"opera_android":[{"version_added":"48","notes":"Opera also continues to support the prefixed versions."},{"prefix":"-webkit-","version_added":"14","notes":"Opera 22 added Windows support."}],"safari":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"4"}],"safari_ios":{"version_added":"1"},"samsunginternet_android":[{"version_added":"10.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"help":{"__compat":{"description":"help","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":false},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"inherit":{"__compat":{"description":"inherit","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":false},"ie":{"version_added":"8"},"opera":{"version_added":"9"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"move":{"__compat":{"description":"move","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":false},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"no-drop":{"__compat":{"description":"no-drop","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1.5"},"firefox_android":{"version_added":false},"ie":{"version_added":"6"},"opera":{"version_added":"10.6"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"none":{"__compat":{"description":"none","support":{"chrome":{"version_added":"5"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3"},"firefox_android":{"version_added":false},"ie":{"version_added":"9"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"not-allowed":{"__compat":{"description":"not-allowed","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1.5"},"firefox_android":{"version_added":false},"ie":{"version_added":"6"},"opera":{"version_added":"10.6"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"pointer":{"__compat":{"description":"pointer","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":false},"ie":{"version_added":"6"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"progress":{"__compat":{"description":"progress","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":false},"ie":{"version_added":"6"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"row-resize":{"__compat":{"description":"row-resize","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1.5"},"firefox_android":{"version_added":false},"ie":{"version_added":"6"},"opera":{"version_added":"10.6"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"text":{"__compat":{"description":"text","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":false},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"unidirectional_resize":{"__compat":{"description":"Unidirectional resize cursors (n-resize, e-resize, s-resize, w-resize, ne-resize, nw-resize, se-resize, and sw-resize)","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":false},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"url":{"__compat":{"description":"url()","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1.5","notes":"Firefox 4 added macOS support."},"firefox_android":{"version_added":false},"ie":{"version_added":"6"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"url_positioning_syntax":{"__compat":{"description":"url() positioning syntax","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"1.5","notes":"Firefox 4 added macOS support."},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"vertical-text":{"__compat":{"description":"vertical-text","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1.5"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"10.6"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"wait":{"__compat":{"description":"wait","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":false},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"zoom":{"__compat":{"description":"Zoom cursors (zoom-in and zoom-out)","support":{"chrome":[{"version_added":"37"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"37"},{"prefix":"-webkit-","version_added":"18"}],"edge":{"version_added":"12"},"firefox":[{"version_added":"24"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":[{"version_added":"24"},{"prefix":"-webkit-","version_added":"15","version_removed":"23"}],"opera_android":[{"version_added":"24"},{"prefix":"-webkit-","version_added":"14","version_removed":"24"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3"}],"safari_ios":{"version_added":"1"},"samsunginternet_android":[{"version_added":"3.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"custom-property":{"__compat":{"description":"--*","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/--*","spec_url":"https://drafts.csswg.org/css-variables/#defining-variables","support":{"chrome":{"version_added":"49"},"chrome_android":{"version_added":"49"},"edge":{"version_added":"15"},"firefox":{"version_added":"31"},"firefox_android":{"version_added":"31"},"ie":{"version_added":false},"opera":{"version_added":"36"},"opera_android":{"version_added":"36"},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"49"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"env":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/env()","spec_url":"https://drafts.csswg.org/css-env/#env-function","description":"env()","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"65"},"firefox_android":{"version_added":"65"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":[{"version_added":"11.1"},{"version_added":"11","version_removed":"11.1","alternative_name":"constant"}],"safari_ios":[{"version_added":"11.3"},{"version_added":"11","version_removed":"11.3","alternative_name":"constant"}],"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"var":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/var()","spec_url":"https://drafts.csswg.org/css-variables/#using-variables","description":"var()","support":{"chrome":{"version_added":"49"},"chrome_android":{"version_added":"49"},"edge":{"version_added":"15"},"firefox":{"version_added":"31"},"firefox_android":{"version_added":"31"},"ie":{"version_added":false},"opera":{"version_added":"36"},"opera_android":{"version_added":"36"},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"50"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"direction":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/direction","spec_url":"https://drafts.csswg.org/css-writing-modes/#direction","support":{"chrome":{"version_added":"2"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"5.5"},"opera":{"version_added":"9.2"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"display":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/display","spec_url":"https://drafts.csswg.org/css-display/#the-display-properties","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"contents":{"__compat":{"support":{"chrome":{"version_added":"65"},"chrome_android":{"version_added":"65"},"edge":{"version_added":"79"},"firefox":{"version_added":"37"},"firefox_android":{"version_added":"37"},"ie":{"version_added":false},"opera":{"version_added":"52"},"opera_android":{"version_added":"47"},"safari":{"version_added":"11.1"},"safari_ios":{"version_added":"11.3"},"samsunginternet_android":{"version_added":"9.0"},"webview_android":{"version_added":"65"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"contents_unusual":{"__compat":{"description":"Specific behavior of unusual elements when display: contents is applied to them","support":{"chrome":{"version_added":"65"},"chrome_android":{"version_added":"65"},"edge":{"version_added":"79"},"firefox":{"version_added":"59"},"firefox_android":{"version_added":"59"},"ie":{"version_added":false},"opera":{"version_added":"52"},"opera_android":{"version_added":"47"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"9.0"},"webview_android":{"version_added":"65"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"display-outside":{"__compat":{"description":"<display-outside>","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/display-outside","spec_url":"https://drafts.csswg.org/css-display/#typedef-display-outside","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"flex":{"__compat":{"support":{"chrome":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"21"}],"chrome_android":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"25"}],"edge":{"version_added":"12"},"firefox":{"version_added":"20","notes":"Firefox 28 added multi-line flexbox support."},"firefox_android":{"version_added":"20","notes":"Firefox 28 added multi-line flexbox support."},"ie":[{"version_added":"11","partial_implementation":true,"notes":"IE incorrectly positions inline block content inside flex containers. See the discussion on Microsoft Answers."},{"alternative_name":"-ms-flexbox","version_added":"8","partial_implementation":true,"notes":"IE incorrectly positions inline block content inside flex containers. See the discussion on Microsoft Answers."}],"opera":[{"version_added":"16"},{"prefix":"-webkit-","version_added":"15"},{"version_added":"12.1","version_removed":"15"}],"opera_android":[{"version_added":"16"},{"prefix":"-webkit-","version_added":"14"},{"version_added":"12.1","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":[{"version_added":"2.0"},{"prefix":"-webkit-","version_added":"1.5"}],"webview_android":[{"version_added":"4.4"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"flow-root":{"__compat":{"support":{"chrome":{"version_added":"58"},"chrome_android":{"version_added":"58"},"edge":{"version_added":"79"},"firefox":{"version_added":"53"},"firefox_android":{"version_added":"53"},"ie":{"version_added":false},"opera":{"version_added":"45"},"opera_android":{"version_added":"43"},"safari":{"version_added":"13"},"safari_ios":{"version_added":"13"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"58"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid":{"__compat":{"support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":[{"version_added":"16"},{"prefix":"-ms-","version_added":"12"}],"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"prefix":"-ms-","version_added":"10","partial_implementation":true,"notes":"Internet Explorer implements an older version of the specification."},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"notes":"Samsung Internet added this earlier than the corresponding Chrome version would indicate.","version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"inline-block":{"__compat":{"support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":[{"version_added":"8"},{"version_added":"6","partial_implementation":true,"notes":"Until Internet Explorer 8, inline-block is only for natural inline elements."}],"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"inline-flex":{"__compat":{"support":{"chrome":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"21"}],"chrome_android":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"25"}],"edge":{"version_added":"12"},"firefox":{"version_added":"20","notes":"Firefox 28 added multi-line flexbox support."},"firefox_android":{"version_added":"20","notes":"Firefox 28 added multi-line flexbox support."},"ie":[{"version_added":"11"},{"alternative_name":"-ms-inline-flexbox","version_added":"8"}],"opera":[{"version_added":"16"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"16"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"6.1"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"6.1"}],"samsunginternet_android":[{"version_added":"2.0"},{"prefix":"-webkit-","version_added":"1.5"}],"webview_android":[{"version_added":"4.4"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"inline-grid":{"__compat":{"support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":[{"version_added":"16"},{"prefix":"-ms-","version_added":"12"}],"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"prefix":"-ms-","version_added":"10","partial_implementation":true,"notes":"Internet Explorer implements an older version of the specification."},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"notes":"Samsung Internet added this earlier than the corresponding Chrome version would indicate.","version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"inline-table":{"__compat":{"support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"list-item":{"__compat":{"support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"6"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"legend-support":{"__compat":{"description":"Supported on <legend>","support":{"chrome":{"version_added":"71"},"chrome_android":{"version_added":"71"},"edge":{"version_added":"79"},"firefox":{"version_added":"64"},"firefox_android":{"version_added":"64"},"ie":{"version_added":false},"opera":{"version_added":"58"},"opera_android":{"version_added":"50"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"71"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"multi-keyword_values":{"__compat":{"description":"Multi-keyword values","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"70"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"none":{"__compat":{"support":{"chrome":{"version_added":"1","notes":"Chrome 65 stopped creating layout objects for elements inside an <iframe> with display:none applied."},"chrome_android":{"version_added":"18","notes":"Chrome 65 stopped creating layout objects for elements inside an <iframe> with display:none applied."},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"7","notes":"Opera 52 stopped creating layout objects for elements inside an <iframe> with display:none applied."},"opera_android":{"version_added":"10.1","notes":"Opera Android 47 stopped creating layout objects for elements inside an <iframe> with display:none applied."},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0","notes":"Chrome 65 stopped creating layout objects for elements inside an <iframe> with display:none applied."},"webview_android":{"version_added":"≤37","notes":"WebView 65 stopped creating layout objects for elements inside an <iframe> with display:none applied."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ruby_values":{"__compat":{"description":"ruby, ruby-base, ruby-base-container, ruby-text, and ruby-text-container","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"79"},"firefox":{"version_added":"38"},"firefox_android":{"version_added":"38"},"ie":{"version_added":"7"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"table_values":{"__compat":{"description":"table, table-cell, table-column, table-column-group, table-footer-group, table-header-group, table-row, and table-row-group","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"xul_box_values":{"__compat":{"description":"-moz-box and -moz-inline-box","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"1","version_removed":"64","notes":"This is still available to Firefox UI code."},{"version_added":"62","flags":[{"name":"layout.css.xul-box-display-values.content.enabled","type":"preference","value_to_set":"true"}]}],"firefox_android":[{"version_added":"4","version_removed":"64","notes":"This is still available to Firefox UI code."},{"version_added":"62","flags":[{"name":"layout.css.xul-box-display-values.content.enabled","type":"preference","value_to_set":"true"}]}],"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"xul_deck_values":{"__compat":{"description":"-moz-deck","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"1","version_removed":"62","notes":"This is still available to Firefox UI code."},{"version_added":"62","flags":[{"name":"layout.css.xul-display-values.content.enabled","type":"preference","value_to_set":"true"}]}],"firefox_android":[{"version_added":"4","version_removed":"62","notes":"This is still available to Firefox UI code."},{"version_added":"62","flags":[{"name":"layout.css.xul-display-values.content.enabled","type":"preference","value_to_set":"true"}]}],"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"xul_grid_values":{"__compat":{"description":"-moz-grid, -moz-grid-group, and -moz-grid-line","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"1","version_removed":"62","notes":"This is still available to Firefox UI code."},{"version_added":"62","flags":[{"name":"layout.css.xul-display-values.content.enabled","type":"preference","value_to_set":"true"}]}],"firefox_android":[{"version_added":"4","version_removed":"62","notes":"This is still available to Firefox UI code."},{"version_added":"62","flags":[{"name":"layout.css.xul-display-values.content.enabled","type":"preference","value_to_set":"true"}]}],"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"xul_inline_grid_stack":{"__compat":{"description":"-moz-inline-grid and -moz-inline-stack","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"1","version_removed":"62","notes":"Available to Firefox UI code."},{"version_added":"62","version_removed":"70","flags":[{"name":"layout.css.xul-display-values.content.enabled","type":"preference","value_to_set":"true"}]}],"firefox_android":[{"version_added":"4","version_removed":"62","notes":"This is still available to Firefox UI code."},{"version_added":"62","flags":[{"name":"layout.css.xul-display-values.content.enabled","type":"preference","value_to_set":"true"}]}],"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"xul_popup_values":{"__compat":{"description":"-moz-popup","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"1","version_removed":"62","notes":"This is still available to Firefox UI code."},{"version_added":"62","flags":[{"name":"layout.css.xul-display-values.content.enabled","type":"preference","value_to_set":"true"}]}],"firefox_android":[{"version_added":"4","version_removed":"62","notes":"This is still available to Firefox UI code."},{"version_added":"62","flags":[{"name":"layout.css.xul-display-values.content.enabled","type":"preference","value_to_set":"true"}]}],"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"xul_stack_value":{"__compat":{"description":"-moz-stack","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"1","version_removed":"62","notes":"This is still available to Firefox UI code."},{"version_added":"62","flags":[{"name":"layout.css.xul-display-values.content.enabled","type":"preference","value_to_set":"true"}]}],"firefox_android":[{"version_added":"4","version_removed":"62","notes":"This is still available to Firefox UI code."},{"version_added":"62","flags":[{"name":"layout.css.xul-display-values.content.enabled","type":"preference","value_to_set":"true"}]}],"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}}},"empty-cells":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/empty-cells","spec_url":"https://drafts.csswg.org/css2/#empty-cells","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"4"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"filter":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/filter","spec_url":"https://drafts.fxtf.org/filter-effects/#FilterProperty","support":{"chrome":[{"version_added":"53"},{"prefix":"-webkit-","version_added":"18","notes":"In Chrome 18 to 19, the saturate() function only takes integers instead of decimal or percentage values. From Chrome 20, this bug is fixed."}],"chrome_android":{"version_added":"53"},"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"35"},{"prefix":"-webkit-","version_added":"49"}],"firefox_android":[{"version_added":"35"},{"prefix":"-webkit-","version_added":"49"}],"ie":{"version_added":false,"notes":"Internet Explorer 4 to 9 implemented a non-standard filter property. The syntax was completely different from this one and is not documented here."},"opera":[{"version_added":"40"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"41"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9.1"},{"prefix":"-webkit-","version_added":"6"}],"safari_ios":[{"version_added":"9.3"},{"prefix":"-webkit-","version_added":"6"}],"samsunginternet_android":{"version_added":"6.0"},"webview_android":[{"version_added":"53"},{"prefix":"-webkit-","version_added":"4.4"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"svg":{"__compat":{"description":"On SVG elements","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"35"},"firefox_android":{"version_added":"35"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"flex-basis":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/flex-basis","spec_url":"https://drafts.csswg.org/css-flexbox/#flex-basis-property","support":{"chrome":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"22"}],"chrome_android":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"25"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"22","notes":"Since Firefox 28, multi-line flexbox is supported."},{"prefix":"-webkit-","version_added":"49"}],"firefox_android":[{"version_added":"22","notes":"Since Firefox 28, multi-line flexbox is supported."},{"prefix":"-webkit-","version_added":"49"}],"ie":{"version_added":"11","notes":"When a non-auto flex-basis is specified, Internet Explorer 10 and 11 always uses a content-box box model to calculate the size of a flex item, even if box-sizing: border-box is applied to the element. See Flexbug #7 for more info."},"opera":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":[{"version_added":"2.0"},{"prefix":"-webkit-","version_added":"1.5"}],"webview_android":[{"version_added":"4.4"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"auto":{"__compat":{"description":"auto","support":{"chrome":{"version_added":"22"},"chrome_android":{"version_added":"25"},"edge":{"version_added":"12"},"firefox":{"version_added":"22"},"firefox_android":{"version_added":"22"},"ie":{"version_added":"11"},"opera":{"version_added":"12.1"},"opera_android":{"version_added":"12.1"},"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"content":{"__compat":{"description":"content","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"79"},"firefox":{"version_added":"61"},"firefox_android":{"version_added":"61"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"max-content":{"__compat":{"support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"66"},{"version_added":"22","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"22","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"min-content":{"__compat":{"support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"66"},{"version_added":"22","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"22","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"flex-direction":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/flex-direction","spec_url":"https://drafts.csswg.org/css-flexbox/#flex-direction-property","support":{"chrome":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"21"}],"chrome_android":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"25"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"20","partial_implementation":true,"notes":["Since Firefox 28, multi-line flexbox is supported.","Before Firefox 81, overflow with *-reverse is unsupported. See bug 1042151."]},{"prefix":"-webkit-","version_added":"49"}],"firefox_android":[{"version_added":"20","partial_implementation":true,"notes":["Since Firefox 28, multi-line flexbox is supported.","Before Firefox 81, overflow with *-reverse is unsupported. See bug 1042151."]},{"prefix":"-webkit-","version_added":"49"}],"ie":[{"version_added":"11"},{"prefix":"-ms-","version_added":"10"}],"opera":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":[{"version_added":"2.0"},{"prefix":"-webkit-","version_added":"1.5"}],"webview_android":[{"version_added":"4.4"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"flex-flow":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/flex-flow","spec_url":"https://drafts.csswg.org/css-flexbox/#flex-flow-property","support":{"chrome":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"21"}],"chrome_android":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"25"}],"edge":{"version_added":"12"},"firefox":[{"version_added":"28"},{"prefix":"-webkit-","version_added":"49"}],"firefox_android":[{"version_added":"28"},{"prefix":"-webkit-","version_added":"49"}],"ie":{"version_added":"11"},"opera":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":[{"version_added":"2.0"},{"prefix":"-webkit-","version_added":"1.5"}],"webview_android":[{"version_added":"4.4"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"flex-grow":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/flex-grow","spec_url":"https://drafts.csswg.org/css-flexbox/#flex-grow-property","support":{"chrome":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"22"}],"chrome_android":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"25"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":{"version_added":"20","notes":"Since Firefox 28, multi-line flexbox is supported."},"firefox_android":{"version_added":"20","notes":"Since Firefox 28, multi-line flexbox is supported."},"ie":[{"version_added":"11"},{"version_added":"10","alternative_name":"-ms-flex-positive"}],"opera":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":[{"version_added":"2.0"},{"prefix":"-webkit-","version_added":"1.5"}],"webview_android":[{"version_added":"4.4"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"less_than_zero_animate":{"__compat":{"description":"<0 animate","support":{"chrome":{"version_added":"49"},"chrome_android":{"version_added":"49"},"edge":{"version_added":"79"},"firefox":{"version_added":"32","notes":"Before Firefox 32, Firefox wasn't able to animate values starting or stopping at 0."},"firefox_android":{"version_added":"32","notes":"Before Firefox 32, Firefox wasn't able to animate values starting or stopping at 0."},"ie":{"version_added":false},"opera":{"version_added":"36"},"opera_android":{"version_added":"36"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"49"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}}},"flex-shrink":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/flex-shrink","spec_url":"https://drafts.csswg.org/css-flexbox/#flex-shrink-property","support":{"chrome":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"22"}],"chrome_android":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"25"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"20","notes":["Since Firefox 28, multi-line flexbox is supported.","Before Firefox 32, Firefox wasn't able to animate values starting or stopping at 0."]},{"prefix":"-webkit-","version_added":"49"}],"firefox_android":[{"version_added":"20","notes":["Since Firefox 28, multi-line flexbox is supported.","Before Firefox 32, Firefox wasn't able to animate values starting or stopping at 0."]},{"prefix":"-webkit-","version_added":"49"}],"ie":{"version_added":"10","notes":"Internet Explorer 10 uses 0 instead of 1 as the initial value for the flex-shrink property. A workaround is to always set an explicit value for flex-shrink. See Flexbug #6 for more info."},"opera":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"8"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"8"}],"samsunginternet_android":[{"version_added":"2.0"},{"prefix":"-webkit-","version_added":"1.5"}],"webview_android":[{"version_added":"4.4"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"flex-wrap":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/flex-wrap","spec_url":"https://drafts.csswg.org/css-flexbox/#flex-wrap-property","support":{"chrome":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"21"}],"chrome_android":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"25"}],"edge":{"version_added":"12"},"firefox":{"version_added":"28"},"firefox_android":{"version_added":"52"},"ie":{"version_added":"11","partial_implementation":true,"notes":"Partial support due to large number of bugs present. See Flexbugs."},"opera":{"version_added":"17"},"opera_android":{"version_added":"18"},"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"6.1"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"6.1"}],"samsunginternet_android":[{"version_added":"2.0"},{"prefix":"-webkit-","version_added":"1.5"}],"webview_android":[{"version_added":"4.4"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"flex":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/flex","spec_url":"https://drafts.csswg.org/css-flexbox/#flex-property","support":{"chrome":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"21"}],"chrome_android":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"25"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"20","notes":["Since Firefox 28, multi-line flexbox is supported.","Before Firefox 32, Firefox wasn't able to animate values starting or stopping at 0.","Until Firefox 61, flex items that are sized according to their content are sized using fit-content, not max-content."]},{"prefix":"-webkit-","version_added":"49"}],"firefox_android":[{"version_added":"20","notes":["Since Firefox 28, multi-line flexbox is supported.","Before Firefox 32, Firefox wasn't able to animate values starting or stopping at 0.","Until Firefox 61, flex items that are sized according to their content are sized using fit-content, not max-content."]},{"prefix":"-webkit-","version_added":"49"}],"ie":[{"version_added":"11","notes":["Internet Explorer 11 ignores uses of calc() in the flex-basis part of the flex syntax. This can be worked around by using the longhand properties instead of the shorthand. See Flexbug #8 for more info.","Internet Explorer 11 considers a unitless value in the flex-basis part to be syntactically invalid (and will thus be ignored). A workaround is to always include a unit in the flex-basis part of the flex shorthand value. See Flexbug #4 for more info."]},{"prefix":"-ms-","version_added":"10","notes":["Internet Explorer 10 and 11 ignore uses of calc() in the flex-basis part of the flex syntax. This can be worked around by using the longhand properties instead of the shorthand. See Flexbug #8 for more info.","Internet Explorer 10 and 11 consider a unitless value in the flex-basis part to be syntactically invalid (and will thus be ignored). A workaround is to always include a unit in the flex-basis part of the flex shorthand value. See Flexbug #4 for more info."]}],"opera":{"version_added":"12.1"},"opera_android":{"version_added":"12.1"},"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":[{"version_added":"2.0"},{"prefix":"-webkit-","version_added":"1.5"}],"webview_android":[{"version_added":"4.4"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"float":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/float","spec_url":"https://drafts.csswg.org/css-logical/#float-clear","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"flow_relative_values":{"__compat":{"description":"Flow-relative values inline-start and inline-end","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"55"},"firefox_android":{"version_added":"55"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"font-family":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-family","spec_url":["https://drafts.csswg.org/css-fonts/#generic-font-families","https://drafts.csswg.org/css-fonts/#font-family-prop"],"support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"Not supported on option elements. See bug 1536148."},"firefox_android":{"version_added":"4"},"ie":{"version_added":"3"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"system_ui":{"__compat":{"description":"system-ui","support":{"chrome":{"version_added":"56"},"chrome_android":{"version_added":"56"},"edge":{"version_added":"79"},"firefox":[{"version_added":"92"},{"alternative_name":"-apple-system","version_added":"43","notes":"Supported on macOS only."}],"firefox_android":{"version_added":"92"},"ie":{"version_added":false},"opera":{"version_added":"43"},"opera_android":{"version_added":"43"},"safari":{"alternative_name":"-apple-system","version_added":"9","notes":"Supported since macOS 10.11."},"safari_ios":{"alternative_name":"-apple-system","version_added":"9"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"56"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"font-feature-settings":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-feature-settings","spec_url":"https://drafts.csswg.org/css-fonts/#font-feature-settings-prop","support":{"chrome":[{"version_added":"48"},{"prefix":"-webkit-","version_added":"16"}],"chrome_android":{"version_added":"48"},"edge":{"version_added":"15"},"firefox":[{"version_added":"34","notes":"The ISO/IEC CD 14496-22 3rd edition suggests using the ssty feature to provide glyph variants more suitable for use in scripts (for example primes used as superscripts). Starting with Firefox 29, this is done automatically by the MathML rendering engine. The ISO/IEC CD 14496-22 3rd edition also suggests applying the dtls feature to letters when placing mathematical accents to get dotless forms (for example dotless i, j with a hat). Starting with Firefox 35, this is done automatically by the MathML rendering engine. You can override the default values determined by the MathML rendering engine with CSS."},{"prefix":"-moz-","version_added":"15","notes":"From Firefox 4 to Firefox 14 (inclusive), Firefox supported an older, slightly different syntax. See OpenType Font Feature support in Firefox 4."}],"firefox_android":[{"version_added":"34","notes":"The ISO/IEC CD 14496-22 3rd edition suggests using the ssty feature to provide glyph variants more suitable for use in scripts (for example primes used as superscripts). Starting with Firefox 29, this is done automatically by the MathML rendering engine. The ISO/IEC CD 14496-22 3rd edition also suggests applying the dtls feature to letters when placing mathematical accents to get dotless forms (for example dotless i, j with a hat). Starting with Firefox 35, this is done automatically by the MathML rendering engine. You can override the default values determined by the MathML rendering engine with CSS."},{"prefix":"-moz-","version_added":"15","notes":"From Firefox 4 to Firefox 14 (inclusive), Firefox supported an older, slightly different syntax. See OpenType Font Feature support in Firefox 4."}],"ie":{"version_added":"10"},"opera":[{"version_added":"35"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"35"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9.1"},{"partial_implementation":true,"version_added":"4","version_removed":"6"}],"safari_ios":[{"version_added":"9.3"},{"partial_implementation":true,"version_added":"3.2","version_removed":"6.1"}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"font-kerning":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-kerning","spec_url":"https://drafts.csswg.org/css-fonts/#font-kerning-prop","support":{"chrome":[{"version_added":"33"},{"prefix":"-webkit-","version_added":"29","version_removed":"33"}],"chrome_android":[{"version_added":"33"},{"prefix":"-webkit-","version_added":"29","version_removed":"33"}],"edge":{"version_added":"79"},"firefox":{"version_added":"32"},"firefox_android":{"version_added":"32"},"ie":{"version_added":false},"opera":[{"version_added":"20"},{"prefix":"-webkit-","version_added":"16","version_removed":"20"}],"opera_android":[{"version_added":"20"},{"prefix":"-webkit-","version_added":"16","version_removed":"20"}],"safari":[{"version_added":"9"},{"version_added":"6","prefix":"-webkit-"}],"safari_ios":[{"version_added":"9"},{"version_added":"6","prefix":"-webkit-"}],"samsunginternet_android":[{"version_added":"2.0"},{"prefix":"-webkit-","version_added":"1.0","version_removed":"2.0"}],"webview_android":[{"version_added":"4.4.3"},{"prefix":"-webkit-","version_added":"4.4","version_removed":"4.4.3"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"font-language-override":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-language-override","spec_url":"https://drafts.csswg.org/css-fonts/#font-language-override-prop","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"34"},{"prefix":"-moz-","version_added":"4"}],"firefox_android":[{"version_added":"34"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"font-optical-sizing":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-optical-sizing","spec_url":"https://drafts.csswg.org/css-fonts/#font-optical-sizing-def","support":{"chrome":{"version_added":"79"},"chrome_android":{"version_added":"79"},"edge":{"version_added":"17"},"firefox":{"version_added":"62"},"firefox_android":{"version_added":"62"},"ie":{"version_added":false},"opera":{"version_added":"66"},"opera_android":{"version_added":false},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"12.0"},"webview_android":{"version_added":"79"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"font-size-adjust":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-size-adjust","spec_url":"https://drafts.csswg.org/css-fonts-5/#font-size-adjust-prop","support":{"chrome":{"version_added":"43","version_removed":"91","flags":[{"type":"preference","name":"Enable experimental Web Platform features"}]},"chrome_android":{"version_added":"43","version_removed":"91","flags":[{"type":"preference","name":"Enable experimental Web Platform features"}]},"edge":{"version_added":"79","version_removed":"91","flags":[{"type":"preference","name":"Enable experimental Web Platform features"}]},"firefox":[{"version_added":"40"},{"version_added":"3","notes":"Before Firefox 40, font-size-adjust: 0 was incorrectly interpreted as font-size-adjust: none (bug 1144885)."},{"version_added":"1","notes":"Before Firefox 3, font-size-adjust was supported on Windows only."}],"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":"30","version_removed":"77","flags":[{"type":"preference","name":"Enable experimental Web Platform features"}]},"opera_android":{"version_added":"30","version_removed":"64","flags":[{"type":"preference","name":"Enable experimental Web Platform features"}]},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"two-values":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-size-adjust","spec_url":"https://drafts.csswg.org/css-fonts-5/#font-size-adjust-prop","description":"Two-value syntax","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"92"},{"version_added":"91","version_removed":"92","flags":[{"type":"preference","name":"layout.css.font-size-adjust.basis.enabled"}]}],"firefox_android":[{"version_added":"92"},{"version_added":"91","version_removed":"92","flags":[{"type":"preference","name":"layout.css.font-size-adjust.basis.enabled"}]}],"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"font-size":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-size","spec_url":"https://drafts.csswg.org/css-fonts/#font-size-prop","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"5.5"},"opera":{"version_added":"7"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"rem_values":{"__compat":{"description":"rem values","support":{"chrome":{"version_added":"31"},"chrome_android":{"version_added":"42"},"edge":{"version_added":"12"},"firefox":{"version_added":"31","notes":["Before Firefox 57, animations using em units are not affected by changes to the font-size of the animated element's parent (bug 1254424).","Before Firefox 57, some language settings' inherited font-size is smaller than expected (bug 1391341)."]},"firefox_android":{"version_added":"31"},"ie":[{"version_added":"11"},{"partial_implementation":true,"version_added":"9","version_removed":"10"}],"opera":{"version_added":"28"},"opera_android":{"version_added":"28"},"safari":{"version_added":"7"},"safari_ios":{"version_added":"7"},"samsunginternet_android":{"version_added":"4.0"},"webview_android":{"version_added":"4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"xxx-large":{"__compat":{"description":"xxx-large keyword","support":{"chrome":{"version_added":"79"},"chrome_android":{"version_added":"79"},"edge":{"version_added":"79"},"firefox":{"version_added":"70"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"12.0"},"webview_android":{"version_added":"79"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"font-smooth":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-smooth","support":{"chrome":{"version_added":"5","alternative_name":"-webkit-font-smoothing"},"chrome_android":{"version_added":"18","alternative_name":"-webkit-font-smoothing"},"edge":{"version_added":"79","alternative_name":"-webkit-font-smoothing"},"firefox":{"version_added":"25","alternative_name":"-moz-osx-font-smoothing","notes":"Only works on macOS."},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","alternative_name":"-webkit-font-smoothing"},"opera_android":{"version_added":"14","alternative_name":"-webkit-font-smoothing"},"safari":{"version_added":"4","alternative_name":"-webkit-font-smoothing"},"safari_ios":{"version_added":"3.2","alternative_name":"-webkit-font-smoothing"},"samsunginternet_android":{"alternative_name":"-webkit-font-smoothing","version_added":"1.0"},"webview_android":{"version_added":"≤37","alternative_name":"-webkit-font-smoothing"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"font-stretch":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-stretch","spec_url":"https://drafts.csswg.org/css-fonts/#font-stretch-prop","support":{"chrome":{"version_added":"60","notes":"A font-stretch definition must be added to the @font-face before this property will function."},"chrome_android":{"version_added":"60","notes":"A font-stretch definition must be added to the @font-face before this property will function."},"edge":{"version_added":"12"},"firefox":{"version_added":"9"},"firefox_android":{"version_added":"9"},"ie":{"version_added":"9"},"opera":{"version_added":"47","notes":"A font-stretch definition must be added to the @font-face before this property will function."},"opera_android":{"version_added":"44","notes":"A font-stretch definition must be added to the @font-face before this property will function."},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"8.0","notes":"A font-stretch definition must be added to the @font-face before this property will function."},"webview_android":{"version_added":"60","notes":"A font-stretch definition must be added to the @font-face before this property will function."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"percentage":{"__compat":{"description":"<percentage> syntax","support":{"chrome":{"version_added":"62"},"chrome_android":{"version_added":"62"},"edge":{"version_added":"18"},"firefox":{"version_added":"61"},"firefox_android":{"version_added":"61"},"ie":{"version_added":false},"opera":{"version_added":"49"},"opera_android":{"version_added":"46"},"safari":{"version_added":"11.1"},"safari_ios":{"version_added":"11.3"},"samsunginternet_android":{"version_added":"8.0"},"webview_android":{"version_added":"62"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"font-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-style","spec_url":"https://drafts.csswg.org/css-fonts/#font-style-prop","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"Before Firefox 44, oblique was not distinguished from italic."},"firefox_android":{"version_added":"4","notes":"Before Firefox 44, oblique was not distinguished from italic."},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"oblique-angle":{"__compat":{"description":"oblique can accept an <angle>","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"61"},"firefox_android":{"version_added":"61"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"font-synthesis":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-synthesis","spec_url":"https://drafts.csswg.org/css-fonts/#font-synthesis","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"34"},"firefox_android":{"version_added":"34"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"small-caps":{"__compat":{"description":"small-caps","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"93"},"firefox_android":{"version_added":"93"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}}},"font-variant-alternates":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-variant-alternates","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"34"},"firefox_android":{"version_added":"34"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":true}},"annotation":{"__compat":{"description":"annotation()","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-variant-alternates#annotation()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"34"},"firefox_android":{"version_added":"34"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":true}}},"character_variant":{"__compat":{"description":"character-variant()","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-variant-alternates#character-variant()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"34"},"firefox_android":{"version_added":"34"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":true}}},"ornaments":{"__compat":{"description":"ornaments()","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-variant-alternates#ornaments()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"34"},"firefox_android":{"version_added":"34"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":true}}},"styleset":{"__compat":{"description":"styleset()","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-variant-alternates#styleset()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"34"},"firefox_android":{"version_added":"34"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":true}}},"stylistic":{"__compat":{"description":"stylistic()","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-variant-alternates#stylistic()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"34"},"firefox_android":{"version_added":"34"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":true}}},"swash":{"__compat":{"description":"swash()","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-variant-alternates#swash()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"34"},"firefox_android":{"version_added":"34"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":true}}}},"font-variant-caps":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-variant-caps","spec_url":"https://drafts.csswg.org/css-fonts/#font-variant-caps-prop","support":{"chrome":{"version_added":"52"},"chrome_android":{"version_added":"52"},"edge":{"version_added":"79"},"firefox":{"version_added":"34"},"firefox_android":{"version_added":"34"},"ie":{"version_added":false},"opera":{"version_added":"39"},"opera_android":{"version_added":"41"},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"52"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"font-variant-east-asian":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-variant-east-asian","spec_url":"https://drafts.csswg.org/css-fonts/#font-variant-east-asian-prop","support":{"chrome":{"version_added":"63"},"chrome_android":{"version_added":"63"},"edge":{"version_added":"79"},"firefox":{"version_added":"34"},"firefox_android":{"version_added":"34"},"ie":{"version_added":false},"opera":{"version_added":"50"},"opera_android":{"version_added":"46"},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":"8.0"},"webview_android":{"version_added":"63"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"font-variant-ligatures":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-variant-ligatures","spec_url":"https://drafts.csswg.org/css-fonts/#font-variant-ligatures-prop","support":{"chrome":[{"version_added":"34"},{"prefix":"-webkit-","version_added":"31"}],"chrome_android":{"version_added":"34"},"edge":[{"version_added":"79"},{"prefix":"-webkit-","version_added":"79"}],"firefox":{"version_added":"34"},"firefox_android":{"version_added":"34"},"ie":{"version_added":false},"opera":[{"version_added":"21"},{"prefix":"-webkit-","version_added":"19"}],"opera_android":[{"version_added":"21"},{"prefix":"-webkit-","version_added":"19"}],"safari":[{"version_added":"9.1"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"9.3"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":{"version_added":"2.0"},"webview_android":[{"version_added":"37"},{"prefix":"-webkit-","version_added":"4.4"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"font-variant-numeric":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-variant-numeric","spec_url":"https://drafts.csswg.org/css-fonts/#font-variant-numeric-prop","support":{"chrome":{"version_added":"52"},"chrome_android":{"version_added":"52"},"edge":{"version_added":"79"},"firefox":{"version_added":"34"},"firefox_android":{"version_added":"34"},"ie":{"version_added":false},"opera":{"version_added":"39"},"opera_android":{"version_added":"41"},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"52"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"font-variant-position":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-variant-position","spec_url":"https://drafts.csswg.org/css-fonts/#font-variant-position-prop","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"34"},"firefox_android":{"version_added":"34"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"font-variant":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-variant","spec_url":"https://drafts.csswg.org/css-fonts/#font-variant-prop","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"11"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"css_fonts_shorthand":{"__compat":{"description":"CSS Fonts Module Level 3 shorthand","support":{"chrome":{"version_added":"52"},"chrome_android":{"version_added":"52"},"edge":{"version_added":"79"},"firefox":{"version_added":"34"},"firefox_android":{"version_added":"34"},"ie":{"version_added":false},"opera":{"version_added":"39"},"opera_android":{"version_added":"41"},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"52"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"greek_accented_characters":{"__compat":{"description":"Greek accented characters","support":{"chrome":{"version_added":false,"notes":"Some operating systems may correctly omit accents in all-uppercase Greek text."},"chrome_android":{"version_added":false,"notes":"Some operating systems may correctly omit accents in all-uppercase Greek text."},"edge":{"version_added":false,"notes":"Some operating systems may correctly omit accents in all-uppercase Greek text."},"firefox":{"version_added":false,"notes":"Some operating systems may correctly omit accents in all-uppercase Greek text."},"firefox_android":{"version_added":false,"notes":"Some operating systems may correctly omit accents in all-uppercase Greek text."},"ie":{"version_added":false},"opera":{"version_added":false,"notes":"Some operating systems may correctly omit accents in all-uppercase Greek text."},"opera_android":{"version_added":false,"notes":"Some operating systems may correctly omit accents in all-uppercase Greek text."},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false,"notes":"Some operating systems may correctly omit accents in all-uppercase Greek text."},"webview_android":{"version_added":false,"notes":"Some operating systems may correctly omit accents in all-uppercase Greek."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"turkic_is":{"__compat":{"description":"iİ and ıI","support":{"chrome":{"version_added":"31"},"chrome_android":{"version_added":"31"},"edge":{"version_added":"12"},"firefox":{"version_added":"14"},"firefox_android":{"version_added":"14"},"ie":{"version_added":"4"},"opera":{"version_added":"18"},"opera_android":{"version_added":"18"},"safari":{"version_added":"8"},"safari_ios":{"version_added":"8"},"samsunginternet_android":{"version_added":"2.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"uppercase_eszett":{"__compat":{"description":"ßSS","support":{"chrome":{"version_added":false,"notes":"Some operating systems may capitalize ß as SS."},"chrome_android":{"version_added":false,"notes":"Some operating systems may capitalize ß as SS."},"edge":{"version_added":false,"notes":"Some operating systems may capitalize ß as SS."},"firefox":{"version_added":false,"notes":"Some operating systems may capitalize ß as SS."},"firefox_android":{"version_added":false,"notes":"Some operating systems may capitalize ß as SS."},"ie":{"version_added":false},"opera":{"version_added":false,"notes":"Some operating systems may capitalize ß as SS."},"opera_android":{"version_added":false,"notes":"Some operating systems may capitalize ß as SS."},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false,"notes":"Some operating systems may capitalize ß as SS."},"webview_android":{"version_added":false,"notes":"Some operating systems may capitalize ß as SS."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"font-variation-settings":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-variation-settings","spec_url":"https://drafts.csswg.org/css-fonts/#font-rend-desc","support":{"chrome":{"version_added":"62"},"chrome_android":{"version_added":"62"},"edge":{"version_added":"17"},"firefox":{"version_added":"62"},"firefox_android":{"version_added":"62"},"ie":{"version_added":false},"opera":{"version_added":"49"},"opera_android":{"version_added":"46"},"safari":{"version_added":"11","notes":"Requires macOS 10.13 High Sierra or later."},"safari_ios":{"version_added":"11","notes":"Requires iOS 11 or later."},"samsunginternet_android":{"version_added":"8.0"},"webview_android":{"version_added":"62"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"font-weight":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font-weight","spec_url":"https://drafts.csswg.org/css-fonts/#font-weight-prop","support":{"chrome":{"version_added":"2"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"3"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"number":{"__compat":{"description":"<number> syntax","support":{"chrome":{"version_added":"62"},"chrome_android":{"version_added":"62"},"edge":{"version_added":"17"},"firefox":{"version_added":"61"},"firefox_android":{"version_added":"61"},"ie":{"version_added":false},"opera":{"version_added":"49"},"opera_android":{"version_added":"46"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"8.0"},"webview_android":{"version_added":"62"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"font":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/font","spec_url":"https://drafts.csswg.org/css-fonts/#font-prop","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"3"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"font_stretch_support":{"__compat":{"description":"Support for font-stretch values in shorthand","support":{"chrome":{"version_added":"60"},"chrome_android":{"version_added":"60"},"edge":{"version_added":"79"},"firefox":{"version_added":"43"},"firefox_android":{"version_added":"43"},"ie":{"version_added":false},"opera":{"version_added":"47"},"opera_android":{"version_added":"44"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"8.0"},"webview_android":{"version_added":"60"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"system_fonts":{"__compat":{"description":"System fonts","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"6"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"forced-color-adjust":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/forced-color-adjust","spec_url":"https://drafts.csswg.org/css-color-adjust/#forced-color-adjust-prop","support":{"chrome":[{"version_added":"89"},{"version_added":"79","flags":[{"type":"preference","name":"forced-colors","value_to_set":"enabled"}]}],"chrome_android":{"version_added":false},"edge":[{"version_added":"79"},{"version_added":"12","alternative_name":"-ms-high-contrast-adjust"}],"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":"10","alternative_name":"-ms-high-contrast-adjust"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"gap":{"flex_context":{"__compat":{"description":"Supported in Flex Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/gap","spec_url":"https://drafts.csswg.org/css-align/#gap-shorthand","support":{"chrome":{"version_added":"84"},"chrome_android":{"version_added":"84"},"edge":{"version_added":"84"},"firefox":{"version_added":"63"},"firefox_android":{"version_added":"63"},"ie":{"version_added":false},"opera":{"version_added":"70"},"opera_android":{"version_added":"60"},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"84"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid_context":{"__compat":{"description":"Supported in Grid Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/gap","spec_url":"https://drafts.csswg.org/css-align/#gap-shorthand","support":{"chrome":[{"version_added":"66"},{"version_added":"57","alternative_name":"grid-gap"}],"chrome_android":[{"version_added":"66"},{"version_added":"57","alternative_name":"grid-gap"}],"edge":[{"version_added":"16"},{"version_added":"16","alternative_name":"grid-gap"}],"firefox":[{"version_added":"61"},{"version_added":"52","alternative_name":"grid-gap"}],"firefox_android":[{"version_added":"61"},{"version_added":"52","alternative_name":"grid-gap"}],"ie":{"version_added":false},"opera":[{"version_added":"53"},{"version_added":"44","alternative_name":"grid-gap"}],"opera_android":[{"version_added":"47"},{"version_added":"43","alternative_name":"grid-gap"}],"safari":[{"version_added":"12"},{"version_added":"10.1","alternative_name":"grid-gap"}],"safari_ios":[{"version_added":"12"},{"version_added":"10.3","alternative_name":"grid-gap"}],"samsunginternet_android":[{"version_added":"9.0"},{"alternative_name":"grid-gap","version_added":"7.0"}],"webview_android":[{"version_added":"66"},{"version_added":"57","alternative_name":"grid-gap"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"calc_values":{"__compat":{"description":"calc() values","support":{"chrome":{"version_added":"66"},"chrome_android":{"version_added":"66"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"53"},"opera_android":{"version_added":"47"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"9.0"},"webview_android":{"version_added":"66"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"percentage_values":{"__compat":{"description":"<percentage> values","support":{"chrome":{"version_added":"66"},"chrome_android":{"version_added":"66"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"53"},"opera_android":{"version_added":"47"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"9.0"},"webview_android":{"version_added":"66"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"multicol_context":{"__compat":{"description":"Supported in Multi-column Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/gap","spec_url":"https://drafts.csswg.org/css-align/#gap-shorthand","support":{"chrome":{"version_added":"66"},"chrome_android":{"version_added":"66"},"edge":{"version_added":"16"},"firefox":{"version_added":"61"},"firefox_android":{"version_added":"61"},"ie":{"version_added":false},"opera":{"version_added":"53"},"opera_android":{"version_added":"47"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"9.0"},"webview_android":{"version_added":"66"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"grid-area":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/grid-area","spec_url":"https://drafts.csswg.org/css-grid/#propdef-grid-area","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid-auto-columns":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/grid-auto-columns","spec_url":"https://drafts.csswg.org/css-grid/#auto-tracks","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":[{"version_added":"16"},{"version_added":"12","version_removed":"79","alternative_name":"-ms-grid-columns"}],"firefox":[{"version_added":"70"},{"version_added":"52","version_removed":"70","partial_implementation":true,"notes":"Does not accept multiple track-size values. See bug 1339672."}],"firefox_android":[{"version_added":"79"},{"version_added":"52","version_removed":"79","partial_implementation":true,"notes":"Does not accept multiple track-size values. See bug 1339672."}],"ie":{"version_added":"10","alternative_name":"-ms-grid-columns"},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid-auto-flow":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/grid-auto-flow","spec_url":"https://drafts.csswg.org/css-grid/#grid-auto-flow-property","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid-auto-rows":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/grid-auto-rows","spec_url":"https://drafts.csswg.org/css-grid/#auto-tracks","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":[{"version_added":"16"},{"version_added":"12","version_removed":"79","alternative_name":"-ms-grid-rows"}],"firefox":[{"version_added":"70"},{"version_added":"52","version_removed":"70","partial_implementation":true,"notes":"Does not accept multiple track-size values. See bug 1339672."}],"firefox_android":[{"version_added":"79"},{"version_added":"52","version_removed":"79","partial_implementation":true,"notes":"Does not accept multiple track-size values. See bug 1339672."}],"ie":{"version_added":"10","alternative_name":"-ms-grid-rows"},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid-column-end":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/grid-column-end","spec_url":"https://drafts.csswg.org/css-grid/#line-placement","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid-column-start":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/grid-column-start","spec_url":"https://drafts.csswg.org/css-grid/#line-placement","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid-column":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/grid-column","spec_url":"https://drafts.csswg.org/css-grid/#placement-shorthands","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid-row-end":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/grid-row-end","spec_url":"https://drafts.csswg.org/css-grid/#line-placement","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid-row-start":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/grid-row-start","spec_url":"https://drafts.csswg.org/css-grid/#line-placement","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid-row":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/grid-row","spec_url":"https://drafts.csswg.org/css-grid/#placement-shorthands","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid-template-areas":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/grid-template-areas","spec_url":"https://drafts.csswg.org/css-grid/#grid-template-areas-property","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid-template-columns":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/grid-template-columns","spec_url":["https://drafts.csswg.org/css-grid/#track-sizing","https://drafts.csswg.org/css-grid/#subgrids","https://drafts.csswg.org/css-grid-3/#masonry-layout"],"support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":[{"version_added":"16"},{"alternative_name":"-ms-grid-columns","version_added":"12","version_removed":"79"}],"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":"10","alternative_name":"-ms-grid-columns"},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"animation":{"__compat":{"description":"Animation of tracks","support":{"chrome":{"version_added":false,"notes":"See bug 759665."},"chrome_android":{"version_added":false,"notes":"See bug 759665."},"edge":{"version_added":false,"notes":"See bug 759665."},"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":{"version_added":false,"notes":"See bug 759665."},"opera_android":{"version_added":false,"notes":"See bug 759665."},"safari":{"version_added":false,"notes":"See bug 204580."},"safari_ios":{"version_added":false,"notes":"See bug 204580."},"samsunginternet_android":{"version_added":false,"notes":"See bug 759665."},"webview_android":{"version_added":false,"notes":"See bug 759665."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"fit-content":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/fit-content","spec_url":"https://drafts.csswg.org/css-sizing-4/#sizing-values","description":"fit-content()","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"minmax":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/minmax()","spec_url":"https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-minmax","description":"minmax()","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"repeat":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/repeat()","spec_url":"https://drafts.csswg.org/css-grid/#repeat-notation","description":"repeat()","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":[{"version_added":"76"},{"version_added":"57","version_removed":"76","notes":"repeat(auto-fill, ...) and repeat(auto-fit, ...) only support one repeated column (see bug 1341507).","partial_implementation":true},{"version_added":"52","version_removed":"57","notes":"calc() doesn't work in repeat() (see bug 1350069).","partial_implementation":true}],"firefox_android":[{"version_added":"79"},{"version_added":"57","version_removed":"79","notes":"repeat(auto-fill, ...) and repeat(auto-fit, ...) only support one repeated column (see bug 1341507).","partial_implementation":true},{"version_added":"52","version_removed":"57","notes":"calc() doesn't work in repeat() (see bug 1350069).","partial_implementation":true}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"subgrid":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/CSS_Grid_Layout/Subgrid","spec_url":"https://drafts.csswg.org/css-grid/#subgrids","description":"subgrid","support":{"chrome":{"version_added":false,"notes":"See bug 618969."},"chrome_android":{"version_added":false,"notes":"See bug 618969."},"edge":{"version_added":false,"notes":"See bug 618969."},"firefox":[{"version_added":"71"},{"version_added":"69","notes":"Enabled by default in Firefox Nightly.","flags":[{"type":"preference","name":"layout.css.grid-template-subgrid-value.enabled","value_to_set":"true"}]}],"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false,"notes":"See bug 618969."},"opera_android":{"version_added":false,"notes":"See bug 618969."},"safari":{"version_added":false,"notes":"See bug 202115."},"safari_ios":{"version_added":false,"notes":"See bug 202115."},"samsunginternet_android":{"version_added":false,"notes":"See bug 618969."},"webview_android":{"version_added":false,"notes":"See bug 618969."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"masonry":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/CSS_Grid_Layout/Masonry_Layout","spec_url":"https://drafts.csswg.org/css-grid-3/#masonry-layout","description":"masonry","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"77","flags":[{"type":"preference","name":"layout.css.grid-template-masonry-value.enabled","value_to_set":"true"}]},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"grid-template-rows":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/grid-template-rows","spec_url":["https://drafts.csswg.org/css-grid/#track-sizing","https://drafts.csswg.org/css-grid/#subgrids","https://drafts.csswg.org/css-grid-3/#masonry-layout"],"support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":[{"version_added":"16"},{"alternative_name":"-ms-grid-rows","version_added":"12","version_removed":"79"}],"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":"10","alternative_name":"-ms-grid-rows"},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"animation":{"__compat":{"description":"Animation of tracks","support":{"chrome":{"version_added":false,"notes":"See bug 759665."},"chrome_android":{"version_added":false,"notes":"See bug 759665."},"edge":{"version_added":false,"notes":"See bug 759665."},"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":{"version_added":false,"notes":"See bug 759665."},"opera_android":{"version_added":false,"notes":"See bug 759665."},"safari":{"version_added":false,"notes":"See bug 204580."},"safari_ios":{"version_added":false,"notes":"See bug 204580."},"samsunginternet_android":{"version_added":false,"notes":"See bug 759665."},"webview_android":{"version_added":false,"notes":"See bug 759665."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"fit-content":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/fit-content","spec_url":"https://drafts.csswg.org/css-sizing-4/#sizing-values","description":"fit-content()","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"masonry":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/CSS_Grid_Layout/Masonry_Layout","spec_url":"https://drafts.csswg.org/css-grid-3/#masonry-layout","description":"masonry","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"77","flags":[{"type":"preference","name":"layout.css.grid-template-masonry-value.enabled","value_to_set":"true"}]},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"minmax":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/minmax()","spec_url":"https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-minmax","description":"minmax()","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"repeat":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/repeat()","spec_url":"https://drafts.csswg.org/css-grid/#repeat-notation","description":"repeat()","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":[{"version_added":"76"},{"version_added":"57","version_removed":"76","notes":"repeat(auto-fill, ...) and repeat(auto-fit, ...) only support one repeated column (see bug 1341507).","partial_implementation":true},{"version_added":"52","version_removed":"57","notes":"calc() doesn't work in repeat() (see bug 1350069).","partial_implementation":true}],"firefox_android":[{"version_added":"79"},{"version_added":"57","version_removed":"79","notes":"repeat(auto-fill, ...) and repeat(auto-fit, ...) only support one repeated column (see bug 1341507).","partial_implementation":true},{"version_added":"52","version_removed":"57","notes":"calc() doesn't work in repeat() (see bug 1350069).","partial_implementation":true}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"subgrid":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/CSS_Grid_Layout/Subgrid","spec_url":"https://drafts.csswg.org/css-grid/#subgrids","description":"subgrid","support":{"chrome":{"version_added":false,"notes":"See bug 618969."},"chrome_android":{"version_added":false,"notes":"See bug 618969."},"edge":{"version_added":false,"notes":"See bug 618969."},"firefox":[{"version_added":"71"},{"version_added":"69","notes":"Enabled by default in Firefox Nightly.","flags":[{"type":"preference","name":"layout.css.grid-template-subgrid-value.enabled","value_to_set":"true"}]}],"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false,"notes":"See bug 618969."},"opera_android":{"version_added":false,"notes":"See bug 618969."},"safari":{"version_added":false,"notes":"See bug 202115."},"safari_ios":{"version_added":false,"notes":"See bug 202115."},"samsunginternet_android":{"version_added":false,"notes":"See bug 618969."},"webview_android":{"version_added":false,"notes":"See bug 618969."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"grid-template":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/grid-template","spec_url":"https://drafts.csswg.org/css-grid/#explicit-grid-shorthand","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/grid","spec_url":"https://drafts.csswg.org/css-grid/#grid-shorthand","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0","notes":"This was added early so is out of sync with the equivalent Chromium version."},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"hanging-punctuation":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/hanging-punctuation","spec_url":"https://drafts.csswg.org/css-text/#hanging-punctuation-property","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"10","partial_implementation":true,"notes":"The force-end keyword is recognized but has no effect."},"safari_ios":{"version_added":"10","partial_implementation":true,"notes":"The force-end keyword is recognized but has no effect."},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"height":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/height","spec_url":"https://drafts.csswg.org/css-sizing/#preferred-size-properties","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"fit-content":{"__compat":{"description":"fit-content","support":{"chrome":{"version_added":"46"},"chrome_android":{"version_added":"46"},"edge":{"version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"33"},"opera_android":{"version_added":"33"},"safari":[{"version_added":"11"},{"version_added":"9","prefix":"-webkit-"}],"safari_ios":[{"version_added":"11"},{"version_added":"9","prefix":"-webkit-"}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"46"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"fit-content_function":{"__compat":{"description":"fit-content()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"firefox_android":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"max-content":{"__compat":{"description":"max-content","support":{"chrome":{"version_added":"46"},"chrome_android":{"version_added":"46"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"3","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"4","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"46"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"min-content":{"__compat":{"description":"min-content","support":{"chrome":{"version_added":"46"},"chrome_android":{"version_added":"46"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"3","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"4","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"46"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"stretch":{"__compat":{"description":"stretch","support":{"chrome":{"alternative_name":"-webkit-fill-available","version_added":"28"},"chrome_android":{"alternative_name":"-webkit-fill-available","version_added":"28"},"edge":{"alternative_name":"-webkit-fill-available","version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"alternative_name":"-webkit-fill-available","version_added":"15"},"opera_android":{"alternative_name":"-webkit-fill-available","version_added":"15"},"safari":{"alternative_name":"-webkit-fill-available","version_added":"9"},"safari_ios":{"alternative_name":"-webkit-fill-available","version_added":"9"},"samsunginternet_android":{"version_added":"5.0","alternative_name":"-webkit-fill-available"},"webview_android":{"alternative_name":"-webkit-fill-available","version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"hyphens":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/hyphens","spec_url":"https://drafts.csswg.org/css-text/#hyphens-property","support":{"chrome":[{"partial_implementation":true,"version_added":"55","notes":"auto value is only supported on macOS and Android and for languages the OS provides hyphenation for."},{"prefix":"-webkit-","version_added":"13","notes":"Only -webkit-hyphens: none is supported."}],"chrome_android":[{"version_added":"55"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"partial_implementation":true,"version_added":"79","notes":"auto value is only supported on macOS and Android and for languages the OS provides hyphenation for."},{"prefix":"-webkit-","version_added":"79","notes":"Only -webkit-hyphens: none is supported."},{"prefix":"-ms-","version_added":"12","version_removed":"79","partial_implementation":true,"notes":"Only works if the specified language is the same as the language of the underlying OS."}],"firefox":[{"version_added":"43"},{"prefix":"-moz-","version_added":"6","notes":"Automatic hyphenation only works for languages with hyphenation dictionaries that are integrated into Firefox."}],"firefox_android":[{"version_added":"43"},{"prefix":"-moz-","version_added":"6","notes":"Automatic hyphenation only works for languages with hyphenation dictionaries that are integrated into Firefox."}],"ie":{"prefix":"-ms-","version_added":"10","partial_implementation":true,"notes":"Only works if the specified language is the same as the language of the underlying OS."},"opera":{"version_added":"44","partial_implementation":true,"notes":"auto value is only supported on macOS and Android."},"opera_android":{"version_added":"43"},"safari":{"prefix":"-webkit-","version_added":"5.1"},"safari_ios":{"prefix":"-webkit-","version_added":"4.2"},"samsunginternet_android":[{"version_added":"6.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"55"},{"prefix":"-webkit-","version_added":"4"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"afrikaans":{"__compat":{"description":"Hyphenation dictionary for Afrikaans (af, af-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"bosnian":{"__compat":{"description":"Hyphenation dictionary for Bosnian, Serbian, and Serbo-Croatian (sh, sh-*, sr, sr-*, bs, bs-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"bulgarian":{"__compat":{"description":"Hyphenation dictionary for Bulgarian (bg, bg-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"catalan":{"__compat":{"description":"Hyphenation dictionary for Catalan (ca, ca-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"croatian":{"__compat":{"description":"Hyphenation dictionary for Croatian (hr, hr-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"czech":{"__compat":{"description":"Hyphenation dictionary for Czech (cs, cs-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"danish":{"__compat":{"description":"Hyphenation dictionary for Danish (da, da-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"dutch":{"__compat":{"description":"Hyphenation dictionary for Dutch (nl, nl-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"english":{"__compat":{"description":"Hyphenation dictionary for English (en, en-*)","support":{"chrome":{"version_added":"55"},"chrome_android":{"version_added":"55"},"edge":{"version_added":"12"},"firefox":{"version_added":"6","notes":"For English, Firefox uses an en-US dictionary"},"firefox_android":{"version_added":"6","notes":"For English, Firefox uses an en-US dictionary"},"ie":{"version_added":"10"},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"5.1","notes":"For English, Safari uses different en-GB and en-US dictionaries."},"safari_ios":{"version_added":"6","notes":"For English, Safari uses different en-GB and en-US dictionaries."},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"55"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"esperanto":{"__compat":{"description":"Hyphenation dictionary for Esperanto (eo, eo-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"estonian":{"__compat":{"description":"Hyphenation dictionary for Estonian (et, et-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"finish":{"__compat":{"description":"Hyphenation dictionary for Finnish (fi, fi-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"french":{"__compat":{"description":"Hyphenation dictionary for French (fr, fr-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"galician":{"__compat":{"description":"Hyphenation dictionary for Galician (gl, gl-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"9"},"firefox_android":{"version_added":"9"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"german_reformed_orthography":{"__compat":{"description":"Hyphenation dictionary for German, Reformed Orthography of 1996 (de, de-1996, de-DE, de-AT, de-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"german_swiss_orthography":{"__compat":{"description":"Hyphenation dictionary for German, Swiss Orthography (de-CH, de-CH-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"german_traditional_orthography":{"__compat":{"description":"Hyphenation dictionary for German, Traditional Orthography of 1901 (de-1901, de-AT-1901, de-DE-1901)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"hungarian":{"__compat":{"description":"Hyphenation dictionary for Hungarian (hu, hu-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"9"},"firefox_android":{"version_added":"9"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"icelandic":{"__compat":{"description":"Hyphenation dictionary for Icelandic (is, is-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"interlingua":{"__compat":{"description":"Hyphenation dictionary for Interlingua (ia, ia-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"italian":{"__compat":{"description":"Hyphenation dictionary for Italian (it, it-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"9"},"firefox_android":{"version_added":"9"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"kurmanji":{"__compat":{"description":"Hyphenation dictionary for Kurmanji (kmr, kmr-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"latin":{"__compat":{"description":"Hyphenation dictionary for Latin (la, la-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"lithuanian":{"__compat":{"description":"Hyphenation dictionary for Lithuanian (lt, lt-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"mongolian":{"__compat":{"description":"Hyphenation dictionary for Mongolian (mn, mn-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"norwegian_nn":{"__compat":{"description":"Hyphenation dictionary for Norwegian (Nynorsk) (nn, nn-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"norwegian_no":{"__compat":{"description":"Hyphenation dictionary for Norwegian (Bokmål) (no, no-*, nb, nb-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"polish":{"__compat":{"description":"Hyphenation dictionary for Polish (pl, pl-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"31"},"firefox_android":{"version_added":"31"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"portuguese":{"__compat":{"description":"Hyphenation dictionary for Portuguese (pt, pt-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"portuguese_brazillian":{"__compat":{"description":"Hyphenation dictionary for Brazilian Portuguese (pt-BR)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8","notes":"For Brazilian Portuguese, Firefox uses a Portuguese dictionary."},"firefox_android":{"version_added":"8","notes":"For Brazilian Portuguese, Firefox uses a Portuguese dictionary."},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"russian":{"__compat":{"description":"Hyphenation dictionary for Russian (ru, ru-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"slovenian":{"__compat":{"description":"Hyphenation dictionary for Slovenian (sl, sl-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"spanish":{"__compat":{"description":"Hyphenation dictionary for Spanish (es, es-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"swedish":{"__compat":{"description":"Hyphenation dictionary for Swedish (sv, sv-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"turkish":{"__compat":{"description":"Hyphenation dictionary for Turkish (tr, tr-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"9"},"firefox_android":{"version_added":"9"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ukrainian":{"__compat":{"description":"Hyphenation dictionary for Ukrainian (uk, uk-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"9"},"firefox_android":{"version_added":"9"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"upper_sorbian":{"__compat":{"description":"Hyphenation dictionary for Upper Sorbian (hsb, hsb-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"welsh":{"__compat":{"description":"Hyphenation dictionary for Welsh (cy, cy-*)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"8"},"firefox_android":{"version_added":"8"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"image-orientation":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/image-orientation","spec_url":"https://drafts.csswg.org/css-images/#the-image-orientation","support":{"chrome":{"version_added":"81"},"chrome_android":{"version_added":"81"},"edge":{"version_added":"81"},"firefox":{"version_added":"26"},"firefox_android":{"version_added":"26"},"ie":{"version_added":false},"opera":{"version_added":"67"},"opera_android":{"version_added":false},"safari":{"version_added":"13.1"},"safari_ios":{"version_added":"13.4"},"samsunginternet_android":{"version_added":"13.0"},"webview_android":{"version_added":"81"}},"status":{"experimental":true,"standard_track":true,"deprecated":false}},"flip_and_angle":{"__compat":{"description":"flip & <angle>","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"26","version_removed":"63"},"firefox_android":{"version_added":"26","version_removed":"63"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}}},"image-rendering":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/image-rendering","spec_url":"https://drafts.csswg.org/css-images/#the-image-rendering","support":{"chrome":{"version_added":"13"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"3.6"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"6"},"safari_ios":{"version_added":"6"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"crisp-edges":{"__compat":{"support":{"chrome":{"alternative_name":"-webkit-optimize-contrast","version_added":"13"},"chrome_android":{"alternative_name":"-webkit-optimize-contrast","version_added":"18"},"edge":{"alternative_name":"-webkit-optimize-contrast","version_added":"79"},"firefox":[{"version_added":"65"},{"prefix":"-moz-","version_added":"3.6"}],"firefox_android":[{"version_added":"65"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"alternative_name":"-webkit-optimize-contrast","version_added":"15"},"opera_android":{"alternative_name":"-webkit-optimize-contrast","version_added":"14"},"safari":{"alternative_name":"-webkit-optimize-contrast","version_added":"6"},"safari_ios":{"alternative_name":"-webkit-optimize-contrast","version_added":"6"},"samsunginternet_android":{"alternative_name":"-webkit-optimize-contrast","version_added":"1.0"},"webview_android":{"alternative_name":"-webkit-optimize-contrast","version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"optimizeQuality":{"__compat":{"support":{"chrome":{"version_added":"28"},"chrome_android":{"version_added":"28"},"edge":{"version_added":"79"},"firefox":{"version_added":"3.6"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"7"},"safari_ios":{"version_added":"7"},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"optimizeSpeed":{"__compat":{"support":{"chrome":{"version_added":"28"},"chrome_android":{"version_added":"28"},"edge":{"version_added":"79"},"firefox":{"version_added":"3.6"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"7"},"safari_ios":{"version_added":"7"},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"pixelated":{"__compat":{"support":{"chrome":{"version_added":"41"},"chrome_android":{"version_added":"41"},"edge":{"version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"26"},"opera_android":{"version_added":"26"},"safari":{"version_added":"10"},"safari_ios":{"version_added":"10"},"samsunginternet_android":{"version_added":"4.0"},"webview_android":{"version_added":"41"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"ime-mode":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/ime-mode","spec_url":"https://drafts.csswg.org/css-ui/#input-method-editor","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":[{"version_added":"12","version_removed":"79"},{"prefix":"-ms-","version_added":"12","version_removed":"79"}],"firefox":{"version_added":"3"},"firefox_android":{"version_added":"4"},"ie":[{"version_added":"5"},{"prefix":"-ms-","version_added":"8"}],"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"initial-letter-align":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/initial-letter-align","spec_url":"https://drafts.csswg.org/css-inline/#aligning-initial-letter","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false,"notes":"See bug 1273021."},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"initial-letter":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/initial-letter","spec_url":"https://drafts.csswg.org/css-inline/#sizing-drop-initials","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false,"notes":"See bug 1223880."},"firefox_android":{"version_added":false,"notes":"See bug 1223880."},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"inline-size":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/inline-size","spec_url":"https://drafts.csswg.org/css-logical/#dimension-properties","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"fit-content":{"__compat":{"description":"fit-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"prefix":"-moz-","version_added":"41"},"firefox_android":{"prefix":"-moz-","version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"alternative_name":"-webkit-fill-available","version_added":"5.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"fit-content_function":{"__compat":{"description":"fit-content()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"firefox_android":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"max-content":{"__compat":{"description":"max-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"min-content":{"__compat":{"description":"min-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"inset-block-end":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/inset-block-end","spec_url":"https://drafts.csswg.org/css-logical/#position-properties","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":[{"version_added":"87"},{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"firefox":[{"version_added":"63"},{"alternative_name":"offset-block-end","version_added":"41","version_removed":"63"}],"firefox_android":[{"version_added":"63"},{"alternative_name":"offset-block-end","version_added":"41","version_removed":"63"}],"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"62"},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"inset-block-start":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/inset-block-start","spec_url":"https://drafts.csswg.org/css-logical/#position-properties","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":[{"version_added":"87"},{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"firefox":[{"version_added":"63"},{"alternative_name":"offset-block-start","version_added":"41","version_removed":"63"}],"firefox_android":[{"version_added":"63"},{"alternative_name":"offset-block-start","version_added":"41","version_removed":"63"}],"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"62"},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"inset-block":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/inset-block","spec_url":"https://drafts.csswg.org/css-logical/#propdef-inset-block","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":[{"version_added":"87"},{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"firefox":[{"version_added":"63"},{"alternative_name":"offset-block","version_added":"41","version_removed":"63"}],"firefox_android":[{"version_added":"63"},{"alternative_name":"offset-block","version_added":"41","version_removed":"63"}],"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"62"},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"inset-inline-end":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/inset-inline-end","spec_url":"https://drafts.csswg.org/css-logical/#position-properties","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":[{"version_added":"87"},{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"firefox":[{"version_added":"63"},{"alternative_name":"offset-inline-end","version_added":"41","version_removed":"63"}],"firefox_android":[{"version_added":"63"},{"alternative_name":"offset-inline-end","version_added":"41","version_removed":"63"}],"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"62"},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"inset-inline-start":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/inset-inline-start","spec_url":"https://drafts.csswg.org/css-logical/#position-properties","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":[{"version_added":"87"},{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"firefox":[{"version_added":"63"},{"alternative_name":"offset-inline-start","version_added":"41","version_removed":"63"}],"firefox_android":[{"version_added":"63"},{"alternative_name":"offset-inline-start","version_added":"41","version_removed":"63"}],"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"62"},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"inset-inline":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/inset-inline","spec_url":"https://drafts.csswg.org/css-logical/#propdef-inset-inline","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":[{"version_added":"87"},{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"firefox":[{"version_added":"63"},{"alternative_name":"offset-inline","version_added":"41","version_removed":"63"}],"firefox_android":[{"version_added":"63"},{"alternative_name":"offset-inline","version_added":"41","version_removed":"63"}],"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"62"},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"inset":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/inset","spec_url":"https://drafts.csswg.org/css-logical/#propdef-inset","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":[{"version_added":"87"},{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"62"},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"isolation":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/isolation","spec_url":"https://drafts.fxtf.org/compositing/#isolation","support":{"chrome":{"version_added":"41"},"chrome_android":{"version_added":"41"},"edge":{"version_added":"79"},"firefox":{"version_added":"36"},"firefox_android":{"version_added":"36"},"ie":{"version_added":false},"opera":{"version_added":"30"},"opera_android":{"version_added":"30"},"safari":{"version_added":"8"},"safari_ios":{"version_added":"8"},"samsunginternet_android":{"version_added":"4.0"},"webview_android":{"version_added":"41"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"justify-content":{"flex_context":{"__compat":{"description":"Supported in Flex Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/justify-content","spec_url":["https://drafts.csswg.org/css-align/#align-justify-content","https://drafts.csswg.org/css-flexbox/#justify-content-property"],"support":{"chrome":[{"version_added":"52"},{"version_added":"29","partial_implementation":true,"notes":"Older versions of the specification treat absolute positioned children as though they are a 0 by 0 flex item. Later specification versions take the children out of the flow and set their positions based on align and justify properties. Chrome implements the new behavior beginning with Chrome 52."},{"prefix":"-webkit-","version_added":"21"}],"chrome_android":[{"version_added":"52"},{"version_added":"29","partial_implementation":true,"notes":"Older versions of the specification treat absolute positioned children as though they are a 0 by 0 flex item. Later specification versions take the children out of the flow and set their positions based on align and justify properties. Chrome implements the new behavior beginning with Chrome 52."},{"prefix":"-webkit-","version_added":"25"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"20","notes":"Before Firefox 27, Firefox supported only single-line flexbox."},{"prefix":"-webkit-","version_added":"49"}],"firefox_android":[{"version_added":"20","notes":"Before Firefox 27, Firefox supported only single-line flexbox."},{"prefix":"-webkit-","version_added":"49"}],"ie":{"version_added":"11"},"opera":{"version_added":"12.1"},"opera_android":{"version_added":"12.1"},"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"6.1"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"6.1"}],"samsunginternet_android":[{"version_added":"6.0"},{"partial_implementation":true,"version_added":"2.0","notes":"Older versions of the specification treat absolute positioned children as though they are a 0 by 0 flex item. Later specification versions take the children out of the flow and set their positions based on align and justify properties. Chrome implements the new behavior beginning with Chrome 52."},{"prefix":"-webkit-","version_added":"1.5"}],"webview_android":[{"version_added":"52"},{"version_added":"4.4","partial_implementation":true,"notes":"Older versions of the specification treat absolute positioned children as though they are a 0 by 0 flex item. Later specification versions take the children out of the flow and set their positions based on align and justify properties. Chrome implements the new behavior beginning with Chrome 52."},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"baseline":{"__compat":{"description":"baseline","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":"45","version_removed":"60","notes":"justify-content no longer accepts baseline values"},"firefox_android":{"version_added":"45","version_removed":"60","notes":"justify-content no longer accepts baseline values"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"first_last_baseline":{"__compat":{"description":"first baseline and last baseline","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"52","version_removed":"60","notes":"justify-content no longer accepts baseline values"},"firefox_android":{"version_added":"52","version_removed":"60","notes":"justify-content no longer accepts baseline values"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"left_right":{"__compat":{"description":"left and right","support":{"chrome":{"version_added":"93"},"chrome_android":{"version_added":"93"},"edge":{"version_added":"93"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false,"notes":"This value is recognized, but has no effect."},"opera":{"version_added":false,"notes":"This value is recognized, but has no effect."},"opera_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"webview_android":{"version_added":"93"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"safe_unsafe":{"__compat":{"description":"safe and unsafe","support":{"chrome":{"version_added":false,"notes":"This value is recognized, but has no effect."},"chrome_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"edge":{"version_added":false,"notes":"This value is recognized, but has no effect."},"firefox":{"version_added":"63"},"firefox_android":{"version_added":"63"},"ie":{"version_added":false},"opera":{"version_added":false,"notes":"This value is recognized, but has no effect."},"opera_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"safari":{"version_added":false,"notes":"This value is recognized, but has no effect."},"safari_ios":{"version_added":false,"notes":"This value is recognized, but has no effect."},"samsunginternet_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"webview_android":{"version_added":false,"notes":"This value is recognized, but has no effect."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"space-evenly":{"__compat":{"description":"space-evenly","support":{"chrome":{"version_added":"60"},"chrome_android":{"version_added":"60"},"edge":{"version_added":"79"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"47"},"opera_android":{"version_added":"44"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"8.0"},"webview_android":{"version_added":"60"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"start_end":{"__compat":{"description":"start and end","support":{"chrome":{"version_added":"93"},"chrome_android":{"version_added":"93"},"edge":{"version_added":"93"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":false,"notes":"This value is recognized, but has no effect."},"opera_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":false,"notes":"This value is recognized, but has no effect."},"webview_android":{"version_added":"93"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"stretch":{"__compat":{"description":"stretch","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"grid_context":{"__compat":{"description":"Supported in Grid Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/justify-content","spec_url":["https://drafts.csswg.org/css-align/#align-justify-content","https://drafts.csswg.org/css-flexbox/#justify-content-property"],"support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"52"},"edge":{"version_added":"16"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.2"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"justify-items":{"flex_context":{"__compat":{"description":"Supported in Flex Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/justify-items","spec_url":"https://drafts.csswg.org/css-align/#justify-items-property","support":{"chrome":{"version_added":"52"},"chrome_android":{"version_added":"52"},"edge":{"version_added":"12"},"firefox":{"version_added":"20"},"firefox_android":{"version_added":"20"},"ie":{"version_added":"11"},"opera":{"version_added":"12.1"},"opera_android":{"version_added":"12.1"},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"52"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid_context":{"__compat":{"description":"Supported in Grid Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/justify-items","spec_url":"https://drafts.csswg.org/css-align/#justify-items-property","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"justify-self":{"flex_context":{"__compat":{"description":"Supported in Flex Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/justify-self","spec_url":"https://drafts.csswg.org/css-align/#justify-self-property","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid_context":{"__compat":{"description":"Supported in Grid Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/justify-self","spec_url":"https://drafts.csswg.org/css-align/#justify-self-property","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"16"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"prefix":"-ms-","version_added":"10","partial_implementation":true,"notes":"Internet Explorer 10 and 11 have the property -ms-grid-column-align, which acts in a similar way to justify-self."},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"justify-tracks":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/justify-tracks","spec_url":"https://drafts.csswg.org/css-grid-3/#tracks-alignment","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"77","flags":[{"type":"preference","name":"layout.css.grid-template-masonry-value.enabled","value_to_set":"true"}]},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"left":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/left","spec_url":"https://drafts.csswg.org/css-position/#insets","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"5.5"},"opera":{"version_added":"5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"letter-spacing":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/letter-spacing","spec_url":"https://drafts.csswg.org/css-text/#letter-spacing-property","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"svg":{"__compat":{"description":"SVG support","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"72"},"firefox_android":{"version_added":false},"ie":{"version_added":"9"},"opera":{"version_added":"7"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"line-break":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/line-break","spec_url":"https://drafts.csswg.org/css-text/#line-break-property","support":{"chrome":[{"version_added":"58"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"58"},{"prefix":"-webkit-","version_added":"18"}],"edge":{"version_added":"14"},"firefox":{"version_added":"69"},"firefox_android":{"version_added":false},"ie":[{"version_added":"5.5"},{"prefix":"-ms-","version_added":"8"}],"opera":[{"version_added":"45"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"43"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"11"},{"version_added":"3","prefix":"-webkit-"},{"version_added":"2","version_removed":"3","prefix":"-khtml-"}],"safari_ios":[{"version_added":"11"},{"version_added":"1","prefix":"-webkit-"}],"samsunginternet_android":[{"version_added":"7.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"58"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"line-height-step":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/line-height-step","spec_url":"https://drafts.csswg.org/css-rhythm/#line-height-step","support":{"chrome":{"version_added":"60","flags":[{"type":"runtime_flag","name":"--enable-blink-features=CSSSnapSize"}]},"chrome_android":{"version_added":"60","flags":[{"type":"runtime_flag","name":"--enable-blink-features=CSSSnapSize"}]},"edge":{"version_added":"79","flags":[{"type":"runtime_flag","name":"--enable-blink-features=CSSSnapSize"}]},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"47","flags":[{"type":"runtime_flag","name":"--enable-blink-features=CSSSnapSize"}]},"opera_android":{"version_added":"44","flags":[{"type":"runtime_flag","name":"--enable-blink-features=CSSSnapSize"}]},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"line-height":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/line-height","spec_url":"https://drafts.csswg.org/css-inline/#line-height-property","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"-moz-block-height":{"__compat":{"support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"3.6"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}}},"list-style-image":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/list-style-image","spec_url":"https://drafts.csswg.org/css-lists/#image-markers","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"Before Firefox 86, this property did not accept an <image> type, and required the URL of an image."},"firefox_android":{"version_added":"4","notes":"Before Firefox 86, this property did not accept an <image> type, and required the URL of an image."},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"list-style-position":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/list-style-position","spec_url":"https://drafts.csswg.org/css-lists/#list-style-position-property","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"list-style-type":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/list-style-type","spec_url":["https://drafts.csswg.org/css-lists/#text-markers","https://drafts.csswg.org/css-counter-styles/#extending-css2"],"support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"afar":{"__compat":{"description":"afar","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"amharic":{"__compat":{"description":"amaric","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"amharic-abegede":{"__compat":{"description":"amaric-abegede","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"arabic-indic":{"__compat":{"description":"arabic-indic","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"armenian":{"__compat":{"description":"armenian","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"6"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"asterisks":{"__compat":{"description":"asterisks","support":{"chrome":{"version_added":"13","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"4.4","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"bengali":{"__compat":{"description":"bengali","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"binary":{"__compat":{"description":"binary","support":{"chrome":{"version_added":"13","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"4.4","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"cambodian":{"__compat":{"description":"cambodian","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"33"},"firefox_android":{"version_added":"33"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"circle":{"__compat":{"description":"circle","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"cjk-decimal":{"__compat":{"description":"cjk-decimal","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"28"},"firefox_android":{"version_added":"28"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"cjk-earthly-branch":{"__compat":{"description":"cjk-earthly-branch","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"cjk-heavenly-stem":{"__compat":{"description":"cjk-heavenly-stem","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"cjk-ideographic":{"__compat":{"description":"cjk-ideographic","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":[{"version_added":"15"},{"version_added":"7","partial_implementation":true,"notes":"Until version 15, only decimal numbers display."}],"opera_android":[{"version_added":"14"},{"version_added":"10.1","partial_implementation":true,"notes":"Until version 15, only decimal numbers display."}],"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"decimal":{"__compat":{"description":"decimal","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"decimal-leading-zero":{"__compat":{"description":"decimal-leading-zero","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"8"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"devanagari":{"__compat":{"description":"devanagari","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"disc":{"__compat":{"description":"disc","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"disclosure-closed":{"__compat":{"description":"disclosure-closed","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"33"},"firefox_android":{"version_added":"33"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"disclosure-open":{"__compat":{"description":"disclosure-open","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"33"},"firefox_android":{"version_added":"33"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic":{"__compat":{"description":"ethiopic","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-abegede":{"__compat":{"description":"ethiopic-abegede","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-abegede-am-et":{"__compat":{"description":"ethiopic-abegede-am-et","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-abegede-gez":{"__compat":{"description":"ethiopic-abegede-gez","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-abegede-ti-er":{"__compat":{"description":"ethiopic-abegede-ti-er","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-abegede-ti-et":{"__compat":{"description":"ethiopic-abegede-ti-et","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-halehame":{"__compat":{"description":"ethiopic-halehame","support":{"chrome":{"version_added":"45"},"chrome_android":{"version_added":"45"},"edge":{"version_added":"79"},"firefox":{"version_added":"1","prefix":"-moz-"},"firefox_android":{"version_added":"4","prefix":"-moz-"},"ie":{"version_added":false},"opera":{"version_added":"32"},"opera_android":{"version_added":"32"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-halehame-aa-er":{"__compat":{"description":"ethiopic-halehame-aa-er","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-halehame-aa-et":{"__compat":{"description":"ethiopic-halehame-aa-et","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-halehame-am":{"__compat":{"description":"ethiopic-halehame-am","support":{"chrome":{"version_added":"45"},"chrome_android":{"version_added":"45"},"edge":{"version_added":"79"},"firefox":{"version_added":"1","prefix":"-moz-"},"firefox_android":{"version_added":"4","prefix":"-moz-"},"ie":{"version_added":false},"opera":{"version_added":"32"},"opera_android":{"version_added":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-halehame-am-et":{"__compat":{"description":"ethiopic-halehame-am-et","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-halehame-gez":{"__compat":{"description":"ethiopic-halehame-gez","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-halehame-om-et":{"__compat":{"description":"ethiopic-halehame-om-et","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-halehame-sid-et":{"__compat":{"description":"ethiopic-halehame-sid-et","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-halehame-so-et":{"__compat":{"description":"ethiopic-halehame-so-et","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-halehame-ti-er":{"__compat":{"description":"ethiopic-halehame-ti-er","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"1","prefix":"-moz-"},"firefox_android":{"version_added":"4","prefix":"-moz-"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"3"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-halehame-ti-et":{"__compat":{"description":"ethiopic-halehame-ti-et","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"1","prefix":"-moz-"},"firefox_android":{"version_added":"4","prefix":"-moz-"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"3"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-halehame-tig":{"__compat":{"description":"ethiopic-halehame-tig","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ethiopic-numeric":{"__compat":{"description":"ethiopic-numeric","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"33","notes":"Before Firefox 38, Firefox added a dot as suffix of the number for ethiopic-numeric (for example, ፫. instead of ፫). The specification later defined the absence of a suffix, which Firefox 38 followed."},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33","notes":"Before Firefox 38, Firefox added a dot as suffix of the number for ethiopic-numeric (for example, ፫. instead of ፫). The specification later defined the absence of a suffix, which Firefox 38 followed."},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"footnotes":{"__compat":{"description":"footnotes","support":{"chrome":{"version_added":"13","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"4.4","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"georgian":{"__compat":{"description":"georgian","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"6"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"gujarati":{"__compat":{"description":"gujarati","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"gurmukhi":{"__compat":{"description":"gurmukhi","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"hangul":{"__compat":{"description":"hangul","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"1","prefix":"-moz-"},"firefox_android":{"version_added":"4","prefix":"-moz-"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"hangul-consonant":{"__compat":{"description":"hangul-consonant","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"1","prefix":"-moz-"},"firefox_android":{"version_added":"4","prefix":"-moz-"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"hebrew":{"__compat":{"description":"hebrew","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"hiragana":{"__compat":{"description":"hiragana","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"hiragana-iroha":{"__compat":{"description":"hiragana-iroha","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"japanese-formal":{"__compat":{"description":"japanese-formal","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"28"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"28"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"japanese-informal":{"__compat":{"description":"japanese-informal","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"28"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"28"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"kannada":{"__compat":{"description":"kannada","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"katakana":{"__compat":{"description":"katakana","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"katakana-iroha":{"__compat":{"description":"katakana-iroha","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"khmer":{"__compat":{"description":"khmer","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"korean-hangul-formal":{"__compat":{"description":"korean-hangul-formal","support":{"chrome":{"version_added":"45"},"chrome_android":{"version_added":"45"},"edge":{"version_added":"79"},"firefox":{"version_added":"28"},"firefox_android":{"version_added":"28"},"ie":{"version_added":false},"opera":{"version_added":"32"},"opera_android":{"version_added":"32"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"korean-hanja-formal":{"__compat":{"description":"korean-hanja-formal","support":{"chrome":{"version_added":"45"},"chrome_android":{"version_added":"45"},"edge":{"version_added":"79"},"firefox":{"version_added":"28"},"firefox_android":{"version_added":"28"},"ie":{"version_added":false},"opera":{"version_added":"32"},"opera_android":{"version_added":"32"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"korean-hanja-informal":{"__compat":{"description":"korean-hanja-informal","support":{"chrome":{"version_added":"45"},"chrome_android":{"version_added":"45"},"edge":{"version_added":"79"},"firefox":{"version_added":"28"},"firefox_android":{"version_added":"28"},"ie":{"version_added":false},"opera":{"version_added":"32"},"opera_android":{"version_added":"32"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"lao":{"__compat":{"description":"lao","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"lower-alpha":{"__compat":{"description":"lower-alpha","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"6"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"lower-armenian":{"__compat":{"description":"lower-armenian","support":{"chrome":{"version_added":"13"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"33"},"firefox_android":{"version_added":"33"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"lower-greek":{"__compat":{"description":"lower-greek","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"6"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"lower-hexadecimal":{"__compat":{"description":"lower-hexadecimal","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"lower-latin":{"__compat":{"description":"lower-latin","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"6"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"lower-norwegian":{"__compat":{"description":"lower-norwegian","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"lower-roman":{"__compat":{"description":"lower-roman","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"6"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"malayalam":{"__compat":{"description":"malayalam","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"mongolian":{"__compat":{"description":"mongolian","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"33"},"firefox_android":{"version_added":"33"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"myanmar":{"__compat":{"description":"myanmar","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"octal":{"__compat":{"description":"octal","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"1","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"oriya":{"__compat":{"description":"oriya","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"oromo":{"__compat":{"description":"oromo","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"1","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"persian":{"__compat":{"description":"persian","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"sidama":{"__compat":{"description":"sidama","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"1","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"simp-chinese-formal":{"__compat":{"description":"simp-chinese-formal","support":{"chrome":{"version_added":"45"},"chrome_android":{"version_added":"45"},"edge":{"version_added":"79"},"firefox":[{"version_added":"28"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"28"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"32"},"opera_android":{"version_added":"32"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"simp-chinese-informal":{"__compat":{"description":"simp-chinese-informal","support":{"chrome":{"version_added":"45"},"chrome_android":{"version_added":"45"},"edge":{"version_added":"79"},"firefox":[{"version_added":"28"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"28"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"32"},"opera_android":{"version_added":"32"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"somali":{"__compat":{"description":"somali","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"1","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"square":{"__compat":{"description":"square","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"string":{"__compat":{"description":"<string>","support":{"chrome":{"version_added":"79"},"chrome_android":{"version_added":"79"},"edge":{"version_added":"79"},"firefox":{"version_added":"39"},"firefox_android":{"version_added":"39"},"ie":{"version_added":false},"opera":{"version_added":"66"},"opera_android":{"version_added":false},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"12.0"},"webview_android":{"version_added":"79"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"symbols":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/symbols()","spec_url":"https://drafts.csswg.org/css-counter-styles/#symbols-function","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"35"},"firefox_android":{"version_added":"35"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"tamil":{"__compat":{"description":"tamil","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"telugu":{"__compat":{"description":"telugu","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"thai":{"__compat":{"description":"thai","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":[{"version_added":"33"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"33"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"tibetan":{"__compat":{"description":"tibetan","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"33"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"tigre":{"__compat":{"description":"tigre","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"1","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"tigrinya-er":{"__compat":{"description":"tigrinya-er","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"1","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"tigrinya-er-abegede":{"__compat":{"description":"tigrinya-er-abegede","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"1","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"tigrinya-et":{"__compat":{"description":"tigrinya-et","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"1","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"tigrinya-et-abegede":{"__compat":{"description":"tigrinya-et-abegede","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"1","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"trad-chinese-formal":{"__compat":{"description":"trad-chinese-formal","support":{"chrome":{"version_added":"45"},"chrome_android":{"version_added":"45"},"edge":{"version_added":"79"},"firefox":[{"version_added":"28"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"28"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"32"},"opera_android":{"version_added":"32"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"trad-chinese-informal":{"__compat":{"description":"trad-chinese-informal","support":{"chrome":{"version_added":"45"},"chrome_android":{"version_added":"45"},"edge":{"version_added":"79"},"firefox":[{"version_added":"28"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"28"},{"prefix":"-moz-","version_added":"4"}],"ie":{"version_added":false},"opera":{"version_added":"32"},"opera_android":{"version_added":"32"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"upper-alpha":{"__compat":{"description":"upper-alpha","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"6"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"upper-armenian":{"__compat":{"description":"upper-armenian","support":{"chrome":{"version_added":"13"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"33"},"firefox_android":{"version_added":"33"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"upper-greek":{"__compat":{"description":"upper-greek","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"6"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"upper-hexadecimal":{"__compat":{"description":"upper-hexadecimal","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"upper-latin":{"__compat":{"description":"upper-latin","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"6"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"upper-norwegian":{"__compat":{"description":"upper-norwegian","support":{"chrome":{"version_added":"6","version_removed":"45"},"chrome_android":{"version_added":"18","version_removed":"45"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"32"},"opera_android":{"version_added":"14","version_removed":"32"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","version_removed":"5.0"},"webview_android":{"version_added":"3","version_removed":"45"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"upper-roman":{"__compat":{"description":"upper-roman","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"6"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"urdu":{"__compat":{"description":"urdu","support":{"chrome":{"version_added":"6"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"prefix":"-moz-","version_added":"33"},"firefox_android":{"prefix":"-moz-","version_added":"33"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"list-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/list-style","spec_url":"https://drafts.csswg.org/css-lists/#list-style-property","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"symbols":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/symbols()","spec_url":"https://drafts.csswg.org/css-counter-styles/#symbols-function","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"35"},"firefox_android":{"version_added":"35"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"margin-block-end":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/margin-block-end","spec_url":"https://drafts.csswg.org/css-scroll-snap/#scroll-padding","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"margin-block-start":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/margin-block-start","spec_url":"https://drafts.csswg.org/css-logical/#margin-properties","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"margin-block":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/margin-block","spec_url":"https://drafts.csswg.org/css-logical/#propdef-margin-block","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":[{"version_added":"87"},{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"48","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"margin-bottom":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/margin-bottom","spec_url":"https://drafts.csswg.org/css-box/#margin-physical","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"3"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"auto":{"__compat":{"description":"auto","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12","notes":"The auto value is not supported in quirks mode."},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"6","notes":"The auto value is not supported in quirks mode."},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"margin-inline-end":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/margin-inline-end","spec_url":"https://drafts.csswg.org/css-logical/#margin-properties","support":{"chrome":[{"version_added":"69"},{"version_added":"2","alternative_name":"-webkit-margin-end"}],"chrome_android":[{"version_added":"69"},{"version_added":"18","alternative_name":"-webkit-margin-end"}],"edge":[{"version_added":"79"},{"version_added":"79","alternative_name":"-webkit-margin-end"}],"firefox":[{"version_added":"41"},{"version_added":"3","alternative_name":"-moz-margin-end"}],"firefox_android":[{"version_added":"41"},{"version_added":"4","alternative_name":"-moz-margin-end"}],"ie":{"version_added":false},"opera":[{"version_added":"56"},{"version_added":"15","alternative_name":"-webkit-margin-end"}],"opera_android":[{"version_added":"48"},{"version_added":"14","alternative_name":"-webkit-margin-end"}],"safari":[{"version_added":"12.1"},{"version_added":"3","alternative_name":"-webkit-margin-end"}],"safari_ios":[{"version_added":"12.2"},{"version_added":"3","alternative_name":"-webkit-margin-end"}],"samsunginternet_android":[{"version_added":"10.0"},{"alternative_name":"-webkit-margin-end","version_added":"1.0"}],"webview_android":[{"version_added":"69"},{"version_added":"2","alternative_name":"-webkit-margin-end"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"margin-inline-start":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/margin-inline-start","spec_url":"https://drafts.csswg.org/css-logical/#margin-properties","support":{"chrome":[{"version_added":"69"},{"version_added":"2","alternative_name":"-webkit-margin-start"}],"chrome_android":[{"version_added":"69"},{"version_added":"18","alternative_name":"-webkit-margin-start"}],"edge":[{"version_added":"79"},{"version_added":"79","alternative_name":"-webkit-margin-start"}],"firefox":[{"version_added":"41"},{"version_added":"3","alternative_name":"-moz-margin-start"}],"firefox_android":[{"version_added":"41"},{"version_added":"4","alternative_name":"-moz-margin-start"}],"ie":{"version_added":false},"opera":[{"version_added":"56"},{"version_added":"15","alternative_name":"-webkit-margin-start"}],"opera_android":[{"version_added":"48"},{"version_added":"14","alternative_name":"-webkit-margin-start"}],"safari":[{"version_added":"12.1"},{"version_added":"3","alternative_name":"-webkit-margin-start"}],"safari_ios":[{"version_added":"12.2"},{"version_added":"3","alternative_name":"-webkit-margin-start"}],"samsunginternet_android":[{"version_added":"10.0"},{"alternative_name":"-webkit-margin-start","version_added":"1.0"}],"webview_android":[{"version_added":"69"},{"version_added":"2","alternative_name":"-webkit-margin-start"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"margin-inline":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/margin-inline","spec_url":"https://drafts.csswg.org/css-logical/#propdef-margin-inline","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":[{"version_added":"87"},{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"48","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"margin-left":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/margin-left","spec_url":"https://drafts.csswg.org/css-box/#margin-physical","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"3"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"auto":{"__compat":{"description":"auto","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12","notes":"The auto value is not supported in quirks mode."},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"6","notes":"The auto value is not supported in quirks mode."},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"margin-right":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/margin-right","spec_url":"https://drafts.csswg.org/css-box/#margin-physical","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"3"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"auto":{"__compat":{"description":"auto","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12","notes":"The auto value is not supported in quirks mode."},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"6","notes":"The auto value is not supported in quirks mode."},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"margin-top":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/margin-top","spec_url":"https://drafts.csswg.org/css-box/#margin-physical","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"3"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"auto":{"__compat":{"description":"auto","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12","notes":"The auto value is not supported in quirks mode."},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"6","notes":"The auto value is not supported in quirks mode."},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"margin-trim":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/margin-trim","spec_url":"https://drafts.csswg.org/css-box-4/#margin-trim","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false,"notes":"See bug 1506241."},"firefox_android":{"version_added":false,"notes":"See bug 1506241."},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"margin":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/margin","spec_url":"https://drafts.csswg.org/css-box/#margin","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"3"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"auto":{"__compat":{"description":"auto","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12","notes":"The auto value is not supported in quirks mode."},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"6","notes":"The auto value is not supported in quirks mode."},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"mask-border-outset":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mask-border-outset","spec_url":"https://drafts.fxtf.org/css-masking/#the-mask-border-outset","support":{"chrome":{"alternative_name":"-webkit-mask-box-image-outset","version_added":"1"},"chrome_android":{"alternative_name":"-webkit-mask-box-image-outset","version_added":"18"},"edge":{"alternative_name":"-webkit-mask-box-image-outset","version_added":"79"},"firefox":{"version_added":false,"notes":"See bug 877294."},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"alternative_name":"-webkit-mask-box-image-outset","version_added":"15"},"opera_android":{"alternative_name":"-webkit-mask-box-image-outset","version_added":"14"},"safari":{"alternative_name":"-webkit-mask-box-image-outset","version_added":"3.1"},"safari_ios":{"alternative_name":"-webkit-mask-box-image-outset","version_added":"3"},"samsunginternet_android":{"alternative_name":"-webkit-mask-box-image-outset","version_added":"1.0"},"webview_android":{"alternative_name":"-webkit-mask-box-image-outset","version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"mask-border-repeat":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mask-border-repeat","spec_url":"https://drafts.fxtf.org/css-masking/#the-mask-border-repeat","support":{"chrome":{"alternative_name":"-webkit-mask-box-image-repeat","version_added":"1"},"chrome_android":{"alternative_name":"-webkit-mask-box-image-repeat","version_added":"18"},"edge":{"alternative_name":"-webkit-mask-box-image-repeat","version_added":"79"},"firefox":{"version_added":false,"notes":"See bug 877294."},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"alternative_name":"-webkit-mask-box-image-repeat","version_added":"15"},"opera_android":{"alternative_name":"-webkit-mask-box-image-repeat","version_added":"14"},"safari":{"alternative_name":"-webkit-mask-box-image-repeat","version_added":"3.1"},"safari_ios":{"alternative_name":"-webkit-mask-box-image-repeat","version_added":"3"},"samsunginternet_android":{"alternative_name":"-webkit-mask-box-image-repeat","version_added":"1.0"},"webview_android":{"alternative_name":"-webkit-mask-box-image-repeat","version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"mask-border-slice":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mask-border-slice","spec_url":"https://drafts.fxtf.org/css-masking/#the-mask-border-slice","support":{"chrome":{"alternative_name":"-webkit-mask-box-image-slice","version_added":"1"},"chrome_android":{"alternative_name":"-webkit-mask-box-image-slice","version_added":"18"},"edge":{"alternative_name":"-webkit-mask-box-image-slice","version_added":"79"},"firefox":{"version_added":false,"notes":"See bug 877294."},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"alternative_name":"-webkit-mask-box-image-slice","version_added":"15"},"opera_android":{"alternative_name":"-webkit-mask-box-image-slice","version_added":"14"},"safari":{"alternative_name":"-webkit-mask-box-image-slice","version_added":"3.1"},"safari_ios":{"alternative_name":"-webkit-mask-box-image-slice","version_added":"3"},"samsunginternet_android":{"alternative_name":"-webkit-mask-box-image-slice","version_added":"1.0"},"webview_android":{"alternative_name":"-webkit-mask-box-image-slice","version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"mask-border-source":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mask-border-source","spec_url":"https://drafts.fxtf.org/css-masking/#the-mask-border-source","support":{"chrome":{"alternative_name":"-webkit-mask-box-image-source","version_added":"1"},"chrome_android":{"alternative_name":"-webkit-mask-box-image-source","version_added":"18"},"edge":{"alternative_name":"-webkit-mask-box-image-source","version_added":"79"},"firefox":{"version_added":false,"notes":"See bug 877294."},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"alternative_name":"-webkit-mask-box-image-source","version_added":"15"},"opera_android":{"alternative_name":"-webkit-mask-box-image-source","version_added":"14"},"safari":{"alternative_name":"-webkit-mask-box-image-source","version_added":"3.1"},"safari_ios":{"alternative_name":"-webkit-mask-box-image-source","version_added":"3"},"samsunginternet_android":{"alternative_name":"-webkit-mask-box-image-source","version_added":"1.0"},"webview_android":{"alternative_name":"-webkit-mask-box-image-source","version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"mask-border-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mask-border-width","spec_url":"https://drafts.fxtf.org/css-masking/#the-mask-border-width","support":{"chrome":{"alternative_name":"-webkit-mask-box-image-width","version_added":"1"},"chrome_android":{"alternative_name":"-webkit-mask-box-image-width","version_added":"18"},"edge":{"alternative_name":"-webkit-mask-box-image-width","version_added":"79"},"firefox":{"version_added":false,"notes":"See bug 877294."},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"alternative_name":"-webkit-mask-box-image-width","version_added":"15"},"opera_android":{"alternative_name":"-webkit-mask-box-image-width","version_added":"14"},"safari":{"alternative_name":"-webkit-mask-box-image-width","version_added":"3.1"},"safari_ios":{"alternative_name":"-webkit-mask-box-image-width","version_added":"3"},"samsunginternet_android":{"alternative_name":"-webkit-mask-box-image-width","version_added":"1.0"},"webview_android":{"alternative_name":"-webkit-mask-box-image-width","version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"mask-border":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mask-border","spec_url":"https://drafts.fxtf.org/css-masking/#the-mask-border","support":{"chrome":{"alternative_name":"-webkit-mask-box-image","version_added":"1"},"chrome_android":{"alternative_name":"-webkit-mask-box-image","version_added":"18"},"edge":{"alternative_name":"-webkit-mask-box-image","version_added":"79"},"firefox":{"version_added":false,"notes":"See bug 877294."},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"alternative_name":"-webkit-mask-box-image","version_added":"15"},"opera_android":{"alternative_name":"-webkit-mask-box-image","version_added":"14"},"safari":{"alternative_name":"-webkit-mask-box-image","version_added":"3.1"},"safari_ios":{"alternative_name":"-webkit-mask-box-image","version_added":"3"},"samsunginternet_android":{"alternative_name":"-webkit-mask-box-image","version_added":"1.0"},"webview_android":{"alternative_name":"-webkit-mask-box-image","version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"mask-clip":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mask-clip","spec_url":"https://drafts.fxtf.org/css-masking/#the-mask-clip","support":{"chrome":{"prefix":"-webkit-","version_added":"1"},"chrome_android":{"prefix":"-webkit-","version_added":"18"},"edge":{"prefix":"-webkit-","version_added":"79"},"firefox":{"version_added":"53"},"firefox_android":{"version_added":"53"},"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":{"prefix":"-webkit-","version_added":"4"},"safari_ios":{"prefix":"-webkit-","version_added":"3.2"},"samsunginternet_android":{"prefix":"-webkit-","version_added":"1.0"},"webview_android":{"prefix":"-webkit-","version_added":"2"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"border":{"__compat":{"description":"border","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"4"},"safari_ios":{"version_added":"3.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"content":{"__compat":{"description":"content","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"4"},"safari_ios":{"version_added":"3.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"padding":{"__compat":{"description":"padding","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"4"},"safari_ios":{"version_added":"3.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"text":{"__compat":{"description":"text","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"4"},"safari_ios":{"version_added":"3.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}}},"mask-composite":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mask-composite","spec_url":"https://drafts.fxtf.org/css-masking/#the-mask-composite","support":{"chrome":{"version_added":false,"notes":"See also -webkit-mask-composite for a similar non-standard property that uses different keywords."},"chrome_android":{"version_added":false,"notes":"See also -webkit-mask-composite for a similar non-standard property that uses different keywords."},"edge":{"version_added":"18","version_removed":"79"},"firefox":{"version_added":"53"},"firefox_android":{"version_added":"53"},"ie":{"version_added":false},"opera":{"version_added":false,"notes":"See also -webkit-mask-composite for a similar non-standard property that uses different keywords."},"opera_android":{"version_added":false,"notes":"See also -webkit-mask-composite for a similar non-standard property that uses different keywords."},"safari":{"version_added":false,"notes":"See also -webkit-mask-composite for a similar non-standard property that uses different keywords."},"safari_ios":{"version_added":false,"notes":"See also -webkit-mask-composite for a similar non-standard property that uses different keywords."},"samsunginternet_android":{"version_added":false,"notes":"See also -webkit-mask-composite for a similar non-standard property that uses different keywords."},"webview_android":{"version_added":false,"notes":"See also -webkit-mask-composite for a similar non-standard property that uses different keywords."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"mask-image":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mask-image","spec_url":"https://drafts.fxtf.org/css-masking/#the-mask-image","support":{"chrome":{"prefix":"-webkit-","version_added":"1","notes":"From version 8, Chrome added support for gradient values. Initially, Chrome supported only -webkit- prefixed values for gradients (such as -webkit-linear-gradient()). Later, support for unprefixed values was added."},"chrome_android":{"prefix":"-webkit-","version_added":"18","notes":"From version 8, Chrome added support for gradient values. Initially, Chrome supported only -webkit- prefixed values for gradients (such as -webkit-linear-gradient()). Later, support for unprefixed values was added."},"edge":[{"prefix":"-webkit-","version_added":"79"},{"version_added":"16","version_removed":"79"}],"firefox":{"version_added":"53"},"firefox_android":{"version_added":"53"},"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":{"prefix":"-webkit-","version_added":"4","notes":"Initially, Safari supported only -webkit- prefixed values for gradients (such as -webkit-linear-gradient()). Later, support for unprefixed values was added."},"safari_ios":{"prefix":"-webkit-","version_added":"3.2","notes":"Initially, Safari supported only -webkit- prefixed values for gradients (such as -webkit-linear-gradient()). Later, support for unprefixed values was added."},"samsunginternet_android":{"version_added":"1.0","prefix":"-webkit-"},"webview_android":{"prefix":"-webkit-","version_added":"2","notes":"Initially, Android supported only -webkit- prefixed values for gradients (such as -webkit-linear-gradient()). Later, support for unprefixed values was added."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"multiple_mask_images":{"__compat":{"description":"Multiple mask images","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"18"},"firefox":{"version_added":"53"},"firefox_android":{"version_added":"53"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"4"},"safari_ios":{"version_added":"3.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"svg_masks":{"__compat":{"description":"SVG masks","support":{"chrome":{"version_added":"8"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"18"},"firefox":{"version_added":"53"},"firefox_android":{"version_added":"53"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"4"},"safari_ios":{"version_added":"3.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"mask-mode":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mask-mode","spec_url":"https://drafts.fxtf.org/css-masking/#the-mask-mode","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"53"},"firefox_android":{"version_added":"53"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"mask-origin":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mask-origin","spec_url":"https://drafts.fxtf.org/css-masking/#the-mask-origin","support":{"chrome":{"prefix":"-webkit-","version_added":"1","notes":"The margin-box value is unsupported."},"chrome_android":{"prefix":"-webkit-","version_added":"18","notes":"The margin-box value is unsupported."},"edge":{"prefix":"-webkit-","version_added":"79","notes":"The margin-box value is unsupported."},"firefox":{"version_added":"53"},"firefox_android":{"version_added":"53"},"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15","notes":"The margin-box value is unsupported."},"opera_android":{"prefix":"-webkit-","version_added":"14","notes":"The margin-box value is unsupported."},"safari":{"prefix":"-webkit-","version_added":"4","notes":"The margin-box value is unsupported."},"safari_ios":{"prefix":"-webkit-","version_added":"3.2","notes":"The margin-box value is unsupported."},"samsunginternet_android":{"prefix":"-webkit-","version_added":"1.0","notes":"The margin-box value is unsupported."},"webview_android":{"prefix":"-webkit-","version_added":"2","notes":"The margin-box value is unsupported."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"fill-box":{"__compat":{"description":"fill-box","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false,"notes":"See bug 137293."},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"non_standard_values":{"__compat":{"description":"Non-standard values content, padding, border","support":{"chrome":{"prefix":"-webkit-","version_added":"1"},"chrome_android":{"prefix":"-webkit-","version_added":"18"},"edge":{"prefix":"-webkit-","version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":{"prefix":"-webkit-","version_added":"4"},"safari_ios":{"prefix":"-webkit-","version_added":"3.2"},"samsunginternet_android":{"prefix":"-webkit-","version_added":"1.0"},"webview_android":{"prefix":"-webkit-","version_added":"2"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"stroke-box":{"__compat":{"description":"stroke-box","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false,"notes":"See bug 137293."},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"view-box":{"__compat":{"description":"view-box","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false,"notes":"See bug 137293."},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}}},"mask-position":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mask-position","spec_url":"https://drafts.fxtf.org/css-masking/#the-mask-position","support":{"chrome":{"prefix":"-webkit-","version_added":"1"},"chrome_android":{"prefix":"-webkit-","version_added":"18"},"edge":[{"prefix":"-webkit-","version_added":"79"},{"version_added":"18","version_removed":"79"}],"firefox":{"version_added":"53"},"firefox_android":{"version_added":"53"},"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":{"prefix":"-webkit-","version_added":"3.1"},"safari_ios":{"prefix":"-webkit-","version_added":"2"},"samsunginternet_android":{"prefix":"-webkit-","version_added":"1.0"},"webview_android":{"prefix":"-webkit-","version_added":"2"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"three_value_syntax":{"__compat":{"description":"Support for three-value syntax of position","support":{"chrome":{"prefix":"-webkit-","version_added":"1","version_removed":"68"},"chrome_android":{"prefix":"-webkit-","version_added":"18","version_removed":"68"},"edge":{"version_added":"18","version_removed":"79"},"firefox":{"version_added":"53","version_removed":"70"},"firefox_android":{"version_added":"53","version_removed":"79"},"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15","version_removed":"55"},"opera_android":{"prefix":"-webkit-","version_added":"14","version_removed":"48"},"safari":{"prefix":"-webkit-","version_added":"3.1"},"safari_ios":{"prefix":"-webkit-","version_added":"2"},"samsunginternet_android":{"prefix":"-webkit-","version_added":"1.0","version_removed":"10.0"},"webview_android":{"prefix":"-webkit-","version_added":"2","version_removed":"68"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"mask-repeat":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mask-repeat","spec_url":"https://drafts.fxtf.org/css-masking/#the-mask-repeat","support":{"chrome":{"prefix":"-webkit-","version_added":"1"},"chrome_android":{"prefix":"-webkit-","version_added":"18"},"edge":[{"prefix":"-webkit-","version_added":"79"},{"version_added":"18","version_removed":"79"}],"firefox":{"version_added":"53"},"firefox_android":{"version_added":"53"},"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":{"prefix":"-webkit-","version_added":"3.1"},"safari_ios":{"prefix":"-webkit-","version_added":"2"},"samsunginternet_android":{"prefix":"-webkit-","version_added":"1.0"},"webview_android":{"prefix":"-webkit-","version_added":"2"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"mask-size":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mask-size","spec_url":"https://drafts.fxtf.org/css-masking/#the-mask-size","support":{"chrome":{"prefix":"-webkit-","version_added":"4"},"chrome_android":{"prefix":"-webkit-","version_added":"18"},"edge":[{"prefix":"-webkit-","version_added":"79"},{"version_added":"18","version_removed":"79"}],"firefox":{"version_added":"53"},"firefox_android":{"version_added":"53"},"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":{"prefix":"-webkit-","version_added":"4"},"safari_ios":{"prefix":"-webkit-","version_added":"2"},"samsunginternet_android":{"prefix":"-webkit-","version_added":"1.0"},"webview_android":{"prefix":"-webkit-","version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"mask-type":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mask-type","spec_url":"https://drafts.fxtf.org/css-masking/#the-mask-type","support":{"chrome":{"version_added":"24"},"chrome_android":{"version_added":"25"},"edge":{"version_added":"79"},"firefox":{"version_added":"35"},"firefox_android":{"version_added":"35"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"7"},"safari_ios":{"version_added":"7"},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"mask":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mask","spec_url":"https://drafts.fxtf.org/css-masking/#the-mask","support":{"chrome":[{"version_added":"1","partial_implementation":true,"notes":"While the property is recognized, values applied to it don't have any effect."},{"prefix":"-webkit-","version_added":"1","notes":"The prefixed property can be used with SVG and HTML with a slightly different syntax, which allows setting the non-standard -webkit-mask-attachment property."}],"chrome_android":[{"version_added":"18","partial_implementation":true,"notes":"While the property is recognized, values applied to it don't have any effect."},{"prefix":"-webkit-","version_added":"18","notes":"The prefixed property can be used with SVG and HTML with a slightly different syntax, which allows setting the non-standard -webkit-mask-attachment property."}],"edge":[{"version_added":"79","partial_implementation":true,"notes":"While the property is recognized, values applied to it don't have any effect."},{"prefix":"-webkit-","version_added":"79","notes":"The prefixed property can be used with SVG and HTML with a slightly different syntax, which allows setting the non-standard -webkit-mask-attachment property."},{"version_added":"12","version_removed":"79"}],"firefox":{"version_added":"2","notes":"From Firefox 10, the default color space when handling masks is sRGB. Previously, the default and only supported color space was linear RGB. This changes the appearance of mask effects, but brings Firefox into compliance with the second edition of the SVG 1.1 specification."},"firefox_android":{"version_added":"4","notes":"From Firefox 10, the default color space when handling masks is sRGB. Previously, the default and only supported color space was linear RGB. This changes the appearance of mask effects, but brings Firefox into compliance with the second edition of the SVG 1.1 specification."},"ie":{"version_added":false},"opera":[{"version_added":"15","partial_implementation":true,"notes":"While the property is recognized, values applied to it don't have any effect."},{"prefix":"-webkit-","version_added":"15","notes":"The prefixed property can be used with SVG and HTML with a slightly different syntax, which allows setting the non-standard -webkit-mask-attachment property."}],"opera_android":[{"version_added":"14","partial_implementation":true,"notes":"While the property is recognized, values applied to it don't have any effect."},{"prefix":"-webkit-","version_added":"14","notes":"The prefixed property can be used with SVG and HTML with a slightly different syntax, which allows setting the non-standard -webkit-mask-attachment property."}],"safari":[{"version_added":"3.1","partial_implementation":true,"notes":"While the property is recognized, values applied to it don't have any effect."},{"prefix":"-webkit-","version_added":"3.1","notes":"The prefixed property can be used with SVG and HTML with a slightly different syntax, which allows setting the non-standard -webkit-mask-attachment property."}],"safari_ios":[{"version_added":"2","partial_implementation":true,"notes":"While the property is recognized, values applied to it don't have any effect."},{"prefix":"-webkit-","version_added":"2","notes":"The prefixed property can be used with SVG and HTML with a slightly different syntax, which allows setting the non-standard -webkit-mask-attachment property."}],"samsunginternet_android":[{"version_added":"1.0","partial_implementation":true,"notes":"While the property is recognized, values applied to it don't have any effect."},{"prefix":"-webkit-","version_added":"1.0","notes":"The prefixed property can be used with SVG and HTML with a slightly different syntax, which allows setting the non-standard -webkit-mask-attachment property."}],"webview_android":[{"version_added":"2","partial_implementation":true,"notes":"While the property is recognized, values applied to it don't have any effect."},{"prefix":"-webkit-","version_added":"2","notes":"The prefixed property can be used with SVG and HTML with a slightly different syntax, which allows setting the non-standard -webkit-mask-attachment property."}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"masonry-auto-flow":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/masonry-auto-flow","spec_url":"https://drafts.csswg.org/css-grid-3/#masonry-auto-flow","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"math-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/math-style","spec_url":"https://w3c.github.io/mathml-core/#the-math-style-property","support":{"chrome":{"version_added":"83","flags":[{"type":"preference","name":"Enable experimental Web Platform features"}]},"chrome_android":{"version_added":"83","flags":[{"type":"preference","name":"Enable experimental Web Platform features"}]},"edge":{"version_added":false},"firefox":{"version_added":"83","flags":[{"type":"preference","name":"layout.css.math-style.enabled","value_to_set":"true"}]},"firefox_android":{"version_added":"83","flags":[{"type":"preference","name":"layout.css.math-style.enabled","value_to_set":"true"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"max-block-size":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/max-block-size","spec_url":"https://drafts.csswg.org/css-logical/#propdef-max-block-size","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"fit-content":{"__compat":{"description":"fit-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"alternative_name":"-webkit-fill-available","version_added":"5.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"fit-content_function":{"__compat":{"description":"fit-content()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"firefox_android":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"max-content":{"__compat":{"description":"max-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"min-content":{"__compat":{"description":"min-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"max-height":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/max-height","spec_url":"https://drafts.csswg.org/css-sizing-4/#width-height-keywords","support":{"chrome":{"version_added":"18"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"CSS 2.1 leaves the behavior of max-height with table undefined. Firefox supports applying max-height to table elements."},"firefox_android":{"version_added":"4"},"ie":{"version_added":"7"},"opera":{"version_added":"7","notes":"CSS 2.1 leaves the behavior of max-height with table undefined. Opera supports applying max-height to table elements."},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"fit-content":{"__compat":{"description":"fit-content","support":{"chrome":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"25"}],"chrome_android":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"25"}],"edge":[{"version_added":"79"},{"prefix":"-webkit-","version_added":"79"}],"firefox":{"partial_implementation":true,"prefix":"-moz-","version_added":"3","notes":"Firefox implements the definitions given in CSS3 Basic Box. This defines available and not fit-available. Also, the definition of fit-content is simpler than in CSS3 Sizing."},"firefox_android":{"partial_implementation":true,"prefix":"-moz-","version_added":"4","notes":"Firefox implements the definitions given in CSS3 Basic Box. This defines available and not fit-available. Also, the definition of fit-content is simpler than in CSS3 Sizing."},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"7"},{"alternative_name":"intrinsic","version_added":"2"}],"safari_ios":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"7"},{"alternative_name":"intrinsic","version_added":"1"}],"samsunginternet_android":[{"version_added":"5.0"},{"prefix":"-webkit-","version_added":"1.5"}],"webview_android":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"fit-content_function":{"__compat":{"description":"fit-content()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"firefox_android":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"max-content":{"__compat":{"description":"max-content","support":{"chrome":{"version_added":"46"},"chrome_android":{"version_added":"46"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"3","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"4","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":[{"version_added":"11"},{"version_added":"9","prefix":"-webkit-"}],"safari_ios":[{"version_added":"11"},{"version_added":"9","prefix":"-webkit-"}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"46"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"min-content":{"__compat":{"description":"min-content","support":{"chrome":{"version_added":"46"},"chrome_android":{"version_added":"46"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"3","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"4","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":[{"version_added":"11"},{"version_added":"9","prefix":"-webkit-"}],"safari_ios":[{"version_added":"11"},{"version_added":"9","prefix":"-webkit-"}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"46"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"stretch":{"__compat":{"description":"stretch","support":{"chrome":{"alternative_name":"-webkit-fill-available","version_added":"28"},"chrome_android":{"alternative_name":"-webkit-fill-available","version_added":"28"},"edge":{"alternative_name":"-webkit-fill-available","version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"alternative_name":"-webkit-fill-available","version_added":"15"},"opera_android":{"alternative_name":"-webkit-fill-available","version_added":"15"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"1.5","alternative_name":"-webkit-fill-available"},"webview_android":{"alternative_name":"-webkit-fill-available","version_added":"4.4"}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}}},"max-inline-size":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/max-inline-size","spec_url":"https://drafts.csswg.org/css-logical/#propdef-max-inline-size","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"10.1"}],"safari_ios":[{"version_added":"12.2"},{"prefix":"-webkit-","version_added":"10.3"}],"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"fit-content":{"__compat":{"description":"fit-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"prefix":"-moz-","version_added":"41"},"firefox_android":{"prefix":"-moz-","version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"alternative_name":"-webkit-fill-available","version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"fit-content_function":{"__compat":{"description":"fit-content()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"firefox_android":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"max-content":{"__compat":{"description":"max-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"min-content":{"__compat":{"description":"min-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"max-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/max-width","spec_url":"https://drafts.csswg.org/css-sizing-4/#width-height-keywords","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"CSS 2.1 leaves the behavior of max-width with table undefined. Firefox supports applying max-width to table elements."},"firefox_android":{"version_added":"4","notes":"CSS 2.1 leaves the behavior of max-width with table undefined. Firefox supports applying max-width to table elements."},"ie":{"version_added":"7"},"opera":{"version_added":"4","notes":"CSS 2.1 leaves the behavior of max-width with table undefined. Opera supports applying max-width to table elements."},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"fit-content":{"__compat":{"description":"fit-content","support":{"chrome":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"25"}],"chrome_android":{"version_added":"46"},"edge":[{"version_added":"79"},{"prefix":"-webkit-","version_added":"79"}],"firefox":{"partial_implementation":true,"prefix":"-moz-","version_added":"3","notes":"Firefox implements the definitions given in CSS3 Basic Box. This defines available and not fit-available. Also, the definition of fit-content is simpler than in CSS3 Sizing."},"firefox_android":{"partial_implementation":true,"prefix":"-moz-","version_added":"4","notes":"Firefox implements the definitions given in CSS3 Basic Box. This defines available and not fit-available. Also, the definition of fit-content is simpler than in CSS3 Sizing."},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"7"},{"alternative_name":"intrinsic","version_added":"2"}],"safari_ios":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"7"},{"alternative_name":"intrinsic","version_added":"1"}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"fit-content_function":{"__compat":{"description":"fit-content()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"firefox_android":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"max-content":{"__compat":{"description":"max-content","support":{"chrome":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"22"}],"chrome_android":{"version_added":"46"},"edge":[{"version_added":"79"},{"prefix":"-webkit-","version_added":"79"}],"firefox":[{"version_added":"66"},{"version_added":"3","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"4","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"7"},{"alternative_name":"intrinsic","version_added":"2"}],"safari_ios":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"7"},{"alternative_name":"intrinsic","version_added":"1"}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"min-content":{"__compat":{"description":"min-content","support":{"chrome":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"25"}],"chrome_android":{"version_added":"46"},"edge":[{"version_added":"79"},{"prefix":"-webkit-","version_added":"79"}],"firefox":[{"version_added":"66"},{"version_added":"3","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"4","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"7"},{"alternative_name":"intrinsic","version_added":"2"}],"safari_ios":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"7"},{"alternative_name":"intrinsic","version_added":"1"}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"46"}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"stretch":{"__compat":{"description":"stretch","support":{"chrome":{"alternative_name":"-webkit-fill-available","version_added":"22"},"chrome_android":{"alternative_name":"-webkit-fill-available","version_added":"25"},"edge":{"alternative_name":"-webkit-fill-available","version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"alternative_name":"-webkit-fill-available","version_added":"15"},"opera_android":{"alternative_name":"-webkit-fill-available","version_added":"14"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"1.5","alternative_name":"-webkit-fill-available"},"webview_android":{"alternative_name":"-webkit-fill-available","version_added":"4.4"}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}}},"min-block-size":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/min-block-size","spec_url":"https://drafts.csswg.org/css-logical/#propdef-min-block-size","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"fit-content":{"__compat":{"description":"fit-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"alternative_name":"-webkit-fill-available","version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"fit-content_function":{"__compat":{"description":"fit-content()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"firefox_android":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"max-content":{"__compat":{"description":"max-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"min-content":{"__compat":{"description":"min-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"min-height":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/min-height","spec_url":"https://drafts.csswg.org/css-sizing/#width-height-keywords","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3","notes":"CSS 2.1 leaves the behavior of min-height with table undefined. Firefox supports applying min-height to table elements."},"firefox_android":{"version_added":"4","notes":"CSS 2.1 leaves the behavior of min-height with table undefined. Firefox supports applying min-height to table elements."},"ie":{"version_added":"7","notes":"In Internet Explorer 10 and 11, a min-height declaration on a column-direction flex container doesn't apply to the container's flex items. See Flexbug #3 for more info."},"opera":{"version_added":"4","notes":"CSS 2.1 leaves the behavior of min-height with table undefined. Opera supports applying min-height to table elements."},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"auto":{"__compat":{"description":"auto","support":{"chrome":{"version_added":"21"},"chrome_android":{"version_added":"25"},"edge":{"version_added":"79"},"firefox":{"version_added":"16","version_removed":"22","notes":"Firefox 18 and later used auto as the initial value for min-height."},"firefox_android":{"version_added":"16","version_removed":"22","notes":"Firefox 18 and later used auto as the initial value for min-height."},"ie":{"version_added":false},"opera":{"version_added":"12.1"},"opera_android":{"version_added":"14"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"fit-content":{"__compat":{"description":"fit-content","support":{"chrome":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"25"}],"chrome_android":{"version_added":"46"},"edge":[{"version_added":"79"},{"prefix":"-webkit-","version_added":"79"}],"firefox":{"prefix":"-moz-","version_added":"3","notes":"Firefox implements the definitions given in CSS3 Basic Box. This defines available and not fit-available. Also, the definition of fit-content is simpler than in CSS3 Sizing."},"firefox_android":{"prefix":"-moz-","version_added":"4","notes":"Firefox implements the definitions given in CSS3 Basic Box. This defines available and not fit-available. Also, the definition of fit-content is simpler than in CSS3 Sizing."},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"6.1"},{"alternative_name":"intrinsic","version_added":"2"}],"safari_ios":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"6.1"},{"alternative_name":"intrinsic","version_added":"1"}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"fit-content_function":{"__compat":{"description":"fit-content()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"firefox_android":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"max-content":{"__compat":{"description":"max-content","support":{"chrome":{"version_added":"46"},"chrome_android":{"version_added":"46"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"3","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"4","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":[{"version_added":"11"},{"version_added":"9","prefix":"-webkit-"}],"safari_ios":[{"version_added":"11"},{"version_added":"9","prefix":"-webkit-"}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"46"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"min-content":{"__compat":{"description":"min-content","support":{"chrome":{"version_added":"46"},"chrome_android":{"version_added":"46"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"3","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"4","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":[{"version_added":"11"},{"version_added":"9","prefix":"-webkit-"}],"safari_ios":[{"version_added":"11"},{"version_added":"9","prefix":"-webkit-"}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"46"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"stretch":{"__compat":{"description":"stretch","support":{"chrome":{"alternative_name":"-webkit-fill-available","version_added":"28"},"chrome_android":{"alternative_name":"-webkit-fill-available","version_added":"28"},"edge":{"alternative_name":"-webkit-fill-available","version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"alternative_name":"-webkit-fill-available","version_added":"15"},"opera_android":{"alternative_name":"-webkit-fill-available","version_added":"15"},"safari":{"alternative_name":"-webkit-fill-available","version_added":"9"},"safari_ios":{"alternative_name":"-webkit-fill-available","version_added":"9"},"samsunginternet_android":{"alternative_name":"-webkit-fill-available","version_added":"1.5"},"webview_android":{"alternative_name":"-webkit-fill-available","version_added":"4.4"}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}}},"min-inline-size":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/min-inline-size","spec_url":"https://drafts.csswg.org/css-logical/#propdef-min-inline-size","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"fit-content":{"__compat":{"description":"fit-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"prefix":"-moz-","version_added":"41"},"firefox_android":{"prefix":"-moz-","version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"alternative_name":"-webkit-fill-available","version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"fit-content_function":{"__compat":{"description":"fit-content()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"firefox_android":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"max-content":{"__compat":{"description":"max-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"min-content":{"__compat":{"description":"min-content","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"41","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"min-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/min-width","spec_url":"https://drafts.csswg.org/css-sizing/#min-size-properties","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"CSS 2.1 leaves the behavior of min-width with table undefined. Firefox supports applying min-width to table elements."},"firefox_android":{"version_added":"4","notes":"CSS 2.1 leaves the behavior of min-width with table undefined. Firefox supports applying min-width to table elements."},"ie":{"version_added":"7"},"opera":{"version_added":"4","notes":"CSS 2.1 leaves the behavior of min-width with table undefined. Opera supports applying min-width to table elements."},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"auto":{"__compat":{"description":"auto","support":{"chrome":{"version_added":"21","notes":"Chrome uses auto as the initial value for min-width."},"chrome_android":{"version_added":"25","notes":"Chrome uses auto as the initial value for min-width."},"edge":{"version_added":"12","notes":"Edge uses auto as the initial value for min-width."},"firefox":[{"version_added":"34"},{"version_added":"16","version_removed":"22","notes":"Firefox 18 and later (until the value was removed), used auto as the initial value for min-width."}],"firefox_android":[{"version_added":"34"},{"version_added":"16","version_removed":"22","notes":"Firefox 18 and later (until the value was removed), used auto as the initial value for min-width."}],"ie":{"version_added":false},"opera":{"version_added":"12.1","notes":"Opera uses auto as the initial value for min-width."},"opera_android":{"version_added":"14","notes":"Opera uses auto as the initial value for min-width."},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"1.5","notes":"Samsung Internet uses auto as the initial value for min-width."},"webview_android":{"version_added":"37","notes":"Chrome uses auto as the initial value for min-width."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"fit-content":{"__compat":{"description":"fit-content","support":{"chrome":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"25"}],"chrome_android":{"version_added":"46"},"edge":[{"version_added":"79"},{"prefix":"-webkit-","version_added":"79"}],"firefox":{"prefix":"-moz-","version_added":"3","notes":"Firefox implements the definitions given in CSS3 Basic Box. This defines available and not fit-available. Also, the definition of fit-content is simpler than in CSS3 Sizing."},"firefox_android":{"prefix":"-moz-","version_added":"4","notes":"Firefox implements the definitions given in CSS3 Basic Box. This defines available and not fit-available. Also, the definition of fit-content is simpler than in CSS3 Sizing."},"ie":{"version_added":false},"opera":[{"version_added":"33"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":{"version_added":"33"},"safari":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":{"alternative_name":"-webkit-fill-available","version_added":"5.0"},"webview_android":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"fit-content_function":{"__compat":{"description":"fit-content()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"firefox_android":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"max-content":{"__compat":{"description":"max-content","support":{"chrome":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"25"}],"chrome_android":{"version_added":"46"},"edge":[{"version_added":"79"},{"prefix":"-webkit-","version_added":"79"}],"firefox":[{"version_added":"66"},{"version_added":"3","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"4","prefix":"-moz-"}],"ie":{"version_added":false},"opera":[{"version_added":"33"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":{"version_added":"33"},"safari":[{"version_added":"11"},{"alternative_name":"intrinsic","version_added":"2"}],"safari_ios":[{"version_added":"11"},{"alternative_name":"intrinsic","version_added":"1"}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"min-content":{"__compat":{"description":"min-content","support":{"chrome":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"25"},{"alternative_name":"min-intrinsic","version_added":"25","version_removed":"48"}],"chrome_android":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"25"},{"alternative_name":"min-intrinsic","version_added":"25","version_removed":"48"}],"edge":[{"version_added":"79"},{"prefix":"-webkit-","version_added":"79"}],"firefox":[{"version_added":"66"},{"version_added":"3","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"4","prefix":"-moz-"}],"ie":{"version_added":false},"opera":[{"version_added":"33"},{"prefix":"-webkit-","version_added":"≤15"},{"alternative_name":"min-intrinsic","version_added":"≤15","version_removed":"35"}],"opera_android":[{"version_added":"33"},{"prefix":"-webkit-","version_added":"≤14"},{"alternative_name":"min-intrinsic","version_added":"≤14","version_removed":"35"}],"safari":[{"version_added":"11"},{"alternative_name":"min-intrinsic","version_added":"2"}],"safari_ios":[{"version_added":"11"},{"alternative_name":"min-intrinsic","version_added":"1"}],"samsunginternet_android":[{"version_added":"5.0"},{"prefix":"-webkit-","version_added":"1.5"},{"alternative_name":"min-intrinsic","version_added":"1.5","version_removed":"5.0"}],"webview_android":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"≤37"},{"alternative_name":"min-intrinsic","version_added":"≤37","version_removed":"48"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"stretch":{"__compat":{"description":"stretch","support":{"chrome":{"alternative_name":"-webkit-fill-available","version_added":"22"},"chrome_android":{"alternative_name":"-webkit-fill-available","version_added":"25"},"edge":{"alternative_name":"-webkit-fill-available","version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"alternative_name":"-webkit-fill-available","version_added":"15"},"opera_android":{"alternative_name":"-webkit-fill-available","version_added":"14"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"1.5","alternative_name":"-webkit-fill-available"},"webview_android":{"alternative_name":"-webkit-fill-available","version_added":"4.4"}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}}},"mix-blend-mode":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/mix-blend-mode","spec_url":"https://drafts.fxtf.org/compositing/#mix-blend-mode","support":{"chrome":{"version_added":"41"},"chrome_android":{"version_added":"41"},"edge":{"version_added":"79"},"firefox":{"version_added":"32"},"firefox_android":{"version_added":"32"},"ie":{"version_added":false},"opera":{"version_added":"28"},"opera_android":{"version_added":"28"},"safari":{"version_added":"8"},"safari_ios":{"version_added":"8"},"samsunginternet_android":{"version_added":"4.0"},"webview_android":{"version_added":"41"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"svg":{"__compat":{"description":"On SVG elements","support":{"chrome":{"version_added":"41"},"chrome_android":{"version_added":false},"edge":{"version_added":"79"},"firefox":{"version_added":"32"},"firefox_android":{"version_added":"32"},"ie":{"version_added":false},"opera":{"version_added":"28"},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"object-fit":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/object-fit","spec_url":"https://drafts.csswg.org/css-images/#the-object-fit","support":{"chrome":{"version_added":"32"},"chrome_android":{"version_added":"32"},"edge":[{"version_added":"79"},{"version_added":"16","version_removed":"79","partial_implementation":true,"notes":"Only supported for <img> elements."}],"firefox":{"version_added":"36"},"firefox_android":{"version_added":"36"},"ie":{"version_added":false},"opera":[{"version_added":"19"},{"prefix":"-o-","version_added":"11.6","version_removed":"15"}],"opera_android":[{"version_added":"19"},{"prefix":"-o-","version_added":"12","version_removed":"14"}],"safari":{"version_added":"10"},"safari_ios":{"version_added":"10"},"samsunginternet_android":{"version_added":"2.0"},"webview_android":{"version_added":"4.4.3"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"object-position":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/object-position","spec_url":"https://drafts.csswg.org/css-images/#the-object-position","support":{"chrome":{"version_added":"32"},"chrome_android":{"version_added":"32"},"edge":[{"version_added":"79"},{"version_added":"16","version_removed":"79","partial_implementation":true,"notes":"Only supported for <img> elements."}],"firefox":{"version_added":"36"},"firefox_android":{"version_added":"36"},"ie":{"version_added":false},"opera":[{"version_added":"19"},{"prefix":"-o-","version_added":"11.6","version_removed":"15"}],"opera_android":[{"version_added":"19"},{"prefix":"-o-","version_added":"12","version_removed":"14"}],"safari":{"version_added":"10"},"safari_ios":{"version_added":"10"},"samsunginternet_android":{"version_added":"2.0"},"webview_android":{"version_added":"4.4.3"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"three_value_syntax":{"__compat":{"description":"Support for three-value syntax of position","support":{"chrome":{"version_added":"32","version_removed":"68"},"chrome_android":{"version_added":"32","version_removed":"68"},"edge":{"version_added":"16","version_removed":"79"},"firefox":{"version_added":"36","version_removed":"70"},"firefox_android":{"version_added":"36","version_removed":"79"},"ie":{"version_added":false},"opera":[{"version_added":"19","version_removed":"55"},{"prefix":"-o-","version_added":"11.6","version_removed":"15"}],"opera_android":[{"version_added":"19","version_removed":"48"},{"prefix":"-o-","version_added":"12","version_removed":"14"}],"safari":{"version_added":"10","version_removed":"13.1"},"safari_ios":{"version_added":"10","version_removed":"13.4"},"samsunginternet_android":{"version_added":"2.0","version_removed":"10.0"},"webview_android":{"version_added":"4.4.3","version_removed":"68"}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}}},"offset-anchor":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/offset-anchor","spec_url":"https://drafts.fxtf.org/motion/#offset-anchor-property","support":{"chrome":{"version_added":"79"},"chrome_android":{"version_added":"79"},"edge":{"version_added":"79"},"firefox":[{"version_added":"72"},{"version_added":"70","version_removed":"72","flags":[{"type":"preference","name":"layout.css.motion-path.enabled","value_to_set":"true"}]}],"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"12.0"},"webview_android":{"version_added":"79"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"offset-distance":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/offset-distance","spec_url":"https://drafts.fxtf.org/motion/#offset-distance-property","support":{"chrome":[{"version_added":"55"},{"alternative_name":"motion-distance","version_added":"46"}],"chrome_android":[{"version_added":"55"},{"alternative_name":"motion-distance","version_added":"46"}],"edge":[{"version_added":"79"},{"alternative_name":"motion-distance","version_added":"79"}],"firefox":[{"version_added":"72"},{"version_added":"69","version_removed":"72","flags":[{"type":"preference","name":"layout.css.motion-path.enabled","value_to_set":"true"}]}],"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":[{"version_added":"42"},{"alternative_name":"motion-distance","version_added":"33"}],"opera_android":[{"version_added":"42"},{"alternative_name":"motion-distance","version_added":"33"}],"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":[{"version_added":"6.0"},{"alternative_name":"motion-distance","version_added":"5.0"}],"webview_android":[{"version_added":"55"},{"alternative_name":"motion-distance","version_added":"46"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"offset-path":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/offset-path","spec_url":"https://drafts.fxtf.org/motion/#offset-path-property","support":{"chrome":[{"version_added":"55"},{"alternative_name":"motion-path","version_added":"46"}],"chrome_android":[{"version_added":"55"},{"alternative_name":"motion-path","version_added":"46"}],"edge":[{"version_added":"79"},{"alternative_name":"motion-path","version_added":"79"}],"firefox":[{"version_added":"72"},{"version_added":"63","version_removed":"72","flags":[{"type":"preference","name":"layout.css.motion-path.enabled","value_to_set":"true"}]}],"firefox_android":{"version_added":"63","flags":[{"type":"preference","name":"layout.css.motion-path.enabled","value_to_set":"true"}]},"ie":{"version_added":false},"opera":[{"version_added":"45"},{"alternative_name":"motion-path","version_added":"32"}],"opera_android":[{"version_added":"43"},{"alternative_name":"motion-path","version_added":"32"}],"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":[{"version_added":"6.0","notes":"path() is the only value type supported."},{"alternative_name":"motion-path","version_added":"5.0"}],"webview_android":[{"version_added":"55"},{"alternative_name":"motion-path","version_added":"46"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"path-support":{"__compat":{"description":"Supports the path() function as a value","support":{"chrome":{"version_added":"64"},"chrome_android":{"version_added":"64"},"edge":{"version_added":"79"},"firefox":{"version_added":"63"},"firefox_android":{"version_added":"63"},"ie":{"version_added":false},"opera":{"version_added":"51"},"opera_android":{"version_added":"47"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"9.0"},"webview_android":{"version_added":"64"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"ray-support":{"__compat":{"description":"Supports the ray() function as a value","support":{"chrome":{"version_added":"64"},"chrome_android":{"version_added":"64"},"edge":{"version_added":"79"},"firefox":{"version_added":"71","flags":[{"type":"preference","name":"layout.css.motion-path.enabled","value_to_set":"true"}]},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"51"},"opera_android":{"version_added":"47"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"9.0"},"webview_android":{"version_added":"64"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"offset-position":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/offset-position","spec_url":"https://drafts.fxtf.org/motion/#offset-position-property","support":{"chrome":{"version_added":false,"notes":"See bug 638055."},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false,"notes":"See bug 1559232."},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"offset-rotate":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/offset-rotate","spec_url":"https://drafts.fxtf.org/motion/#offset-rotate-property","support":{"chrome":[{"version_added":"56"},{"alternative_name":"offset-rotation","version_added":"55"},{"alternative_name":"motion-rotation","version_added":"46"}],"chrome_android":[{"version_added":"56"},{"alternative_name":"offset-rotation","version_added":"55"},{"alternative_name":"motion-rotation","version_added":"46"}],"edge":[{"version_added":"79"},{"alternative_name":"offset-rotation","version_added":"79"},{"alternative_name":"motion-rotation","version_added":"79"}],"firefox":[{"version_added":"72"},{"version_added":"69","version_removed":"72","flags":[{"type":"preference","name":"layout.css.motion-path.enabled","value_to_set":"true"}]}],"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":[{"version_added":"43"},{"alternative_name":"offset-rotation","version_added":"42"},{"alternative_name":"motion-rotation","version_added":"33"}],"opera_android":[{"version_added":"43"},{"alternative_name":"offset-rotation","version_added":"42"},{"alternative_name":"motion-rotation","version_added":"33"}],"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":[{"version_added":"6.0"},{"alternative_name":"offset-rotation","version_added":"6.0"},{"alternative_name":"motion-rotation","version_added":"5.0"}],"webview_android":[{"version_added":"56"},{"alternative_name":"offset-rotation","version_added":"55"},{"alternative_name":"motion-rotation","version_added":"46"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"offset":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/offset","spec_url":"https://drafts.fxtf.org/motion/#offset-shorthand","support":{"chrome":[{"version_added":"55"},{"alternative_name":"motion","version_added":"46"}],"chrome_android":[{"version_added":"55"},{"alternative_name":"motion","version_added":"46"}],"edge":[{"version_added":"79"},{"alternative_name":"motion","version_added":"79"}],"firefox":[{"version_added":"72"},{"version_added":"71","version_removed":"72","flags":[{"type":"preference","name":"layout.css.motion-path.enabled","value_to_set":"true"}]}],"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":[{"version_added":"42"},{"alternative_name":"motion","version_added":"33"}],"opera_android":[{"version_added":"42"},{"alternative_name":"motion","version_added":"33"}],"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":[{"version_added":"6.0"},{"alternative_name":"motion","version_added":"5.0"}],"webview_android":[{"version_added":"55"},{"alternative_name":"motion","version_added":"46"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"opacity":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/opacity","spec_url":"https://drafts.csswg.org/css-color/#transparency","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":[{"version_added":"1"},{"prefix":"-moz-","version_added":"1","version_removed":"3.5"}],"firefox_android":{"version_added":"4"},"ie":{"version_added":"9"},"opera":{"version_added":"9"},"opera_android":{"version_added":"10.1"},"safari":[{"version_added":"2"},{"version_added":"1.1","version_removed":"2","prefix":"-khtml-"}],"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"percentages":{"__compat":{"description":"Support for percentage opacity values","support":{"chrome":{"version_added":"78"},"chrome_android":{"version_added":"78"},"edge":{"version_added":"79"},"firefox":{"version_added":"70"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"12.0"},"webview_android":{"version_added":"78"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"order":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/order","spec_url":"https://drafts.csswg.org/css-display/#order-property","support":{"chrome":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"21"}],"chrome_android":[{"version_added":"29"},{"prefix":"-webkit-","version_added":"25"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"20","notes":"Since Firefox 28, multi-line flexbox is supported."},{"prefix":"-webkit-","version_added":"49"}],"firefox_android":[{"version_added":"20","notes":"Since Firefox 28, multi-line flexbox is supported."},{"prefix":"-webkit-","version_added":"49"}],"ie":[{"version_added":"11"},{"prefix":"-ms-","version_added":"10"}],"opera":{"version_added":"12.1"},"opera_android":{"version_added":"12.1"},"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":[{"version_added":"2.0"},{"prefix":"-webkit-","version_added":"1.5"}],"webview_android":[{"version_added":"4.4"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"orphans":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/orphans","spec_url":"https://drafts.csswg.org/css-break/#widows-orphans","support":{"chrome":{"version_added":"25"},"chrome_android":{"version_added":"25"},"edge":{"version_added":"12"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":"8"},"opera":{"version_added":"9.2"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"outline-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/outline-color","spec_url":"https://drafts.csswg.org/css-ui/#outline-color","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":[{"version_added":"1.5"},{"prefix":"-moz-","version_added":"1","version_removed":"3.6"}],"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"invert":{"__compat":{"description":"invert","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"79"},"firefox":{"version_removed":"3","version_added":"1"},"firefox_android":{"version_added":false},"ie":{"version_added":"8"},"opera":{"version_added":"7","version_removed":"15"},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"outline-offset":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/outline-offset","spec_url":"https://drafts.csswg.org/css-ui/#outline-offset","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"15"},"firefox":{"version_added":"1.5","notes":"Before Firefox 88, an outline does not follow the shape of border-radius."},"firefox_android":{"version_added":"4","notes":"Before Firefox 88, an outline does not follow the shape of border-radius."},"ie":{"version_added":false},"opera":{"version_added":"9.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"outline-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/outline-style","spec_url":"https://drafts.csswg.org/css-ui/#outline-style","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":[{"version_added":"1.5","notes":"Before Firefox 88, an outline does not follow the shape of border-radius."},{"prefix":"-moz-","version_added":"1","version_removed":"3.6"}],"firefox_android":{"version_added":"4","notes":"Before Firefox 88, an outline does not follow the shape of border-radius."},"ie":{"version_added":"8"},"opera":{"version_added":"7"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"auto":{"__compat":{"description":"auto","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"1.5"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"outline-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/outline-width","spec_url":"https://drafts.csswg.org/css-ui/#outline-width","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":[{"version_added":"1.5","notes":"Before Firefox 88, an outline does not follow the shape of border-radius."},{"prefix":"-moz-","version_added":"1","version_removed":"3.6"}],"firefox_android":{"version_added":"4","notes":"Before Firefox 88, an outline does not follow the shape of border-radius."},"ie":{"version_added":"8"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"outline":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/outline","spec_url":"https://drafts.csswg.org/css-ui/#outline","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":[{"version_added":"1.5","notes":"Before Firefox 88, outline does not follow the shape of border-radius."},{"prefix":"-moz-","version_added":"1","version_removed":"3.6"}],"firefox_android":{"version_added":"4","notes":"Before Firefox 88, outline does not follow the shape of border-radius."},"ie":{"version_added":"8"},"opera":{"version_added":"7"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"overflow-anchor":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/overflow-anchor","spec_url":"https://drafts.csswg.org/css-scroll-anchoring/#exclusion-api","support":{"chrome":{"version_added":"56"},"chrome_android":{"version_added":"56"},"edge":{"version_added":"79"},"firefox":{"version_added":"66"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"43"},"opera_android":{"version_added":"43"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"56"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"overflow-block":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/overflow-block","spec_url":"https://drafts.csswg.org/css-overflow/#logical","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"69"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"overflow-clip-box-block":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Mozilla/Gecko/Chrome/CSS/overflow-clip-box-block","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"59"},"firefox_android":{"version_added":"59"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"overflow-clip-box-inline":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Mozilla/Gecko/Chrome/CSS/overflow-clip-box-inline","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"59"},"firefox_android":{"version_added":"59"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"overflow-clip-box":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Mozilla/Gecko/Chrome/CSS/overflow-clip-box","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"29"},"firefox_android":{"version_added":"29"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":false}},"shorthand":{"__compat":{"description":"Two values; property as shorthand","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"59"},"firefox_android":{"version_added":"59"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}}},"overflow-clip-margin":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/overflow-clip-margin","spec_url":"https://drafts.csswg.org/css-overflow/#overflow-clip-margin","support":{"chrome":{"version_added":"90"},"chrome_android":{"version_added":"90"},"edge":{"version_added":"90"},"firefox":{"version_added":false,"notes":"See bug 1661582."},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"76"},"opera_android":{"version_added":"64"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"15.0"},"webview_android":{"version_added":"90"}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"overflow-inline":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/overflow-inline","spec_url":"https://drafts.csswg.org/css-overflow/#logical","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"69"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"overflow-wrap":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/overflow-wrap","spec_url":"https://drafts.csswg.org/css-text/#overflow-wrap-property","support":{"chrome":[{"version_added":"23"},{"alternative_name":"word-wrap","version_added":"1"}],"chrome_android":[{"version_added":"25"},{"alternative_name":"word-wrap","version_added":"18"}],"edge":[{"version_added":"18"},{"alternative_name":"word-wrap","version_added":"12"}],"firefox":[{"version_added":"49"},{"alternative_name":"word-wrap","version_added":"3.5"}],"firefox_android":[{"version_added":"49"},{"alternative_name":"word-wrap","version_added":"4"}],"ie":{"alternative_name":"word-wrap","version_added":"5.5"},"opera":[{"version_added":"12.1"},{"alternative_name":"word-wrap","version_added":"10.5"}],"opera_android":[{"version_added":"12.1"},{"alternative_name":"word-wrap","version_added":"11"}],"safari":[{"version_added":"7"},{"alternative_name":"word-wrap","version_added":"1"}],"safari_ios":[{"version_added":"7"},{"alternative_name":"word-wrap","version_added":"1"}],"samsunginternet_android":[{"version_added":"1.5"},{"alternative_name":"word-wrap","version_added":"1.0"}],"webview_android":[{"version_added":"4.4"},{"alternative_name":"word-wrap","version_added":"1"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"anywhere":{"__compat":{"description":"anywhere","support":{"chrome":{"version_added":"80"},"chrome_android":{"version_added":"80"},"edge":{"version_added":"80"},"firefox":{"version_added":"65"},"firefox_android":{"version_added":"65"},"ie":{"version_added":false},"opera":{"version_added":"67"},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"13.0"},"webview_android":{"version_added":"80"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"break-word":{"__compat":{"description":"break-word","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3.5"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"5.5"},"opera":{"version_added":"10.5"},"opera_android":{"version_added":"11"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"overflow-x":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/overflow-x","spec_url":"https://drafts.csswg.org/css-overflow/#overflow-properties","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3.5"},"firefox_android":{"version_added":"4"},"ie":[{"version_added":"5"},{"prefix":"-ms-","version_added":"8"}],"opera":{"version_added":"9.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"clip":{"__compat":{"description":"clip value","support":{"chrome":{"version_added":"90"},"chrome_android":{"version_added":"90"},"edge":{"version_added":"90"},"firefox":[{"version_added":"81"},{"alternative_name":"-moz-hidden-unscrollable","version_added":"3.5","version_removed":"81"}],"firefox_android":[{"version_added":"81"},{"alternative_name":"-moz-hidden-unscrollable","version_added":"4","version_removed":"81"}],"ie":{"version_added":false},"opera":{"version_added":"76"},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"15.0"},"webview_android":{"version_added":"90"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"overflow-y":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/overflow-y","spec_url":"https://drafts.csswg.org/css-overflow/#overflow-properties","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3.5"},"firefox_android":{"version_added":"4"},"ie":[{"version_added":"5"},{"prefix":"-ms-","version_added":"8"}],"opera":{"version_added":"9.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"clip":{"__compat":{"description":"clip value","support":{"chrome":{"version_added":"90"},"chrome_android":{"version_added":"90"},"edge":{"version_added":"90"},"firefox":[{"version_added":"81"},{"alternative_name":"-moz-hidden-unscrollable","version_added":"3.5","version_removed":"81"}],"firefox_android":[{"version_added":"81"},{"alternative_name":"-moz-hidden-unscrollable","version_added":"4","version_removed":"81"}],"ie":{"version_added":false},"opera":{"version_added":"76"},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"15.0"},"webview_android":{"version_added":"90"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"overflow":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/overflow","spec_url":"https://drafts.csswg.org/css-overflow/#propdef-overflow","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"After Firefox 3.6, the overflow property is correctly applied to table group elements (<thead>, <tbody>, <tfoot>)."},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4","notes":"From version 4 to 6, Internet Explorer enlarges an element with overflow: visible (default value) to fit the content inside it. height and width behave like min-height and min-width, respectively."},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"clip":{"__compat":{"description":"clip value","support":{"chrome":{"version_added":"90"},"chrome_android":{"version_added":"90"},"edge":{"version_added":"90"},"firefox":[{"version_added":"81"},{"alternative_name":"-moz-hidden-unscrollable","version_added":"1.5","version_removed":"81"}],"firefox_android":[{"version_added":"81"},{"alternative_name":"-moz-hidden-unscrollable","version_added":"4","version_removed":"81"}],"ie":{"version_added":false},"opera":{"version_added":"76"},"opera_android":{"version_added":false},"safari":{"version_added":"preview"},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"15.0"},"webview_android":{"version_added":"90"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"multiple_keywords":{"__compat":{"description":"Multiple keyword syntax for overflow-x and overflow-y","support":{"chrome":{"version_added":"68"},"chrome_android":{"version_added":"68"},"edge":{"version_added":"79"},"firefox":{"version_added":"61"},"firefox_android":{"version_added":"61"},"ie":{"version_added":false},"opera":{"version_added":"55"},"opera_android":{"version_added":"48"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"68"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"overscroll-behavior-block":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/overscroll-behavior-block","spec_url":"https://drafts.csswg.org/css-overscroll/#overscroll-behavior-longhands-logical","support":{"chrome":{"version_added":"77"},"chrome_android":{"version_added":"77"},"edge":{"version_added":"79"},"firefox":{"version_added":"73"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"64"},"opera_android":{"version_added":"55"},"safari":{"version_added":false,"notes":"See bug 176454."},"safari_ios":{"version_added":false,"notes":"See bug 176454."},"samsunginternet_android":{"version_added":"12.0"},"webview_android":{"version_added":"77"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"overscroll-behavior-inline":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/overscroll-behavior-inline","spec_url":"https://drafts.csswg.org/css-overscroll/#overscroll-behavior-longhands-logical","support":{"chrome":{"version_added":"77"},"chrome_android":{"version_added":"77"},"edge":{"version_added":"79"},"firefox":{"version_added":"73"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"64"},"opera_android":{"version_added":"55"},"safari":{"version_added":false,"notes":"See bug 176454."},"safari_ios":{"version_added":false,"notes":"See bug 176454."},"samsunginternet_android":{"version_added":"12.0"},"webview_android":{"version_added":"77"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"overscroll-behavior-x":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/overscroll-behavior-x","spec_url":"https://drafts.csswg.org/css-overscroll/#overscroll-behavior-longhands-physical","support":{"chrome":{"version_added":"63"},"chrome_android":{"version_added":"63"},"edge":{"version_added":"18","partial_implementation":true,"notes":"Currently the none value incorrectly behaves as contain (allowing for the elastic bounce effect)."},"firefox":{"version_added":"59"},"firefox_android":{"version_added":"59"},"ie":{"version_added":false},"opera":{"version_added":"50"},"opera_android":{"version_added":"46"},"safari":{"version_added":false,"notes":"See bug 176454."},"safari_ios":{"version_added":false,"notes":"See bug 176454."},"samsunginternet_android":{"version_added":"8.0"},"webview_android":{"version_added":"63"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"overscroll-behavior-y":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/overscroll-behavior-y","spec_url":"https://drafts.csswg.org/css-overscroll/#overscroll-behavior-longhands-physical","support":{"chrome":{"version_added":"63"},"chrome_android":{"version_added":"63"},"edge":{"version_added":"18","partial_implementation":true,"notes":"Currently the none value incorrectly behaves as contain (allowing for the elastic bounce effect)."},"firefox":{"version_added":"59"},"firefox_android":{"version_added":"59"},"ie":{"version_added":false},"opera":{"version_added":"50"},"opera_android":{"version_added":"46"},"safari":{"version_added":false,"notes":"See bug 176454."},"safari_ios":{"version_added":false,"notes":"See bug 176454."},"samsunginternet_android":{"version_added":"8.0"},"webview_android":{"version_added":"63"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"overscroll-behavior":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/overscroll-behavior","spec_url":"https://drafts.csswg.org/css-overscroll/#overscroll-behavior-properties","support":{"chrome":{"version_added":"63"},"chrome_android":{"version_added":"63"},"edge":{"version_added":"18","partial_implementation":true,"notes":"Currently the none value incorrectly behaves as contain (allowing for the elastic bounce effect)."},"firefox":{"version_added":"59"},"firefox_android":{"version_added":"59"},"ie":{"version_added":false},"opera":{"version_added":"50"},"opera_android":{"version_added":"46"},"safari":{"version_added":false,"notes":"See bug 176454."},"safari_ios":{"version_added":false,"notes":"See bug 176454."},"samsunginternet_android":{"version_added":"8.0"},"webview_android":{"version_added":"63"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"padding-block-end":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/padding-block-end","spec_url":"https://drafts.csswg.org/css-logical/#padding-properties","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"padding-block-start":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/padding-block-start","spec_url":"https://drafts.csswg.org/css-logical/#padding-properties","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"padding-block":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/padding-block","spec_url":"https://drafts.csswg.org/css-logical/#propdef-padding-block","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":[{"version_added":"87"},{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"48","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"padding-bottom":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/padding-bottom","spec_url":"https://drafts.csswg.org/css-box/#padding-physical","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"padding-inline-end":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/padding-inline-end","spec_url":"https://drafts.csswg.org/css-logical/#padding-properties","support":{"chrome":[{"version_added":"69"},{"version_added":"2","alternative_name":"-webkit-padding-end"}],"chrome_android":[{"version_added":"69"},{"version_added":"18","alternative_name":"-webkit-padding-end"}],"edge":[{"version_added":"79"},{"version_added":"79","alternative_name":"-webkit-padding-end"}],"firefox":[{"version_added":"41"},{"version_added":"3","alternative_name":"-moz-padding-end"}],"firefox_android":[{"version_added":"41"},{"version_added":"4","alternative_name":"-moz-padding-end"}],"ie":{"version_added":false},"opera":[{"version_added":"56"},{"version_added":"15","alternative_name":"-webkit-padding-end"}],"opera_android":[{"version_added":"48"},{"version_added":"14","alternative_name":"-webkit-padding-end"}],"safari":[{"version_added":"12.1"},{"version_added":"3","alternative_name":"-webkit-padding-end"}],"safari_ios":[{"version_added":"12.2"},{"version_added":"3","alternative_name":"-webkit-padding-end"}],"samsunginternet_android":[{"version_added":"10.0"},{"alternative_name":"-webkit-padding-end","version_added":"1.0"}],"webview_android":[{"version_added":"69"},{"version_added":"2","alternative_name":"-webkit-padding-end"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"padding-inline-start":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/padding-inline-start","spec_url":"https://drafts.csswg.org/css-logical/#padding-properties","support":{"chrome":[{"version_added":"69"},{"version_added":"2","alternative_name":"-webkit-padding-start"}],"chrome_android":[{"version_added":"69"},{"version_added":"18","alternative_name":"-webkit-padding-start"}],"edge":[{"version_added":"79"},{"version_added":"79","alternative_name":"-webkit-padding-start"}],"firefox":[{"version_added":"41"},{"version_added":"3","alternative_name":"-moz-padding-start"}],"firefox_android":[{"version_added":"41"},{"version_added":"4","alternative_name":"-moz-padding-start"}],"ie":{"version_added":false},"opera":[{"version_added":"56"},{"version_added":"15","alternative_name":"-webkit-padding-start"}],"opera_android":[{"version_added":"48"},{"version_added":"14","alternative_name":"-webkit-padding-start"}],"safari":[{"version_added":"12.1"},{"version_added":"3","alternative_name":"-webkit-padding-start"}],"safari_ios":[{"version_added":"12.2"},{"version_added":"3","alternative_name":"-webkit-padding-start"}],"samsunginternet_android":[{"version_added":"10.0"},{"alternative_name":"-webkit-padding-start","version_added":"1.0"}],"webview_android":[{"version_added":"69"},{"version_added":"2","alternative_name":"-webkit-padding-start"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"padding-inline":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/padding-inline","spec_url":"https://drafts.csswg.org/css-logical/#propdef-padding-inline","support":{"chrome":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"chrome_android":[{"version_added":"87"},{"version_added":"69","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"edge":[{"version_added":"87"},{"version_added":"79","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"firefox":{"version_added":"66"},"firefox_android":{"version_added":"66"},"ie":{"version_added":false},"opera":[{"version_added":"73"},{"version_added":"56","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]}],"opera_android":{"version_added":"48","flags":[{"type":"preference","name":"enable-experimental-web-platform-features","value_to_set":"enabled"}]},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"padding-left":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/padding-left","spec_url":"https://drafts.csswg.org/css-box/#padding-physical","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"padding-right":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/padding-right","spec_url":"https://drafts.csswg.org/css-box/#padding-physical","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"padding-top":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/padding-top","spec_url":"https://drafts.csswg.org/css-box/#padding-physical","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"padding":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/padding","spec_url":"https://drafts.csswg.org/css-box/#padding-shorthand","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"page-break-after":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/page-break-after","spec_url":["https://drafts.csswg.org/css-logical/#page","https://drafts.csswg.org/css-page/#page-break-after"],"support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"The values avoid, left, and right are unsupported."},"firefox_android":{"version_added":"4","notes":"The values avoid, left, and right are unsupported."},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"page-break-before":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/page-break-before","spec_url":["https://drafts.csswg.org/css-logical/#page","https://drafts.csswg.org/css-page/#page-break-before"],"support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"The values avoid, left, and right are unsupported."},"firefox_android":{"version_added":"4","notes":"The values avoid, left, and right are unsupported."},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.2"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"page-break-inside":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/page-break-inside","spec_url":"https://drafts.csswg.org/css-page/#page-break-inside","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"19","notes":"Until Firefox 25, page-break-inside: avoid did not work with the height of a block."},"firefox_android":{"version_added":"19","notes":"Until Firefox 25, page-break-inside: avoid did not work with the height of a block."},"ie":{"version_added":"8"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"page":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/page","spec_url":"https://drafts.csswg.org/css-page/#using-named-pages","support":{"chrome":{"version_added":"85"},"chrome_android":{"version_added":"85"},"edge":{"version_added":"85"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"71"},"opera_android":{"version_added":"60"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"85"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"paint-order":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/paint-order","spec_url":"https://svgwg.org/svg2-draft/painting.html#PaintOrder","support":{"chrome":{"version_added":"35"},"chrome_android":{"version_added":"35"},"edge":{"version_added":"17"},"firefox":{"version_added":"60"},"firefox_android":{"version_added":"60"},"ie":{"version_added":false},"opera":{"version_added":"22"},"opera_android":{"version_added":"22"},"safari":{"version_added":"8"},"safari_ios":{"version_added":"8"},"samsunginternet_android":{"version_added":"3.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"perspective-origin":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/perspective-origin","spec_url":"https://drafts.csswg.org/css-transforms-2/#perspective-origin-property","support":{"chrome":[{"version_added":"36"},{"prefix":"-webkit-","version_added":"12"}],"chrome_android":[{"version_added":"36"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"prefix":"-moz-","version_added":"10"},{"version_added":"49","prefix":"-webkit-"}],"firefox_android":[{"version_added":"16"},{"prefix":"-moz-","version_added":"10"},{"version_added":"49","prefix":"-webkit-"}],"ie":{"version_added":"10"},"opera":[{"version_added":"23"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"24"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"4"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"2"}],"samsunginternet_android":[{"version_added":"3.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"37"},{"prefix":"-webkit-","version_added":"3"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"three_value_syntax":{"__compat":{"description":"Support for three-value syntax of position","support":{"chrome":[{"version_added":"36","version_removed":"68"},{"prefix":"-webkit-","version_added":"12"}],"chrome_android":[{"version_added":"36","version_removed":"68"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12","version_removed":"79"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16","version_removed":"70"},{"prefix":"-moz-","version_added":"10"},{"version_added":"49","prefix":"-webkit-"}],"firefox_android":[{"version_added":"16"},{"prefix":"-moz-","version_added":"10"},{"version_added":"49","prefix":"-webkit-"}],"ie":{"version_added":"10"},"opera":{"prefix":"-webkit-","version_added":"15","version_removed":"55"},"opera_android":{"prefix":"-webkit-","version_added":"14","version_removed":"48"},"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"4"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"2"}],"samsunginternet_android":[{"version_added":"3.0","version_removed":"10.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"37","version_removed":"68"},{"prefix":"-webkit-","version_added":"3"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"perspective":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/perspective","spec_url":"https://drafts.csswg.org/css-transforms-2/#perspective-property","support":{"chrome":[{"version_added":"36"},{"prefix":"-webkit-","version_added":"12"}],"chrome_android":[{"version_added":"36"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"prefix":"-moz-","version_added":"10"},{"version_added":"49","prefix":"-webkit-"}],"firefox_android":[{"version_added":"16"},{"prefix":"-moz-","version_added":"10"},{"version_added":"49","prefix":"-webkit-"}],"ie":{"version_added":"10"},"opera":[{"version_added":"23"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"24"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"4"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"2"}],"samsunginternet_android":[{"version_added":"3.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"37"},{"prefix":"-webkit-","version_added":"3"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"place-content":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/place-content","spec_url":"https://drafts.csswg.org/css-align/#place-content","support":{"chrome":{"version_added":"59"},"chrome_android":{"version_added":"59"},"edge":{"version_added":"79"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":"46"},"opera_android":{"version_added":"43"},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"59"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"flex_context":{"__compat":{"description":"Supported in Flex Layout","support":{"chrome":{"version_added":"59"},"chrome_android":{"version_added":"59"},"edge":{"version_added":"79"},"firefox":{"version_added":"45","notes":"Starting with version 60, you can only specify a single value if it is valid for both align-content and justify-content."},"firefox_android":{"version_added":"45","notes":"Starting with version 60, you can only specify a single value if it is valid for both align-content and justify-content."},"ie":{"version_added":false},"opera":{"version_added":"46"},"opera_android":{"version_added":"43"},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"59"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid_context":{"__compat":{"description":"Supported in Grid Layout","support":{"chrome":{"version_added":"59"},"chrome_android":{"version_added":"59"},"edge":{"version_added":"79"},"firefox":{"version_added":"53","notes":"Starting with version 60, you can only specify a single value if it is valid for both align-content and justify-content."},"firefox_android":{"version_added":"53","notes":"Starting with version 60, you can only specify a single value if it is valid for both align-content and justify-content."},"ie":{"version_added":false},"opera":{"version_added":"46"},"opera_android":{"version_added":"43"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"59"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"place-items":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/place-items","spec_url":"https://drafts.csswg.org/css-align/#place-items-property","support":{"chrome":{"version_added":"59"},"chrome_android":{"version_added":"59"},"edge":{"version_added":"79"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":"46"},"opera_android":{"version_added":"43"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"59"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"flex_context":{"__compat":{"description":"Supported in Flex Layout","support":{"chrome":{"version_added":"59"},"chrome_android":{"version_added":"59"},"edge":{"version_added":"79"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":"46"},"opera_android":{"version_added":"43"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"59"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid_context":{"__compat":{"description":"Supported in Grid Layout","support":{"chrome":{"version_added":"59"},"chrome_android":{"version_added":"59"},"edge":{"version_added":"79"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":"46"},"opera_android":{"version_added":"43"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"59"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"place-self":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/place-self","spec_url":"https://drafts.csswg.org/css-align/#place-self-property","support":{"chrome":{"version_added":"59"},"chrome_android":{"version_added":"59"},"edge":{"version_added":"79"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":"46"},"opera_android":{"version_added":"43"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"59"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"flex_context":{"__compat":{"description":"Supported in Flex Layout","support":{"chrome":{"version_added":"59"},"chrome_android":{"version_added":"59"},"edge":{"version_added":"79"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":"46"},"opera_android":{"version_added":"43"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"59"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid_context":{"__compat":{"description":"Supported in Grid Layout","support":{"chrome":{"version_added":"59"},"chrome_android":{"version_added":"59"},"edge":{"version_added":"79"},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":"46"},"opera_android":{"version_added":"43"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"59"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"pointer-events":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/pointer-events","spec_url":"https://svgwg.org/svg2-draft/interact.html#PointerEventsProperty","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1.5"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"11"},"opera":{"version_added":"9"},"opera_android":{"version_added":"14"},"safari":{"version_added":"4"},"safari_ios":{"version_added":"3.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"2"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"html_elements":{"__compat":{"description":"Applies to HTML elements","support":{"chrome":{"version_added":"2"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3.6"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"11"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"4"},"safari_ios":{"version_added":"3.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}}},"position":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/position","spec_url":"https://drafts.csswg.org/css-position/#position-property","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":["Before Firefox 57, absolute positioning did not work correctly when applied to elements inside tables that have border-collapse applied to them (bug 1379306).","Before Firefox 30, absolute positioning of table rows and row groups was not supported (bug 63895)."]},"firefox_android":{"version_added":"4","notes":["Before Firefox 57, absolute positioning did not work correctly when applied to elements inside tables that have border-collapse applied to them (bug 1379306).","Before Firefox 30, absolute positioning of table rows and row groups was not supported (bug 63895)."]},"ie":{"version_added":"4"},"opera":{"version_added":"4"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"absolutely_positioned_flex_children":{"__compat":{"description":"Absolutely-positioned flex children","support":{"chrome":{"version_added":"52"},"chrome_android":{"version_added":"52"},"edge":{"version_added":"12"},"firefox":{"version_added":"52"},"firefox_android":{"version_added":"52"},"ie":{"version_added":"10"},"opera":{"version_added":"39"},"opera_android":{"version_added":"41"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"52"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"fixed":{"__compat":{"description":"fixed","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"Before Firefox 44, position: fixed didn't create a stacking context in most cases. Firefox and the specification have been modified to mimic Chrome and Safari's long-time behavior."},"firefox_android":{"version_added":"4","notes":"Before Firefox 44, position: fixed didn't create a stacking context in most cases. Firefox and the specification have been modified to mimic Chrome and Safari's long-time behavior."},"ie":{"version_added":"7","notes":"In Internet Explorer, fixed positioning doesn't work if the document is in quirks mode."},"opera":{"version_added":"4"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"position_sticky_table_elements":{"__compat":{"description":"Table elements as sticky positioning containers","support":{"chrome":{"version_added":"56"},"chrome_android":{"version_added":"56"},"edge":{"version_added":"16"},"firefox":{"version_added":"59"},"firefox_android":{"version_added":"59"},"ie":{"version_added":false},"opera":{"version_added":"43"},"opera_android":{"version_added":"43"},"safari":{"version_added":"8"},"safari_ios":{"version_added":"8"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"56"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"sticky":{"__compat":{"description":"sticky","support":{"chrome":{"version_added":"56"},"chrome_android":{"version_added":"56"},"edge":{"version_added":"16"},"firefox":{"version_added":"32"},"firefox_android":{"version_added":"32"},"ie":{"version_added":false},"opera":{"version_added":"43"},"opera_android":{"version_added":"43"},"safari":[{"version_added":"13"},{"prefix":"-webkit-","version_added":"6.1"}],"safari_ios":[{"version_added":"13"},{"prefix":"-webkit-","version_added":"6.1"}],"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"56"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"quotes":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/quotes","spec_url":"https://drafts.csswg.org/css-content/#quotes","support":{"chrome":{"version_added":"11"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1.5"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"4"},"opera_android":{"version_added":"14"},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"quotes_auto":{"__compat":{"description":"auto keyword","support":{"chrome":{"version_added":"87"},"chrome_android":{"version_added":"87"},"edge":{"version_added":"87"},"firefox":{"version_added":"70"},"firefox_android":{"version_added":false},"ie":{"version_added":false,"notes":"This value is not supported, but the default browser behavior is to choose appropriate quotes for the user's language setting"},"opera":{"version_added":"73"},"opera_android":{"version_added":false,"notes":"This value is not supported, but the default browser behavior is to choose appropriate quotes for the user's language setting"},"safari":{"version_added":false,"notes":"This value is not supported, but the default browser behavior is to choose appropriate quotes for the user's language setting"},"safari_ios":{"version_added":false,"notes":"This value is not supported, but the default browser behavior is to choose appropriate quotes for the user's language setting"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"resize":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/resize","spec_url":"https://drafts.csswg.org/css-ui/#resize","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"4"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":"12.1"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"block_level_support":{"__compat":{"description":"Support on block level, replaced, table cell, or inline block elements","support":{"chrome":{"version_added":"4"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"5","notes":"resize doesn't have any effect on <iframe>. See bug 680823)"},"firefox_android":{"version_added":"5","notes":"resize doesn't have any effect on <iframe>. See bug 680823)"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"4"},"safari_ios":{"version_added":"3.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"flow_relative_support":{"__compat":{"description":"Support for flow-relative values block and inline","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"63"},"firefox_android":{"version_added":"63"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"right":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/right","spec_url":"https://drafts.csswg.org/css-position/#insets","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"5.5"},"opera":{"version_added":"5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"rotate":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/rotate","spec_url":"https://drafts.csswg.org/css-transforms-2/#individual-transforms","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"72"},{"version_added":"60","version_removed":"72","flags":[{"type":"preference","name":"layout.css.individual-transform.enabled","value_to_set":"true"}]}],"firefox_android":[{"version_added":"79"},{"version_added":"60","version_removed":"79","flags":[{"type":"preference","name":"layout.css.individual-transform.enabled","value_to_set":"true"}]}],"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"x_y_z_angle":{"__compat":{"description":"x, y, or z axis name plus angle value","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"72"},{"version_added":"65","version_removed":"72","flags":[{"type":"preference","name":"layout.css.individual-transform.enabled","value_to_set":"true"}]}],"firefox_android":{"version_added":"65","flags":[{"type":"preference","name":"layout.css.individual-transform.enabled","value_to_set":"true"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"row-gap":{"flex_context":{"__compat":{"description":"Supported in Flex Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/row-gap","spec_url":"https://drafts.csswg.org/css-align/#column-row-gap","support":{"chrome":{"version_added":"84"},"chrome_android":{"version_added":"84"},"edge":{"version_added":"84"},"firefox":{"version_added":"63"},"firefox_android":{"version_added":"63"},"ie":{"version_added":false},"opera":{"version_added":"70"},"opera_android":{"version_added":"60"},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"84"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"grid_context":{"__compat":{"description":"Supported in Grid Layout","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/row-gap","spec_url":"https://drafts.csswg.org/css-align/#column-row-gap","support":{"chrome":[{"version_added":"66"},{"version_added":"57","alternative_name":"grid-row-gap"}],"chrome_android":[{"version_added":"66"},{"version_added":"57","alternative_name":"grid-row-gap"}],"edge":[{"version_added":"16"},{"version_added":"16","alternative_name":"grid-row-gap"}],"firefox":[{"version_added":"61"},{"version_added":"52","alternative_name":"grid-row-gap"}],"firefox_android":[{"version_added":"61"},{"version_added":"52","alternative_name":"grid-row-gap"}],"ie":{"version_added":false},"opera":[{"version_added":"53"},{"version_added":"44","alternative_name":"grid-row-gap"}],"opera_android":[{"version_added":"47"},{"version_added":"43","alternative_name":"grid-row-gap"}],"safari":[{"version_added":"12"},{"version_added":"10.1","alternative_name":"grid-row-gap"}],"safari_ios":[{"version_added":"12"},{"version_added":"10.3","alternative_name":"grid-row-gap"}],"samsunginternet_android":[{"version_added":"9.0"},{"alternative_name":"grid-row-gap","version_added":"6.0"}],"webview_android":[{"version_added":"66"},{"version_added":"57","alternative_name":"grid-row-gap"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"ruby-align":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/ruby-align","spec_url":"https://drafts.csswg.org/css-ruby/#ruby-align-property","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"38"},"firefox_android":{"version_added":"38"},"ie":{"version_added":false,"notes":"Internet Explorer 9 and later supports an earlier draft of CSS Ruby with non-standard values for this property: auto, left, center, right, distribute-letter, distribute-space, and line-edge."},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"ruby-position":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/ruby-position","spec_url":"https://drafts.csswg.org/css-ruby/#rubypos","support":{"chrome":[{"version_added":"84"},{"version_added":"1","prefix":"-webkit-","notes":"Implemented as a non-standard, prefixed, version of ruby-position, -webkit-ruby-position: it has two properties: before and after (both equivalent, for ltr and rtl scripts to the standard over value used with ruby-align: start)."}],"chrome_android":[{"version_added":"84"},{"version_added":"18","prefix":"-webkit-","notes":"Implemented as a non-standard, prefixed, version of ruby-position, -webkit-ruby-position: it has two properties: before and after (both equivalent, for ltr and rtl scripts to the standard over value used with ruby-align: start)."}],"edge":[{"version_added":"84"},{"version_added":"79","prefix":"-webkit-","notes":"Implemented as a non-standard, prefixed, version of ruby-position, -webkit-ruby-position: it has two properties: before and after (both equivalent, for ltr and rtl scripts to the standard over value used with ruby-align: start)."},{"version_added":"12","version_removed":"79"}],"firefox":{"version_added":"38"},"firefox_android":{"version_added":"38"},"ie":{"version_added":false,"notes":"Internet Explorer 9 and later support an old draft values: inline (equivalent of having display: inline on the ruby), and above (synonym of the modern over)."},"opera":[{"version_added":"70"},{"version_added":"15","prefix":"-webkit-","notes":"Implemented as a non-standard, prefixed, version of ruby-position, -webkit-ruby-position: it has two properties: before and after (both equivalent, for ltr and rtl scripts to the standard over value used with ruby-align: start)."}],"opera_android":[{"version_added":"60"},{"version_added":"14","prefix":"-webkit-","notes":"Implemented as a non-standard, prefixed, version of ruby-position, -webkit-ruby-position: it has two properties: before and after (both equivalent, for ltr and rtl scripts to the standard over value used with ruby-align: start)."}],"safari":{"version_added":"6.1","prefix":"-webkit-","notes":"Implemented as a non-standard, prefixed, version of ruby-position, -webkit-ruby-position: it has two properties: before and after (both equivalent, for ltr and rtl scripts to the standard over value used with ruby-align: start)."},"safari_ios":{"version_added":"6.1","prefix":"-webkit-","notes":"Implemented as a non-standard, prefixed, version of ruby-position, -webkit-ruby-position: it has two properties: before and after (both equivalent, for ltr and rtl scripts to the standard over value used with ruby-align: start)."},"samsunginternet_android":{"version_added":"14.0"},"webview_android":[{"version_added":"84"},{"version_added":"1","prefix":"-webkit-","notes":"Implemented as a non-standard, prefixed, version of ruby-position, -webkit-ruby-position: it has two properties: before and after (both equivalent, for ltr and rtl scripts to the standard over value used with ruby-align: start)."}]},"status":{"experimental":true,"standard_track":true,"deprecated":false}},"alternate":{"__compat":{"description":"alternate","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"88"},"firefox_android":{"version_added":"88"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"inter-character":{"__compat":{"description":"inter-character","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false,"notes":"See bug 1055672."},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}}},"scale":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scale","spec_url":"https://drafts.csswg.org/css-transforms-2/#individual-transforms","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"72"},{"version_added":"60","version_removed":"72","flags":[{"type":"preference","name":"layout.css.individual-transform.enabled","value_to_set":"true"}]}],"firefox_android":[{"version_added":"79"},{"version_added":"60","version_removed":"79","flags":[{"type":"preference","name":"layout.css.individual-transform.enabled","value_to_set":"true"}]}],"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-behavior":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-behavior","spec_url":"https://drafts.csswg.org/css-overflow/#smooth-scrolling","support":{"chrome":{"version_added":"61"},"chrome_android":{"version_added":"61"},"edge":{"version_added":"79"},"firefox":{"version_added":"36"},"firefox_android":{"version_added":"36"},"ie":{"version_added":false},"opera":{"version_added":"48"},"opera_android":{"version_added":"45"},"safari":{"version_added":"14","flags":[{"type":"preference","name":"CSSOM View Smooth Scrolling"}]},"safari_ios":{"version_added":"14","flags":[{"type":"preference","name":"CSSOM View Smooth Scrolling"}]},"samsunginternet_android":{"version_added":"8.0"},"webview_android":{"version_added":"61"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-margin-block-end":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-margin-block-end","spec_url":"https://drafts.csswg.org/css-scroll-snap/#margin-longhands-logical","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-margin-block-start":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-margin-block-start","spec_url":"https://drafts.csswg.org/css-scroll-snap/#margin-longhands-logical","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-margin-block":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-margin-block","spec_url":"https://drafts.csswg.org/css-scroll-snap/#propdef-scroll-margin-block","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-margin-bottom":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-margin-bottom","spec_url":"https://drafts.csswg.org/css-scroll-snap/#margin-longhands-physical","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":[{"version_added":"14.1"},{"version_added":"11","alternative_name":"scroll-snap-margin-bottom","partial_implementation":true,"notes":"Before version 14.1, scroll margin is not applied for scrolls to fragment target or scrollIntoView(), see bug 189265."}],"safari_ios":[{"version_added":"14.5"},{"version_added":"11","alternative_name":"scroll-snap-margin-bottom","partial_implementation":true,"notes":"Before version 14.5, scroll margin is not applied for scrolls to fragment target or scrollIntoView(), see bug 189265."}],"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-margin-inline-end":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-margin-inline-end","spec_url":"https://drafts.csswg.org/css-scroll-snap/#margin-longhands-logical","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-margin-inline-start":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-margin-inline-start","spec_url":"https://drafts.csswg.org/css-scroll-snap/#margin-longhands-logical","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-margin-inline":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-margin-inline","spec_url":"https://drafts.csswg.org/css-scroll-snap/#propdef-scroll-margin-inline","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-margin-left":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-margin-left","spec_url":"https://drafts.csswg.org/css-scroll-snap/#margin-longhands-physical","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":[{"version_added":"14.1"},{"version_added":"11","alternative_name":"scroll-snap-margin-left","partial_implementation":true,"notes":"Before version 14.1, scroll margin is not applied for scrolls to fragment target or scrollIntoView(), see bug 189265."}],"safari_ios":[{"version_added":"14.5"},{"version_added":"11","alternative_name":"scroll-snap-margin-left","partial_implementation":true,"notes":"Before version 14.5, scroll margin is not applied for scrolls to fragment target or scrollIntoView(), see bug 189265."}],"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-margin-right":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-margin-right","spec_url":"https://drafts.csswg.org/css-scroll-snap/#margin-longhands-physical","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":[{"version_added":"14.1"},{"version_added":"11","alternative_name":"scroll-snap-margin-right","partial_implementation":true,"notes":"Before version 14.1, scroll margin is not applied for scrolls to fragment target or scrollIntoView(), see bug 189265."}],"safari_ios":[{"version_added":"14.5"},{"version_added":"11","alternative_name":"scroll-snap-margin-right","partial_implementation":true,"notes":"Before version 14.5, scroll margin is not applied for scrolls to fragment target or scrollIntoView(), see bug 189265."}],"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-margin-top":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-margin-top","spec_url":"https://drafts.csswg.org/css-scroll-snap/#margin-longhands-physical","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":[{"version_added":"14.1"},{"version_added":"11","alternative_name":"scroll-snap-margin-top","partial_implementation":true,"notes":"Before version 14.1, scroll margin is not applied for scrolls to fragment target or scrollIntoView(), see bug 189265."}],"safari_ios":[{"version_added":"14.5"},{"version_added":"11","alternative_name":"scroll-snap-margin-top","partial_implementation":true,"notes":"Before version 14.5, scroll margin is not applied for scrolls to fragment target or scrollIntoView(), see bug 189265."}],"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-margin":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-margin","spec_url":"https://drafts.csswg.org/css-scroll-snap/#scroll-margin","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":[{"version_added":"90"},{"version_added":"68","version_removed":"90","partial_implementation":true,"notes":"The scroll-margin property can cause an element's visibility to be incorrectly calculated for element.focus(). See bug 1708303."}],"firefox_android":[{"version_added":"90"},{"version_added":"68","version_removed":"90","partial_implementation":true,"notes":"The scroll-margin property can cause an element's visibility to be incorrectly calculated for element.focus(). See bug 1708303."}],"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":[{"version_added":"14.1"},{"version_added":"11","alternative_name":"scroll-snap-margin","partial_implementation":true,"notes":"Before version 14.1, scroll margin is not applied for scrolls to fragment target or scrollIntoView(), see bug 189265."}],"safari_ios":[{"version_added":"14.5"},{"version_added":"11","alternative_name":"scroll-snap-margin","partial_implementation":true,"notes":"Before version 14.5, scroll margin is not applied for scrolls to fragment target or scrollIntoView(), see bug 189265."}],"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-padding-block-end":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-padding-block-end","spec_url":"https://drafts.csswg.org/css-scroll-snap/#padding-longhands-logical","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"15"},"safari_ios":{"version_added":"15"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-padding-block-start":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-padding-block-start","spec_url":"https://drafts.csswg.org/css-scroll-snap/#padding-longhands-logical","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"15"},"safari_ios":{"version_added":"15"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-padding-block":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-padding-block","spec_url":"https://drafts.csswg.org/css-scroll-snap/#propdef-scroll-padding-block","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"15"},"safari_ios":{"version_added":"15"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-padding-bottom":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-padding-bottom","spec_url":"https://drafts.csswg.org/css-scroll-snap/#padding-longhands-physical","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"11","partial_implementation":true,"notes":"Scroll padding is not applied for scrolls to fragment target or scrollIntoView(), see bug 179379."},"safari_ios":{"version_added":"11","partial_implementation":true,"notes":"Scroll padding is not applied for scrolls to fragment target or scrollIntoView(), see bug 179379."},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-padding-inline-end":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-padding-inline-end","spec_url":"https://drafts.csswg.org/css-scroll-snap/#padding-longhands-logical","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"15"},"safari_ios":{"version_added":"15"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-padding-inline-start":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-padding-inline-start","spec_url":"https://drafts.csswg.org/css-scroll-snap/#padding-longhands-logical","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"15"},"safari_ios":{"version_added":"15"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-padding-inline":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-padding-inline","spec_url":"https://drafts.csswg.org/css-scroll-snap/#propdef-scroll-padding-inline","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"15"},"safari_ios":{"version_added":"15"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-padding-left":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-padding-left","spec_url":"https://drafts.csswg.org/css-scroll-snap/#padding-longhands-physical","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"11","partial_implementation":true,"notes":"Scroll padding is not applied for scrolls to fragment target or scrollIntoView(), see bug 179379."},"safari_ios":{"version_added":"11","partial_implementation":true,"notes":"Scroll padding is not applied for scrolls to fragment target or scrollIntoView(), see bug 179379."},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-padding-right":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-padding-right","spec_url":"https://drafts.csswg.org/css-scroll-snap/#padding-longhands-physical","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"11","partial_implementation":true,"notes":"Scroll padding is not applied for scrolls to fragment target or scrollIntoView(), see bug 179379."},"safari_ios":{"version_added":"11","partial_implementation":true,"notes":"Scroll padding is not applied for scrolls to fragment target or scrollIntoView(), see bug 179379."},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-padding-top":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-padding-top","spec_url":"https://drafts.csswg.org/css-scroll-snap/#padding-longhands-physical","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"11","partial_implementation":true,"notes":"Scroll padding is not applied for scrolls to fragment target or scrollIntoView(), see bug 179379."},"safari_ios":{"version_added":"11","partial_implementation":true,"notes":"Scroll padding is not applied for scrolls to fragment target or scrollIntoView(), see bug 179379."},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-padding":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-padding","spec_url":"https://drafts.csswg.org/css-scroll-snap/#scroll-padding","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"11","partial_implementation":true,"notes":"Scroll padding is not applied for scrolls to fragment target or scrollIntoView(), see bug 179379."},"safari_ios":{"version_added":"11","partial_implementation":true,"notes":"Scroll padding is not applied for scrolls to fragment target or scrollIntoView(), see bug 179379."},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-snap-align":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-snap-align","spec_url":"https://drafts.csswg.org/css-scroll-snap/#scroll-snap-align","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":{"version_added":"79"},"firefox":{"version_added":"68"},"firefox_android":{"version_added":"68"},"ie":{"version_added":false},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-snap-coordinate":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-snap-coordinate","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_removed":"68","version_added":"39"},"firefox_android":{"version_removed":"68","version_added":"39"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"scroll-snap-destination":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-snap-destination","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_removed":"68","version_added":"39"},"firefox_android":{"version_removed":"68","version_added":"39"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"scroll-snap-points-x":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-snap-points-x","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_removed":"68","version_added":"39"},"firefox_android":{"version_removed":"68","version_added":"39"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"9","prefix":"-webkit-"},"safari_ios":{"version_added":"9","prefix":"-webkit-"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"scroll-snap-points-y":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-snap-points-y","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_removed":"68","version_added":"39"},"firefox_android":{"version_removed":"68","version_added":"39"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"9","prefix":"-webkit-"},"safari_ios":{"version_added":"9","prefix":"-webkit-"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"scroll-snap-stop":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-snap-stop","spec_url":"https://drafts.csswg.org/css-scroll-snap/#scroll-snap-stop","support":{"chrome":{"version_added":"75"},"chrome_android":{"version_added":"75"},"edge":{"version_added":"79"},"firefox":{"version_added":false,"notes":"See bug 1312165."},"firefox_android":{"version_added":false,"notes":"See bug 1312165."},"ie":{"version_added":false},"opera":{"version_added":"62"},"opera_android":{"version_added":"54"},"safari":{"version_added":"15"},"safari_ios":{"version_added":"15"},"samsunginternet_android":{"version_added":"11.0"},"webview_android":{"version_added":"75"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scroll-snap-type-x":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-snap-type-x","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_removed":"68","version_added":"39"},"firefox_android":{"version_removed":"68","version_added":"39"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"scroll-snap-type-y":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-snap-type-y","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_removed":"68","version_added":"39"},"firefox_android":{"version_removed":"68","version_added":"39"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"scroll-snap-type":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scroll-snap-type","spec_url":"https://drafts.csswg.org/css-scroll-snap/#scroll-snap-type","support":{"chrome":{"version_added":"69"},"chrome_android":{"version_added":"69"},"edge":[{"version_added":"79"},{"version_added":"12","version_removed":"79","prefix":"-ms-","notes":"Edge supports an earlier draft of CSS Scroll Snap without axis values."}],"firefox":[{"version_added":"68"},{"version_added":"39","version_removed":"68","notes":"An earlier draft of CSS Scroll Snap without axis values."}],"firefox_android":[{"version_added":"68"},{"version_added":"39","version_removed":"68","notes":"An earlier draft of CSS Scroll Snap without axis values."}],"ie":{"version_added":"10","prefix":"-ms-","notes":"Internet Explorer supports an earlier draft of CSS Scroll Snap without axis values."},"opera":{"version_added":"56"},"opera_android":{"version_added":"48"},"safari":[{"version_added":"11"},{"version_added":"9","prefix":"-webkit-","notes":"Older Safari versions support an earlier draft of CSS Scroll Snap without axis values."}],"safari_ios":[{"version_added":"11"},{"version_added":"9","prefix":"-webkit-","notes":"Older Safari versions support an earlier draft of CSS Scroll Snap without axis values."}],"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"69"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scrollbar-3dlight-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scrollbar-3dlight-color","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":[{"version_added":"5"},{"version_added":"8","prefix":"-ms-"}],"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"deprecated":true,"experimental":false,"standard_track":false}}},"scrollbar-arrow-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scrollbar-arrow-color","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":[{"version_added":"5"},{"version_added":"8","prefix":"-ms-"}],"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"deprecated":true,"experimental":false,"standard_track":false}}},"scrollbar-base-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scrollbar-base-color","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":[{"version_added":"5"},{"version_added":"8","prefix":"-ms-"}],"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"deprecated":true,"experimental":false,"standard_track":false}}},"scrollbar-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scrollbar-color","spec_url":"https://drafts.csswg.org/css-scrollbars/#scrollbar-color","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"64","notes":"On macOS, you need to set the General > Show scroll bars setting in System Preferences to \"Always\" for this property to have any effect."},"firefox_android":{"version_added":"64"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"scrollbar-darkshadow-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scrollbar-darkshadow-color","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":[{"version_added":"5"},{"version_added":"8","prefix":"-ms-"}],"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"deprecated":true,"experimental":false,"standard_track":false}}},"scrollbar-face-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scrollbar-face-color","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":[{"version_added":"5"},{"version_added":"8","prefix":"-ms-"}],"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"deprecated":true,"experimental":false,"standard_track":false}}},"scrollbar-gutter":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scrollbar-gutter","spec_url":"https://drafts.csswg.org/css-overflow/#scrollbar-gutter-property","support":{"chrome":[{"version_added":"94"},{"version_added":"88","version_removed":"94","flags":[{"type":"preference","name":"Enable experimental Web Platform features"}]}],"chrome_android":[{"version_added":"94"},{"version_added":"88","version_removed":"94","flags":[{"type":"preference","name":"Enable experimental Web Platform features"}]}],"edge":{"version_added":"94"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"80"},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":"94"}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"scrollbar-highlight-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scrollbar-highlight-color","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":[{"version_added":"5"},{"version_added":"8","prefix":"-ms-"}],"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"deprecated":true,"experimental":false,"standard_track":false}}},"scrollbar-shadow-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scrollbar-shadow-color","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":[{"version_added":"5"},{"version_added":"8","prefix":"-ms-"}],"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"deprecated":true,"experimental":false,"standard_track":false}}},"scrollbar-width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/scrollbar-width","spec_url":"https://drafts.csswg.org/css-scrollbars/#scrollbar-width","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"64"},"firefox_android":{"version_added":"64"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"shape-image-threshold":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/shape-image-threshold","spec_url":"https://drafts.csswg.org/css-shapes/#shape-image-threshold-property","support":{"chrome":{"version_added":"37"},"chrome_android":{"version_added":"37"},"edge":{"version_added":"79"},"firefox":{"version_added":"62"},"firefox_android":{"version_added":"62"},"ie":{"version_added":false},"opera":{"version_added":"24"},"opera_android":{"version_added":"24"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"3.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"percentages":{"__compat":{"description":"Support for percentage opacity values","support":{"chrome":{"version_added":"78"},"chrome_android":{"version_added":"78"},"edge":{"version_added":"79"},"firefox":{"version_added":"70"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"12.0"},"webview_android":{"version_added":"78"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"shape-margin":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/shape-margin","spec_url":"https://drafts.csswg.org/css-shapes/#shape-margin-property","support":{"chrome":{"version_added":"37"},"chrome_android":{"version_added":"37"},"edge":{"version_added":"79"},"firefox":{"version_added":"62"},"firefox_android":{"version_added":"62"},"ie":{"version_added":false},"opera":{"version_added":"24"},"opera_android":{"version_added":"24"},"safari":[{"version_added":"10.1"},{"prefix":"-webkit-","version_added":"10.1"}],"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"3.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"shape-outside":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/shape-outside","spec_url":"https://drafts.csswg.org/css-shapes/#shape-outside-property","support":{"chrome":{"version_added":"37"},"chrome_android":{"version_added":"37"},"edge":{"version_added":"79"},"firefox":{"version_added":"62"},"firefox_android":{"version_added":"62"},"ie":{"version_added":false},"opera":{"version_added":"24"},"opera_android":{"version_added":"24"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"3.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"circle":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/basic-shape#circle()","description":"circle()","support":{"chrome":{"version_added":"37"},"chrome_android":{"version_added":"37"},"edge":{"version_added":"79"},"firefox":{"version_added":"62"},"firefox_android":{"version_added":"62"},"ie":{"version_added":false},"opera":{"version_added":"24"},"opera_android":{"version_added":"24"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"3.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"gradient":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/gradient","spec_url":"https://drafts.csswg.org/css-images/#gradients","description":"<gradient>","support":{"chrome":{"version_added":"37"},"chrome_android":{"version_added":"37"},"edge":{"version_added":"79"},"firefox":{"version_added":"62"},"firefox_android":{"version_added":"62"},"ie":{"version_added":false},"opera":{"version_added":"24"},"opera_android":{"version_added":"24"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"3.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"image":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/image","spec_url":"https://drafts.csswg.org/css-images/#image-values","description":"<image>","support":{"chrome":{"version_added":"37"},"chrome_android":{"version_added":"37"},"edge":{"version_added":"79"},"firefox":{"version_added":"62"},"firefox_android":{"version_added":"62"},"ie":{"version_added":false},"opera":{"version_added":"24"},"opera_android":{"version_added":"24"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"3.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"inset":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/basic-shape#inset()","description":"inset()","support":{"chrome":{"version_added":"37"},"chrome_android":{"version_added":"37"},"edge":{"version_added":"79"},"firefox":{"version_added":"62"},"firefox_android":{"version_added":"62"},"ie":{"version_added":false},"opera":{"version_added":"24"},"opera_android":{"version_added":"24"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"3.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"polygon":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/basic-shape#polygon()","description":"polygon()","support":{"chrome":{"version_added":"37"},"chrome_android":{"version_added":"37"},"edge":{"version_added":"79"},"firefox":{"version_added":"62"},"firefox_android":{"version_added":"62"},"ie":{"version_added":false},"opera":{"version_added":"24"},"opera_android":{"version_added":"24"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"3.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"three_value_syntax":{"__compat":{"description":"Support for three-value syntax of position in circle and ellipse","support":{"chrome":{"version_added":"37","version_removed":"68"},"chrome_android":{"version_added":"37","version_removed":"68"},"edge":{"version_added":false},"firefox":{"version_added":"62","version_removed":"70"},"firefox_android":{"version_added":"62","version_removed":"79"},"ie":{"version_added":false},"opera":{"version_added":"24","version_removed":"55"},"opera_android":{"version_added":"24","version_removed":"48"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"3.0","version_removed":"10.0"},"webview_android":{"version_added":"37","version_removed":"68"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"tab-size":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/tab-size","spec_url":"https://drafts.csswg.org/css-text/#tab-size-property","support":{"chrome":{"version_added":"21","notes":"This property is not yet animatable."},"chrome_android":{"version_added":"25","notes":"This property is not yet animatable."},"edge":{"version_added":"79","notes":"This property is not yet animatable."},"firefox":[{"version_added":"91"},{"prefix":"-moz-","version_added":"4","notes":"Before Firefox 53, this property was not animatable."}],"firefox_android":[{"version_added":"91"},{"prefix":"-moz-","version_added":"4","notes":"Before Firefox 53, this property was not animatable."}],"ie":{"version_added":false},"opera":[{"version_added":"15"},{"prefix":"-o-","version_added":"10.5","version_removed":"15"}],"opera_android":[{"version_added":"14"},{"prefix":"-o-","version_added":"11","version_removed":"14"}],"safari":{"version_added":"7"},"safari_ios":{"version_added":"7"},"samsunginternet_android":{"version_added":"1.5","notes":"This property is not yet animatable."},"webview_android":{"version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"length":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/length","spec_url":"https://drafts.csswg.org/css-values/#lengths","description":"<length>","support":{"chrome":{"version_added":"42"},"chrome_android":{"version_added":"42"},"edge":{"version_added":"79"},"firefox":{"version_added":"53"},"firefox_android":{"version_added":"53"},"ie":{"version_added":false},"opera":{"version_added":"29"},"opera_android":{"version_added":"29"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"4.0"},"webview_android":{"version_added":"56"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"table-layout":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/table-layout","spec_url":"https://drafts.csswg.org/css2/#width-layout","support":{"chrome":{"version_added":"14"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"5"},"opera":{"version_added":"7"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"3"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1.5"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"text-align-last":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-align-last","spec_url":"https://drafts.csswg.org/css-text/#text-align-last-property","support":{"chrome":{"version_added":"47"},"chrome_android":{"version_added":"47"},"edge":{"version_added":"12"},"firefox":[{"version_added":"49"},{"prefix":"-moz-","version_added":"12","version_removed":"53"}],"firefox_android":[{"version_added":"49"},{"prefix":"-moz-","version_added":"14","version_removed":"53"}],"ie":{"version_added":"5.5","partial_implementation":true,"notes":["IE only supports text-align-last when text-align is set to justify.","The start and end values are not supported."]},"opera":{"version_added":"34"},"opera_android":{"version_added":"34"},"safari":{"version_added":false,"notes":"See WebKit bug 76173."},"safari_ios":{"version_added":false,"notes":"See WebKit bug 76173."},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"47"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"text-align":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-align","spec_url":["https://drafts.csswg.org/css-logical/#text-align","https://drafts.csswg.org/css-text/#text-align-property"],"support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"3"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"block_alignment_values":{"__compat":{"description":"Prefixed center, left, and right values for block alignment","support":{"chrome":{"prefix":"-webkit-","version_added":"1"},"chrome_android":{"prefix":"-webkit-","version_added":"18"},"edge":{"prefix":"-webkit-","version_added":"79"},"firefox":{"prefix":"-moz-","version_added":"1"},"firefox_android":{"prefix":"-moz-","version_added":"4"},"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":[{"prefix":"-webkit-","version_added":"1.3"},{"prefix":"-khtml-","version_added":"1"}],"safari_ios":[{"prefix":"-webkit-","version_added":"1"},{"prefix":"-khtml-","version_added":"1"}],"samsunginternet_android":{"version_added":"1.0","prefix":"-webkit-"},"webview_android":{"prefix":"-webkit-","version_added":"37"}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}},"flow_relative_values_start_and_end":{"__compat":{"description":"Flow-relative values start and end","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3.1"},"safari_ios":{"version_added":"2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"justify-all":{"__compat":{"description":"justify-all","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"match-parent":{"__compat":{"description":"match-parent","support":{"chrome":{"version_added":"16"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"40"},"firefox_android":{"version_added":"40"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"string":{"__compat":{"description":"Character-based alignment in a table column (<string> value)","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}}},"text-combine-upright":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-combine-upright","spec_url":"https://drafts.csswg.org/css-writing-modes/#text-combine-upright","support":{"chrome":[{"version_added":"48"},{"partial_implementation":true,"alternative_name":"-webkit-text-combine","version_added":"9","notes":"This property was initially named -webkit-text-combine according to a 2011 version of the CSS3 Writing Modes specification, supporting the values none and horizontal without digits."}],"chrome_android":[{"version_added":"48"},{"partial_implementation":true,"alternative_name":"-webkit-text-combine","version_added":"18","notes":"This property was initially named -webkit-text-combine according to a 2011 version of the CSS3 Writing Modes specification, supporting the values none and horizontal without digits."}],"edge":[{"version_added":"15","version_removed":"79"},{"version_added":"12","version_removed":"79","alternative_name":"-ms-text-combine-horizontal"}],"firefox":{"version_added":"48","notes":"Before version 81, Firefox implemented the property as animatable. This was corrected to spec in 81."},"firefox_android":{"version_added":"48","notes":"Before version 81, Firefox implemented the property as animatable. This was corrected to spec in 81."},"ie":{"version_added":"11","alternative_name":"-ms-text-combine-horizontal"},"opera":[{"version_added":"35"},{"partial_implementation":true,"alternative_name":"-webkit-text-combine","version_added":"15","notes":"This property was initially named -webkit-text-combine according to a 2011 version of the CSS3 Writing Modes specification, supporting the values none and horizontal without digits."}],"opera_android":[{"version_added":"35"},{"partial_implementation":true,"alternative_name":"-webkit-text-combine","version_added":"14","notes":"This property was initially named -webkit-text-combine according to a 2011 version of the CSS3 Writing Modes specification, supporting the values none and horizontal without digits."}],"safari":{"partial_implementation":true,"alternative_name":"-webkit-text-combine","version_added":"5.1","notes":"This property was initially named -webkit-text-combine according to a 2011 version of the CSS3 Writing Modes specification, supporting the values none and horizontal without digits."},"safari_ios":{"partial_implementation":true,"alternative_name":"-webkit-text-combine","version_added":"5","notes":"This property was initially named -webkit-text-combine according to a 2011 version of the CSS3 Writing Modes specification, supporting the values none and horizontal without digits."},"samsunginternet_android":[{"version_added":"5.0"},{"partial_implementation":true,"alternative_name":"-webkit-text-combine","version_added":"1.0","notes":"This property was initially named -webkit-text-combine according to a 2011 version of the CSS3 Writing Modes specification, supporting the values none and horizontal without digits."}],"webview_android":[{"version_added":"48"},{"partial_implementation":true,"alternative_name":"-webkit-text-combine","version_added":"≤37","notes":"This property was initially named -webkit-text-combine according to a 2011 version of the CSS3 Writing Modes specification, supporting the values none and horizontal without digits."}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"digits":{"__compat":{"description":"digits","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":[{"version_added":"15","version_removed":"79"},{"version_added":"12","version_removed":"79","alternative_name":"-ms-text-combine-horizontal"}],"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":"11","alternative_name":"-ms-text-combine-horizontal"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"text-decoration-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-decoration-color","spec_url":"https://drafts.csswg.org/css-text-decor/#text-decoration-color-property","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":[{"version_added":"36"},{"prefix":"-moz-","version_added":"6","version_removed":"39"}],"firefox_android":[{"version_added":"36"},{"prefix":"-moz-","version_added":"6","version_removed":"39"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"8"}],"safari_ios":[{"version_added":"12.2"},{"prefix":"-webkit-","version_added":"8"}],"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"text-decoration-line":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-decoration-line","spec_url":"https://drafts.csswg.org/css-text-decor/#text-decoration-line-property","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":[{"version_added":"36"},{"prefix":"-moz-","version_added":"6","version_removed":"39"}],"firefox_android":[{"version_added":"36"},{"prefix":"-moz-","version_added":"6","version_removed":"39"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"8"}],"safari_ios":[{"version_added":"12.2"},{"prefix":"-webkit-","version_added":"8"}],"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"blink":{"__compat":{"description":"blink","support":{"chrome":{"version_added":"57","notes":"The blink value does not have any effect."},"chrome_android":{"version_added":"57","notes":"The blink value does not have any effect."},"edge":{"version_added":"79","notes":"The blink value does not have any effect."},"firefox":{"version_added":"26","notes":"The blink value does not have any effect."},"firefox_android":{"version_added":"26","notes":"The blink value does not have any effect."},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"8"},"safari_ios":{"version_added":"8"},"samsunginternet_android":{"version_added":"7.0","notes":"The blink value does not have any effect."},"webview_android":{"version_added":"57","notes":"The blink value does not have any effect."}},"status":{"experimental":false,"standard_track":true,"deprecated":true}}}},"text-decoration-skip-ink":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-decoration-skip-ink","spec_url":"https://drafts.csswg.org/css-text-decor-4/#text-decoration-skip-ink-property","support":{"chrome":{"version_added":"64"},"chrome_android":{"version_added":"64"},"edge":{"version_added":"79"},"firefox":{"version_added":"70"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"50"},"opera_android":{"version_added":"46"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"9.0"},"webview_android":{"version_added":"64"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"all":{"__compat":{"description":"all","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"75"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"text-decoration-skip":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-decoration-skip","spec_url":"https://drafts.csswg.org/css-text-decor-4/#text-decoration-skipping","support":{"chrome":{"version_added":"57","version_removed":"64","notes":"Only supports the deprecated ink value."},"chrome_android":{"version_added":"57","version_removed":"64","notes":"Only supports the deprecated ink value."},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"44","version_removed":"50","notes":"Only supports the deprecated ink value."},"opera_android":{"version_added":"43","version_removed":"46","notes":"Only supports the deprecated ink value."},"safari":[{"version_added":"12.1","notes":"Supports only none, auto, and objects values."},{"version_added":"7","prefix":"-webkit-","notes":"Supports only none, auto, and objects values."}],"safari_ios":[{"version_added":"12.2","notes":"Supports only none, auto, and objects values."},{"version_added":"7","prefix":"-webkit-","notes":"Supports only none, auto, and objects values."}],"samsunginternet_android":{"version_added":"7.0","version_removed":"9.0","notes":"Only supports the deprecated ink value."},"webview_android":{"version_added":"57","version_removed":"64","notes":"Only supports the deprecated ink value."}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"text-decoration-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-decoration-style","spec_url":"https://drafts.csswg.org/css-text-decor/#text-decoration-style-property","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":[{"version_added":"36"},{"prefix":"-moz-","version_added":"6","version_removed":"39"}],"firefox_android":[{"version_added":"36"},{"prefix":"-moz-","version_added":"6","version_removed":"39"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"8"}],"safari_ios":[{"version_added":"12.2"},{"prefix":"-webkit-","version_added":"8"}],"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"wavy":{"__compat":{"description":"wavy","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":{"version_added":"6"},"firefox_android":{"version_added":"6"},"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"version_added":"8"},"safari_ios":{"version_added":"8"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"text-decoration-thickness":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-decoration-thickness","spec_url":"https://drafts.csswg.org/css-text-decor-4/#text-decoration-width-property","support":{"chrome":[{"version_added":"89"},{"version_added":"87","version_removed":"89","partial_implementation":true,"notes":"The text-decoration-thickness property does not work unless either text-underline-offset is set to something other than auto or text-decoration-color is set to something other than currentColor. See Chromium bug 1154537."}],"chrome_android":[{"version_added":"89"},{"version_added":"87","version_removed":"89","partial_implementation":true,"notes":"The text-decoration-thickness property does not work unless either text-underline-offset is set to something other than auto or text-decoration-color is set to something other than currentColor. See Chromium bug 1154537."}],"edge":[{"version_added":"89"},{"version_added":"87","version_removed":"89","partial_implementation":true,"notes":"The text-decoration-thickness property does not work unless either text-underline-offset is set to something other than auto or text-decoration-color is set to something other than currentColor. See Chromium bug 1154537."}],"firefox":[{"version_added":"70"},{"version_added":"69","alternative_name":"text-decoration-width","flags":[{"type":"preference","name":"layout.css.text-decoration-width.enabled","value_to_set":"true"}]}],"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":[{"version_added":"75"},{"version_added":"73","version_removed":"75","partial_implementation":true,"notes":"The text-decoration-thickness property does not work unless either text-underline-offset is set to something other than auto or text-decoration-color is set to something other than currentColor. See Chromium bug 1154537."}],"opera_android":{"version_added":"63"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"15.0"},"webview_android":[{"version_added":"89"},{"version_added":"87","version_removed":"89","partial_implementation":true,"notes":"The text-decoration-thickness property does not work unless either text-underline-offset is set to something other than auto or text-decoration-color is set to something other than currentColor. See Chromium bug 1154537."}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"percentage":{"__compat":{"description":"percentage values","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"74"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"text-decoration":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-decoration","spec_url":"https://drafts.csswg.org/css-text-decor/#text-decoration-property","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"3"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"blink":{"__compat":{"description":"blink","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"1","version_removed":"23"},"firefox_android":{"version_added":"4","version_removed":"23"},"ie":{"version_added":false},"opera":{"version_added":"4","version_removed":"15"},"opera_android":{"version_added":"10.1","version_removed":"14"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":true}}},"shorthand":{"__compat":{"description":"Shorthand","support":{"chrome":{"version_added":"57"},"chrome_android":{"version_added":"57"},"edge":{"version_added":"79"},"firefox":[{"version_added":"36"},{"partial_implementation":true,"version_added":"6"}],"firefox_android":[{"version_added":"36"},{"partial_implementation":true,"version_added":"6"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":{"prefix":"-webkit-","version_added":"8"},"safari_ios":{"prefix":"-webkit-","version_added":"8"},"samsunginternet_android":{"version_added":"7.0"},"webview_android":{"version_added":"57"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"text-decoration-thickness":{"__compat":{"description":"text-decoration-thickness included in shorthand","support":{"chrome":{"version_added":"87"},"chrome_android":{"version_added":"87"},"edge":{"version_added":"87"},"firefox":{"version_added":"70"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"73"},"opera_android":{"version_added":"62"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":true,"standard_track":false,"deprecated":false}}}},"text-emphasis-color":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-emphasis-color","spec_url":"https://drafts.csswg.org/css-text-decor/#text-emphasis-color-property","support":{"chrome":{"prefix":"-webkit-","version_added":"25"},"chrome_android":{"prefix":"-webkit-","version_added":"25"},"edge":{"prefix":"-webkit-","version_added":"79"},"firefox":{"version_added":"46"},"firefox_android":{"version_added":"46"},"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":[{"version_added":"7"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"7"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":{"prefix":"-webkit-","version_added":"1.5"},"webview_android":{"prefix":"-webkit-","version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"text-emphasis-position":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-emphasis-position","spec_url":"https://drafts.csswg.org/css-text-decor/#text-emphasis-position-property","support":{"chrome":{"prefix":"-webkit-","version_added":"25"},"chrome_android":{"prefix":"-webkit-","version_added":"25"},"edge":{"prefix":"-webkit-","version_added":"79"},"firefox":{"version_added":"46"},"firefox_android":{"version_added":"46"},"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":[{"version_added":"7"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"7"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":{"prefix":"-webkit-","version_added":"1.5"},"webview_android":{"prefix":"-webkit-","version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"left_and_right":{"__compat":{"description":"left and right","support":{"chrome":{"version_added":"62"},"chrome_android":{"version_added":"62"},"edge":{"version_added":"79"},"firefox":{"version_added":"46"},"firefox_android":{"version_added":"46"},"ie":{"version_added":false},"opera":{"version_added":"49"},"opera_android":{"version_added":"46"},"safari":{"version_added":"8"},"safari_ios":{"version_added":"8"},"samsunginternet_android":{"version_added":"8.0"},"webview_android":{"version_added":"62"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"text-emphasis-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-emphasis-style","spec_url":"https://drafts.csswg.org/css-text-decor/#text-emphasis-style-property","support":{"chrome":{"prefix":"-webkit-","version_added":"25"},"chrome_android":{"prefix":"-webkit-","version_added":"25"},"edge":{"prefix":"-webkit-","version_added":"79"},"firefox":{"version_added":"46"},"firefox_android":{"version_added":"46"},"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":[{"version_added":"7"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"7"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":{"prefix":"-webkit-","version_added":"1.5"},"webview_android":{"prefix":"-webkit-","version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"text-emphasis":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-emphasis","spec_url":"https://drafts.csswg.org/css-text-decor/#text-emphasis-property","support":{"chrome":{"prefix":"-webkit-","version_added":"25"},"chrome_android":{"prefix":"-webkit-","version_added":"25"},"edge":{"prefix":"-webkit-","version_added":"79"},"firefox":{"version_added":"46"},"firefox_android":{"version_added":"46"},"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":[{"version_added":"7"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"7"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":{"prefix":"-webkit-","version_added":"1.5"},"webview_android":{"prefix":"-webkit-","version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"text-indent":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-indent","spec_url":"https://drafts.csswg.org/css-text/#text-indent-property","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"3"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"each-line":{"__compat":{"description":"each-line","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"preview"},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"hanging":{"__compat":{"description":"hanging","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"preview"},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"text-justify":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-justify","spec_url":"https://drafts.csswg.org/css-text/#text-justify-property","support":{"chrome":{"version_added":"32","flags":[{"type":"preference","name":"Enable Experimental Web Platform Features","value_to_set":"true"}],"notes":"inter-word and distribute (deprecated) values are supported, but distribute behavior is buggy."},"chrome_android":{"version_added":"32","flags":[{"type":"preference","name":"Enable Experimental Web Platform Features","value_to_set":"true"}],"notes":"inter-word and distribute (deprecated) values are supported, but distribute behavior is buggy."},"edge":{"version_added":"12","notes":"Standard values inter-character and none are supported. The deprecated distribute value is also supported."},"firefox":{"version_added":"55"},"firefox_android":{"version_added":"55"},"ie":{"version_added":"11","notes":"Standard values inter-character and none are supported. The deprecated distribute value is also supported."},"opera":{"version_added":"19","flags":[{"type":"preference","name":"Enable Experimental Web Platform Features","value_to_set":"true"}],"notes":"inter-word and distribute (deprecated) values are supported, but distribute behavior is buggy."},"opera_android":{"version_added":"19","flags":[{"type":"preference","name":"Enable Experimental Web Platform Features","value_to_set":"true"}],"notes":"inter-word and distribute (deprecated) values are supported, but distribute behavior is buggy."},"safari":{"version_added":false,"notes":"See bug 9945."},"safari_ios":{"version_added":false,"notes":"See bug 9945."},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"text-orientation":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-orientation","spec_url":"https://drafts.csswg.org/css-writing-modes/#text-orientation","support":{"chrome":[{"version_added":"48"},{"prefix":"-webkit-","version_added":"11"}],"chrome_android":[{"version_added":"48"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"79"},{"prefix":"-webkit-","version_added":"79"}],"firefox":{"version_added":"41"},"firefox_android":{"version_added":"41"},"ie":{"version_added":false},"opera":[{"version_added":"35"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"35"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"14"},{"prefix":"-webkit-","version_added":"5.1"}],"safari_ios":[{"version_added":"14"},{"prefix":"-webkit-","version_added":"5"}],"samsunginternet_android":[{"version_added":"5.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"48"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"sideways":{"__compat":{"description":"sideways","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"44","notes":"sideways-right has become an alias of sideways."},"firefox_android":{"version_added":"44","notes":"sideways-right has become an alias of sideways."},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"text-overflow":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-overflow","spec_url":"https://drafts.csswg.org/css-overflow/#text-overflow","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"7","notes":"Until Firefox 10, handling of text-overflow on blocks with inline overflow on both horizontal sides was incorrect. Before Firefox 10, if only one value was specified (such as text-overflow: ellipsis;), text was ellipsed on both sides of the block, instead of only the end edge based on the block's text direction."},"firefox_android":{"version_added":"7","notes":"Until Firefox 10, handling of text-overflow on blocks with inline overflow on both horizontal sides was incorrect. Before Firefox 10, if only one value was specified (such as text-overflow: ellipsis;), text was ellipsed on both sides of the block, instead of only the end edge based on the block's text direction."},"ie":[{"version_added":"6"},{"prefix":"-ms-","version_added":"8"}],"opera":[{"version_added":"11"},{"prefix":"-o-","version_added":"9","version_removed":"15"}],"opera_android":[{"version_added":"11"},{"prefix":"-o-","version_added":"10.1","version_removed":"14"}],"safari":{"version_added":"1.3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"fade_function":{"__compat":{"description":"fade()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"fade_value":{"__compat":{"description":"fade","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"string":{"__compat":{"description":"<string>","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"9"},"firefox_android":{"version_added":"9"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"two_value_syntax":{"__compat":{"description":"Two-value syntax","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"9"},"firefox_android":{"version_added":"9"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"text-rendering":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-rendering","spec_url":"https://svgwg.org/svg2-draft/painting.html#TextRenderingProperty","support":{"chrome":{"version_added":"4","notes":["This property is only supported on Windows and Linux.","Initial versions had bugs on Windows and Linux that broke font substitition, small-caps, letter-spacing or caused text to overlap."]},"chrome_android":{"version_added":"18","notes":["This property is only supported on Windows and Linux.","Initial versions had bugs on Windows and Linux that broke font substitition, small-caps, letter-spacing or caused text to overlap."]},"edge":{"version_added":"79","notes":["This property is only supported on Windows and Linux.","Initial versions had bugs on Windows and Linux that broke font substitition, small-caps, letter-spacing or caused text to overlap."]},"firefox":{"version_added":"1","notes":["This property is only supported on Windows and Linux.","The optimizeSpeed option has no effect on Firefox 4 because the standard code for text rendering is already fast and there is not a faster code path at this time. See bug 595688 for details."]},"firefox_android":{"version_added":"46"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"4.2"},"samsunginternet_android":{"version_added":"1.0","notes":"This property is only supported on Windows and Linux. Samsung Internet is not on Windows or Linux."},"webview_android":{"version_added":"3","notes":"From version 3 to 4.3, there is a serious bug where text-rendering: optimizeLegibility causes custom web fonts to not render. This was fixed in version 4.4."}},"status":{"experimental":false,"standard_track":false,"deprecated":false}},"auto":{"__compat":{"description":"auto","support":{"chrome":{"version_added":"4","notes":"Chrome treats auto as optimizeSpeed."},"chrome_android":{"version_added":"18","notes":"Chrome treats auto as optimizeSpeed."},"edge":{"version_added":"79","notes":"Edge treats auto as optimizeSpeed."},"firefox":{"version_added":"1","notes":"If the font size is 20 pixels or higher, Firefox treats auto as optimizeLegibility. For smaller text, Firefox treats auto as optimizeSpeed. The 20-pixel threshold can be changed with the browser.display.auto_quality_min_font_size preference."},"firefox_android":{"version_added":"46","notes":"If the font size is 20 pixels or higher, Firefox treats auto as optimizeLegibility. For smaller text, Firefox treats auto as optimizeSpeed. The 20-pixel threshold can be changed with the browser.display.auto_quality_min_font_size preference."},"ie":{"version_added":false},"opera":{"version_added":"15","notes":"Opera treats auto as optimizeSpeed."},"opera_android":{"version_added":"14","notes":"Opera treats auto as optimizeSpeed."},"safari":{"version_added":"5","notes":"Safari treats auto as optimizeSpeed. See WebKit bug 41363."},"safari_ios":{"version_added":"4.2","notes":"Safari treats auto as optimizeSpeed. See WebKit bug 41363."},"samsunginternet_android":{"version_added":"1.0","notes":"Samsung Internet treats auto as optimizeSpeed."},"webview_android":{"version_added":"≤37","notes":"WebView treats auto as optimizeSpeed."}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"geometricPrecision":{"__compat":{"description":"geometricPrecision","support":{"chrome":{"version_added":"13","notes":"Supports true geometric precision without rounding up or down to the nearest supported font size in the operating system."},"chrome_android":{"version_added":"18","notes":"Supports true geometric precision without rounding up or down to the nearest supported font size in the operating system."},"edge":{"version_added":"79","notes":"Supports true geometric precision without rounding up or down to the nearest supported font size in the operating system."},"firefox":{"version_added":"1","notes":"Firefox treats geometricPrecision the same as optimizeLegibility."},"firefox_android":{"version_added":"46","notes":"Firefox treats geometricPrecision the same as optimizeLegibility."},"ie":{"version_added":false},"opera":{"version_added":"15","notes":"Supports true geometric precision without rounding up or down to the nearest supported font size in the operating system."},"opera_android":{"version_added":"14","notes":"Supports true geometric precision without rounding up or down to the nearest supported font size in the operating system."},"safari":{"version_added":"6"},"safari_ios":{"version_added":"6"},"samsunginternet_android":{"version_added":"1.0","notes":"Supports true geometric precision without rounding up or down to the nearest supported font size in the operating system."},"webview_android":{"version_added":"37","notes":"Supports true geometric precision without rounding up or down to the nearest supported font size in the operating system."}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}}},"text-shadow":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-shadow","spec_url":"https://drafts.csswg.org/css-text-decor/#text-shadow-property","support":{"chrome":{"version_added":"2"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3.5","notes":["Firefox versions before 57 have a bug whereby transitions will not work when transitioning from a text-shadow with a color specified to a text-shadow without a color specified (bug 726550).","From Firefox 4, the blur radius is capped at 300 for performance reasons.","Firefox theoretically supports infinite text-shadows (don't try it).","If the <color> value is unspecified, then Firefox uses the value of the element's color property."]},"firefox_android":{"version_added":"4","notes":["Firefox versions before 57 have a bug whereby transitions will not work when transitioning from a text-shadow with a color specified to a text-shadow without a color specified (bug 726550).","From Firefox 4, the blur radius is capped at 300 for performance reasons.","Firefox theoretically supports infinite text-shadows (don't try it).","If the <color> value is unspecified, then Firefox uses the value of the element's color property."]},"ie":{"version_added":"10"},"opera":{"version_added":"9.5","notes":["Opera supports a maximum of 6-9 text-shadows for performance reasons. The blur radius is limited to 100px.","Opera 9.5 to 10.1 adheres to the old, reverse painting order (in CSS2, the first specified shadow is on the bottom)."]},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.1","notes":["In Safari, any shadows that do not explicitly specify a color are transparent.","Safari 1.1 to 3.2 only supports one text-shadow (displays the first shadow of a comma-separated list and ignores the rest). Safari 4.0 (WebKit 528) and later support multiple text-shadows."]},"safari_ios":{"version_added":"1","notes":["In Safari, any shadows that do not explicitly specify a color are transparent.","Safari iOS 1 and 2 only support one text-shadow (displays the first shadow of a comma-separated list and ignores the rest). Safari iOS 3 (WebKit 528) and later support multiple text-shadows."]},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"text-size-adjust":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-size-adjust","spec_url":"https://drafts.csswg.org/css-size-adjust/#adjustment-control","support":{"chrome":[{"version_added":"54"},{"prefix":"-webkit-","version_added":false,"notes":"Instead of ignoring the -webkit-text-size-adjust property, a bug prevents desktop Chrome users from zooming in or out. The bug was fixed after Chrome 26."}],"chrome_android":{"version_added":"54"},"edge":[{"version_added":"79"},{"prefix":"-webkit-","version_added":"12","version_removed":"79"}],"firefox":{"version_added":false},"firefox_android":[{"prefix":"-webkit-","version_added":"49"},{"prefix":"-moz-","version_added":"14"},{"prefix":"-webkit-","version_added":"44","flags":[{"type":"preference","name":"layout.css.prefixes.webkit","value_to_set":"true"}]}],"ie":{"version_added":false},"opera":{"version_added":"41"},"opera_android":{"version_added":"41"},"safari":[{"version_added":false},{"prefix":"-webkit-","version_added":false,"notes":"Instead of ignoring the -webkit-text-size-adjust property, a bug prevents desktop Safari users from zooming in or out. The bug was fixed after Safari 5."}],"safari_ios":{"prefix":"-webkit-","version_added":"1"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"54"}},"status":{"experimental":true,"standard_track":true,"deprecated":false}},"percentages":{"__compat":{"description":"<percentage>","support":{"chrome":{"version_added":"54"},"chrome_android":{"version_added":"54"},"edge":{"version_added":"12"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"41"},"opera_android":{"version_added":"41"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"54"}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}}},"text-transform":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-transform","spec_url":"https://drafts.csswg.org/css-text/#text-transform","support":{"chrome":{"version_added":"1","notes":"The text-transform property does not work for ::first-line pseudo-elements (nor for the one-colon syntax). See Chromium bug 129669."},"chrome_android":{"version_added":"18","notes":"The text-transform property does not work for ::first-line pseudo-elements (nor for the one-colon syntax). See Chromium bug 129669."},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"7","notes":"Since Opera 15, the text-transform property does not work for ::first-line pseudo-elements (nor for the one-colon syntax). See Chromium bug 129669."},"opera_android":{"version_added":"11"},"safari":{"version_added":"1","notes":"The text-transform property does not work for ::first-line pseudo-elements (also not for the old one-colon syntax). See WebKit bug 3409."},"safari_ios":{"version_added":"1","notes":"The text-transform property does not work for ::first-line pseudo-elements (also not for the old one-colon syntax). See WebKit bug 3409."},"samsunginternet_android":{"version_added":"1.0","notes":"The text-transform property does not work for ::first-line pseudo-elements (nor for the one-colon syntax). See Chromium bug 129669."},"webview_android":{"version_added":"1","notes":"The text-transform property does not work for ::first-line pseudo-elements (nor for the one-colon syntax). See Chromium bug 129669."}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"capitalize":{"__compat":{"support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":"Before Firefox 14, some punctuation characters could interfere with correct capitalization. See bug 731536."},"firefox_android":{"version_added":"4","notes":"Before Firefox 14, some punctuation characters could interfere with correct capitalization. See bug 731536."},"ie":{"version_added":"4"},"opera":{"version_added":"7"},"opera_android":{"version_added":"11"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"dutch_ij_digraph":{"__compat":{"description":"Dutch IJ digraph","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"14"},"firefox_android":{"version_added":"14"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"full-size-kana":{"__compat":{"description":"full-size-kana","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"64"},"firefox_android":{"version_added":"64"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"full-width":{"__compat":{"description":"full-width","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"19"},"firefox_android":{"version_added":"19"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"greek_accented_characters":{"__compat":{"description":"Greek accented letters","support":{"chrome":{"version_added":"34"},"chrome_android":{"version_added":"34"},"edge":{"version_added":"79"},"firefox":{"version_added":"15"},"firefox_android":{"version_added":"15"},"ie":{"version_added":false},"opera":{"version_added":"21"},"opera_android":{"version_added":"21"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"2.0"},"webview_android":{"version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"lowercase_sigma":{"__compat":{"description":"Σσ or word-final ς","support":{"chrome":{"version_added":"30"},"chrome_android":{"version_added":"30"},"edge":{"version_added":"12"},"firefox":{"version_added":"14"},"firefox_android":{"version_added":"14"},"ie":{"version_added":"4"},"opera":{"version_added":"17"},"opera_android":{"version_added":"18"},"safari":{"version_added":"6"},"safari_ios":{"version_added":"6"},"samsunginternet_android":{"version_added":"2.0"},"webview_android":{"version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"turkic_is":{"__compat":{"description":"iİ and ıI","support":{"chrome":{"version_added":"31"},"chrome_android":{"version_added":"31"},"edge":{"version_added":"12"},"firefox":{"version_added":"14"},"firefox_android":{"version_added":"14"},"ie":{"version_added":"4"},"opera":{"version_added":"18"},"opera_android":{"version_added":"18"},"safari":{"version_added":"8"},"safari_ios":{"version_added":"8"},"samsunginternet_android":{"version_added":"2.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"uppercase_eszett":{"__compat":{"description":"ßSS","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"18"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":"7"},"opera_android":{"version_added":"11"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"text-underline-offset":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-underline-offset","spec_url":"https://drafts.csswg.org/css-text-decor-4/#underline-offset","support":{"chrome":{"version_added":"87"},"chrome_android":{"version_added":"87"},"edge":{"version_added":"87"},"firefox":[{"version_added":"70"},{"version_added":"69","flags":[{"type":"preference","name":"layout.css.text-underline-offset.enabled","value_to_set":"true"}]}],"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"73"},"opera_android":{"version_added":false},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":"12.2"},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"percentage":{"__compat":{"description":"percentage values","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"74"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"text-underline-position":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/text-underline-position","spec_url":"https://drafts.csswg.org/css-text-decor/#text-underline-position-property","support":{"chrome":{"version_added":"33"},"chrome_android":{"version_added":"33"},"edge":{"version_added":"12"},"firefox":{"version_added":"74"},"firefox_android":{"version_added":false},"ie":{"version_added":"6"},"opera":{"version_added":"20"},"opera_android":{"version_added":"20"},"safari":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"9"}],"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"2.0"},"webview_android":{"version_added":"4.4.3"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"above_below":{"__compat":{"description":"above and below","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"79"},"firefox":{"version_added":"74"},"firefox_android":{"version_added":false},"ie":{"version_added":"6"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":false,"deprecated":false}}},"auto-pos":{"__compat":{"description":"auto-pos","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":"6"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":false,"deprecated":false}}},"from-font":{"__compat":{"description":"from-font","support":{"chrome":{"version_added":"87"},"chrome_android":{"version_added":"87"},"edge":{"version_added":"87"},"firefox":{"version_added":"74"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"14.0"},"webview_android":{"version_added":"87"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"left_right":{"__compat":{"description":"left and right","support":{"chrome":{"version_added":"71"},"chrome_android":{"version_added":"71"},"edge":{"version_added":"79"},"firefox":{"version_added":"74"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"58"},"opera_android":{"version_added":"50"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"10.0"},"webview_android":{"version_added":"71"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"under":{"__compat":{"description":"under","support":{"chrome":{"version_added":"33"},"chrome_android":{"version_added":"33"},"edge":{"version_added":"79"},"firefox":{"version_added":"74"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"20"},"opera_android":{"version_added":"20"},"safari":{"version_added":"12.1"},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"2.0"},"webview_android":{"version_added":"4.4.3"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"top":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/top","spec_url":"https://drafts.csswg.org/css-position/#insets","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"5","notes":"In Internet Explorer versions before 7, when both top and bottom are specified, the element position is overconstrained and the top property has precedence; the computed value of bottom is set to -top, while its specified value is ignored."},"opera":{"version_added":"6"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"touch-action":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/touch-action","spec_url":["https://compat.spec.whatwg.org/#touch-action","https://w3c.github.io/pointerevents/#the-touch-action-css-property"],"support":{"chrome":{"version_added":"36"},"chrome_android":{"version_added":"36"},"edge":{"version_added":"12"},"firefox":{"version_added":"52","notes":"Not applicable to Firefox platforms that support neither pointer nor touch events."},"firefox_android":{"version_added":"52"},"ie":[{"version_added":"11"},{"version_added":"10","prefix":"-ms-"}],"opera":{"version_added":"23"},"opera_android":{"version_added":"24"},"safari":{"version_added":"13"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":"3.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"axis-pan":{"__compat":{"description":"pan-x and pan-y","support":{"chrome":{"version_added":"36"},"chrome_android":{"version_added":"36"},"edge":{"version_added":"12"},"firefox":{"version_added":"52","notes":"Not applicable to Firefox platforms that support neither pointer nor touch events."},"firefox_android":{"version_added":"52"},"ie":[{"version_added":"11"},{"version_added":"10","prefix":"-ms-"}],"opera":{"version_added":"23"},"opera_android":{"version_added":"24"},"safari":{"version_added":"13"},"safari_ios":{"version_added":"13"},"samsunginternet_android":{"version_added":"3.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"double-tap-zoom":{"__compat":{"support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":[{"version_added":"11"},{"version_added":"10","prefix":"-ms-"}],"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}},"manipulation":{"__compat":{"support":{"chrome":{"version_added":"36"},"chrome_android":{"version_added":"36"},"edge":{"version_added":"12"},"firefox":{"version_added":"52","notes":"Not applicable to Firefox platforms that support neither pointer nor touch events."},"firefox_android":{"version_added":"52"},"ie":[{"version_added":"11"},{"version_added":"10","prefix":"-ms-"}],"opera":{"version_added":"23"},"opera_android":{"version_added":"24"},"safari":{"version_added":"13"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":"3.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"none":{"__compat":{"support":{"chrome":{"version_added":"36"},"chrome_android":{"version_added":"36"},"edge":{"version_added":"12"},"firefox":{"version_added":"52","notes":"Not applicable to Firefox platforms that support neither pointer nor touch events."},"firefox_android":{"version_added":"52"},"ie":[{"version_added":"11"},{"version_added":"10","prefix":"-ms-"}],"opera":{"version_added":"23"},"opera_android":{"version_added":"24"},"safari":{"version_added":"13"},"safari_ios":{"version_added":"13"},"samsunginternet_android":{"version_added":"3.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"pinch-zoom":{"__compat":{"support":{"chrome":{"version_added":"56"},"chrome_android":{"version_added":"56"},"edge":{"version_added":"12"},"firefox":{"version_added":"85","notes":"Not applicable to Firefox platforms that support neither pointer nor touch events."},"firefox_android":{"version_added":"85","notes":"Not applicable to Firefox platforms that support neither pointer nor touch events."},"ie":[{"version_added":"11"},{"version_added":"10","prefix":"-ms-"}],"opera":{"version_added":"43"},"opera_android":{"version_added":"43"},"safari":{"version_added":"13"},"safari_ios":{"version_added":"13"},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"56"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"unidirectional-pan":{"__compat":{"description":"pan-up, pan-down, pan-left and pan-right","support":{"chrome":{"version_added":"55"},"chrome_android":{"version_added":"55"},"edge":{"version_added":"79"},"firefox":{"version_added":false,"notes":"See bug 1285685."},"firefox_android":{"version_added":false,"notes":"See bug 1285685."},"ie":{"version_added":false},"opera":{"version_added":"42"},"opera_android":{"version_added":"42"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"55"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"transform-box":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/transform-box","spec_url":"https://drafts.csswg.org/css-transforms/#transform-box","support":{"chrome":{"version_added":"64"},"chrome_android":{"version_added":"64"},"edge":{"version_added":"79"},"firefox":{"version_added":"55"},"firefox_android":{"version_added":"55"},"ie":{"version_added":false},"opera":{"version_added":"51"},"opera_android":{"version_added":"47"},"safari":{"version_added":"11"},"safari_ios":{"version_added":"11"},"samsunginternet_android":{"version_added":"9.0"},"webview_android":{"version_added":"64"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"transform-origin":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/transform-origin","spec_url":"https://drafts.csswg.org/css-transforms/#transform-origin-property","support":{"chrome":[{"version_added":"36"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"36"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"prefix":"-moz-","version_added":"3.5"},{"prefix":"-webkit-","version_added":"49"}],"firefox_android":[{"version_added":"16"},{"prefix":"-moz-","version_added":"4"},{"prefix":"-webkit-","version_added":"49"}],"ie":[{"version_added":"10"},{"prefix":"-ms-","version_added":"9"}],"opera":[{"version_added":"23"},{"prefix":"-webkit-","version_added":"15"},{"version_added":"12.1","version_removed":"15"},{"prefix":"-o-","version_added":"10.5","version_removed":"15"}],"opera_android":[{"version_added":"24"},{"prefix":"-webkit-","version_added":"15"},{"version_added":"12.1","version_removed":"15"},{"prefix":"-o-","version_added":"11","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"2"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"1"}],"samsunginternet_android":[{"version_added":"3.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"37"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"support_in_svg":{"__compat":{"description":"Support in SVG","support":{"chrome":{"version_added":"19"},"chrome_android":{"version_added":"25"},"edge":{"version_added":"17"},"firefox":{"version_added":"43","notes":"Keywords and percentages refer to the canvas instead of the object itself. See bug 1209061."},"firefox_android":{"version_added":"43","notes":"Keywords and percentages refer to the canvas instead of the object itself. See bug 1209061."},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"6","notes":"Only supported for transformations applied using the CSS transform property (e.g. .className { transform: rotate(45deg); transform-origin: center; }). It has no effect on transformations applied using the transform SVG attribute (e.g. <rect style="transform-origin: center;" transform="rotate(45)" />)."},"safari_ios":{"version_added":"6","notes":"Only supported for transformations applied using the CSS transform property (e.g. .className { transform: rotate(45deg); transform-origin: center; }). It has no effect on transformations applied using the transform SVG attribute (e.g. <rect style="transform-origin: center;" transform="rotate(45)" />)."},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"three_value_syntax":{"__compat":{"description":"Three-value syntax","matches":{"regex_value":"^([\\d\\w%-]+|calc\\(.+\\))\\s+([\\d\\w%-]+|calc\\(.+\\))\\s+([\\d\\w-]+|calc\\(.+\\))$"},"support":{"chrome":{"version_added":"12"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"10"},"firefox_android":{"version_added":"10"},"ie":{"version_added":"9"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5"},"safari_ios":{"version_added":"3.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"transform-style":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/transform-style","spec_url":"https://drafts.csswg.org/css-transforms-2/#transform-style-property","support":{"chrome":[{"version_added":"36"},{"prefix":"-webkit-","version_added":"12"}],"chrome_android":[{"version_added":"36"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"prefix":"-moz-","version_added":"10"},{"prefix":"-webkit-","version_added":"49"}],"firefox_android":[{"version_added":"16"},{"prefix":"-moz-","version_added":"10"},{"prefix":"-webkit-","version_added":"49"}],"ie":{"version_added":false},"opera":[{"version_added":"23"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"24"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"4"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"2"}],"samsunginternet_android":[{"version_added":"3.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"37"},{"prefix":"-webkit-","version_added":"3"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"transform":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/transform","spec_url":["https://drafts.csswg.org/css-transforms-2/#transform-functions","https://drafts.csswg.org/css-transforms/#transform-property"],"support":{"chrome":[{"version_added":"36"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"36"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"prefix":"-webkit-","version_added":"49"}],"firefox_android":[{"version_added":"16"},{"prefix":"-webkit-","version_added":"49"}],"ie":[{"version_added":"10","notes":"Internet Explorer does not support the global values initial and unset."},{"prefix":"-webkit-","version_added":"11"},{"prefix":"-ms-","version_added":"9","notes":"Internet Explorer 5.5 or later supports a proprietary Matrix Filter which can be used to achieve a similar effect."}],"opera":[{"version_added":"23"},{"prefix":"-webkit-","version_added":"15"},{"version_added":"12.1","version_removed":"15"},{"prefix":"-o-","version_added":"10.5","version_removed":"15"}],"opera_android":[{"version_added":"24"},{"prefix":"-webkit-","version_added":"14"},{"version_added":"12.1","version_removed":"14"},{"prefix":"-o-","version_added":"11","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3.1"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3.2"}],"samsunginternet_android":[{"version_added":"3.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"37"},{"prefix":"-webkit-","version_added":"2","notes":"Android 2.3 has a bug where input forms will \"jump\" when typing, if any container element has a -webkit-transform."}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"3d":{"__compat":{"description":"3D support","matches":{"keywords":["matrix3d","translate3d","translateZ","scale3d","scaleZ","rotate3d","rotateX","rotateY","rotateZ","perspective"]},"support":{"chrome":{"version_added":"12"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"16"},"firefox_android":{"version_added":"16"},"ie":{"version_added":"10","notes":"Internet Explorer 9.0 or earlier has no support for 3D transforms. Mixing 3D and 2D transform functions, such as -ms-transform:rotate(10deg) translateZ(0);, will prevent the entire property from being applied."},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"4"},"safari_ios":{"version_added":"3.2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"prefix":"-webkit-","version_added":"3"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"transition-delay":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/transition-delay","spec_url":"https://drafts.csswg.org/css-transitions/#transition-delay-property","support":{"chrome":[{"version_added":"26"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"26"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"prefix":"-moz-","version_added":"4"},{"version_added":"49","prefix":"-webkit-"}],"firefox_android":[{"version_added":"16"},{"prefix":"-moz-","version_added":"4"},{"version_added":"49","prefix":"-webkit-"}],"ie":[{"version_added":"10"},{"prefix":"-ms-","version_added":"10"}],"opera":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"15"},{"prefix":"-o-","version_added":"11.6","version_removed":"15"}],"opera_android":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"14"},{"prefix":"-o-","version_added":"12","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"4"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"2"}],"samsunginternet_android":[{"version_added":"1.5"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"≤37"},{"prefix":"-webkit-","version_added":"2"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"transition-duration":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/transition-duration","spec_url":"https://drafts.csswg.org/css-transitions/#transition-duration-property","support":{"chrome":[{"version_added":"26"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"26"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"prefix":"-moz-","version_added":"4"},{"version_added":"49","prefix":"-webkit-"}],"firefox_android":[{"version_added":"16"},{"prefix":"-moz-","version_added":"4"},{"version_added":"49","prefix":"-webkit-"}],"ie":[{"version_added":"10"},{"prefix":"-ms-","version_added":"10"}],"opera":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"15"},{"prefix":"-o-","version_added":"10","version_removed":"15"}],"opera_android":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"14"},{"prefix":"-o-","version_added":"10.1","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3.1"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"2"}],"samsunginternet_android":[{"version_added":"1.5"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"≤37"},{"prefix":"-webkit-","version_added":"2"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"transition-property":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/transition-property","spec_url":"https://drafts.csswg.org/css-transitions/#transition-property-property","support":{"chrome":[{"version_added":"26"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"26"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"prefix":"-moz-","version_added":"4"},{"version_added":"49","prefix":"-webkit-"}],"firefox_android":[{"version_added":"16"},{"prefix":"-moz-","version_added":"4"},{"version_added":"49","prefix":"-webkit-"}],"ie":[{"version_added":"10"},{"prefix":"-ms-","version_added":"10"}],"opera":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"15"},{"prefix":"-o-","version_added":"11.6","version_removed":"15"}],"opera_android":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"14"},{"prefix":"-o-","version_added":"12","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3.1"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"2"}],"samsunginternet_android":[{"version_added":"1.5"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"≤37"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"IDENT_value":{"__compat":{"description":"IDENT value","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"16"},"firefox_android":{"version_added":"16"},"ie":{"version_added":"10"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"4"},"safari_ios":{"version_added":"3"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"transition-timing-function":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/transition-timing-function","spec_url":"https://drafts.csswg.org/css-transitions/#transition-timing-function-property","support":{"chrome":[{"version_added":"26"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"26"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16"},{"prefix":"-moz-","version_added":"4"},{"version_added":"49","prefix":"-webkit-"}],"firefox_android":[{"version_added":"16"},{"prefix":"-moz-","version_added":"4"},{"version_added":"49","prefix":"-webkit-"}],"ie":[{"version_added":"10"},{"prefix":"-ms-","version_added":"10"}],"opera":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"15"},{"prefix":"-o-","version_added":"11.6","version_removed":"15"}],"opera_android":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"14"},{"prefix":"-o-","version_added":"12","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3.1"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"2"}],"samsunginternet_android":[{"version_added":"1.5"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"≤37"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"jump":{"__compat":{"description":"jump- keywords for steps()","support":{"chrome":{"version_added":"77"},"chrome_android":{"version_added":"77"},"edge":{"version_added":"79"},"firefox":{"version_added":"65"},"firefox_android":{"version_added":"65"},"ie":{"version_added":false},"opera":{"version_added":"64"},"opera_android":{"version_added":"55"},"safari":{"version_added":"14"},"safari_ios":{"version_added":"14"},"samsunginternet_android":{"version_added":"12.0"},"webview_android":{"version_added":"77"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"transition":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/transition","spec_url":"https://drafts.csswg.org/css-transitions/#transition-shorthand-property","support":{"chrome":[{"version_added":"26"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"26"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"16","notes":["Before Firefox 57, transitions do not work when transitioning from a text-shadow with a color specified to a text-shadow without a color specified (see bug 726550).","Before Firefox 57, cancelling a filling animation (for example, with animation-fill-mode: forwards set) can trigger a transition set on the same element, although only once (see bug 1192592 and these test cases for more information).","Before Firefox 57, the background-position property can't be transitioned between two values containing different numbers of <position> values, for example background-position: 10px 10px; and background-position: 20px 20px, 30px 30px; (see bug 1390446)."]},{"prefix":"-moz-","version_added":"4"},{"version_added":"49","prefix":"-webkit-"}],"firefox_android":[{"version_added":"16","notes":["Before Firefox 57, transitions do not work when transitioning from a text-shadow with a color specified to a text-shadow without a color specified (see bug 726550).","Before Firefox 57, cancelling a filling animation (for example, with animation-fill-mode: forwards set) can trigger a transition set on the same element, although only once (see bug 1192592 and these test cases for more information).","Before Firefox 57, the background-position property can't be transitioned between two values containing different numbers of <position> values, for example background-position: 10px 10px; and background-position: 20px 20px, 30px 30px; (see bug 1390446)."]},{"prefix":"-moz-","version_added":"4"},{"version_added":"49","prefix":"-webkit-"}],"ie":[{"version_added":"10"},{"prefix":"-ms-","version_added":"10"}],"opera":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"15"},{"prefix":"-o-","version_added":"10.1","version_removed":"15"}],"opera_android":[{"version_added":"12.1"},{"prefix":"-webkit-","version_added":"14"},{"prefix":"-o-","version_added":"10.1","version_removed":"14"}],"safari":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"3.1"}],"safari_ios":[{"version_added":"9"},{"prefix":"-webkit-","version_added":"2"}],"samsunginternet_android":[{"version_added":"1.5"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"≤37"},{"prefix":"-webkit-","version_added":"2"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"gradients":{"__compat":{"description":"Gradients","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":"10"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"translate":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/translate","spec_url":"https://drafts.csswg.org/css-transforms-2/#individual-transforms","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":[{"version_added":"72"},{"version_added":"60","version_removed":"72","flags":[{"type":"preference","name":"layout.css.individual-transform.enabled","value_to_set":"true"}]}],"firefox_android":[{"version_added":"79"},{"version_added":"60","version_removed":"79","flags":[{"type":"preference","name":"layout.css.individual-transform.enabled","value_to_set":"true"}]}],"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"14.1"},"safari_ios":{"version_added":"14.5"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"unicode-bidi":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/unicode-bidi","spec_url":"https://drafts.csswg.org/css-writing-modes/#unicode-bidi","support":{"chrome":{"version_added":"2"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"5.5"},"opera":{"version_added":"9.2"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1.3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"isolate":{"__compat":{"description":"isolate","support":{"chrome":[{"version_added":"48"},{"prefix":"-webkit-","version_added":"16","notes":["Avoiding using -webkit-isolate. It can lock up older versions of Safari (up to version 9) and Chrome (up to version 47).","Since Chrome 19, the syntax from a previous version of the specification, where the isolate keyword could be used together with bidi-override, is allowed."]}],"chrome_android":{"version_added":"48"},"edge":[{"version_added":"79"},{"prefix":"-webkit-","version_added":"79"}],"firefox":[{"version_added":"50"},{"prefix":"-moz-","version_added":"10","version_removed":"54","notes":"From Firefox 10 to Firefox 16 (inclusive), the isolate keyword could be used together with bidi-override, which was the syntax from a previous version of the specification. From Firefox 17, only one value is allowed. Use isolate-override instead the previous isolate bidi-override."}],"firefox_android":[{"version_added":"50"},{"prefix":"-moz-","version_added":"10","version_removed":"54","notes":"From Firefox 10 to Firefox 16 (inclusive), the isolate keyword could be used together with bidi-override, which was the syntax from a previous version of the specification. From Firefox 17, only one value is allowed. Use isolate-override instead the previous isolate bidi-override."}],"ie":{"version_added":false},"opera":[{"version_added":"35"},{"prefix":"-webkit-","version_added":"15","notes":["Avoiding using -webkit-isolate. It can lock up older versions of Opera (up to version 34).","The syntax from a previous version of the specification, where the isolate keyword could be used together with bidi-override, is allowed."]}],"opera_android":{"version_added":"35"},"safari":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"6","notes":"Avoiding using -webkit-isolate. It can lock up older versions of Safari (up to version 9) and Chrome (up to version 47)."}],"safari_ios":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"6","notes":"Avoiding using -webkit-isolate. It can lock up older versions of Safari (up to version 9) and Chrome (up to version 47)."}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"48"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"isolate-override":{"__compat":{"description":"isolate-override","support":{"chrome":{"version_added":"48"},"chrome_android":{"version_added":"48"},"edge":{"version_added":"79"},"firefox":[{"version_added":"50"},{"prefix":"-moz-","version_added":"17","version_removed":"54"}],"firefox_android":[{"version_added":"50"},{"prefix":"-moz-","version_added":"17","version_removed":"54"}],"ie":{"version_added":false},"opera":{"version_added":"35"},"opera_android":{"version_added":"35"},"safari":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"7"}],"safari_ios":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"7"}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"48"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"plaintext":{"__compat":{"description":"plaintext","support":{"chrome":{"version_added":"48"},"chrome_android":{"version_added":"48"},"edge":{"version_added":"79"},"firefox":[{"version_added":"50"},{"prefix":"-moz-","version_added":"10","version_removed":"54","notes":["Before Firefox 50, the plaintext value was ignored for vertical writing modes (bug 1302734).","Before Firefox 15, plaintext didn't do anything to an inline element. The specification changed and the implementation was changed in Firefox 15."]}],"firefox_android":[{"version_added":"50"},{"prefix":"-moz-","version_added":"10","version_removed":"54","notes":["Before Firefox 50, the plaintext value was ignored for vertical writing modes (bug 1302734).","Before Firefox 15, plaintext didn't do anything to an inline element. The specification changed and the implementation was changed in Firefox 15."]}],"ie":{"version_added":false},"opera":{"version_added":"35"},"opera_android":{"version_added":"35"},"safari":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"6"}],"safari_ios":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"6"}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"48"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"user-modify":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/user-modify","support":{"chrome":{"prefix":"-webkit-","version_added":"1"},"chrome_android":{"prefix":"-webkit-","version_added":"18"},"edge":{"prefix":"-webkit-","version_added":"12"},"firefox":{"prefix":"-moz-","version_added":"1","partial_implementation":true,"notes":["While the CSS property is parsed and accepted, it does not have any effect.","Scheduled for removal (see bug 1388910)."]},"firefox_android":{"prefix":"-moz-","version_added":"4","partial_implementation":true,"notes":["While the CSS property is parsed and accepted, it does not have any effect.","Scheduled for removal (see bug 1388910)."]},"ie":{"version_added":false},"opera":{"prefix":"-webkit-","version_added":"15"},"opera_android":{"prefix":"-webkit-","version_added":"14"},"safari":[{"version_added":"3","prefix":"-webkit-"},{"version_added":"2","version_removed":"3","prefix":"-khtml-"}],"safari_ios":{"version_added":"5","prefix":"-webkit-"},"samsunginternet_android":{"version_added":"1.0","prefix":"-webkit-"},"webview_android":{"prefix":"-webkit-","version_added":"37"}},"status":{"experimental":false,"standard_track":false,"deprecated":true}},"read-write-plaintext-only":{"__compat":{"support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":false,"deprecated":true}}}},"user-select":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/user-select","spec_url":"https://drafts.csswg.org/css-ui/#content-selection","support":{"chrome":[{"version_added":"54"},{"prefix":"-webkit-","version_added":"1"}],"chrome_android":[{"version_added":"54"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"79"},{"prefix":"-ms-","version_added":"12","version_removed":"79"},{"prefix":"-webkit-","version_added":"12"}],"firefox":[{"version_added":"69"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-moz-","version_added":"1"}],"firefox_android":[{"version_added":"79"},{"prefix":"-webkit-","version_added":"49"},{"prefix":"-moz-","version_added":"4"}],"ie":{"prefix":"-ms-","version_added":"10"},"opera":[{"version_added":"41"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"41"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"3","prefix":"-webkit-"},{"version_added":"2","version_removed":"3","prefix":"-khtml-"}],"safari_ios":{"prefix":"-webkit-","version_added":"3"},"samsunginternet_android":[{"version_added":"6.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"54"},{"prefix":"-webkit-","version_added":"≤37"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"all":{"__compat":{"support":{"chrome":{"version_added":"53"},"chrome_android":{"version_added":"53"},"edge":{"version_added":"79"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":false},"opera":{"version_added":"40"},"opera_android":{"version_added":"41"},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":"6.0"},"webview_android":{"version_added":"53"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"auto":{"__compat":{"support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"10"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"2"},"safari_ios":{"version_added":"3"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"contain":{"__compat":{"support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"alternative_name":"element","version_added":"12","version_removed":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"alternative_name":"element","version_added":"10"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"none":{"__compat":{"support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":[{"version_added":"21"},{"version_added":"1","version_removed":"65","prefix":"-moz-"}],"firefox_android":[{"version_added":"21"},{"version_added":"4","version_removed":"65","prefix":"-moz-"}],"ie":{"version_added":"10"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"2"},"safari_ios":{"version_added":"3"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"text":{"__compat":{"support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"10"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"2","partial_implementation":true,"notes":"Allows typing in the <html> container."},"safari_ios":{"version_added":"3","partial_implementation":true,"notes":"Allows typing in the <html> container."},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"vertical-align":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/vertical-align","spec_url":"https://drafts.csswg.org/css2/#propdef-vertical-align","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"4"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"visibility":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/visibility","spec_url":"https://drafts.csswg.org/css2/#visibility","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4","notes":["Internet Explorer doesn't support visibility: initial.","Up to Internet Explorer 7, descendants of hidden elements will still be invisible even if they have visibility set to visible."]},"opera":{"version_added":"4"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"collapse":{"__compat":{"description":"collapse","support":{"chrome":{"version_added":"1","notes":["Chrome treats visibility: collapse like hidden, leaving a white gap.","Chrome supports the collapse value only on <tr>, <thead>, <tbody>, and <tfoot>, but not on <col> and <colgroup> elements."]},"chrome_android":{"version_added":"18","notes":["Chrome treats visibility: collapse like hidden, leaving a white gap.","Chrome supports the collapse value only on <tr>, <thead>, <tbody>, and <tfoot>, but not on <col> and <colgroup> elements."]},"edge":{"version_added":"12"},"firefox":{"version_added":"1","notes":["Firefox doesn't hide borders when hiding <col> and <colgroup> elements if border-collapse: collapse is set.","Prior to Firefox 88, collapse is not supported on ruby annotations."]},"firefox_android":{"version_added":"4","notes":["Firefox doesn't hide borders when hiding <col> and <colgroup> elements if border-collapse: collapse is set.","Prior to Firefox 88, collapse is not supported on ruby annotations."]},"ie":{"version_added":"10"},"opera":{"version_added":"4","notes":["Opera treats visibility: collapse like hidden, leaving a white gap.","Opera supports the collapse value only on <tr>, <thead>, <tbody>, and <tfoot>, but not on <col> and <colgroup> elements."]},"opera_android":{"version_added":"10.1","notes":["Opera treats visibility: collapse like hidden, leaving a white gap.","Opera supports the collapse value only on <tr>, <thead>, <tbody>, and <tfoot>, but not on <col> and <colgroup> elements."]},"safari":{"version_added":"1.3","notes":["Safari treats visibility: collapse like hidden, leaving a white gap.","Safari supports the collapse value only on <tr>, <thead>, <tbody>, and <tfoot>, but not on <col> and <colgroup> elements."]},"safari_ios":{"version_added":"1","notes":["Safari treats visibility: collapse like hidden, leaving a white gap.","Safari supports the collapse value only on <tr>, <thead>, <tbody>, and <tfoot>, but not on <col> and <colgroup> elements."]},"samsunginternet_android":{"version_added":"1.0","notes":["Samsung Internet treats visibility: collapse like hidden, leaving a white gap.","Samsung Internet supports the collapse value only on <tr>, <thead>, <tbody>, and <tfoot>, but not on <col> and <colgroup> elements."]},"webview_android":{"version_added":"≤37","notes":["WebView treats visibility: collapse like hidden, leaving a white gap.","WebView supports the collapse value only on <tr>, <thead>, <tbody>, and <tfoot>, but not on <col> and <colgroup> elements."]}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"white-space":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/white-space","spec_url":"https://drafts.csswg.org/css-text/#white-space-property","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"5.5"},"opera":{"version_added":"4"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"break-spaces":{"__compat":{"description":"break-spaces","support":{"chrome":{"version_added":"76"},"chrome_android":{"version_added":"76"},"edge":{"version_added":"79"},"firefox":{"version_added":"69"},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"62"},"opera_android":{"version_added":"54"},"safari":{"version_added":"13.1"},"safari_ios":{"version_added":"13.4"},"samsunginternet_android":{"version_added":"12.0"},"webview_android":{"version_added":"76"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"nowrap":{"__compat":{"description":"nowrap","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"5.5"},"opera":{"version_added":"4"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"pre":{"__compat":{"description":"pre","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"6"},"opera":{"version_added":"4"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"pre-line":{"__compat":{"description":"pre-line","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3.5"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"8"},"opera":{"version_added":"9.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"pre-wrap":{"__compat":{"description":"pre-wrap","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":[{"version_added":"3"},{"prefix":"-moz-","version_added":"1","version_removed":"3.6"}],"firefox_android":{"version_added":"4"},"ie":{"version_added":"8","notes":"From Internet Explorer 5.5 to 7, word-wrap: break-word; can be used for line breaks in pre elements."},"opera":{"version_added":"8"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"svg_support":{"__compat":{"description":"Support in SVG","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":"12","version_removed":"79"},"firefox":{"version_added":"36"},"firefox_android":{"version_added":"36"},"ie":{"version_added":"10"},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"textarea_support":{"__compat":{"description":"Support on <textarea>","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"36"},"firefox_android":{"version_added":"36"},"ie":{"version_added":"5.5"},"opera":{"version_added":"4"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"widows":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/widows","spec_url":["https://drafts.csswg.org/css-break/#widows-orphans","https://drafts.csswg.org/css-multicol/#filling-columns"],"support":{"chrome":{"version_added":"25"},"chrome_android":{"version_added":"25"},"edge":{"version_added":"12"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":"8"},"opera":{"version_added":"9.2"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1.3"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"width":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/width","spec_url":"https://drafts.csswg.org/css-sizing-4/#width-height-keywords","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"10.1"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"animatable":{"__compat":{"description":"Animatable","support":{"chrome":{"version_added":"26"},"chrome_android":{"version_added":"26"},"edge":{"version_added":"12"},"firefox":{"version_added":"16"},"firefox_android":{"version_added":"16"},"ie":{"version_added":"11"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"6.1"},"safari_ios":{"version_added":"6.1"},"samsunginternet_android":{"version_added":"1.5"},"webview_android":{"version_added":"4.4"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"fill":{"__compat":{"description":"fill","support":{"chrome":{"version_added":"46"},"chrome_android":{"version_added":"46"},"edge":{"version_added":"79"},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"33"},"opera_android":{"version_added":"33"},"safari":{"version_added":"12"},"safari_ios":{"version_added":"12"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"46"}},"status":{"experimental":true,"standard_track":false,"deprecated":false}}},"fit-content":{"__compat":{"spec_url":"https://drafts.csswg.org/css-sizing-4/#valdef-width-fit-content","description":"fit-content","support":{"chrome":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"22"},{"alternative_name":"intrinsic","version_added":"1","version_removed":"48"}],"chrome_android":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"25"},{"alternative_name":"intrinsic","version_added":"18","version_removed":"48"}],"edge":[{"version_added":"79"},{"prefix":"-webkit-","version_added":"79"}],"firefox":{"prefix":"-moz-","version_added":"3"},"firefox_android":{"prefix":"-moz-","version_added":"4"},"ie":{"version_added":false},"opera":[{"version_added":"33"},{"prefix":"-webkit-","version_added":"15"},{"alternative_name":"intrinsic","version_added":"15","version_removed":"35"}],"opera_android":[{"version_added":"33"},{"prefix":"-webkit-","version_added":"14"},{"alternative_name":"intrinsic","version_added":"14","version_removed":"35"}],"safari":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"7"},{"alternative_name":"intrinsic","version_added":"2"}],"safari_ios":[{"version_added":"11"},{"prefix":"-webkit-","version_added":"7"},{"alternative_name":"intrinsic","version_added":"1"}],"samsunginternet_android":[{"version_added":"5.0"},{"prefix":"-webkit-","version_added":"1.5"},{"alternative_name":"intrinsic","version_added":"1.0","version_removed":"5.0"}],"webview_android":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"≤37"},{"alternative_name":"intrinsic","version_added":"1","version_removed":"48"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"fit-content_function":{"__compat":{"description":"fit-content()","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"firefox_android":{"version_added":"91","flags":[{"type":"preference","name":"layout.css.fit-content-function.enabled"}]},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}},"max-content":{"__compat":{"spec_url":"https://drafts.csswg.org/css-sizing-3/#valdef-width-max-content","description":"max-content","support":{"chrome":[{"version_added":"46"},{"prefix":"-webkit-","version_added":"22"}],"chrome_android":{"version_added":"46"},"edge":[{"version_added":"79"},{"prefix":"-webkit-","version_added":"79"}],"firefox":[{"version_added":"66"},{"version_added":"3","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"4","prefix":"-moz-"}],"ie":{"version_added":false},"opera":{"version_added":"44"},"opera_android":{"version_added":"43"},"safari":[{"version_added":"11"},{"alternative_name":"intrinsic","version_added":"2"}],"safari_ios":[{"version_added":"11"},{"alternative_name":"intrinsic","version_added":"1"}],"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"46"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"min-content":{"__compat":{"spec_url":"https://drafts.csswg.org/css-sizing-3/#valdef-width-min-content","description":"min-content","support":{"chrome":[{"version_added":"46"},{"alternative_name":"min-intrinsic","version_added":"1","version_removed":"48"}],"chrome_android":[{"version_added":"46"},{"alternative_name":"min-intrinsic","version_added":"18","version_removed":"48"}],"edge":{"version_added":"79"},"firefox":[{"version_added":"66"},{"version_added":"3","prefix":"-moz-"}],"firefox_android":[{"version_added":"66"},{"version_added":"4","prefix":"-moz-"}],"ie":{"version_added":false},"opera":[{"version_added":"33"},{"alternative_name":"min-intrinsic","version_added":"15","version_removed":"35"}],"opera_android":[{"version_added":"33"},{"alternative_name":"min-intrinsic","version_added":"14","version_removed":"35"}],"safari":[{"version_added":"11"},{"alternative_name":"min-intrinsic","version_added":"2"}],"safari_ios":[{"version_added":"11"},{"alternative_name":"min-intrinsic","version_added":"1"}],"samsunginternet_android":[{"version_added":"5.0"},{"alternative_name":"min-intrinsic","version_added":"1.0","version_removed":"5.0"}],"webview_android":[{"version_added":"46"},{"alternative_name":"min-intrinsic","version_added":"1","version_removed":"48"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"stretch":{"__compat":{"description":"stretch","support":{"chrome":{"alternative_name":"-webkit-fill-available","version_added":"22"},"chrome_android":{"alternative_name":"-webkit-fill-available","version_added":"25"},"edge":{"alternative_name":"-webkit-fill-available","version_added":"79"},"firefox":{"alternative_name":"-moz-available","version_added":"3"},"firefox_android":{"alternative_name":"-moz-available","version_added":"4"},"ie":{"version_added":false},"opera":{"alternative_name":"-webkit-fill-available","version_added":"15"},"opera_android":{"alternative_name":"-webkit-fill-available","version_added":"14"},"safari":{"alternative_name":"-webkit-fill-available","version_added":"6.1"},"safari_ios":{"alternative_name":"-webkit-fill-available","version_added":"6.1"},"samsunginternet_android":{"version_added":"5.0","alternative_name":"-webkit-fill-available"},"webview_android":{"alternative_name":"-webkit-fill-available","version_added":"4.4"}},"status":{"experimental":true,"standard_track":true,"deprecated":false}}}},"will-change":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/will-change","spec_url":"https://drafts.csswg.org/css-will-change/#will-change","support":{"chrome":{"version_added":"36"},"chrome_android":{"version_added":"36"},"edge":{"version_added":"79"},"firefox":{"version_added":"36"},"firefox_android":{"version_added":"36"},"ie":{"version_added":false},"opera":{"version_added":"24"},"opera_android":{"version_added":"24"},"safari":{"version_added":"9.1"},"safari_ios":{"version_added":"9.3"},"samsunginternet_android":{"version_added":"3.0"},"webview_android":{"version_added":"37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"word-break":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/word-break","spec_url":"https://drafts.csswg.org/css-text/#word-break-property","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"15"},"firefox_android":{"version_added":"15"},"ie":[{"version_added":"5.5","notes":"No version of Internet Explorer supports the initial value."},{"prefix":"-ms-","version_added":"8","notes":"Don't use -ms-word-break, which is a synonym for word-break."}],"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"break-word":{"__compat":{"description":"break-word","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"79"},"firefox":{"version_added":"67"},"firefox_android":{"version_added":"67"},"ie":{"version_added":false},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3"},"safari_ios":{"version_added":"2"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"keep-all":{"__compat":{"description":"keep-all","support":{"chrome":{"version_added":"44"},"chrome_android":{"version_added":"44"},"edge":{"version_added":"12"},"firefox":{"version_added":"15"},"firefox_android":{"version_added":"15"},"ie":{"version_added":"5.5"},"opera":{"version_added":"31"},"opera_android":{"version_added":"32"},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":"4.0"},"webview_android":{"version_added":"44"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"word-spacing":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/word-spacing","spec_url":"https://drafts.csswg.org/css-text/#word-spacing-property","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"6"},"opera":{"version_added":"3.5"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"1"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"percentages":{"__compat":{"description":"<percentage> values","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"45"},"firefox_android":{"version_added":"45"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":"6.1"},"safari_ios":{"version_added":"6.1"},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"svg":{"__compat":{"description":"SVG support","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"72"},"firefox_android":{"version_added":false},"ie":{"version_added":"9"},"opera":{"version_added":"7"},"opera_android":{"version_added":"14"},"safari":{"version_added":"5.1"},"safari_ios":{"version_added":"5"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"writing-mode":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/writing-mode","spec_url":"https://drafts.csswg.org/css-writing-modes/#block-flow","support":{"chrome":[{"version_added":"48"},{"prefix":"-webkit-","version_added":"8"}],"chrome_android":[{"version_added":"48"},{"prefix":"-webkit-","version_added":"18"}],"edge":[{"version_added":"12"},{"prefix":"-webkit-","version_added":"12"}],"firefox":{"version_added":"41","notes":"Firefox 42 added support for bidirectional and RTL scripts in vertical modes."},"firefox_android":{"version_added":"41","notes":"Firefox 42 added support for bidirectional and RTL scripts in vertical modes."},"ie":[{"version_added":"9","notes":"Internet Explorer's implementation differs from the specification."},{"prefix":"-ms-","version_added":"9","notes":"Internet Explorer's implementation differs from the specification."}],"opera":[{"version_added":"35"},{"prefix":"-webkit-","version_added":"15"}],"opera_android":[{"version_added":"35"},{"prefix":"-webkit-","version_added":"14"}],"safari":[{"version_added":"10.1"},{"prefix":"-webkit-","version_added":"5.1"}],"safari_ios":[{"version_added":"10.3"},{"prefix":"-webkit-","version_added":"5"}],"samsunginternet_android":[{"version_added":"5.0"},{"prefix":"-webkit-","version_added":"1.0"}],"webview_android":[{"version_added":"48"},{"prefix":"-webkit-","version_added":"3"}]},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"horizontal_vertical_values":{"__compat":{"description":"horizontal-tb, vertical-lr, and vertical-rl","support":{"chrome":{"version_added":"48"},"chrome_android":{"version_added":"48"},"edge":{"version_added":"79"},"firefox":{"version_added":"43"},"firefox_android":{"version_added":"43"},"ie":{"version_added":false},"opera":{"version_added":"35"},"opera_android":{"version_added":"35"},"safari":{"version_added":"9"},"safari_ios":{"version_added":"9"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"48"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"sideways_values":{"__compat":{"description":"sideways-lr and sideways-rl","support":{"chrome":{"version_added":false},"chrome_android":{"version_added":false},"edge":{"version_added":false},"firefox":{"version_added":"43"},"firefox_android":{"version_added":"43"},"ie":{"version_added":false},"opera":{"version_added":false},"opera_android":{"version_added":false},"safari":{"version_added":false},"safari_ios":{"version_added":false},"samsunginternet_android":{"version_added":false},"webview_android":{"version_added":false}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}},"svg_values":{"__compat":{"description":"lr, lr-tb, rl, rl-tb, tb, and tb-rl","support":{"chrome":{"version_added":"48"},"chrome_android":{"version_added":"48"},"edge":{"version_added":"12"},"firefox":{"version_added":"43"},"firefox_android":{"version_added":"43"},"ie":{"version_added":"9"},"opera":{"version_added":"35"},"opera_android":{"version_added":"35"},"safari":{"version_added":"10.1"},"safari_ios":{"version_added":"10.3"},"samsunginternet_android":{"version_added":"5.0"},"webview_android":{"version_added":"48"}},"status":{"experimental":false,"standard_track":true,"deprecated":true}}}},"z-index":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/z-index","spec_url":"https://drafts.csswg.org/css2/#z-index","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"1"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"4"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}},"negative_values":{"__compat":{"description":"Negative values","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":"3"},"firefox_android":{"version_added":"4"},"ie":{"version_added":"4"},"opera":{"version_added":"4"},"opera_android":{"version_added":"14"},"safari":{"version_added":"1"},"safari_ios":{"version_added":"1"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":true,"deprecated":false}}}},"zoom":{"__compat":{"mdn_url":"https://developer.mozilla.org/docs/Web/CSS/zoom","support":{"chrome":{"version_added":"1"},"chrome_android":{"version_added":"18"},"edge":{"version_added":"12"},"firefox":{"version_added":false,"notes":"See bug 390936."},"firefox_android":{"version_added":false,"notes":"See bug 390936."},"ie":{"version_added":"5.5"},"opera":{"version_added":"15"},"opera_android":{"version_added":"14"},"safari":{"version_added":"3.1"},"safari_ios":{"version_added":"3"},"samsunginternet_android":{"version_added":"1.0"},"webview_android":{"version_added":"≤37"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}},"reset":{"__compat":{"description":"The reset value","mdn_url":"https://developer.mozilla.org/docs/Web/CSS/zoom#Values","support":{"chrome":{"version_added":"1","version_removed":"59"},"chrome_android":{"version_added":"18","version_removed":"59"},"edge":{"version_added":false},"firefox":{"version_added":false},"firefox_android":{"version_added":false},"ie":{"version_added":false},"opera":{"version_added":"15","version_removed":"46"},"opera_android":{"version_added":"14","version_removed":"43"},"safari":{"version_added":"3.1"},"safari_ios":{"version_added":"3"},"samsunginternet_android":{"version_added":"1.0","version_removed":"7.0"},"webview_android":{"version_added":"≤37","version_removed":"59"}},"status":{"experimental":false,"standard_track":false,"deprecated":false}}}}} \ No newline at end of file diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/shared/specs/webconsole.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/shared/specs/webconsole.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/shared/specs/webconsole.js 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/shared/specs/webconsole.js 2021-10-20 19:28:16.000000000 +0000 @@ -12,14 +12,9 @@ Arg, } = require("devtools/shared/protocol"); -types.addDictType("console.traits", { - evaluateJSAsync: "boolean", -}); - types.addDictType("console.startlisteners", { startedListeners: "array:string", nativeConsoleAPI: "nullable:boolean", - traits: "console.traits", }); types.addDictType("console.stoplisteners", { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/shared/webconsole/analyze-input-string.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/shared/webconsole/analyze-input-string.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/shared/webconsole/analyze-input-string.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/shared/webconsole/analyze-input-string.js 2021-10-20 19:28:16.000000000 +0000 @@ -0,0 +1,406 @@ +/* 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/. */ + +"use strict"; + +const STATE_NORMAL = Symbol("STATE_NORMAL"); +const STATE_QUOTE = Symbol("STATE_QUOTE"); +const STATE_DQUOTE = Symbol("STATE_DQUOTE"); +const STATE_TEMPLATE_LITERAL = Symbol("STATE_TEMPLATE_LITERAL"); +const STATE_ESCAPE_QUOTE = Symbol("STATE_ESCAPE_QUOTE"); +const STATE_ESCAPE_DQUOTE = Symbol("STATE_ESCAPE_DQUOTE"); +const STATE_ESCAPE_TEMPLATE_LITERAL = Symbol("STATE_ESCAPE_TEMPLATE_LITERAL"); +const STATE_SLASH = Symbol("STATE_SLASH"); +const STATE_INLINE_COMMENT = Symbol("STATE_INLINE_COMMENT"); +const STATE_MULTILINE_COMMENT = Symbol("STATE_MULTILINE_COMMENT"); +const STATE_MULTILINE_COMMENT_CLOSE = Symbol("STATE_MULTILINE_COMMENT_CLOSE"); +const STATE_QUESTION_MARK = Symbol("STATE_QUESTION_MARK"); + +const OPEN_BODY = "{[(".split(""); +const CLOSE_BODY = "}])".split(""); +const OPEN_CLOSE_BODY = { + "{": "}", + "[": "]", + "(": ")", +}; + +const NO_AUTOCOMPLETE_PREFIXES = ["var", "const", "let", "function", "class"]; +const OPERATOR_CHARS_SET = new Set(";,:=<>+-*%|&^~!".split("")); + +/** + * Analyses a given string to find the last statement that is interesting for + * later completion. + * + * @param string str + * A string to analyse. + * + * @returns object + * If there was an error in the string detected, then a object like + * + * { err: "ErrorMesssage" } + * + * is returned, otherwise a object like + * + * { + * state: STATE_NORMAL|STATE_QUOTE|STATE_DQUOTE, + * lastStatement: the last statement in the string, + * isElementAccess: boolean that indicates if the lastStatement has an open + * element access (e.g. `x["match`). + * isPropertyAccess: boolean indicating if we are accessing property + * (e.g `true` in `var a = {b: 1};a.b`) + * matchProp: The part of the expression that should match the properties + * on the mainExpression (e.g. `que` when expression is `document.body.que`) + * mainExpression: The part of the expression before any property access, + * (e.g. `a.b` if expression is `a.b.`) + * expressionBeforePropertyAccess: The part of the expression before property access + * (e.g `var a = {b: 1};a` if expression is `var a = {b: 1};a.b`) + * } + */ +// eslint-disable-next-line complexity +exports.analyzeInputString = function(str, timeout = 2500) { + // work variables. + const bodyStack = []; + let state = STATE_NORMAL; + let previousNonWhitespaceChar; + let lastStatement = ""; + let currentIndex = -1; + let dotIndex; + let pendingWhitespaceChars = ""; + const startingTime = Date.now(); + + // Use a string iterator in order to handle character with a length >= 2 (e.g. 😎). + for (const c of str) { + // We are possibly dealing with a very large string that would take a long time to + // analyze (and freeze the process). If the function has been running for more than + // a given time, we stop the analysis (this isn't too bad because the only + // consequence is that we won't provide autocompletion items). + if (Date.now() - startingTime > timeout) { + return { + err: "timeout", + }; + } + + currentIndex += 1; + let resetLastStatement = false; + const isWhitespaceChar = c.trim() === ""; + switch (state) { + // Normal JS state. + case STATE_NORMAL: + if (lastStatement.endsWith("?.") && /\d/.test(c)) { + // If the current char is a number, the engine will consider we're not + // performing an optional chaining, but a ternary (e.g. x ?.4 : 2). + lastStatement = ""; + } + + // Storing the index of dot of the input string + if (c === ".") { + dotIndex = currentIndex; + } + + // If the last characters were spaces, and the current one is not. + if (pendingWhitespaceChars && !isWhitespaceChar) { + // If we have a legitimate property/element access, or potential optional + // chaining call, we append the spaces. + if (c === "[" || c === "." || c === "?") { + lastStatement = lastStatement + pendingWhitespaceChars; + } else { + // if not, we can be sure the statement was over, and we can start a new one. + lastStatement = ""; + } + pendingWhitespaceChars = ""; + } + + if (c == '"') { + state = STATE_DQUOTE; + } else if (c == "'") { + state = STATE_QUOTE; + } else if (c == "`") { + state = STATE_TEMPLATE_LITERAL; + } else if (c == "/") { + state = STATE_SLASH; + } else if (c == "?") { + state = STATE_QUESTION_MARK; + } else if (OPERATOR_CHARS_SET.has(c)) { + // If the character is an operator, we can update the current statement. + resetLastStatement = true; + } else if (isWhitespaceChar) { + // If the previous char isn't a dot or opening bracket, and the current computed + // statement is not a variable/function/class declaration, we track the number + // of consecutive spaces, so we can re-use them at some point (or drop them). + if ( + previousNonWhitespaceChar !== "." && + previousNonWhitespaceChar !== "[" && + !NO_AUTOCOMPLETE_PREFIXES.includes(lastStatement) + ) { + pendingWhitespaceChars += c; + continue; + } + } else if (OPEN_BODY.includes(c)) { + // When opening a bracket or a parens, we store the current statement, in order + // to be able to retrieve it later. + bodyStack.push({ + token: c, + lastStatement, + index: currentIndex, + }); + // And we compute a new statement. + resetLastStatement = true; + } else if (CLOSE_BODY.includes(c)) { + const last = bodyStack.pop(); + if (!last || OPEN_CLOSE_BODY[last.token] != c) { + return { + err: "syntax error", + }; + } + if (c == "}") { + resetLastStatement = true; + } else { + lastStatement = last.lastStatement; + } + } + break; + + // Escaped quote + case STATE_ESCAPE_QUOTE: + state = STATE_QUOTE; + break; + case STATE_ESCAPE_DQUOTE: + state = STATE_DQUOTE; + break; + case STATE_ESCAPE_TEMPLATE_LITERAL: + state = STATE_TEMPLATE_LITERAL; + break; + + // Double quote state > " < + case STATE_DQUOTE: + if (c == "\\") { + state = STATE_ESCAPE_DQUOTE; + } else if (c == "\n") { + return { + err: "unterminated string literal", + }; + } else if (c == '"') { + state = STATE_NORMAL; + } + break; + + // Template literal state > ` < + case STATE_TEMPLATE_LITERAL: + if (c == "\\") { + state = STATE_ESCAPE_TEMPLATE_LITERAL; + } else if (c == "`") { + state = STATE_NORMAL; + } + break; + + // Single quote state > ' < + case STATE_QUOTE: + if (c == "\\") { + state = STATE_ESCAPE_QUOTE; + } else if (c == "\n") { + return { + err: "unterminated string literal", + }; + } else if (c == "'") { + state = STATE_NORMAL; + } + break; + case STATE_SLASH: + if (c == "/") { + state = STATE_INLINE_COMMENT; + } else if (c == "*") { + state = STATE_MULTILINE_COMMENT; + } else { + lastStatement = ""; + state = STATE_NORMAL; + } + break; + + case STATE_INLINE_COMMENT: + if (c === "\n") { + state = STATE_NORMAL; + resetLastStatement = true; + } + break; + + case STATE_MULTILINE_COMMENT: + if (c === "*") { + state = STATE_MULTILINE_COMMENT_CLOSE; + } + break; + + case STATE_MULTILINE_COMMENT_CLOSE: + if (c === "/") { + state = STATE_NORMAL; + resetLastStatement = true; + } else { + state = STATE_MULTILINE_COMMENT; + } + break; + + case STATE_QUESTION_MARK: + state = STATE_NORMAL; + if (c === "?") { + // If we have a nullish coalescing operator, we start a new statement + resetLastStatement = true; + } else if (c !== ".") { + // If we're not dealing with optional chaining (?.), it means we have a ternary, + // so we are starting a new statement that includes the current character. + lastStatement = ""; + } else { + dotIndex = currentIndex; + } + break; + } + + if (!isWhitespaceChar) { + previousNonWhitespaceChar = c; + } + if (resetLastStatement) { + lastStatement = ""; + } else { + lastStatement = lastStatement + c; + } + + // We update all the open stacks lastStatement so they are up-to-date. + bodyStack.forEach(stack => { + if (stack.token !== "}") { + stack.lastStatement = stack.lastStatement + c; + } + }); + } + + let isElementAccess = false; + let lastOpeningBracketIndex = -1; + if (bodyStack.length === 1 && bodyStack[0].token === "[") { + lastStatement = bodyStack[0].lastStatement; + lastOpeningBracketIndex = bodyStack[0].index; + isElementAccess = true; + + if ( + state === STATE_DQUOTE || + state === STATE_QUOTE || + state === STATE_TEMPLATE_LITERAL || + state === STATE_ESCAPE_QUOTE || + state === STATE_ESCAPE_DQUOTE || + state === STATE_ESCAPE_TEMPLATE_LITERAL + ) { + state = STATE_NORMAL; + } + } else if (pendingWhitespaceChars) { + lastStatement = ""; + } + + const lastCompletionCharIndex = isElementAccess + ? lastOpeningBracketIndex + : dotIndex; + + const stringBeforeLastCompletionChar = str.slice(0, lastCompletionCharIndex); + + const isPropertyAccess = + lastCompletionCharIndex && lastCompletionCharIndex > 0; + + // Compute `isOptionalAccess`, so that we can use it + // later for computing `expressionBeforePropertyAccess`. + //Check `?.` before `[` for element access ( e.g `a?.["b` or `a ?. ["b` ) + // and `?` before `.` for regular property access ( e.g `a?.b` or `a ?. b` ) + const optionalElementAccessRegex = /\?\.\s*$/; + const isOptionalAccess = isElementAccess + ? optionalElementAccessRegex.test(stringBeforeLastCompletionChar) + : isPropertyAccess && + str.slice(lastCompletionCharIndex - 1, lastCompletionCharIndex + 1) === + "?."; + + // Get the filtered string for the properties (e.g if `document.qu` then `qu`) + const matchProp = isPropertyAccess + ? str.slice(lastCompletionCharIndex + 1).trimLeft() + : null; + + const expressionBeforePropertyAccess = isPropertyAccess + ? str.slice( + 0, + // For optional access, we can take all the chars before the last "?" char. + isOptionalAccess + ? stringBeforeLastCompletionChar.lastIndexOf("?") + : lastCompletionCharIndex + ) + : str; + + let mainExpression = lastStatement; + if (isPropertyAccess) { + if (isOptionalAccess) { + // Strip anything before the last `?`. + mainExpression = mainExpression.slice(0, mainExpression.lastIndexOf("?")); + } else { + mainExpression = mainExpression.slice( + 0, + -1 * (str.length - lastCompletionCharIndex) + ); + } + } + + mainExpression = mainExpression.trim(); + + return { + state, + isElementAccess, + isPropertyAccess, + expressionBeforePropertyAccess, + lastStatement, + mainExpression, + matchProp, + }; +}; + +/** + * Checks whether the analyzed input string is in an appropriate state to autocomplete, e.g. not + * inside a string, or declaring a variable. + * @param {object} inputAnalysisState The analyzed string to check + * @returns {boolean} Whether the input should be autocompleted + */ +exports.shouldInputBeAutocompleted = function(inputAnalysisState) { + const { err, state, lastStatement } = inputAnalysisState; + + // There was an error analysing the string. + if (err) { + return false; + } + + // If the current state is not STATE_NORMAL, then we are inside string, + // which means that no completion is possible. + if (state != STATE_NORMAL) { + return false; + } + + // Don't complete on just an empty string. + if (lastStatement.trim() == "") { + return false; + } + + if ( + NO_AUTOCOMPLETE_PREFIXES.some(prefix => + lastStatement.startsWith(prefix + " ") + ) + ) { + return false; + } + + return true; +}; + +/** + * Checks whether the analyzed input string is in an appropriate state to be eagerly evaluated. + * @param {object} inputAnalysisState + * @returns {boolean} Whether the input should be eagerly evaluated + */ +exports.shouldInputBeEagerlyEvaluated = function({ lastStatement }) { + const inComputedProperty = + lastStatement.lastIndexOf("[") !== -1 && + lastStatement.lastIndexOf("[") > lastStatement.lastIndexOf("]"); + + const hasPropertyAccess = + lastStatement.includes(".") || lastStatement.includes("["); + + return hasPropertyAccess && !inComputedProperty; +}; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/shared/webconsole/js-property-provider.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/shared/webconsole/js-property-provider.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/shared/webconsole/js-property-provider.js 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/shared/webconsole/js-property-provider.js 2021-10-20 19:28:16.000000000 +0000 @@ -24,6 +24,16 @@ "resource://gre/modules/reflect.jsm", true ); +loader.lazyRequireGetter( + this, + [ + "analyzeInputString", + "shouldInputBeAutocompleted", + "shouldInputBeEagerlyEvaluated", + ], + "devtools/shared/webconsole/analyze-input-string", + true +); // Provide an easy way to bail out of even attempting an autocompletion // if an object has way too many properties. Protects against large objects @@ -355,401 +365,10 @@ }); } -function shouldInputBeEagerlyEvaluated({ lastStatement }) { - const inComputedProperty = - lastStatement.lastIndexOf("[") !== -1 && - lastStatement.lastIndexOf("[") > lastStatement.lastIndexOf("]"); - - const hasPropertyAccess = - lastStatement.includes(".") || lastStatement.includes("["); - - return hasPropertyAccess && !inComputedProperty; -} - -function shouldInputBeAutocompleted(inputAnalysisState) { - const { err, state, lastStatement } = inputAnalysisState; - - // There was an error analysing the string. - if (err) { - return false; - } - - // If the current state is not STATE_NORMAL, then we are inside of an string - // which means that no completion is possible. - if (state != STATE_NORMAL) { - return false; - } - - // Don't complete on just an empty string. - if (lastStatement.trim() == "") { - return false; - } - - if ( - NO_AUTOCOMPLETE_PREFIXES.some(prefix => - lastStatement.startsWith(prefix + " ") - ) - ) { - return false; - } - - return true; -} - function hasArrayIndex(str) { return /\[\d+\]$/.test(str); } -const STATE_NORMAL = Symbol("STATE_NORMAL"); -const STATE_QUOTE = Symbol("STATE_QUOTE"); -const STATE_DQUOTE = Symbol("STATE_DQUOTE"); -const STATE_TEMPLATE_LITERAL = Symbol("STATE_TEMPLATE_LITERAL"); -const STATE_ESCAPE_QUOTE = Symbol("STATE_ESCAPE_QUOTE"); -const STATE_ESCAPE_DQUOTE = Symbol("STATE_ESCAPE_DQUOTE"); -const STATE_ESCAPE_TEMPLATE_LITERAL = Symbol("STATE_ESCAPE_TEMPLATE_LITERAL"); -const STATE_SLASH = Symbol("STATE_SLASH"); -const STATE_INLINE_COMMENT = Symbol("STATE_INLINE_COMMENT"); -const STATE_MULTILINE_COMMENT = Symbol("STATE_MULTILINE_COMMENT"); -const STATE_MULTILINE_COMMENT_CLOSE = Symbol("STATE_MULTILINE_COMMENT_CLOSE"); -const STATE_QUESTION_MARK = Symbol("STATE_QUESTION_MARK"); - -const OPEN_BODY = "{[(".split(""); -const CLOSE_BODY = "}])".split(""); -const OPEN_CLOSE_BODY = { - "{": "}", - "[": "]", - "(": ")", -}; - -const NO_AUTOCOMPLETE_PREFIXES = ["var", "const", "let", "function", "class"]; -const OPERATOR_CHARS_SET = new Set(";,:=<>+-*%|&^~!".split("")); - -/** - * Analyses a given string to find the last statement that is interesting for - * later completion. - * - * @param string str - * A string to analyse. - * - * @returns object - * If there was an error in the string detected, then a object like - * - * { err: "ErrorMesssage" } - * - * is returned, otherwise a object like - * - * { - * state: STATE_NORMAL|STATE_QUOTE|STATE_DQUOTE, - * lastStatement: the last statement in the string, - * isElementAccess: boolean that indicates if the lastStatement has an open - * element access (e.g. `x["match`). - * isPropertyAccess: boolean indicating if we are accessing property - * (e.g `true` in `var a = {b: 1};a.b`) - * matchProp: The part of the expression that should match the properties - * on the mainExpression (e.g. `que` when expression is `document.body.que`) - * mainExpression: The part of the expression before any property access, - * (e.g. `a.b` if expression is `a.b.`) - * expressionBeforePropertyAccess: The part of the expression before property access - * (e.g `var a = {b: 1};a` if expression is `var a = {b: 1};a.b`) - * } - */ -// eslint-disable-next-line complexity -function analyzeInputString(str) { - // work variables. - const bodyStack = []; - let state = STATE_NORMAL; - let previousNonWhitespaceChar; - let lastStatement = ""; - let currentIndex = -1; - let dotIndex; - let pendingWhitespaceChars = ""; - const TIMEOUT = 2500; - const startingTime = Date.now(); - - // Use a string iterator in order to handle character with a length >= 2 (e.g. 😎). - for (const c of str) { - // We are possibly dealing with a very large string that would take a long time to - // analyze (and freeze the process). If the function has been running for more than - // a given time, we stop the analysis (this isn't too bad because the only - // consequence is that we won't provide autocompletion items). - if (Date.now() - startingTime > TIMEOUT) { - return { - err: "timeout", - }; - } - - currentIndex += 1; - let resetLastStatement = false; - const isWhitespaceChar = c.trim() === ""; - switch (state) { - // Normal JS state. - case STATE_NORMAL: - if (lastStatement.endsWith("?.") && /\d/.test(c)) { - // If the current char is a number, the engine will consider we're not - // performing an optional chaining, but a ternary (e.g. x ?.4 : 2). - lastStatement = ""; - } - - // Storing the index of dot of the input string - if (c === ".") { - dotIndex = currentIndex; - } - - // If the last characters were spaces, and the current one is not. - if (pendingWhitespaceChars && !isWhitespaceChar) { - // If we have a legitimate property/element access, or potential optional - // chaining call, we append the spaces. - if (c === "[" || c === "." || c === "?") { - lastStatement = lastStatement + pendingWhitespaceChars; - } else { - // if not, we can be sure the statement was over, and we can start a new one. - lastStatement = ""; - } - pendingWhitespaceChars = ""; - } - - if (c == '"') { - state = STATE_DQUOTE; - } else if (c == "'") { - state = STATE_QUOTE; - } else if (c == "`") { - state = STATE_TEMPLATE_LITERAL; - } else if (c == "/") { - state = STATE_SLASH; - } else if (c == "?") { - state = STATE_QUESTION_MARK; - } else if (OPERATOR_CHARS_SET.has(c)) { - // If the character is an operator, we can update the current statement. - resetLastStatement = true; - } else if (isWhitespaceChar) { - // If the previous char isn't a dot or opening bracket, and the current computed - // statement is not a variable/function/class declaration, we track the number - // of consecutive spaces, so we can re-use them at some point (or drop them). - if ( - previousNonWhitespaceChar !== "." && - previousNonWhitespaceChar !== "[" && - !NO_AUTOCOMPLETE_PREFIXES.includes(lastStatement) - ) { - pendingWhitespaceChars += c; - continue; - } - } else if (OPEN_BODY.includes(c)) { - // When opening a bracket or a parens, we store the current statement, in order - // to be able to retrieve it later. - bodyStack.push({ - token: c, - lastStatement, - index: currentIndex, - }); - // And we compute a new statement. - resetLastStatement = true; - } else if (CLOSE_BODY.includes(c)) { - const last = bodyStack.pop(); - if (!last || OPEN_CLOSE_BODY[last.token] != c) { - return { - err: "syntax error", - }; - } - if (c == "}") { - resetLastStatement = true; - } else { - lastStatement = last.lastStatement; - } - } - break; - - // Escaped quote - case STATE_ESCAPE_QUOTE: - state = STATE_QUOTE; - break; - case STATE_ESCAPE_DQUOTE: - state = STATE_DQUOTE; - break; - case STATE_ESCAPE_TEMPLATE_LITERAL: - state = STATE_TEMPLATE_LITERAL; - break; - - // Double quote state > " < - case STATE_DQUOTE: - if (c == "\\") { - state = STATE_ESCAPE_DQUOTE; - } else if (c == "\n") { - return { - err: "unterminated string literal", - }; - } else if (c == '"') { - state = STATE_NORMAL; - } - break; - - // Template literal state > ` < - case STATE_TEMPLATE_LITERAL: - if (c == "\\") { - state = STATE_ESCAPE_TEMPLATE_LITERAL; - } else if (c == "`") { - state = STATE_NORMAL; - } - break; - - // Single quote state > ' < - case STATE_QUOTE: - if (c == "\\") { - state = STATE_ESCAPE_QUOTE; - } else if (c == "\n") { - return { - err: "unterminated string literal", - }; - } else if (c == "'") { - state = STATE_NORMAL; - } - break; - case STATE_SLASH: - if (c == "/") { - state = STATE_INLINE_COMMENT; - } else if (c == "*") { - state = STATE_MULTILINE_COMMENT; - } else { - lastStatement = ""; - state = STATE_NORMAL; - } - break; - - case STATE_INLINE_COMMENT: - if (c === "\n") { - state = STATE_NORMAL; - resetLastStatement = true; - } - break; - - case STATE_MULTILINE_COMMENT: - if (c === "*") { - state = STATE_MULTILINE_COMMENT_CLOSE; - } - break; - - case STATE_MULTILINE_COMMENT_CLOSE: - if (c === "/") { - state = STATE_NORMAL; - resetLastStatement = true; - } else { - state = STATE_MULTILINE_COMMENT; - } - break; - - case STATE_QUESTION_MARK: - state = STATE_NORMAL; - if (c === "?") { - // If we have a nullish coalescing operator, we start a new statement - resetLastStatement = true; - } else if (c !== ".") { - // If we're not dealing with optional chaining (?.), it means we have a ternary, - // so we are starting a new statement that includes the current character. - lastStatement = ""; - } else { - dotIndex = currentIndex; - } - break; - } - - if (!isWhitespaceChar) { - previousNonWhitespaceChar = c; - } - if (resetLastStatement) { - lastStatement = ""; - } else { - lastStatement = lastStatement + c; - } - - // We update all the open stacks lastStatement so they are up-to-date. - bodyStack.forEach(stack => { - if (stack.token !== "}") { - stack.lastStatement = stack.lastStatement + c; - } - }); - } - - let isElementAccess = false; - let lastOpeningBracketIndex = -1; - if (bodyStack.length === 1 && bodyStack[0].token === "[") { - lastStatement = bodyStack[0].lastStatement; - lastOpeningBracketIndex = bodyStack[0].index; - isElementAccess = true; - - if ( - state === STATE_DQUOTE || - state === STATE_QUOTE || - state === STATE_TEMPLATE_LITERAL || - state === STATE_ESCAPE_QUOTE || - state === STATE_ESCAPE_DQUOTE || - state === STATE_ESCAPE_TEMPLATE_LITERAL - ) { - state = STATE_NORMAL; - } - } else if (pendingWhitespaceChars) { - lastStatement = ""; - } - - const lastCompletionCharIndex = isElementAccess - ? lastOpeningBracketIndex - : dotIndex; - - const stringBeforeLastCompletionChar = str.slice(0, lastCompletionCharIndex); - - const isPropertyAccess = - lastCompletionCharIndex && lastCompletionCharIndex > 0; - - // Compute `isOptionalAccess`, so that we can use it - // later for computing `expressionBeforePropertyAccess`. - //Check `?.` before `[` for element access ( e.g `a?.["b` or `a ?. ["b` ) - // and `?` before `.` for regular property access ( e.g `a?.b` or `a ?. b` ) - const optionalElementAccessRegex = /\?\.\s*$/; - const isOptionalAccess = isElementAccess - ? optionalElementAccessRegex.test(stringBeforeLastCompletionChar) - : isPropertyAccess && - str.slice(lastCompletionCharIndex - 1, lastCompletionCharIndex + 1) === - "?."; - - // Get the filtered string for the properties (e.g if `document.qu` then `qu`) - const matchProp = isPropertyAccess - ? str.slice(lastCompletionCharIndex + 1).trimLeft() - : null; - - const expressionBeforePropertyAccess = isPropertyAccess - ? str.slice( - 0, - // For optional access, we can take all the chars before the last "?" char. - isOptionalAccess - ? stringBeforeLastCompletionChar.lastIndexOf("?") - : lastCompletionCharIndex - ) - : str; - - let mainExpression = lastStatement; - if (isPropertyAccess) { - if (isOptionalAccess) { - // Strip anything before the last `?`. - mainExpression = mainExpression.slice(0, mainExpression.lastIndexOf("?")); - } else { - mainExpression = mainExpression.slice( - 0, - -1 * (str.length - lastCompletionCharIndex) - ); - } - } - - mainExpression = mainExpression.trim(); - - return { - state, - isElementAccess, - isPropertyAccess, - expressionBeforePropertyAccess, - lastStatement, - mainExpression, - matchProp, - }; -} - /** * For a given environment and constructor name, returns its Debugger.Object wrapped * prototype. @@ -1168,6 +787,3 @@ // Export a version that will throw (for tests) exports.FallibleJSPropertyProvider = JSPropertyProvider; - -// Export analyzeInputString (for tests) -exports.analyzeInputString = analyzeInputString; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/shared/webconsole/moz.build firefox-trunk-95.0~a1~hg20211020r596404/devtools/shared/webconsole/moz.build --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/shared/webconsole/moz.build 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/shared/webconsole/moz.build 2021-10-20 19:28:16.000000000 +0000 @@ -26,6 +26,7 @@ ) DevToolsModules( + "analyze-input-string.js", "js-property-provider.js", "network-helper.js", "parser-helper.js", diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/devtools/shared/webconsole/test/xpcshell/test_analyze_input_string.js firefox-trunk-95.0~a1~hg20211020r596404/devtools/shared/webconsole/test/xpcshell/test_analyze_input_string.js --- firefox-trunk-95.0~a1~hg20211017r596111/devtools/shared/webconsole/test/xpcshell/test_analyze_input_string.js 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/devtools/shared/webconsole/test/xpcshell/test_analyze_input_string.js 2021-10-20 19:28:16.000000000 +0000 @@ -5,7 +5,7 @@ const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm"); const { analyzeInputString, -} = require("devtools/shared/webconsole/js-property-provider"); +} = require("devtools/shared/webconsole/analyze-input-string"); add_task(() => { const tests = [ diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/docshell/base/BrowsingContext.cpp firefox-trunk-95.0~a1~hg20211020r596404/docshell/base/BrowsingContext.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/docshell/base/BrowsingContext.cpp 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/docshell/base/BrowsingContext.cpp 2021-10-20 19:28:16.000000000 +0000 @@ -2760,15 +2760,7 @@ PreOrderWalk([&](BrowsingContext* aContext) { if (nsIDocShell* shell = aContext->GetDocShell()) { if (nsPresContext* pc = shell->GetPresContext()) { - // This is a bit of a lie, but it's the code-path that gets taken for - // regular system metrics changes via ThemeChanged(). - // TODO(emilio): The JustThisDocument is a bit suspect here, - // prefers-color-scheme also applies to images or such, but the override - // means that we could need to render the same image both with "light" - // and "dark" appearance, so we just don't bother. - pc->MediaFeatureValuesChanged( - {MediaFeatureChangeReason::SystemMetricsChange}, - MediaFeatureChangePropagation::JustThisDocument); + pc->RecomputeBrowsingContextDependentData(); } } }); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/docshell/base/CanonicalBrowsingContext.cpp firefox-trunk-95.0~a1~hg20211020r596404/docshell/base/CanonicalBrowsingContext.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/docshell/base/CanonicalBrowsingContext.cpp 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/docshell/base/CanonicalBrowsingContext.cpp 2021-10-20 19:28:16.000000000 +0000 @@ -2477,8 +2477,7 @@ MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * suspended Window")); } if (aBFCacheCombo & BFCacheStatus::UNLOAD_LISTENER) { - MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, - (" * beforeunload or unload listener")); + MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * unload listener")); } if (aBFCacheCombo & BFCacheStatus::REQUEST) { MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * requests in the loadgroup")); @@ -2501,6 +2500,9 @@ if (aBFCacheCombo & BFCacheStatus::HAS_USED_VR) { MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * used VR")); } + if (aBFCacheCombo & BFCacheStatus::BEFOREUNLOAD_LISTENER) { + MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * beforeunload listener")); + } } bool CanonicalBrowsingContext::AllowedInBFCache( diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/docshell/base/nsDocShell.cpp firefox-trunk-95.0~a1~hg20211020r596404/docshell/base/nsDocShell.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/docshell/base/nsDocShell.cpp 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/docshell/base/nsDocShell.cpp 2021-10-20 19:28:16.000000000 +0000 @@ -6806,6 +6806,8 @@ enum BFCacheStatusCombo : uint16_t { BFCACHE_SUCCESS, NOT_ONLY_TOPLEVEL = mozilla::dom::BFCacheStatus::NOT_ONLY_TOPLEVEL_IN_BCG, + // If both unload and beforeunload listeners are presented, it'll be + // recorded as unload UNLOAD = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER, UNLOAD_REQUEST = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER | mozilla::dom::BFCacheStatus::REQUEST, @@ -6826,9 +6828,15 @@ mozilla::dom::BFCacheStatus::UNLOAD_LISTENER | mozilla::dom::BFCacheStatus::REQUEST | mozilla::dom::BFCacheStatus::ACTIVE_PEER_CONNECTION, - REMOTE_SUBFRAMES = mozilla::dom::BFCacheStatus::CONTAINS_REMOTE_SUBFRAMES + REMOTE_SUBFRAMES = mozilla::dom::BFCacheStatus::CONTAINS_REMOTE_SUBFRAMES, + BEFOREUNLOAD = mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER, }; + // Beforeunload is recorded as a blocker only if it is the only one to block + // bfcache. + if (aCombo != mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER) { + aCombo &= ~mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER; + } switch (aCombo) { case BFCACHE_SUCCESS: Telemetry::AccumulateCategorical( @@ -6848,6 +6856,10 @@ case UNLOAD: Telemetry::AccumulateCategorical(Telemetry::LABELS_BFCACHE_COMBO::Unload); break; + case BEFOREUNLOAD: + Telemetry::AccumulateCategorical( + Telemetry::LABELS_BFCACHE_COMBO::Beforeunload); + break; case UNLOAD_REQUEST: Telemetry::AccumulateCategorical( Telemetry::LABELS_BFCACHE_COMBO::Unload_Req); @@ -7887,7 +7899,7 @@ NS_ENSURE_SUCCESS(rv, rv); if (!parentSite.Equals(thisSite)) { - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled()) { nsCOMPtr prinURI; BasePrincipal::Cast(thisPrincipal)->GetURI(getter_AddRefs(prinURI)); nsPrintfCString marker("Iframe loaded in background: %s", diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/browser/browser.ini firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/browser/browser.ini --- firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/browser/browser.ini 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/browser/browser.ini 2021-10-20 19:28:16.000000000 +0000 @@ -76,6 +76,9 @@ support-files = dummy_iframe_page.html TestTopLevelNavigationDelegate.jsm +skip-if = + os == "linux" && bits == 64 && !debug # Bug 1725277 + win10_2004 && !debug # Bug 1725277 [browser_alternate_fixup_middle_click_link.js] https_first_disabled = true [browser_backforward_userinteraction.js] diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/browser/file_bug1688368-1.sjs firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/browser/file_bug1688368-1.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/browser/file_bug1688368-1.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/browser/file_bug1688368-1.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -2,7 +2,11 @@ const DELAY = 1 * 1000; // Delay one second before completing the request. -let nsTimer = Components.Constructor("@mozilla.org/timer;1", "nsITimer", "initWithCallback"); +let nsTimer = Components.Constructor( + "@mozilla.org/timer;1", + "nsITimer", + "initWithCallback" +); let timer; @@ -20,13 +24,21 @@ // Note: We need to store a reference to the timer to prevent it from being // canceled when it's GCed. - timer = new nsTimer(() => { - var snowmen = "\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083"; - response.write(snowmen + ` + timer = new nsTimer( + () => { + var snowmen = + "\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083"; + response.write( + snowmen + + ` -`); - response.finish(); - }, DELAY, Ci.nsITimer.TYPE_ONE_SHOT); +` + ); + response.finish(); + }, + DELAY, + Ci.nsITimer.TYPE_ONE_SHOT + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/browser/file_bug1716290-1.sjs firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/browser/file_bug1716290-1.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/browser/file_bug1716290-1.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/browser/file_bug1716290-1.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,7 +1,10 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { if (getState("reloaded") == "reloaded") { - response.setHeader("Content-Type", "text/html; charset=windows-1254", false); + response.setHeader( + "Content-Type", + "text/html; charset=windows-1254", + false + ); response.write("\u00E4"); } else { response.setHeader("Content-Type", "text/html; charset=Shift_JIS", false); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/browser/file_bug1716290-2.sjs firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/browser/file_bug1716290-2.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/browser/file_bug1716290-2.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/browser/file_bug1716290-2.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,5 +1,4 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { if (getState("reloaded") == "reloaded") { response.setHeader("Content-Type", "text/html", false); response.write("\u00E4"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/browser/file_bug1716290-3.sjs firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/browser/file_bug1716290-3.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/browser/file_bug1716290-3.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/browser/file_bug1716290-3.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,5 +1,4 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { if (getState("reloaded") == "reloaded") { response.setHeader("Content-Type", "text/html; charset=iso-2022-kr", false); response.write("\u00E4"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/browser/file_bug1716290-4.sjs firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/browser/file_bug1716290-4.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/browser/file_bug1716290-4.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/browser/file_bug1716290-4.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,5 +1,4 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { if (getState("reloaded") == "reloaded") { response.setHeader("Content-Type", "text/html", false); response.write("\u00FE\u00FF\u00E4"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/browser/print_postdata.sjs firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/browser/print_postdata.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/browser/print_postdata.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/browser/print_postdata.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,7 +1,9 @@ const CC = Components.Constructor; -const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", - "setInputStream"); +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); function handleRequest(request, response) { response.setHeader("Content-Type", "text/plain", false); @@ -13,8 +15,9 @@ var avail; var bytes = []; - while ((avail = body.available()) > 0) + while ((avail = body.available()) > 0) { Array.prototype.push.apply(bytes, body.readByteArray(avail)); + } var data = String.fromCharCode.apply(null, bytes); response.bodyOutputStream.write(data, data.length); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/chrome/bug89419.sjs firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/chrome/bug89419.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/chrome/bug89419.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/chrome/bug89419.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,5 +1,4 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { var redirectstate = "/docshell/test/chrome/bug89419.sjs"; response.setStatusLine("1.1", 302, "Found"); if (getState(redirectstate) == "") { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/mochitest/bug413310-post.sjs firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/mochitest/bug413310-post.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/mochitest/bug413310-post.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/mochitest/bug413310-post.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,7 +1,10 @@ function handleRequest(request, response) { response.setHeader("Content-Type", "text/html"); - response.write("" + - request.method + " " + - Date.now() + - ""); + response.write( + "" + + request.method + + " " + + Date.now() + + "" + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/mochitest/bug530396-noref.sjs firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/mochitest/bug530396-noref.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/mochitest/bug530396-noref.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/mochitest/bug530396-noref.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -3,14 +3,16 @@ response.setHeader("Cache-Control", "no-cache"); response.write(""); + response.write( + "if (window.opener) { window.opener.parent.onloadCount++; window.opener.parent.doNextStep(); }" + ); + response.write("if (!window.opener) window.close();"); + response.write("'>"); } else { response.write("window.parent.doNextStep();'>"); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/mochitest/double_submit.sjs firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/mochitest/double_submit.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/mochitest/double_submit.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/mochitest/double_submit.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -2,7 +2,8 @@ let self = this; -Cu.import("resource://gre/modules/Timer.jsm"); +// eslint-disable-next-line mozilla/use-chromeutils-import +let { setTimeout } = Cu.import("resource://gre/modules/Timer.jsm"); const CC = Components.Constructor; const BinaryInputStream = CC( @@ -18,7 +19,7 @@ ); function log(str) { -// dump(`LOG: ${str}\n`); + // dump(`LOG: ${str}\n`); } function* generateBody(fragments, size) { @@ -26,7 +27,7 @@ let chunkSize = (size / fragments) | 0; let remaining = size; - log(`Chunk size ${chunkSize}`) + log(`Chunk size ${chunkSize}`); while (remaining > 0) { let data = new Uint8Array(Math.min(remaining, chunkSize)); for (let i = 0; i < data.length; ++i) { @@ -35,7 +36,7 @@ } yield data; - log(`Remaining to chunk ${remaining}`) + log(`Remaining to chunk ${remaining}`); remaining -= data.length; } } @@ -47,7 +48,7 @@ result.push(inputStream.readBytes(available)); } - return result.join(''); + return result.join(""); } function now() { @@ -68,7 +69,9 @@ message = "bad"; } else { log("Read POST body"); - let body = new URLSearchParams(readStream(new BinaryInputStream(request.bodyInputStream))); + let body = new URLSearchParams( + readStream(new BinaryInputStream(request.bodyInputStream)) + ); message = body.get("token") || "bad"; log(`The result was ${message}`); } @@ -81,17 +84,21 @@ let header = "`; - log("Set headers") + log("Set headers"); response.setHeader("Content-Type", "text/html", false); - response.setHeader("Content-Length", `${size + header.length + footer.length}`, false); + response.setHeader( + "Content-Length", + `${size + header.length + footer.length}`, + false + ); response.setStatusLine(request.httpVersion, "200", "OK"); response.processAsync(); log("Write header"); response.write(header); - log("Write body") + log("Write body"); for (let data of generateBody(fragments, size)) { - delay = Math.max(0, delayUntil - now()) + delay = Math.max(0, delayUntil - now()); log(`Delay sending fragment for ${delay / fragments}`); let failed = false; await new Promise(resolve => { @@ -112,10 +119,10 @@ } fragments = Math.max(--fragments, 1); - log(`Fragments left ${fragments}`) + log(`Fragments left ${fragments}`); } - log("Write footer") + log("Write footer"); response.write(footer); response.finish(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/mochitest/file_bug475636.sjs firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/mochitest/file_bug475636.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/mochitest/file_bug475636.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/mochitest/file_bug475636.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,10 +1,17 @@ -jsURL = "javascript:" + escape('window.parent.postMessage("JS uri ran", "*");\ +jsURL = + "javascript:" + + escape( + 'window.parent.postMessage("JS uri ran", "*");\ return \'\ \''); -dataURL = "data:text/html," + escape('\ +\'' + ); +dataURL = + "data:text/html," + + escape( + '\ '); +' + ); tests = [ -// Plain document should work as normal -'\ + // Plain document should work as normal + '\ ', -// refresh to plain doc -{ refresh: "file_bug475636.sjs?1", - doc: '' }, + // refresh to plain doc + { refresh: "file_bug475636.sjs?1", doc: "" }, -// meta-refresh to plain doc -'\ + // meta-refresh to plain doc + '\ \ \ ', -// refresh to data url -{ refresh: dataURL, - doc: '' }, + // refresh to data url + { refresh: dataURL, doc: "" }, -// meta-refresh to data url -'\ + // meta-refresh to data url + '\ \ - \ + \ ', -// refresh to js url should not be followed -{ refresh: jsURL, - doc: -'\ + // refresh to js url should not be followed + { + refresh: jsURL, + doc: + '\ ' }, +', + }, -// meta refresh to js url should not be followed -'\ + // meta refresh to js url should not be followed + '\ \ - \ + \ \ ' +', ]; - -function handleRequest(request, response) -{ +function handleRequest(request, response) { dump("@@@@@@@@@hi there: " + request.queryString + "\n"); test = tests[parseInt(request.queryString, 10) - 1]; response.setHeader("Content-Type", "text/html"); if (!test) { response.write(''); - } - else if (typeof test == "string") { + } else if (typeof test == "string") { response.write(test); - } - else if (test.refresh) { + } else if (test.refresh) { response.setHeader("Refresh", "0; url=" + test.refresh); response.write(test.doc); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/mochitest/file_bug580069_2.sjs firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/mochitest/file_bug580069_2.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/mochitest/file_bug580069_2.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/mochitest/file_bug580069_2.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,5 +1,8 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setHeader("Content-Type", "text/html", false); - response.write('file_bug580069_2.sjs'); + response.write( + "file_bug580069_2.sjs" + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/mochitest/file_bug669671.sjs firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/mochitest/file_bug669671.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/mochitest/file_bug669671.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/mochitest/file_bug669671.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,14 +1,17 @@ -function handleRequest(request, response) -{ - var count = parseInt(getState('count')); - if (!count || request.queryString == 'countreset') +function handleRequest(request, response) { + var count = parseInt(getState("count")); + if (!count || request.queryString == "countreset") { count = 0; + } - setState('count', count + 1 + ''); + setState("count", count + 1 + ""); - response.setHeader('Content-Type', 'text/html', false); - response.setHeader('Cache-Control', 'max-age=0'); - response.write('' + - count + ''); + response.setHeader("Content-Type", "text/html", false); + response.setHeader("Cache-Control", "max-age=0"); + response.write( + '" + + count + + "" + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/mochitest/form_submit.sjs firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/mochitest/form_submit.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/mochitest/form_submit.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/mochitest/form_submit.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,7 +1,5 @@ "use strict"; -Cu.import("resource://gre/modules/Timer.jsm"); - const CC = Components.Constructor; const BinaryInputStream = CC( "@mozilla.org/binaryinputstream;1", @@ -23,7 +21,7 @@ if (request.method !== "POST") { message = "bad"; } else { - log("Reading request") + log("Reading request"); let available = 0; let inputStream = new BinaryInputStream(request.bodyInputStream); while ((available = inputStream.available()) > 0) { @@ -31,10 +29,12 @@ } } - log("Setting Headers") + log("Setting Headers"); response.setHeader("Content-Type", "text/html", false); response.setStatusLine(request.httpVersion, "200", "OK"); log("Writing body"); - response.write(''); - log("Done") + response.write( + '' + ); + log("Done"); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/navigation/file_fragment_handling_during_load_frame2.sjs firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/navigation/file_fragment_handling_during_load_frame2.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/navigation/file_fragment_handling_during_load_frame2.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/navigation/file_fragment_handling_during_load_frame2.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -7,7 +7,7 @@ response.setHeader("Cache-Control", "no-cache", false); // Wait a bit. var s = Date.now(); - while (Date.now() - s < 1000); + while (Date.now() - s < 1000) {} response.write(` @@ -16,4 +16,4 @@ `); -} \ No newline at end of file +} diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/navigation/redirect_handlers.sjs firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/navigation/redirect_handlers.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/navigation/redirect_handlers.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/navigation/redirect_handlers.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -20,7 +20,10 @@ setState("sessionhistory_do_redirect", ""); response.setStatusLine("1.1", 302, "Found"); - response.setHeader("Location", - "file_session_history_on_redirect_2.html", false); + response.setHeader( + "Location", + "file_session_history_on_redirect_2.html", + false + ); } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/navigation/slow.sjs firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/navigation/slow.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/navigation/slow.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/navigation/slow.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,12 +1,16 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.processAsync(); - timer = Components.classes["@mozilla.org/timer;1"]. - createInstance(Components.interfaces.nsITimer); - timer.init(function() { - response.finish(); - }, 5000, Components.interfaces.nsITimer.TYPE_ONE_SHOT); + timer = Components.classes["@mozilla.org/timer;1"].createInstance( + Components.interfaces.nsITimer + ); + timer.init( + function() { + response.finish(); + }, + 5000, + Components.interfaces.nsITimer.TYPE_ONE_SHOT + ); response.setStatusLine(null, 200, "OK"); response.setHeader("Content-Type", "text/plain", false); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/navigation/test_rate_limit_location_change.html firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/navigation/test_rate_limit_location_change.html --- firefox-trunk-95.0~a1~hg20211017r596111/docshell/test/navigation/test_rate_limit_location_change.html 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/docshell/test/navigation/test_rate_limit_location_change.html 2021-10-20 19:28:16.000000000 +0000 @@ -29,6 +29,7 @@ "history.back": () => win.history.back(), "history.forward": () => win.history.forward(), "history.go": () => win.history.go(-1), + "location.href": () => win.location.href = win.location.href + "", "location.hash": () => win.location.hash = inc++, "location.host": () => win.location.host = win.location.host + "", "location.hostname": () => win.location.hostname = win.location.hostname + "", @@ -36,6 +37,9 @@ "location.port": () => win.location.port = win.location.port + "", "location.protocol": () => win.location.protocol = win.location.protocol + "", "location.search": () => win.location.search = win.location.search + "", + "location.assign": () => win.location.assign(`${win.location.href}#${inc++}`), + "location.replace": () => win.location.replace(`${win.location.href}#${inc++}`), + "location.reload": () => win.location.reload(), }); async function test() { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/abort/tests/slow.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/abort/tests/slow.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/abort/tests/slow.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/abort/tests/slow.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,11 +1,15 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.processAsync(); - timer = Components.classes["@mozilla.org/timer;1"]. - createInstance(Components.interfaces.nsITimer); - timer.init(function() { - response.write("Here the content. But slowly."); - response.finish(); - }, 1000, Components.interfaces.nsITimer.TYPE_ONE_SHOT); + timer = Components.classes["@mozilla.org/timer;1"].createInstance( + Components.interfaces.nsITimer + ); + timer.init( + function() { + response.write("Here the content. But slowly."); + response.finish(); + }, + 1000, + Components.interfaces.nsITimer.TYPE_ONE_SHOT + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/animation/AnimationEventDispatcher.h firefox-trunk-95.0~a1~hg20211020r596404/dom/animation/AnimationEventDispatcher.h --- firefox-trunk-95.0~a1~hg20211017r596111/dom/animation/AnimationEventDispatcher.h 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/animation/AnimationEventDispatcher.h 2021-10-20 19:28:16.000000000 +0000 @@ -52,7 +52,7 @@ nsCSSPseudoElements::PseudoTypeAsString(aTarget.mPseudoType); if ((aMessage == eAnimationCancel || aMessage == eAnimationEnd) && - profiler_can_accept_markers()) { + profiler_thread_is_being_profiled()) { nsCString markerText; aAnimationName->ToUTF8String(markerText); PROFILER_MARKER_TEXT( @@ -89,7 +89,7 @@ nsCSSPseudoElements::PseudoTypeAsString(aTarget.mPseudoType); if ((aMessage == eTransitionEnd || aMessage == eTransitionCancel) && - profiler_can_accept_markers()) { + profiler_thread_is_being_profiled()) { nsCString markerText; markerText.Assign(nsCSSProps::GetStringValue(aProperty)); if (aMessage == eTransitionCancel) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/animation/KeyframeEffect.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/animation/KeyframeEffect.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/animation/KeyframeEffect.cpp 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/animation/KeyframeEffect.cpp 2021-10-20 19:28:16.000000000 +0000 @@ -39,6 +39,7 @@ #include "nsPresContextInlines.h" #include "nsRefreshDriver.h" #include "js/PropertyAndElement.h" // JS_DefineProperty +#include "WindowRenderer.h" namespace mozilla { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/CCGCScheduler.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/base/CCGCScheduler.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/CCGCScheduler.cpp 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/CCGCScheduler.cpp 2021-10-20 19:28:16.000000000 +0000 @@ -10,6 +10,103 @@ #include "mozilla/dom/ScriptSettings.h" #include "nsRefreshDriver.h" +/* + * GC Scheduling from Firefox + * ========================== + * + * See also GC Scheduling from SpiderMonkey's perspective here: + * https://searchfox.org/mozilla-central/source/js/src/gc/Scheduling.h + * + * From Firefox's perspective GCs can start in 5 different ways: + * + * * The JS engine just starts doing a GC for its own reasons (see above). + * Firefox finds out about these via a callback in nsJSEnvironment.cpp + * * PokeGC() + * * PokeFullGC() + * * PokeShrinkingGC() + * * memory-pressure GCs (via a listener in nsJSEnvironment.cpp). + * + * PokeGC + * ------ + * + * void CCGCScheduler::PokeGC(JS::GCReason aReason, JSObject* aObj, + * TimeDuration aDelay) + * + * PokeGC provides a way for callers to say "Hey, there may be some memory + * associated with this object (via Zone) you can collect." PokeGC will: + * * add the zone to a set, + * * set flags including what kind of GC to run (SetWantMajorGC), + * * then creates the mGCRunner with a short delay. + * + * The delay can allow other calls to PokeGC to add their zones so they can + * be collected together. + * + * See below for what happens when mGCRunner fires. + * + * PokeFullGC + * ---------- + * + * void CCGCScheduler::PokeFullGC() + * + * PokeFullGC will create a timer that will initiate a "full" (all zones) + * collection. This is usually used after a regular collection if a full GC + * seems like a good idea (to collect inter-zone references). + * + * When the timer fires it will: + * * set flags (SetWantMajorGC), + * * start the mGCRunner with zero delay. + * + * See below for when mGCRunner fires. + * + * PokeShrinkingGC + * --------------- + * + * void CCGCScheduler::PokeShrinkingGC() + * + * PokeShrinkingGC is called when Firefox's user is inactive. + * Like PokeFullGC, PokeShrinkingGC uses a timer, but the timeout is longer + * which should prevent the ShrinkingGC from starting if the user only + * glances away for a brief time. When the timer fires it will: + * + * * set flags (SetWantMajorGC), + * * create the mGCRunner. + * + * There is a check if the user is still inactive in GCRunnerFired), if the + * user has become active the shrinking GC is canceled and either a regular + * GC (if requested, see mWantAtLeastRegularGC) or no GC is run. + * + * When mGCRunner fires + * -------------------- + * + * When mGCRunner fires it calls GCRunnerFired. This starts in the + * WaitToMajorGC state: + * + * * If this is a parent process it jumps to the next state + * * If this is a content process it will ask the parent if now is a good + * time to do a GC. (MayGCNow) + * * kill the mGCRunner + * * Exit + * + * Meanwhile the parent process will queue GC requests so that not too many + * are running in parallel overwhelming the CPU cores (see + * IdleSchedulerParent). + * + * When the promise from MayGCNow is resolved it will set some + * state (NoteReadyForMajorGC) and restore the mGCRunner. + * + * When the mGCRunner runs a second time (or this is the parent process and + * which jumped over the above logic. It will be in the StartMajorGC state. + * It will initiate the GC for real, usually. If it's a shrinking GC and the + * user is now active again it may abort. See GCRunnerFiredDoGC(). + * + * The runner will then run the first slice of the garbage collection. + * Later slices are also run by the runner, the final slice kills the runner + * from the GC callback in nsJSEnvironment.cpp. + * + * There is additional logic in the code to handle concurrent requests of + * various kinds. + */ + namespace mozilla { void CCGCScheduler::NoteGCBegin() { @@ -122,6 +219,7 @@ MOZ_CRASH("Unexpected GCRunnerAction"); case GCRunnerAction::WaitToMajorGC: { + MOZ_ASSERT(!mHaveAskedParent, "GCRunner alive after asking the parent"); RefPtr mbPromise = CCGCScheduler::MayGCNow(step.mReason); if (!mbPromise) { @@ -129,10 +227,12 @@ break; } + mHaveAskedParent = true; KillGCRunner(); mbPromise->Then( GetMainThreadSerialEventTarget(), __func__, [this](bool aMayGC) { + mHaveAskedParent = false; if (aMayGC) { if (!NoteReadyForMajorGC()) { // Another GC started and maybe completed while waiting. @@ -150,6 +250,7 @@ } }, [this](mozilla::ipc::ResponseRejectReason r) { + mHaveAskedParent = false; if (!InIncrementalGC()) { KillGCRunner(); NoteWontGC(); @@ -302,7 +403,9 @@ if (!s->mUserIsActive) { if (!nsRefreshDriver::IsRegularRateTimerTicking()) { s->SetWantMajorGC(JS::GCReason::USER_INACTIVE); - s->EnsureGCRunner(0); + if (!s->mHaveAskedParent) { + s->EnsureGCRunner(0); + } } else { s->PokeShrinkingGC(); } @@ -324,7 +427,9 @@ // set that we want a full GC we will get one eventually. s->SetNeedsFullGC(); s->SetWantMajorGC(JS::GCReason::FULL_GC_TIMER); - s->EnsureGCRunner(0); + if (!s->mHaveAskedParent) { + s->EnsureGCRunner(0); + } }, this, StaticPrefs::javascript_options_gc_delay_full(), nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY, "FullGCTimerFired"); @@ -344,8 +449,8 @@ SetNeedsFullGC(); } - if (mGCRunner) { - // There's already a runner for GC'ing, just return + if (mGCRunner || mHaveAskedParent) { + // There's already a GC runner, there or will be, so just return. return; } @@ -420,6 +525,8 @@ } void CCGCScheduler::KillGCRunner() { + // If we're in an incremental GC then killing the timer is only okay if + // we're shutting down. MOZ_ASSERT(!(InIncrementalGC() && !mDidShutdown)); if (mGCRunner) { mGCRunner->Cancel(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/CCGCScheduler.h firefox-trunk-95.0~a1~hg20211020r596404/dom/base/CCGCScheduler.h --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/CCGCScheduler.h 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/CCGCScheduler.h 2021-10-20 19:28:16.000000000 +0000 @@ -170,18 +170,39 @@ void SetWantMajorGC(JS::GCReason aReason) { MOZ_ASSERT(aReason != JS::GCReason::NO_REASON); - if (mMajorGCReason != JS::GCReason::NO_REASON && - mMajorGCReason != JS::GCReason::USER_INACTIVE && - aReason != JS::GCReason::USER_INACTIVE) { + // If the GC being requested is not a shrinking GC set this flag. + // If/when the shrinking GC timer fires but the user is active we check + // this flag before canceling the GC, so as not to cancel the + // non-shrinking GC being requested here. + if (aReason != JS::GCReason::USER_INACTIVE) { mWantAtLeastRegularGC = true; } - mMajorGCReason = aReason; // Force full GCs when called from reftests so that we collect dead zones // that have not been scheduled for collection. if (aReason == JS::GCReason::DOM_WINDOW_UTILS) { SetNeedsFullGC(); } + + // USER_INACTIVE trumps everything, + // FULL_GC_TIMER trumps everything except USER_INACTIVE, + // all other reasons just use the latest reason. + switch (aReason) { + case JS::GCReason::USER_INACTIVE: + mMajorGCReason = aReason; + break; + case JS::GCReason::FULL_GC_TIMER: + if (mMajorGCReason != JS::GCReason::USER_INACTIVE) { + mMajorGCReason = aReason; + } + break; + default: + if (mMajorGCReason != JS::GCReason::USER_INACTIVE && + mMajorGCReason != JS::GCReason::FULL_GC_TIMER) { + mMajorGCReason = aReason; + } + break; + } } // Ensure that the current runner does a cycle collection, and trigger a GC @@ -410,6 +431,10 @@ // duration (or until it goes too long and is finished synchronously.) bool mInIncrementalGC = false; + // We've asked the parent process if now is a good time to GC (do not ask + // again). + bool mHaveAskedParent = false; + // The parent process is ready for us to do a major GC. bool mReadyForMajorGC = false; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/ChromeUtils.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/base/ChromeUtils.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/ChromeUtils.cpp 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/ChromeUtils.cpp 2021-10-20 19:28:16.000000000 +0000 @@ -193,7 +193,7 @@ GlobalObject& aGlobal, const nsACString& aName, const ProfilerMarkerOptionsOrDouble& aOptions, const Optional& aText) { - if (!profiler_can_accept_markers()) { + if (!profiler_thread_is_being_profiled()) { return; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/Document.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/base/Document.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/Document.cpp 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/Document.cpp 2021-10-20 19:28:16.000000000 +0000 @@ -11060,11 +11060,19 @@ nsCOMPtr piTarget = do_QueryInterface(mScriptGlobalObject); if (!allowUnloadListeners && piTarget) { EventListenerManager* manager = piTarget->GetExistingListenerManager(); - if (manager && manager->HasUnloadListeners()) { - MOZ_LOG(gPageCacheLog, mozilla::LogLevel::Verbose, - ("Save of %s blocked due to unload handlers", uri.get())); - aBFCacheCombo |= BFCacheStatus::UNLOAD_LISTENER; - ret = false; + if (manager) { + if (manager->HasUnloadListeners()) { + MOZ_LOG(gPageCacheLog, mozilla::LogLevel::Verbose, + ("Save of %s blocked due to unload handlers", uri.get())); + aBFCacheCombo |= BFCacheStatus::UNLOAD_LISTENER; + ret = false; + } + if (manager->HasBeforeUnloadListeners()) { + MOZ_LOG(gPageCacheLog, mozilla::LogLevel::Verbose, + ("Save of %s blocked due to beforeUnload handlers", uri.get())); + aBFCacheCombo |= BFCacheStatus::BEFOREUNLOAD_LISTENER; + ret = false; + } } } @@ -17456,21 +17464,9 @@ return ColorScheme::Light; } - if (auto* bc = GetBrowsingContext()) { - switch (bc->Top()->PrefersColorSchemeOverride()) { - case dom::PrefersColorSchemeOverride::Dark: - return ColorScheme::Dark; - case dom::PrefersColorSchemeOverride::Light: - return ColorScheme::Light; - case dom::PrefersColorSchemeOverride::None: - case dom::PrefersColorSchemeOverride::EndGuard_: - break; - } - } - if (nsPresContext* pc = GetPresContext()) { - if (pc->IsPrintingOrPrintPreview()) { - return ColorScheme::Light; + if (auto scheme = pc->GetOverriddenColorScheme()) { + return *scheme; } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/Document.h firefox-trunk-95.0~a1~hg20211020r596404/dom/base/Document.h --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/Document.h 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/Document.h 2021-10-20 19:28:17.000000000 +0000 @@ -304,6 +304,7 @@ NOT_ONLY_TOPLEVEL_IN_BCG = 1 << 12, // Status 12 ABOUT_PAGE = 1 << 13, // Status 13 RESTORING = 1 << 14, // Status 14 + BEFOREUNLOAD_LISTENER = 1 << 15, // Status 15 }; } // namespace dom diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/JSExecutionContext.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/base/JSExecutionContext.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/JSExecutionContext.cpp 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/JSExecutionContext.cpp 2021-10-20 19:28:16.000000000 +0000 @@ -199,7 +199,9 @@ return mRv; } - mScript = JS::InstantiateGlobalStencil(mCx, options, stencil); + JS::InstantiateOptions instantiateOptions(options); + + mScript = JS::InstantiateGlobalStencil(mCx, instantiateOptions, stencil); if (!mScript) { return NS_ERROR_OUT_OF_MEMORY; } @@ -212,12 +214,13 @@ } bool JSExecutionContext::UpdateDebugMetadata() { - if (!mCompileOptions.deferDebugMetadata) { + JS::InstantiateOptions options(mCompileOptions); + + if (!options.deferDebugMetadata) { return true; } - return JS::UpdateDebugMetadata(mCx, mScript, mCompileOptions, - mDebuggerPrivateValue, nullptr, - mDebuggerIntroductionScript, nullptr); + return JS::UpdateDebugMetadata(mCx, mScript, options, mDebuggerPrivateValue, + nullptr, mDebuggerIntroductionScript, nullptr); } nsresult JSExecutionContext::JoinDecode(JS::OffThreadToken** aOffThreadToken) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/Location.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/base/Location.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/Location.cpp 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/Location.cpp 2021-10-20 19:28:16.000000000 +0000 @@ -25,6 +25,7 @@ #include "nsGlobalWindow.h" #include "mozilla/Likely.h" #include "nsCycleCollectionParticipant.h" +#include "mozilla/BasePrincipal.h" #include "mozilla/Components.h" #include "mozilla/NullPrincipal.h" #include "mozilla/ServoStyleConsts.h" @@ -548,7 +549,13 @@ SetURI(uri, aSubjectPrincipal, aRv); } -void Location::Reload(bool aForceget, ErrorResult& aRv) { +void Location::Reload(bool aForceget, nsIPrincipal& aSubjectPrincipal, + ErrorResult& aRv) { + if (!CallerSubsumes(&aSubjectPrincipal)) { + aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); + return; + } + nsCOMPtr docShell(GetDocShell()); if (!docShell) { return aRv.Throw(NS_ERROR_FAILURE); @@ -574,6 +581,21 @@ } } + RefPtr bc = GetBrowsingContext(); + if (!bc || bc->IsDiscarded()) { + return; + } + + CallerType callerType = aSubjectPrincipal.IsSystemPrincipal() + ? CallerType::System + : CallerType::NonSystem; + + nsresult rv = bc->CheckLocationChangeRateLimit(callerType); + if (NS_FAILED(rv)) { + aRv.Throw(rv); + return; + } + uint32_t reloadFlags = nsIWebNavigation::LOAD_FLAGS_NONE; if (aForceget) { @@ -581,7 +603,7 @@ nsIWebNavigation::LOAD_FLAGS_BYPASS_PROXY; } - nsresult rv = nsDocShell::Cast(docShell)->Reload(reloadFlags); + rv = nsDocShell::Cast(docShell)->Reload(reloadFlags); if (NS_FAILED(rv) && rv != NS_BINDING_ABORTED) { // NS_BINDING_ABORTED is returned when we attempt to reload a POST result // and the user says no at the "do you want to reload?" prompt. Don't diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/Location.h firefox-trunk-95.0~a1~hg20211020r596404/dom/base/Location.h --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/Location.h 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/Location.h 2021-10-20 19:28:16.000000000 +0000 @@ -43,14 +43,7 @@ ErrorResult& aError); void Reload(bool aForceget, nsIPrincipal& aSubjectPrincipal, - ErrorResult& aError) { - if (!CallerSubsumes(&aSubjectPrincipal)) { - aError.Throw(NS_ERROR_DOM_SECURITY_ERR); - return; - } - - Reload(aForceget, aError); - } + ErrorResult& aError); void GetHref(nsAString& aHref, nsIPrincipal& aSubjectPrincipal, ErrorResult& aError) { @@ -118,8 +111,6 @@ nsresult ToString(nsAString& aString) { return GetHref(aString); } - void Reload(bool aForceget, ErrorResult& aRv); - protected: virtual ~Location(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/moz.build firefox-trunk-95.0~a1~hg20211020r596404/dom/base/moz.build --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/moz.build 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/moz.build 2021-10-20 19:28:16.000000000 +0000 @@ -226,6 +226,7 @@ "PlacesBookmarkGuid.h", "PlacesBookmarkMoved.h", "PlacesBookmarkRemoved.h", + "PlacesBookmarkTags.h", "PlacesBookmarkTime.h", "PlacesBookmarkTitle.h", "PlacesBookmarkUrl.h", diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/Navigator.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/base/Navigator.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/Navigator.cpp 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/Navigator.cpp 2021-10-20 19:28:16.000000000 +0000 @@ -240,7 +240,13 @@ mWebGpu = nullptr; - mLocks = nullptr; + if (mLocks) { + // Unloading a page does not immediately destruct the lock manager actor, + // but we want to abort the lock requests as soon as possible. Explicitly + // call Shutdown() to do that. + mLocks->Shutdown(); + mLocks = nullptr; + } mSharePromise = nullptr; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/nsDOMNavigationTiming.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/base/nsDOMNavigationTiming.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/nsDOMNavigationTiming.cpp 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/nsDOMNavigationTiming.cpp 2021-10-20 19:28:17.000000000 +0000 @@ -170,7 +170,7 @@ Tracing, "Navigation"); if (IsTopLevelContentDocumentInContentProcess()) { - if (profiler_can_accept_markers() || PAGELOAD_LOG_ENABLED()) { + if (profiler_thread_is_being_profiled() || PAGELOAD_LOG_ENABLED()) { TimeDuration elapsed = mLoadEventEnd - mNavigationStart; TimeDuration duration = mLoadEventEnd - mLoadEventStart; nsAutoCString spec; @@ -368,7 +368,7 @@ mTTITimer = nullptr; - if (profiler_can_accept_markers() || PAGELOAD_LOG_ENABLED()) { + if (profiler_thread_is_being_profiled() || PAGELOAD_LOG_ENABLED()) { TimeDuration elapsed = mTTFI - mNavigationStart; MOZ_ASSERT(elapsed.ToMilliseconds() > 0); TimeDuration elapsedLongTask = @@ -447,7 +447,7 @@ mContentfulComposite = aCompositeEndTime; - if (profiler_can_accept_markers() || PAGELOAD_LOG_ENABLED()) { + if (profiler_thread_is_being_profiled() || PAGELOAD_LOG_ENABLED()) { TimeDuration elapsed = mContentfulComposite - mNavigationStart; nsAutoCString spec; if (mLoadedURI) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/nsFrameMessageManager.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/base/nsFrameMessageManager.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/nsFrameMessageManager.cpp 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/nsFrameMessageManager.cpp 2021-10-20 19:28:16.000000000 +0000 @@ -1202,14 +1202,9 @@ if (stencil) { JS::CompileOptions options(cx); FillCompileOptionsForCachedStencil(options); - - if (JS::StencilCanLazilyParse(stencil)) { - // See TryCacheLoadAndCompileScript. - options.setSourceIsLazy(false); - } - + JS::InstantiateOptions instantiateOptions(options); JS::Rooted script( - cx, JS::InstantiateGlobalStencil(cx, options, stencil)); + cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil)); if (script) { if (aRunInUniqueScope) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/nsGlobalWindowInner.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/base/nsGlobalWindowInner.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/nsGlobalWindowInner.cpp 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/nsGlobalWindowInner.cpp 2021-10-20 19:28:16.000000000 +0000 @@ -6241,7 +6241,7 @@ const char* reason = GetTimeoutReasonString(timeout); nsCString str; - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled()) { TimeDuration originalInterval = timeout->When() - timeout->SubmitTime(); str.Append(reason); str.Append(" with interval "); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/nsHistory.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/base/nsHistory.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/nsHistory.cpp 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/nsHistory.cpp 2021-10-20 19:28:16.000000000 +0000 @@ -19,6 +19,7 @@ #include "mozilla/dom/Location.h" #include "mozilla/RefPtr.h" #include "mozilla/StaticPrefs_dom.h" +#include "mozilla/BasePrincipal.h" using namespace mozilla; using namespace mozilla::dom; @@ -137,7 +138,8 @@ aResult.setNull(); } -void nsHistory::Go(int32_t aDelta, CallerType aCallerType, ErrorResult& aRv) { +void nsHistory::Go(int32_t aDelta, nsIPrincipal& aSubjectPrincipal, + ErrorResult& aRv) { LOG(("nsHistory::Go(%d)", aDelta)); nsCOMPtr win(do_QueryReferent(mInnerWindow)); if (!win || !win->HasActiveDocument()) { @@ -149,7 +151,7 @@ // "When the go(delta) method is invoked, if delta is zero, the user agent // must act as if the location.reload() method was called instead." RefPtr location = win->Location(); - return location->Reload(false, aRv); + return location->Reload(false, aSubjectPrincipal, aRv); } RefPtr session_history = GetSessionHistory(); @@ -163,12 +165,16 @@ ? win->GetWindowContext()->HasValidTransientUserGestureActivation() : false; + CallerType callerType = aSubjectPrincipal.IsSystemPrincipal() + ? CallerType::System + : CallerType::NonSystem; + // Ignore the return value from Go(), since returning errors from Go() can // lead to exceptions and a possible leak of history length // AsyncGo throws if we hit the location change rate limit. if (StaticPrefs::dom_window_history_async()) { session_history->AsyncGo(aDelta, /* aRequireUserInteraction = */ false, - userActivation, aCallerType, aRv); + userActivation, callerType, aRv); } else { session_history->Go(aDelta, /* aRequireUserInteraction = */ false, userActivation, IgnoreErrors()); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/nsHistory.h firefox-trunk-95.0~a1~hg20211020r596404/dom/base/nsHistory.h --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/nsHistory.h 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/nsHistory.h 2021-10-20 19:28:16.000000000 +0000 @@ -45,7 +45,7 @@ mozilla::ErrorResult& aRv); void GetState(JSContext* aCx, JS::MutableHandle aResult, mozilla::ErrorResult& aRv) const; - void Go(int32_t aDelta, mozilla::dom::CallerType aCallerType, + void Go(int32_t aDelta, nsIPrincipal& aSubjectPrincipal, mozilla::ErrorResult& aRv); void Back(mozilla::dom::CallerType aCallerType, mozilla::ErrorResult& aRv); void Forward(mozilla::dom::CallerType aCallerType, mozilla::ErrorResult& aRv); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/nsJSUtils.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/base/nsJSUtils.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/nsJSUtils.cpp 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/nsJSUtils.cpp 2021-10-20 19:28:16.000000000 +0000 @@ -90,7 +90,8 @@ return NS_OK; } - if (!JS::UpdateDebugMetadata(cx, script, aOptions, aPrivateValue, + JS::InstantiateOptions instantiateOptions(aOptions); + if (!JS::UpdateDebugMetadata(cx, script, instantiateOptions, aPrivateValue, aElementAttributeName, nullptr, nullptr)) { return NS_ERROR_FAILURE; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/nsLineBreaker.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/base/nsLineBreaker.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/nsLineBreaker.cpp 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/nsLineBreaker.cpp 2021-10-20 19:28:17.000000000 +0000 @@ -14,7 +14,7 @@ #include "mozilla/intl/MozLocale.h" using mozilla::intl::LineBreaker; -using mozilla::intl::Locale; +using mozilla::intl::MozLocale; nsLineBreaker::nsLineBreaker() : mCurrentWordLanguage(nullptr), @@ -471,7 +471,7 @@ mScriptIsChineseOrJapanese = false; } else { if (aHyphenationLanguage && !mCurrentWordLanguage) { - Locale loc = Locale(nsAtomCString(aHyphenationLanguage)); + MozLocale loc = MozLocale(nsAtomCString(aHyphenationLanguage)); if (loc.GetScript().IsEmpty()) { loc.Maximize(); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/PlacesBookmarkTags.h firefox-trunk-95.0~a1~hg20211020r596404/dom/base/PlacesBookmarkTags.h --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/PlacesBookmarkTags.h 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/PlacesBookmarkTags.h 2021-10-20 19:28:16.000000000 +0000 @@ -0,0 +1,54 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_PlacesBookmarkTags_h +#define mozilla_dom_PlacesBookmarkTags_h + +#include "mozilla/dom/PlacesBookmarkChanged.h" + +namespace mozilla { +namespace dom { +class PlacesBookmarkTags final : public PlacesBookmarkChanged { + public: + explicit PlacesBookmarkTags() + : PlacesBookmarkChanged(PlacesEventType::Bookmark_tags_changed) {} + + static already_AddRefed Constructor( + const GlobalObject& aGlobal, const PlacesBookmarkTagsInit& aInitDict) { + RefPtr event = new PlacesBookmarkTags(); + event->mId = aInitDict.mId; + event->mItemType = aInitDict.mItemType; + event->mUrl = aInitDict.mUrl; + event->mGuid = aInitDict.mGuid; + event->mParentGuid = aInitDict.mParentGuid; + event->mTags = aInitDict.mTags; + event->mLastModified = aInitDict.mLastModified; + event->mSource = aInitDict.mSource; + event->mIsTagging = aInitDict.mIsTagging; + return event.forget(); + } + + JSObject* WrapObject(JSContext* aCx, + JS::Handle aGivenProto) override { + return PlacesBookmarkTags_Binding::Wrap(aCx, this, aGivenProto); + } + + const PlacesBookmarkTags* AsPlacesBookmarkTags() const override { + return this; + } + + void GetTags(nsTArray& aTags) const { aTags.Assign(mTags); } + + nsTArray mTags; + + private: + ~PlacesBookmarkTags() = default; +}; + +} // namespace dom +} // namespace mozilla + +#endif diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/PlacesEvent.h firefox-trunk-95.0~a1~hg20211020r596404/dom/base/PlacesEvent.h --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/PlacesEvent.h 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/PlacesEvent.h 2021-10-20 19:28:16.000000000 +0000 @@ -47,6 +47,9 @@ virtual const PlacesBookmarkGuid* AsPlacesBookmarkGuid() const { return nullptr; } + virtual const PlacesBookmarkTags* AsPlacesBookmarkTags() const { + return nullptr; + } virtual const PlacesBookmarkTime* AsPlacesBookmarkTime() const { return nullptr; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/ShadowRoot.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/base/ShadowRoot.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/ShadowRoot.cpp 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/ShadowRoot.cpp 2021-10-20 19:28:16.000000000 +0000 @@ -753,23 +753,15 @@ slot->EnqueueSlotChangeEvent(); } -Element* ShadowRoot::GetFirstFocusable(bool aWithMouse) const { - for (nsIContent* child = GetFirstChild(); child; - child = child->GetNextNode()) { - if (auto* slot = HTMLSlotElement::FromNode(child)) { - const nsTArray>& assignedNodes = slot->AssignedNodes(); - for (const auto& node : assignedNodes) { - if (node->IsElement()) { - Element* assignedElement = node->AsElement(); - if (nsIFrame* frame = assignedElement->GetPrimaryFrame()) { - if (frame->IsFocusable(aWithMouse)) { - return assignedElement; - } - } - } - } - } +// Use aParent as the root element and loop through this tree (including slot +// assigned elements, nested shadow trees) to find the first focusable +// element. +static Element* GetFirstFocusableForParent(nsIContent* aParent, + bool aWithMouse) { + FlattenedChildIterator iter(aParent); + for (nsIContent* child = iter.GetNextChild(); child; + child = iter.GetNextChild()) { if (child->IsElement()) { if (nsIFrame* frame = child->GetPrimaryFrame()) { if (frame->IsFocusable(aWithMouse)) { @@ -778,16 +770,19 @@ } } - if (ShadowRoot* root = child->GetShadowRoot()) { - if (Element* firstFocusable = root->GetFirstFocusable(aWithMouse)) { - return firstFocusable; - } + if (Element* firstFocusable = + GetFirstFocusableForParent(child, aWithMouse)) { + return firstFocusable; } } return nullptr; } +Element* ShadowRoot::GetFirstFocusable(bool aWithMouse) const { + return GetFirstFocusableForParent(Host(), aWithMouse); +} + void ShadowRoot::MaybeSlotHostChild(nsIContent& aChild) { MOZ_ASSERT(aChild.GetParent() == GetHost()); // Check to ensure that the child not an anonymous subtree root because even diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/ShadowRoot.h firefox-trunk-95.0~a1~hg20211020r596404/dom/base/ShadowRoot.h --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/ShadowRoot.h 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/ShadowRoot.h 2021-10-20 19:28:16.000000000 +0000 @@ -68,8 +68,7 @@ // child from the currently-assigned slot, if any. void MaybeUnslotHostChild(nsIContent&); - // Loop through this tree (including slot assigned elements, nested shadow - // trees) to find the first focusable element. + // Find the first focusable element in this tree. Element* GetFirstFocusable(bool aWithMouse) const; // Shadow DOM v1 diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/browser_bug593387.js firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/browser_bug593387.js --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/browser_bug593387.js 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/browser_bug593387.js 2021-10-20 19:28:17.000000000 +0000 @@ -13,6 +13,7 @@ set: [ ["security.csp.enable", false], ["dom.security.skip_about_page_has_csp_assert", true], + ["dom.security.https_first", false], ], }); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/browser_multiple_popups.js firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/browser_multiple_popups.js --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/browser_multiple_popups.js 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/browser_multiple_popups.js 2021-10-20 19:28:16.000000000 +0000 @@ -3,7 +3,7 @@ * time (depending on "dom.allow_mulitple_popups" preference value). */ -const TEST_DOMAIN = "http://example.net"; +const TEST_DOMAIN = "https://example.net"; const TEST_PATH = "/browser/dom/base/test/"; const CHROME_DOMAIN = "chrome://mochitests/content"; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/browser_use_counters.js firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/browser_use_counters.js --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/browser_use_counters.js 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/browser_use_counters.js 2021-10-20 19:28:17.000000000 +0000 @@ -2,7 +2,7 @@ requestLongerTimeout(2); -const gHttpTestRoot = "http://example.com/browser/dom/base/test/"; +const gHttpTestRoot = "https://example.com/browser/dom/base/test/"; /** * Enable local telemetry recording for the duration of the tests. diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/bug1576154.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/bug1576154.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/bug1576154.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/bug1576154.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,8 +1,8 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setStatusLine("1.1", 500, "Internal Server Error"); response.setHeader("Content-Type", "image/svg+xml", false); - let body = ""; + let body = + ""; response.bodyOutputStream.write(body, body.length); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/bug282547.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/bug282547.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/bug282547.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/bug282547.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,8 +1,7 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setStatusLine(null, 401, "Unauthorized"); - response.setHeader("WWW-Authenticate", "basic realm=\"restricted\"", false); + response.setHeader("WWW-Authenticate", 'basic realm="restricted"', false); response.setHeader("Access-Control-Allow-Origin", "*", false); response.setHeader("Access-Control-Allow-Credentials", "true", false); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/bug435425_redirect.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/bug435425_redirect.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/bug435425_redirect.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/bug435425_redirect.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,6 +1,4 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setStatusLine(null, 302, "Moved"); response.setHeader("Location", "http://nosuchdomain.localhost", false); } - diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/bug435425.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/bug435425.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/bug435425.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/bug435425.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,10 +1,11 @@ const CC = Components.Constructor; -const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", - "setInputStream"); +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setHeader("Content-Type", "text/plain", false); if (request.method == "GET") { response.write(request.queryString); @@ -14,11 +15,11 @@ var avail; var bytes = []; - while ((avail = body.available()) > 0) + while ((avail = body.available()) > 0) { Array.prototype.push.apply(bytes, body.readByteArray(avail)); + } var data = String.fromCharCode.apply(null, bytes); response.bodyOutputStream.write(data, data.length); } } - diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/bug444546.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/bug444546.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/bug444546.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/bug444546.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,20 +1,21 @@ const CC = Components.Constructor; -const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", - "setInputStream"); +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setHeader("Content-Type", "text/plain", false); var body = new BinaryInputStream(request.bodyInputStream); var avail; var bytes = []; - while ((avail = body.available()) > 0) + while ((avail = body.available()) > 0) { Array.prototype.push.apply(bytes, body.readByteArray(avail)); + } var data = String.fromCharCode.apply(null, bytes); response.bodyOutputStream.write(data, data.length); } - diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/bug457746.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/bug457746.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/bug457746.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/bug457746.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,11 +1,10 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setHeader("Content-Type", "text/plain; charset=ISO-8859-1", false); - const body = [0xC1]; - var bos = Components.classes["@mozilla.org/binaryoutputstream;1"] - .createInstance(Components.interfaces.nsIBinaryOutputStream); + const body = [0xc1]; + var bos = Components.classes[ + "@mozilla.org/binaryoutputstream;1" + ].createInstance(Components.interfaces.nsIBinaryOutputStream); bos.setOutputStream(response.bodyOutputStream); bos.writeByteArray(body); } - diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/bug461735-redirect1.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/bug461735-redirect1.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/bug461735-redirect1.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/bug461735-redirect1.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,4 +1,8 @@ function handleRequest(request, response) { response.setStatusLine(null, 302, "Found"); - response.setHeader("Location", "http://example.com/tests/dom/base/test/bug461735-post-redirect.js", false); + response.setHeader( + "Location", + "http://example.com/tests/dom/base/test/bug461735-post-redirect.js", + false + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/bug461735-redirect2.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/bug461735-redirect2.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/bug461735-redirect2.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/bug461735-redirect2.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,4 +1,8 @@ function handleRequest(request, response) { response.setStatusLine(null, 302, "Found"); - response.setHeader("Location", "http://mochi.test:8888/tests/dom/base/test/bug461735-post-redirect.js", false); + response.setHeader( + "Location", + "http://mochi.test:8888/tests/dom/base/test/bug461735-post-redirect.js", + false + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/bug466080.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/bug466080.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/bug466080.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/bug466080.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,18 +1,14 @@ +function handleRequest(request, response) { + var body = "loaded"; + var origin = "localhost"; + try { + var origin = request.getHeader("Origin"); + } catch (e) {} -function handleRequest(request, response) -{ - var body = "loaded"; - var origin = "localhost"; - try { - var origin = request.getHeader("Origin"); - } catch(e) {} - - response.setHeader("Access-Control-Allow-Origin", - origin, - false); - response.setHeader("Access-Control-Allow-Credentials", "true", false); - response.setHeader("Access-Control-Allow-Methods", "XMETHOD", false); - response.setHeader("Connection", "Keep-alive", false); + response.setHeader("Access-Control-Allow-Origin", origin, false); + response.setHeader("Access-Control-Allow-Credentials", "true", false); + response.setHeader("Access-Control-Allow-Methods", "XMETHOD", false); + response.setHeader("Connection", "Keep-alive", false); - response.bodyOutputStream.write(body, body.length); + response.bodyOutputStream.write(body, body.length); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/bug475156.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/bug475156.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/bug475156.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/bug475156.sjs 2021-10-20 19:28:17.000000000 +0000 @@ -1,27 +1,23 @@ -function handleRequest(request, response) -{ - if (request.queryString == "") - { - var etag = request.hasHeader("If-Match") ? request.getHeader("If-Match") : null; - if (!etag || etag == getState("etag")) - { +function handleRequest(request, response) { + if (request.queryString == "") { + var etag = request.hasHeader("If-Match") + ? request.getHeader("If-Match") + : null; + if (!etag || etag == getState("etag")) { response.setStatusLine(request.httpVersion, 200, "Ok"); response.setHeader("Content-Type", "text/html"); response.setHeader("ETag", getState("etag")); response.setHeader("Cache-control", "max-age=36000"); response.write(getState("etag")); + } else if (etag) { + response.setStatusLine(request.httpVersion, 412, "Precondition Failed"); } - else if (etag) - { - response.setStatusLine(request.httpVersion, 412, "Precondition Failed"); - } - } - else - { + } else { var etag = request.queryString.match(/^etag=(.*)$/); - if (etag) + if (etag) { setState("etag", etag[1]); - + } + response.setStatusLine(request.httpVersion, 204, "No content"); } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/bug482935.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/bug482935.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/bug482935.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/bug482935.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -3,7 +3,7 @@ try { body = request.getHeader("X-Request"); - } catch(e) { + } catch (e) { body = "request.getHeader() failed! Exception: " + e; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/bug540854.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/bug540854.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/bug540854.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/bug540854.sjs 2021-10-20 19:28:17.000000000 +0000 @@ -1,18 +1,20 @@ const CC = Components.Constructor; -const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", - "setInputStream"); +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setHeader("Content-Type", "text/plain", false); var body = new BinaryInputStream(request.bodyInputStream); var avail; var bytes = []; - while ((avail = body.available()) > 0) + while ((avail = body.available()) > 0) { Array.prototype.push.apply(bytes, body.readByteArray(avail)); + } var data = String.fromCharCode.apply(null, bytes); response.bodyOutputStream.write(data, data.length); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/bug638112.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/bug638112.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/bug638112.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/bug638112.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,26 +1,24 @@ -function getInputStream(path) -{ - var file = Components.classes["@mozilla.org/file/directory_service;1"]. - getService(Components.interfaces.nsIProperties). - get("CurWorkD", Components.interfaces.nsIFile); - var fis = Components.classes['@mozilla.org/network/file-input-stream;1']. - createInstance(Components.interfaces.nsIFileInputStream); +function getInputStream(path) { + var file = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("CurWorkD", Components.interfaces.nsIFile); + var fis = Components.classes[ + "@mozilla.org/network/file-input-stream;1" + ].createInstance(Components.interfaces.nsIFileInputStream); var split = path.split("/"); - for(var i = 0; i < split.length; ++i) { + for (var i = 0; i < split.length; ++i) { file.append(split[i]); } fis.init(file, -1, -1, false); return fis; } - - -function handleRequest(request, response) -{ - var inputStream = getInputStream("tests/dom/base/test/bug638112-response.txt") +function handleRequest(request, response) { + var inputStream = getInputStream( + "tests/dom/base/test/bug638112-response.txt" + ); response.seizePower(); - response.bodyOutputStream.writeFrom(inputStream, - inputStream.available()); + response.bodyOutputStream.writeFrom(inputStream, inputStream.available()); response.finish(); inputStream.close(); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/bug704320_counter.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/bug704320_counter.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/bug704320_counter.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/bug704320_counter.sjs 2021-10-20 19:28:17.000000000 +0000 @@ -1,44 +1,47 @@ // Handle counting loads for bug 704320. -const SHARED_KEY="bug704320_counter"; -const DEFAULT_STATE = {'css': {'count': 0, 'referrers': []}, - 'img': {'count': 0, 'referrers': []}, - 'js': {'count': 0, 'referrers': []}}; -const TYPE_MAP = {'css': 'text/css', - 'js': 'application/javascript', - 'img': 'image/png', - 'html': 'text/html'}; +const SHARED_KEY = "bug704320_counter"; +const DEFAULT_STATE = { + css: { count: 0, referrers: [] }, + img: { count: 0, referrers: [] }, + js: { count: 0, referrers: [] }, +}; +const TYPE_MAP = { + css: "text/css", + js: "application/javascript", + img: "image/png", + html: "text/html", +}; // Writes an image to the response -function WriteOutImage(response) -{ +function WriteOutImage(response) { var file = Components.classes["@mozilla.org/file/directory_service;1"] - .getService(Components.interfaces.nsIProperties) - .get("CurWorkD", Components.interfaces.nsIFile); + .getService(Components.interfaces.nsIProperties) + .get("CurWorkD", Components.interfaces.nsIFile); file.append("tests"); file.append("image"); file.append("test"); file.append("mochitest"); - file.append('blue.png'); + file.append("blue.png"); - var fileStream = Components.classes['@mozilla.org/network/file-input-stream;1'] - .createInstance(Components.interfaces.nsIFileInputStream); + var fileStream = Components.classes[ + "@mozilla.org/network/file-input-stream;1" + ].createInstance(Components.interfaces.nsIFileInputStream); fileStream.init(file, 1, 0, false); response.bodyOutputStream.writeFrom(fileStream, fileStream.available()); } -function handleRequest(request, response) -{ +function handleRequest(request, response) { var query = {}; - request.queryString.split('&').forEach(function (val) { - var [name, value] = val.split('='); + request.queryString.split("&").forEach(function(val) { + var [name, value] = val.split("="); query[name] = unescape(value); }); var referrerLevel = "none"; - if (request.hasHeader('Referer')) { - let referrer = request.getHeader('Referer'); + if (request.hasHeader("Referer")) { + let referrer = request.getHeader("Referer"); if (referrer.indexOf("bug704320") > 0) { referrerLevel = "full"; } else if (referrer == "http://mochi.test:8888/") { @@ -47,7 +50,7 @@ } var state = getSharedState(SHARED_KEY); - if (state === '') { + if (state === "") { state = DEFAULT_STATE; } else { state = JSON.parse(state); @@ -55,7 +58,6 @@ response.setStatusLine(request.httpVersion, 200, "OK"); - //avoid confusing cache behaviors response.setHeader("Cache-Control", "no-cache", false); @@ -73,20 +75,20 @@ return; } - if ('type' in query) { + if ("type" in query) { state[query.type].count++; response.setHeader("Content-Type", TYPE_MAP[query.type], false); if (state[query.type].referrers.indexOf(referrerLevel) < 0) { state[query.type].referrers.push(referrerLevel); } - if (query.type == 'img') { + if (query.type == "img") { WriteOutImage(response); } } - if ('content' in query) { - response.write(unescape(query['content'])); + if ("content" in query) { + response.write(unescape(query.content)); } setSharedState(SHARED_KEY, JSON.stringify(state)); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/bug704320.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/bug704320.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/bug704320.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/bug704320.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,20 +1,46 @@ -var BASE_URL = 'example.com/tests/dom/base/test/bug704320.sjs'; +var BASE_URL = "example.com/tests/dom/base/test/bug704320.sjs"; function createTestUrl(schemeFrom, schemeTo, policy, action, type) { - return schemeTo + '://' + BASE_URL + '?' + - 'action=' + action + '&' + - 'scheme=' + schemeFrom + '-to-' + schemeTo + '&' + - 'policy=' + policy + '&' + - 'type=' + type; + return ( + schemeTo + + "://" + + BASE_URL + + "?" + + "action=" + + action + + "&" + + "scheme=" + + schemeFrom + + "-to-" + + schemeTo + + "&" + + "policy=" + + policy + + "&" + + "type=" + + type + ); } function create2ndLevelIframeUrl(schemeFrom, schemeTo, policy, type) { - return schemeFrom + '://' + BASE_URL + '?' + - 'action=create-2nd-level-iframe&' + - 'scheme-from=' + schemeFrom + '&' + - 'scheme-to=' + schemeTo + '&' + - 'policy=' + policy + '&' + - 'type=' + type; + return ( + schemeFrom + + "://" + + BASE_URL + + "?" + + "action=create-2nd-level-iframe&" + + "scheme-from=" + + schemeFrom + + "&" + + "scheme-to=" + + schemeTo + + "&" + + "policy=" + + policy + + "&" + + "type=" + + type + ); } // Creates the following test cases for the specified scheme and referrer @@ -46,68 +72,113 @@ // results. function createTest(schemeFrom, schemeTo, policy, optionalEarlierPolicy) { var _createTestUrl = createTestUrl.bind( - null, schemeFrom, schemeTo, policy, 'test'); + null, + schemeFrom, + schemeTo, + policy, + "test" + ); var _create2ndLevelIframeUrl = create2ndLevelIframeUrl.bind( - null, schemeFrom, schemeTo, policy); - - var metaReferrerPolicyString = ''; - if (optionalEarlierPolicy && optionalEarlierPolicy != '') { - metaReferrerPolicyString += '\n'; + null, + schemeFrom, + schemeTo, + policy + ); + + var metaReferrerPolicyString = ""; + if (optionalEarlierPolicy && optionalEarlierPolicy != "") { + metaReferrerPolicyString += + '\n'; } metaReferrerPolicyString += ''; - return '\n\ + return ( + "\n\ \n\ \n\ - '+metaReferrerPolicyString+'\n\ - \n\ + " + + metaReferrerPolicyString + + '\n\ + \n\ \n\ \n\ \n\ - \n\ - \n\ - \n\ - \n\ - \n\ - \n\ - \n\ - \n\ - \n\ - \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ \n\ \n\ - '; + ' + ); } function createIframedFormTest(schemeFrom, schemeTo, policy) { - var actionUrl = schemeTo + '://' + BASE_URL; + var actionUrl = schemeTo + "://" + BASE_URL; - return '\n\ + return ( + '\n\ \n\ \n\ - \n\ + \n\ \n\ \n\ -
\n\ + \n\ \n\ - \n\ - \n\ + \n\ + \n\ \n\
\n\ \n\ \n\ - '; + ' + ); } function createIframedWindowLocationTest(schemeFrom, schemeTo, policy) { var url = createTestUrl( - schemeFrom, schemeTo, policy, 'test', 'window.location'); + schemeFrom, + schemeTo, + policy, + "test", + "window.location" + ); - return '\n\ + return ( + '\n\ \n\ \n\ - \n\ + \n\ \n\ \n\ \n\ \n\ - '; + ' + ); } function createPolicyTest(policy, optionalEarlierPolicy) { - var metaReferrerPolicyString = ''; - if (optionalEarlierPolicy && optionalEarlierPolicy != '') { - metaReferrerPolicyString += '\n'; + var metaReferrerPolicyString = ""; + if (optionalEarlierPolicy && optionalEarlierPolicy != "") { + metaReferrerPolicyString += + '\n'; } metaReferrerPolicyString += ''; - return '\n\ + return ( + "\n\ \n\ \n\ - '+metaReferrerPolicyString+'\n\ + " + + metaReferrerPolicyString + + '\n\ \n\ \n\ \n\ @@ -190,53 +290,55 @@ \n\ \n\ - '; + ' + ); } function handleRequest(request, response) { - var sharedKey = 'bug704320.sjs'; - var params = request.queryString.split('&'); - var action = params[0].split('=')[1]; + var sharedKey = "bug704320.sjs"; + var params = request.queryString.split("&"); + var action = params[0].split("=")[1]; - if (action === 'create-1st-level-iframe') { + if (action === "create-1st-level-iframe") { // ?action=create-1st-level-iframe&scheme-from=http&scheme-to=https&policy=origin - var schemeFrom = params[1].split('=')[1]; - var schemeTo = params[2].split('=')[1]; - var policy = params[3].split('=')[1]; - var optionalEarlierPolicy = ''; + var schemeFrom = params[1].split("=")[1]; + var schemeTo = params[2].split("=")[1]; + var policy = params[3].split("=")[1]; + var optionalEarlierPolicy = ""; if (params[4]) { - optionalEarlierPolicy = params[4].split('=')[1]; + optionalEarlierPolicy = params[4].split("=")[1]; } - response.setHeader('Content-Type', 'text/html; charset=utf-8', false); - response.setHeader('Cache-Control', 'no-cache', false); - response.write(createTest(schemeFrom, schemeTo, policy, optionalEarlierPolicy)); - } - else if (action === 'create-2nd-level-iframe') { + response.setHeader("Content-Type", "text/html; charset=utf-8", false); + response.setHeader("Cache-Control", "no-cache", false); + response.write( + createTest(schemeFrom, schemeTo, policy, optionalEarlierPolicy) + ); + } else if (action === "create-2nd-level-iframe") { // ?action=create-2nd-level-iframe&scheme-from=http&scheme-to=https&policy=origin&type=form" - var schemeFrom = params[1].split('=')[1]; - var schemeTo = params[2].split('=')[1]; - var policy = params[3].split('=')[1]; - var type = params[4].split('=')[1]; + var schemeFrom = params[1].split("=")[1]; + var schemeTo = params[2].split("=")[1]; + var policy = params[3].split("=")[1]; + var type = params[4].split("=")[1]; - response.setHeader('Content-Type', 'text/html; charset=utf-8', false); - response.setHeader('Cache-Control', 'no-cache', false); + response.setHeader("Content-Type", "text/html; charset=utf-8", false); + response.setHeader("Cache-Control", "no-cache", false); - if (type === 'form') { + if (type === "form") { response.write(createIframedFormTest(schemeFrom, schemeTo, policy)); - } else if (type === 'window.location') { - response.write(createIframedWindowLocationTest( - schemeFrom, schemeTo, policy)); + } else if (type === "window.location") { + response.write( + createIframedWindowLocationTest(schemeFrom, schemeTo, policy) + ); } - } - else if (action === 'test') { + } else if (action === "test") { // ?action=test&scheme=http-to-https&policy=origin&type=img - var scheme = params[1].split('=')[1]; - var policy = params[2].split('=')[1]; - var type = params[3].split('=')[1]; + var scheme = params[1].split("=")[1]; + var policy = params[2].split("=")[1]; + var type = params[3].split("=")[1]; var result = getSharedState(sharedKey); - if (result === '') { + if (result === "") { result = {}; } else { result = JSON.parse(result); @@ -250,42 +352,43 @@ result[type][scheme] = {}; } - if (request.hasHeader('Referer')) { - result[type][scheme][policy] = request.getHeader('Referer'); + if (request.hasHeader("Referer")) { + result[type][scheme][policy] = request.getHeader("Referer"); } else { - result[type][scheme][policy] = ''; + result[type][scheme][policy] = ""; } setSharedState(sharedKey, JSON.stringify(result)); - if (type === 'link') { - var loc = 'https://example.com/tests/dom/base/test/file_bug704320_redirect.html'; - response.setStatusLine('1.1', 302, 'Found'); - response.setHeader('Location', loc, false); + if (type === "link") { + var loc = + "https://example.com/tests/dom/base/test/file_bug704320_redirect.html"; + response.setStatusLine("1.1", 302, "Found"); + response.setHeader("Location", loc, false); } - if (type === 'window.open') { - response.setHeader('Cache-Control', 'no-cache', false); - response.setHeader('Content-Type', 'text/html', false); - response.write(''); + if (type === "window.open") { + response.setHeader("Cache-Control", "no-cache", false); + response.setHeader("Content-Type", "text/html", false); + response.write( + "" + ); } - } - else if (action === 'get-test-results') { + } else if (action === "get-test-results") { // ?action=get-result - response.setHeader('Cache-Control', 'no-cache', false); - response.setHeader('Content-Type', 'text/plain', false); + response.setHeader("Cache-Control", "no-cache", false); + response.setHeader("Content-Type", "text/plain", false); response.write(getSharedState(sharedKey)); - } - else if (action === 'generate-policy-test') { + } else if (action === "generate-policy-test") { // ?action=generate-policy-test&policy=b64-encoded-string - response.setHeader('Cache-Control', 'no-cache', false); - response.setHeader('Content-Type', 'text/html', false); - var policy = unescape(params[1].split('=')[1]); - var optionalEarlierPolicy = ''; + response.setHeader("Cache-Control", "no-cache", false); + response.setHeader("Content-Type", "text/html", false); + var policy = unescape(params[1].split("=")[1]); + var optionalEarlierPolicy = ""; if (params[2]) { - optionalEarlierPolicy = params[2].split('=')[1]; + optionalEarlierPolicy = params[2].split("=")[1]; } response.write(createPolicyTest(policy, optionalEarlierPolicy)); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/bug819051.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/bug819051.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/bug819051.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/bug819051.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,7 +1,9 @@ -function handleRequest(request, response) -{ - response.setStatusLine(request.httpVersion, 200, "Ok"); - response.setHeader("X-appended-result", request.getHeader("X-appended-to-this")); - response.setHeader("X-Accept-Result", request.getHeader("Accept")); - response.write(""); +function handleRequest(request, response) { + response.setStatusLine(request.httpVersion, 200, "Ok"); + response.setHeader( + "X-appended-result", + request.getHeader("X-appended-to-this") + ); + response.setHeader("X-Accept-Result", request.getHeader("Accept")); + response.write(""); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/chrome/bug421622-referer.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/chrome/bug421622-referer.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/chrome/bug421622-referer.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/chrome/bug421622-referer.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -2,7 +2,8 @@ response.setHeader("Content-Type", "text/plain", false); response.setHeader("Cache-Control", "no-cache", false); - var referer = request.hasHeader("Referer") ? request.getHeader("Referer") - : ""; + var referer = request.hasHeader("Referer") + ? request.getHeader("Referer") + : ""; response.write("Referer: " + referer); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/chrome/bug884693.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/chrome/bug884693.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/chrome/bug884693.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/chrome/bug884693.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,5 +1,4 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { let [status, statusText, encodedBody] = request.queryString.split("&"); let body = decodeURIComponent(encodedBody); response.setStatusLine(request.httpVersion, status, statusText); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/delayedServerEvents.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/delayedServerEvents.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/delayedServerEvents.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/delayedServerEvents.sjs 2021-10-20 19:28:17.000000000 +0000 @@ -1,10 +1,18 @@ // this will take strings_to_send.length*500 ms = 5 sec var timer = null; -var strings_to_send = ["retry:999999999\ndata\r\n\nda", "ta", ":", "de", "layed1\n\n", - "", - "", - "data:delayed2\n\n", "", ""]; +var strings_to_send = [ + "retry:999999999\ndata\r\n\nda", + "ta", + ":", + "de", + "layed1\n\n", + "", + "", + "data:delayed2\n\n", + "", + "", +]; var resp = null; function sendNextString() { @@ -29,7 +37,7 @@ var bytes = strings_to_send.reduce((len, s) => len + s.length, 0); response.seizePower(); - response.write("HTTP/1.1 200 OK\r\n") + response.write("HTTP/1.1 200 OK\r\n"); response.write(`Content-Length: ${bytes}\r\n`); response.write("Content-Type: text/event-stream; charset=utf-8\r\n"); response.write("Cache-Control: no-cache, must-revalidate\r\n"); @@ -37,6 +45,12 @@ resp = response; - timer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer); - timer.initWithCallback(sendNextString, 500, Components.interfaces.nsITimer.TYPE_REPEATING_SLACK); + timer = Components.classes["@mozilla.org/timer;1"].createInstance( + Components.interfaces.nsITimer + ); + timer.initWithCallback( + sendNextString, + 500, + Components.interfaces.nsITimer.TYPE_REPEATING_SLACK + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/file_bug1250148.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/file_bug1250148.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/file_bug1250148.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/file_bug1250148.sjs 2021-10-20 19:28:17.000000000 +0000 @@ -1,7 +1,9 @@ const CC = Components.Constructor; -const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", - "setInputStream"); +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); function utf8decode(s) { return decodeURIComponent(escape(s)); @@ -23,35 +25,46 @@ if (request.method == "POST") { var contentTypeParams = {}; - request.getHeader("Content-Type").split(/\s*\;\s*/).forEach(function(s) { - if (s.indexOf('=') >= 0) { - let [name, value] = s.split('='); - contentTypeParams[name] = value; - } - else { - contentTypeParams[''] = s; - } - }); - - if (contentTypeParams[''] == "multipart/form-data" && - request.queryString == "") { - requestBody.split("--" + contentTypeParams.boundary).slice(1, -1).forEach(function (s) { - - let headers = {}; - let headerEnd = s.indexOf("\r\n\r\n"); - s.substr(2, headerEnd-2).split("\r\n").forEach(function(s) { - // We're assuming UTF8 for now - let [name, value] = s.split(': '); - headers[name] = utf8decode(value); - }); - - let body = s.substring(headerEnd + 4, s.length - 2); - if (!headers["Content-Type"] || headers["Content-Type"] == "text/plain") { - // We're assuming UTF8 for now - body = utf8decode(body); + request + .getHeader("Content-Type") + .split(/\s*\;\s*/) + .forEach(function(s) { + if (s.indexOf("=") >= 0) { + let [name, value] = s.split("="); + contentTypeParams[name] = value; + } else { + contentTypeParams[""] = s; } - result.push({ headers: headers, body: body}); }); + + if ( + contentTypeParams[""] == "multipart/form-data" && + request.queryString == "" + ) { + requestBody + .split("--" + contentTypeParams.boundary) + .slice(1, -1) + .forEach(function(s) { + let headers = {}; + let headerEnd = s.indexOf("\r\n\r\n"); + s.substr(2, headerEnd - 2) + .split("\r\n") + .forEach(function(s) { + // We're assuming UTF8 for now + let [name, value] = s.split(": "); + headers[name] = utf8decode(value); + }); + + let body = s.substring(headerEnd + 4, s.length - 2); + if ( + !headers["Content-Type"] || + headers["Content-Type"] == "text/plain" + ) { + // We're assuming UTF8 for now + body = utf8decode(body); + } + result.push({ headers, body }); + }); } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/file_bug1268962.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/file_bug1268962.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/file_bug1268962.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/file_bug1268962.sjs 2021-10-20 19:28:17.000000000 +0000 @@ -1,90 +1,92 @@ // Test server for bug 1268962 -'use strict'; +"use strict"; Components.utils.importGlobalProperties(["URLSearchParams"]); const HTTPStatus = new Map([ - [100, 'Continue'], - [101, 'Switching Protocol'], - [200, 'OK'], - [201, 'Created'], - [202, 'Accepted'], - [203, 'Non-Authoritative Information'], - [204, 'No Content'], - [205, 'Reset Content'], - [206, 'Partial Content'], - [300, 'Multiple Choice'], - [301, 'Moved Permanently'], - [302, 'Found'], - [303, 'See Other'], - [304, 'Not Modified'], - [305, 'Use Proxy'], - [306, 'unused'], - [307, 'Temporary Redirect'], - [308, 'Permanent Redirect'], - [400, 'Bad Request'], - [401, 'Unauthorized'], - [402, 'Payment Required'], - [403, 'Forbidden'], - [404, 'Not Found'], - [405, 'Method Not Allowed'], - [406, 'Not Acceptable'], - [407, 'Proxy Authentication Required'], - [408, 'Request Timeout'], - [409, 'Conflict'], - [410, 'Gone'], - [411, 'Length Required'], - [412, 'Precondition Failed'], - [413, 'Request Entity Too Large'], - [414, 'Request-URI Too Long'], - [415, 'Unsupported Media Type'], - [416, 'Requested Range Not Satisfiable'], - [417, 'Expectation Failed'], - [500, 'Internal Server Error'], - [501, 'Not Implemented'], - [502, 'Bad Gateway'], - [503, 'Service Unavailable'], - [504, 'Gateway Timeout'], - [505, 'HTTP Version Not Supported'] + [100, "Continue"], + [101, "Switching Protocol"], + [200, "OK"], + [201, "Created"], + [202, "Accepted"], + [203, "Non-Authoritative Information"], + [204, "No Content"], + [205, "Reset Content"], + [206, "Partial Content"], + [300, "Multiple Choice"], + [301, "Moved Permanently"], + [302, "Found"], + [303, "See Other"], + [304, "Not Modified"], + [305, "Use Proxy"], + [306, "unused"], + [307, "Temporary Redirect"], + [308, "Permanent Redirect"], + [400, "Bad Request"], + [401, "Unauthorized"], + [402, "Payment Required"], + [403, "Forbidden"], + [404, "Not Found"], + [405, "Method Not Allowed"], + [406, "Not Acceptable"], + [407, "Proxy Authentication Required"], + [408, "Request Timeout"], + [409, "Conflict"], + [410, "Gone"], + [411, "Length Required"], + [412, "Precondition Failed"], + [413, "Request Entity Too Large"], + [414, "Request-URI Too Long"], + [415, "Unsupported Media Type"], + [416, "Requested Range Not Satisfiable"], + [417, "Expectation Failed"], + [500, "Internal Server Error"], + [501, "Not Implemented"], + [502, "Bad Gateway"], + [503, "Service Unavailable"], + [504, "Gateway Timeout"], + [505, "HTTP Version Not Supported"], ]); -const SAME_ORIGIN = 'http://mochi.test:8888/tests/dom/base/test/file_bug1268962.sjs'; -const CROSS_ORIGIN = 'http://example.com/tests/dom/base/test/file_bug1268962.sjs'; +const SAME_ORIGIN = + "http://mochi.test:8888/tests/dom/base/test/file_bug1268962.sjs"; +const CROSS_ORIGIN = + "http://example.com/tests/dom/base/test/file_bug1268962.sjs"; function handleRequest(request, response) { const queryMap = new URLSearchParams(request.queryString); // Check redirection before everything else. - if (queryMap.has('redirect')) { - let redirect = queryMap.get('redirect'); + if (queryMap.has("redirect")) { + let redirect = queryMap.get("redirect"); let location; - if (redirect == 'sameorigin') { + if (redirect == "sameorigin") { location = SAME_ORIGIN; - } else if (redirect == 'crossorigin') { + } else if (redirect == "crossorigin") { location = CROSS_ORIGIN; } if (location) { // Use HTTP 302 redirection. - response.setStatusLine('1.1', 302, HTTPStatus.get(302)); + response.setStatusLine("1.1", 302, HTTPStatus.get(302)); // Forward query strings except the redirect option. - queryMap.delete('redirect'); - response.setHeader('Location', location + '?' + queryMap.toString()); + queryMap.delete("redirect"); + response.setHeader("Location", location + "?" + queryMap.toString()); return; } } - if (queryMap.has('statusCode')) { - let statusCode = parseInt(queryMap.get('statusCode')); + if (queryMap.has("statusCode")) { + let statusCode = parseInt(queryMap.get("statusCode")); let statusText = HTTPStatus.get(statusCode); - response.setStatusLine('1.1', statusCode, statusText); + response.setStatusLine("1.1", statusCode, statusText); } - if (queryMap.has('cacheControl')) { - let cacheControl = queryMap.get('cacheControl'); - response.setHeader('Cache-Control', cacheControl); + if (queryMap.has("cacheControl")) { + let cacheControl = queryMap.get("cacheControl"); + response.setHeader("Cache-Control", cacheControl); } - if (queryMap.has('allowOrigin')) { - let allowOrigin = queryMap.get('allowOrigin'); - response.setHeader('Access-Control-Allow-Origin', allowOrigin); + if (queryMap.has("allowOrigin")) { + let allowOrigin = queryMap.get("allowOrigin"); + response.setHeader("Access-Control-Allow-Origin", allowOrigin); } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/file_bug28293.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/file_bug28293.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/file_bug28293.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/file_bug28293.sjs 2021-10-20 19:28:17.000000000 +0000 @@ -1,5 +1,4 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setHeader("Content-Type", "text/plain", false); response.write(decodeURIComponent(request.queryString)); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/file_bug503473-frame.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/file_bug503473-frame.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/file_bug503473-frame.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/file_bug503473-frame.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -5,19 +5,18 @@ response.setHeader("Cache-Control", "no-cache", false); response.write( - '' + - '
' + - '' + "" + + "
" + + "" ); response.bodyOutputStream.flush(); // leave the stream open } - diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/file_bug503481.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/file_bug503481.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/file_bug503481.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/file_bug503481.sjs 2021-10-20 19:28:17.000000000 +0000 @@ -5,22 +5,28 @@ function attemptUnblock(s) { try { let blockedResponse = null; - getObjectState("bug503481_" + s, function(x) {blockedResponse = x.wrappedJSObject.r}); + getObjectState("bug503481_" + s, function(x) { + blockedResponse = x.wrappedJSObject.r; + }); blockedResponse.finish(); setObjectState("bug503481_" + s, null); - } catch(e) { + } catch (e) { dump("unable to unblock " + s + "retrying in half a second\n"); - timer = Components.classes["@mozilla.org/timer;1"] - .createInstance(nsITimer); - timer.initWithCallback(function () { attemptUnblock(s) }, 500, nsITimer.TYPE_ONE_SHOT); + timer = Components.classes["@mozilla.org/timer;1"].createInstance(nsITimer); + timer.initWithCallback( + function() { + attemptUnblock(s); + }, + 500, + nsITimer.TYPE_ONE_SHOT + ); } } -function handleRequest(request, response) -{ +function handleRequest(request, response) { var query = {}; - request.queryString.split('&').forEach(function (val) { - var [name, value] = val.split('='); + request.queryString.split("&").forEach(function(val) { + var [name, value] = val.split("="); query[name] = unescape(value); }); @@ -32,7 +38,12 @@ if (query.blockOn) { response.processAsync(); - x = { r: response, QueryInterface: function(iid) { return this } }; + x = { + r: response, + QueryInterface(iid) { + return this; + }, + }; x.wrappedJSObject = x; setObjectState("bug503481_" + query.blockOn, x); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/file_bug622088.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/file_bug622088.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/file_bug622088.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/file_bug622088.sjs 2021-10-20 19:28:17.000000000 +0000 @@ -1,6 +1,5 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { // Echos the referrer back to the requester. - response.setHeader('Content-Type', 'text/plain', false); - response.write(request.getHeader('Referer')); + response.setHeader("Content-Type", "text/plain", false); + response.write(request.getHeader("Referer")); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/file_bug675121.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/file_bug675121.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/file_bug675121.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/file_bug675121.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,15 +1,19 @@ var timer; -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setHeader("Cache-Control", "no-cache", false); response.setHeader("Content-Type", "text/plain", false); response.write("Responded"); response.processAsync(); - timer = Components.classes["@mozilla.org/timer;1"] - .createInstance(Components.interfaces.nsITimer); - timer.initWithCallback(function() { + timer = Components.classes["@mozilla.org/timer;1"].createInstance( + Components.interfaces.nsITimer + ); + timer.initWithCallback( + function() { response.finish(); - // 50ms certainly be enough for one refresh driver firing to happen! - }, 50, Components.interfaces.nsITimer.TYPE_ONE_SHOT); + // 50ms certainly be enough for one refresh driver firing to happen! + }, + 50, + Components.interfaces.nsITimer.TYPE_ONE_SHOT + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/file_bug787778.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/file_bug787778.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/file_bug787778.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/file_bug787778.sjs 2021-10-20 19:28:17.000000000 +0000 @@ -1,8 +1,7 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.processAsync(); response.setHeader("Content-Type", "text/plain", false); response.setHeader("X-Frame-Options", "DENY", false); - + response.finish(); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/file_explicit_user_agent.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/file_explicit_user_agent.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/file_explicit_user_agent.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/file_explicit_user_agent.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,8 +1,6 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { if (request.hasHeader("User-Agent")) { - response.setHeader("Result-User-Agent", - request.getHeader("User-Agent")); + response.setHeader("Result-User-Agent", request.getHeader("User-Agent")); } response.write(""); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/file_restrictedEventSource.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/file_restrictedEventSource.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/file_restrictedEventSource.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/file_restrictedEventSource.sjs 2021-10-20 19:28:17.000000000 +0000 @@ -1,48 +1,69 @@ -function handleRequest(request, response) -{ - if ((request.queryString == "test=user1_xhr" && - request.hasHeader("Authorization") && - request.getHeader("Authorization") == "Basic dXNlciAxOnBhc3N3b3JkIDE=") || - (request.queryString == "test=user1_evtsrc" && - request.hasHeader("Authorization") && - request.getHeader("Authorization") == "Basic dXNlciAxOnBhc3N3b3JkIDE=")) { +function handleRequest(request, response) { + if ( + (request.queryString == "test=user1_xhr" && + request.hasHeader("Authorization") && + request.getHeader("Authorization") == "Basic dXNlciAxOnBhc3N3b3JkIDE=") || + (request.queryString == "test=user1_evtsrc" && + request.hasHeader("Authorization") && + request.getHeader("Authorization") == "Basic dXNlciAxOnBhc3N3b3JkIDE=") + ) { response.setStatusLine(null, 200, "OK"); response.setHeader("Content-Type", "text/event-stream", false); - response.setHeader("Access-Control-Allow-Origin", "http://mochi.test:8888", false); + response.setHeader( + "Access-Control-Allow-Origin", + "http://mochi.test:8888", + false + ); response.setHeader("Access-Control-Allow-Credentials", "true", false); response.setHeader("Cache-Control", "no-cache, must-revalidate", false); if (request.queryString == "test=user1_xhr") { response.setHeader("Set-Cookie", "test=5c", false); } response.write("event: message\ndata: 1\n\n"); - } else if ((request.queryString == "test=user2_xhr" && - request.hasHeader("Authorization") && - request.getHeader("Authorization") == "Basic dXNlciAyOnBhc3N3b3JkIDI=") || - (request.queryString == "test=user2_evtsrc" && - request.hasHeader("Authorization") && - request.getHeader("Authorization") == "Basic dXNlciAyOnBhc3N3b3JkIDI=" && - request.hasHeader("Cookie") && - request.getHeader("Cookie") == "test=5d")) { + } else if ( + (request.queryString == "test=user2_xhr" && + request.hasHeader("Authorization") && + request.getHeader("Authorization") == "Basic dXNlciAyOnBhc3N3b3JkIDI=") || + (request.queryString == "test=user2_evtsrc" && + request.hasHeader("Authorization") && + request.getHeader("Authorization") == "Basic dXNlciAyOnBhc3N3b3JkIDI=" && + request.hasHeader("Cookie") && + request.getHeader("Cookie") == "test=5d") + ) { response.setStatusLine(null, 200, "OK"); response.setHeader("Content-Type", "text/event-stream", false); - response.setHeader("Access-Control-Allow-Origin", "http://mochi.test:8888", false); + response.setHeader( + "Access-Control-Allow-Origin", + "http://mochi.test:8888", + false + ); response.setHeader("Access-Control-Allow-Credentials", "true", false); response.setHeader("Cache-Control", "no-cache, must-revalidate", false); if (request.queryString == "test=user2_xhr") { response.setHeader("Set-Cookie", "test=5d", false); } response.write("event: message\ndata: 1\n\n"); - } else if (request.queryString == "test=user1_xhr" || - request.queryString == "test=user2_xhr") { + } else if ( + request.queryString == "test=user1_xhr" || + request.queryString == "test=user2_xhr" + ) { response.setStatusLine(null, 401, "Unauthorized"); - response.setHeader("WWW-Authenticate", "basic realm=\"restricted\"", false); - response.setHeader("Access-Control-Allow-Origin", "http://mochi.test:8888", false); + response.setHeader("WWW-Authenticate", 'basic realm="restricted"', false); + response.setHeader( + "Access-Control-Allow-Origin", + "http://mochi.test:8888", + false + ); response.setHeader("Access-Control-Allow-Credentials", "true", false); response.write("Unauthorized"); } else { response.setStatusLine(null, 403, "Forbidden"); - response.setHeader("Access-Control-Allow-Origin", "http://mochi.test:8888", false); + response.setHeader( + "Access-Control-Allow-Origin", + "http://mochi.test:8888", + false + ); response.setHeader("Access-Control-Allow-Credentials", "true", false); response.write("Forbidden"); } -} \ No newline at end of file +} diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/file_use_counter_svg_fill_pattern.svg firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/file_use_counter_svg_fill_pattern.svg --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/file_use_counter_svg_fill_pattern.svg 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/file_use_counter_svg_fill_pattern.svg 2021-10-20 19:28:16.000000000 +0000 @@ -8,7 +8,7 @@ - diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/file_x-frame-options_page.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/file_x-frame-options_page.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/file_x-frame-options_page.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/file_x-frame-options_page.sjs 2021-10-20 19:28:17.000000000 +0000 @@ -1,15 +1,18 @@ // SJS file for X-Frame-Options mochitests -function handleRequest(request, response) -{ +function handleRequest(request, response) { var query = {}; var BOUNDARY = "BOUNDARYOMG3984"; - request.queryString.split('&').forEach(function (val) { - var [name, value] = val.split('='); + request.queryString.split("&").forEach(function(val) { + var [name, value] = val.split("="); query[name] = unescape(value); }); - if (query['multipart'] == "1") { - response.setHeader("Content-Type", "multipart/x-mixed-replace;boundary=" + BOUNDARY, false); + if (query.multipart == "1") { + response.setHeader( + "Content-Type", + "multipart/x-mixed-replace;boundary=" + BOUNDARY, + false + ); response.setHeader("Cache-Control", "no-cache", false); response.setStatusLine(request.httpVersion, 200, "OK"); response.write("--" + BOUNDARY + "\r\n"); @@ -20,45 +23,45 @@ } var testHeaders = { - "deny": "DENY", - "sameorigin": "SAMEORIGIN", - "sameorigin2": "SAMEORIGIN, SAMEORIGIN", - "sameorigin3": "SAMEORIGIN,SAMEORIGIN , SAMEORIGIN", - "mixedpolicy": "DENY,SAMEORIGIN", + deny: "DENY", + sameorigin: "SAMEORIGIN", + sameorigin2: "SAMEORIGIN, SAMEORIGIN", + sameorigin3: "SAMEORIGIN,SAMEORIGIN , SAMEORIGIN", + mixedpolicy: "DENY,SAMEORIGIN", /* added for bug 836132 */ - "afa": "ALLOW-FROM http://mochi.test:8888/", - "afd": "ALLOW-FROM http://example.com/", - "afa1": "ALLOW-FROM http://mochi.test:8888", - "afd1": "ALLOW-FROM:example.com", - "afd2": "ALLOW-FROM: example.com", - "afd3": "ALLOW-FROM example.com", - "afd4": "ALLOW-FROM:http://example.com", - "afd5": "ALLOW-FROM: http://example.com", - "afd6": "ALLOW-FROM http://example.com", - "afd7": "ALLOW-FROM:mochi.test:8888", - "afd8": "ALLOW-FROM: mochi.test:8888", - "afd9": "ALLOW-FROM:http://mochi.test:8888", - "afd10": "ALLOW-FROM: http://mochi.test:8888", - "afd11": "ALLOW-FROM mochi.test:8888", - "afd12": "ALLOW-FROM", - "afd13": "ALLOW-FROM ", - "afd14": "ALLOW-FROM:" + afa: "ALLOW-FROM http://mochi.test:8888/", + afd: "ALLOW-FROM http://example.com/", + afa1: "ALLOW-FROM http://mochi.test:8888", + afd1: "ALLOW-FROM:example.com", + afd2: "ALLOW-FROM: example.com", + afd3: "ALLOW-FROM example.com", + afd4: "ALLOW-FROM:http://example.com", + afd5: "ALLOW-FROM: http://example.com", + afd6: "ALLOW-FROM http://example.com", + afd7: "ALLOW-FROM:mochi.test:8888", + afd8: "ALLOW-FROM: mochi.test:8888", + afd9: "ALLOW-FROM:http://mochi.test:8888", + afd10: "ALLOW-FROM: http://mochi.test:8888", + afd11: "ALLOW-FROM mochi.test:8888", + afd12: "ALLOW-FROM", + afd13: "ALLOW-FROM ", + afd14: "ALLOW-FROM:", }; - if (testHeaders.hasOwnProperty(query['xfo'])) { - response.setHeader("X-Frame-Options", testHeaders[query['xfo']], false); + if (testHeaders.hasOwnProperty(query.xfo)) { + response.setHeader("X-Frame-Options", testHeaders[query.xfo], false); } // from the test harness we'll be checking for the presence of this element // to test if the page loaded - response.write("

" + query["testid"] + "

"); + response.write('

' + query.testid + "

"); - if (query['testid'] == "postmessage") { + if (query.testid == "postmessage") { response.write(""); } - if (query['multipart'] == "1") { + if (query.multipart == "1") { response.write("\r\n--" + BOUNDARY + "\r\n"); } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/iframe_meta_refresh.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/iframe_meta_refresh.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/iframe_meta_refresh.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/iframe_meta_refresh.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,11 +1,11 @@ /* -* Test server for iframe refresh from meta http-equiv -*/ + * Test server for iframe refresh from meta http-equiv + */ -const SHARED_KEY="iframe_meta_refresh"; -const DEFAULT_STATE = {'count': 0, 'referrers': []}; +const SHARED_KEY = "iframe_meta_refresh"; +const DEFAULT_STATE = { count: 0, referrers: [] }; const REFRESH_PAGE = -"http://example.com/tests/dom/base/test/iframe_meta_refresh.sjs?action=test"; + "http://example.com/tests/dom/base/test/iframe_meta_refresh.sjs?action=test"; function createContent(refresh) { let metaRefresh = ""; @@ -35,16 +35,15 @@ `; } -function handleRequest(request, response) -{ +function handleRequest(request, response) { Components.utils.importGlobalProperties(["URLSearchParams"]); let query = new URLSearchParams(request.queryString); let action = query.get("action"); var referrerLevel = "none"; - if (request.hasHeader('Referer')) { - let referrer = request.getHeader('Referer'); + if (request.hasHeader("Referer")) { + let referrer = request.getHeader("Referer"); if (referrer.indexOf("test_meta_refresh_referrer") > 0) { referrerLevel = "full"; } else if (referrer == "http://mochi.test:8888/") { @@ -53,7 +52,7 @@ } var state = getSharedState(SHARED_KEY); - if (state === '') { + if (state === "") { state = DEFAULT_STATE; } else { state = JSON.parse(state); @@ -64,20 +63,20 @@ //avoid confusing cache behaviors response.setHeader("Cache-Control", "no-cache", false); - if (action === 'results') { + if (action === "results") { response.setHeader("Content-Type", "text/plain", false); response.write(JSON.stringify(state)); return; } - if (action === 'reset') { + if (action === "reset") { //reset server state setSharedState(SHARED_KEY, JSON.stringify(DEFAULT_STATE)); response.write(""); return; } - if (action === 'test') { + if (action === "test") { let load = query.get("load"); state.count++; if (state.referrers.indexOf(referrerLevel) < 0) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/referrer_change_server.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/referrer_change_server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/referrer_change_server.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/referrer_change_server.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,139 +1,166 @@ -var BASE_URL = 'example.com/tests/dom/base/test/referrer_change_server.sjs'; +var BASE_URL = "example.com/tests/dom/base/test/referrer_change_server.sjs"; function createTestUrl(aPolicy, aAction, aName) { - return 'http://' + BASE_URL + '?' + - 'action=' + aAction + '&' + - 'policy=' + aPolicy + '&' + - 'name=' + aName + '&' + - 'type=link'; + return ( + "http://" + + BASE_URL + + "?" + + "action=" + + aAction + + "&" + + "policy=" + + aPolicy + + "&" + + "name=" + + aName + + "&" + + "type=link" + ); } function createTest(aMetaPolicy, aReferrerPolicy, aName) { - return '\n\ - '+ - '' + - '' + - '' + aReferrerPolicy + '' + - '\n\ + "\n\ \n\ - '; + " + ); } function createTest2(aMetaPolicy, aReferrerPolicy, aName) { - return '\n\ - '+ - '' + - '' + - '' + aReferrerPolicy + '' + - '\n\ + "\n\ \n\ - '; + " + ); } function handleRequest(request, response) { - var sharedKey = 'referrer_change_server.sjs'; - var params = request.queryString.split('&'); - var action = params[0].split('=')[1]; + var sharedKey = "referrer_change_server.sjs"; + var params = request.queryString.split("&"); + var action = params[0].split("=")[1]; - if (action === 'resetState') { + if (action === "resetState") { var state = getSharedState(sharedKey); state = {}; setSharedState(sharedKey, JSON.stringify(state)); response.write(""); return; - } else if (action === 'test') { + } else if (action === "test") { // ?action=test&policy=origin&name=name - var policy = params[1].split('=')[1]; - var name = params[2].split('=')[1]; - var type = params[3].split('=')[1]; + var policy = params[1].split("=")[1]; + var name = params[2].split("=")[1]; + var type = params[3].split("=")[1]; var result = getSharedState(sharedKey); - if (result === '') { + if (result === "") { result = {}; } else { result = JSON.parse(result); } - if (!result["tests"]) { - result["tests"] = {}; + if (!result.tests) { + result.tests = {}; } var referrerLevel = "none"; - var test = {} - if (request.hasHeader('Referer')) { - let referrer = request.getHeader('Referer'); - if (referrer.indexOf("referrer_change_server") > 0) { - referrerLevel = "full"; - } else if (referrer == "http://mochi.test:8888") { - referrerLevel = "origin"; - } - test.referrer = request.getHeader('Referer'); + var test = {}; + if (request.hasHeader("Referer")) { + let referrer = request.getHeader("Referer"); + if (referrer.indexOf("referrer_change_server") > 0) { + referrerLevel = "full"; + } else if (referrer == "http://mochi.test:8888") { + referrerLevel = "origin"; + } + test.referrer = request.getHeader("Referer"); } else { - test.referrer = ''; + test.referrer = ""; } test.policy = referrerLevel; test.expected = policy; - result["tests"][name] = test; + result.tests[name] = test; setSharedState(sharedKey, JSON.stringify(result)); // forward link click to redirect URL to finish test - if (type === 'link') { - var loc = 'https://example.com/tests/dom/base/test/file_change_policy_redirect.html'; - response.setStatusLine('1.1', 302, 'Found'); - response.setHeader('Location', loc, false); + if (type === "link") { + var loc = + "https://example.com/tests/dom/base/test/file_change_policy_redirect.html"; + response.setStatusLine("1.1", 302, "Found"); + response.setHeader("Location", loc, false); } return; - } else if (action === 'get-test-results') { + } else if (action === "get-test-results") { // ?action=get-result - response.setHeader('Cache-Control', 'no-cache', false); - response.setHeader('Content-Type', 'text/plain', false); + response.setHeader("Cache-Control", "no-cache", false); + response.setHeader("Content-Type", "text/plain", false); response.write(getSharedState(sharedKey)); return; - } else if (action === 'generate-policy-test') { + } else if (action === "generate-policy-test") { // ?action=generate-policy-test&referrerPolicy=b64-encoded-string&name=name&newPolicy=b64-encoded-string - response.setHeader('Cache-Control', 'no-cache', false); - response.setHeader('Content-Type', 'text/html; charset=utf-8', false); - var referrerPolicy = unescape(params[1].split('=')[1]); - var name = unescape(params[2].split('=')[1]); - var newPolicy = params[3].split('=')[1]; + response.setHeader("Cache-Control", "no-cache", false); + response.setHeader("Content-Type", "text/html; charset=utf-8", false); + var referrerPolicy = unescape(params[1].split("=")[1]); + var name = unescape(params[2].split("=")[1]); + var newPolicy = params[3].split("=")[1]; response.write(createTest(referrerPolicy, newPolicy, name)); return; - } else if (action === 'generate-policy-test2') { + } else if (action === "generate-policy-test2") { // ?action=generate-policy-test2&referrerPolicy=b64-encoded-string&name=name&newPolicy=b64-encoded-string - response.setHeader('Cache-Control', 'no-cache', false); - response.setHeader('Content-Type', 'text/html; charset=utf-8', false); - var referrerPolicy = unescape(params[1].split('=')[1]); - var name = unescape(params[2].split('=')[1]); - var newPolicy = params[3].split('=')[1]; + response.setHeader("Cache-Control", "no-cache", false); + response.setHeader("Content-Type", "text/html; charset=utf-8", false); + var referrerPolicy = unescape(params[1].split("=")[1]); + var name = unescape(params[2].split("=")[1]); + var newPolicy = params[3].split("=")[1]; response.write(createTest2(referrerPolicy, newPolicy, name)); return; } else { - response.write("I don't know action "+action); + response.write("I don't know action " + action); return; } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/referrer_header.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/referrer_header.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/referrer_header.sjs 2021-10-17 14:42:33.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/referrer_header.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,6 +1,6 @@ - function handleRequest(request, response) { response.setHeader("Referrer-Policy", "same-origin"); - response.write('Loaded'); + response.write( + 'Loaded' + ); } - diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/referrer_testserver.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/referrer_testserver.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/referrer_testserver.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/referrer_testserver.sjs 2021-10-20 19:28:17.000000000 +0000 @@ -1,14 +1,14 @@ /* -* Test server for iframe, anchor, and area referrer attributes. -* https://bugzilla.mozilla.org/show_bug.cgi?id=1175736 -* Also server for further referrer tests such as redirecting tests -* bug 1174913, bug 1175736, bug 1184781 -*/ + * Test server for iframe, anchor, and area referrer attributes. + * https://bugzilla.mozilla.org/show_bug.cgi?id=1175736 + * Also server for further referrer tests such as redirecting tests + * bug 1174913, bug 1175736, bug 1184781 + */ Components.utils.importGlobalProperties(["URLSearchParams"]); const SJS = "referrer_testserver.sjs?"; const SJS_PATH = "/tests/dom/base/test/"; -const BASE_ORIGIN = "example.com" +const BASE_ORIGIN = "example.com"; const BASE_URL = BASE_ORIGIN + SJS_PATH + SJS; const SHARED_KEY = SJS; const SAME_ORIGIN = "mochi.test:8888" + SJS_PATH + SJS; @@ -16,29 +16,57 @@ const IMG_BYTES = atob( "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" + - "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="); + "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" +); -function createTestUrl(aPolicy, aAction, aName, aType, aSchemeFrom, aSchemeTo, - crossOrigin, referrerPolicyHeader) { +function createTestUrl( + aPolicy, + aAction, + aName, + aType, + aSchemeFrom, + aSchemeTo, + crossOrigin, + referrerPolicyHeader +) { var schemeTo = aSchemeTo || "http"; var schemeFrom = aSchemeFrom || "http"; var rpHeader = referrerPolicyHeader || ""; var url = schemeTo + "://"; - url += (crossOrigin ? CROSS_ORIGIN_URL : BASE_URL); + url += crossOrigin ? CROSS_ORIGIN_URL : BASE_URL; url += - "ACTION=" + aAction + "&" + - "policy=" + aPolicy + "&" + - "NAME=" + aName + "&" + - "type=" + aType + "&" + - "RP_HEADER=" + rpHeader + "&" + - "SCHEME_FROM=" + schemeFrom; + "ACTION=" + + aAction + + "&" + + "policy=" + + aPolicy + + "&" + + "NAME=" + + aName + + "&" + + "type=" + + aType + + "&" + + "RP_HEADER=" + + rpHeader + + "&" + + "SCHEME_FROM=" + + schemeFrom; return url; - } +} // test page using iframe referrer attribute // if aParams are set this creates a test where the iframe url is a redirect -function createIframeTestPageUsingRefferer(aMetaPolicy, aAttributePolicy, aNewAttributePolicy, aName, aParams, - aSchemeFrom, aSchemeTo, aChangingMethod) { +function createIframeTestPageUsingRefferer( + aMetaPolicy, + aAttributePolicy, + aNewAttributePolicy, + aName, + aParams, + aSchemeFrom, + aSchemeTo, + aChangingMethod +) { var metaString = ""; if (aMetaPolicy) { metaString = ``; @@ -49,14 +77,23 @@ } else if (aChangingMethod === "property") { changeString = `document.getElementById("myframe").referrerPolicy = "${aNewAttributePolicy}"`; } - var iFrameString = ``; + var iFrameString = ``; var iframeUrl = ""; if (aParams) { aParams.delete("ACTION"); aParams.append("ACTION", "redirectIframe"); iframeUrl = "http://" + CROSS_ORIGIN_URL + aParams.toString(); } else { - iframeUrl = createTestUrl(aAttributePolicy, "test", aName, "iframe", aSchemeFrom, aSchemeTo); + iframeUrl = createTestUrl( + aAttributePolicy, + "test", + aName, + "iframe", + aSchemeFrom, + aSchemeTo + ); } return ` @@ -79,20 +116,62 @@ `; } -function buildAnchorString(aMetaPolicy, aReferrerPolicy, aName, aRelString, aSchemeFrom, aSchemeTo){ +function buildAnchorString( + aMetaPolicy, + aReferrerPolicy, + aName, + aRelString, + aSchemeFrom, + aSchemeTo +) { if (aReferrerPolicy) { - return `${aReferrerPolicy}`; - } - return `link`; + return `${aReferrerPolicy}`; + } + return `link`; } -function buildAreaString(aMetaPolicy, aReferrerPolicy, aName, aRelString, aSchemeFrom, aSchemeTo){ +function buildAreaString( + aMetaPolicy, + aReferrerPolicy, + aName, + aRelString, + aSchemeFrom, + aSchemeTo +) { var result = `image`; result += ``; if (aReferrerPolicy) { - result += `theArea`; + result += `theArea`; } else { - result += `theArea`; + result += `theArea`; } result += ``; @@ -100,7 +179,17 @@ } // test page using anchor or area referrer attribute -function createAETestPageUsingRefferer(aMetaPolicy, aAttributePolicy, aNewAttributePolicy, aName, aRel, aStringBuilder, aSchemeFrom, aSchemeTo, aChangingMethod) { +function createAETestPageUsingRefferer( + aMetaPolicy, + aAttributePolicy, + aNewAttributePolicy, + aName, + aRel, + aStringBuilder, + aSchemeFrom, + aSchemeTo, + aChangingMethod +) { var metaString = ""; if (aMetaPolicy) { metaString = ``; @@ -115,7 +204,14 @@ if (aRel) { relString = `rel="noreferrer"`; } - var elementString = aStringBuilder(aMetaPolicy, aAttributePolicy, aName, relString, aSchemeFrom, aSchemeTo); + var elementString = aStringBuilder( + aMetaPolicy, + aAttributePolicy, + aName, + relString, + aSchemeFrom, + aSchemeTo + ); return ` @@ -133,13 +229,26 @@ } // test page using anchor target=_blank rel=noopener -function createTargetBlankRefferer(aMetaPolicy, aName, aSchemeFrom, - aSchemeTo, aRpHeader) { +function createTargetBlankRefferer( + aMetaPolicy, + aName, + aSchemeFrom, + aSchemeTo, + aRpHeader +) { var metaString = ""; if (aMetaPolicy) { metaString = ``; } - var elementString = `link`; + var elementString = `link`; return ` @@ -161,7 +270,9 @@ function createRedirectImgTestCase(aParams, aAttributePolicy) { var metaString = ""; if (aParams.has("META_POLICY")) { - metaString = ``; + metaString = ``; } aParams.delete("ACTION"); aParams.append("ACTION", "redirectImg"); @@ -175,7 +286,9 @@ Test referrer policies on redirect (img) - + '); + response.write( + '' + ); return; } - if (action === "redirectImg"){ + if (action === "redirectImg") { params.delete("ACTION"); params.append("ACTION", "test"); params.append("type", "img"); // 302 found, 301 Moved Permanently, 303 See Other, 307 Temporary Redirect response.setStatusLine("1.1", 302, "found"); - response.setHeader("Location", "http://" + CROSS_ORIGIN_URL + params.toString(), false); + response.setHeader( + "Location", + "http://" + CROSS_ORIGIN_URL + params.toString(), + false + ); return; } - if (action === "redirectIframe"){ + if (action === "redirectIframe") { params.delete("ACTION"); params.append("ACTION", "test"); params.append("type", "iframe"); // 302 found, 301 Moved Permanently, 303 See Other, 307 Temporary Redirect response.setStatusLine("1.1", 302, "found"); - response.setHeader("Location", "http://" + CROSS_ORIGIN_URL + params.toString(), false); + response.setHeader( + "Location", + "http://" + CROSS_ORIGIN_URL + params.toString(), + false + ); return; } if (action === "test") { @@ -322,7 +503,7 @@ result = result ? JSON.parse(result) : {}; var referrerLevel = "none"; - var test = {} + var test = {}; if (request.hasHeader("Referer")) { var referrer = request.getHeader("Referer"); if (referrer.indexOf("referrer_testserver") > 0) { @@ -375,8 +556,20 @@ var name = params.get("NAME"); // anchor & area - var _getPage = createAETestPageUsingRefferer.bind(null, metaPolicy, attributePolicy, newAttributePolicy, name, rel); - var _getAnchorPage = _getPage.bind(null, buildAnchorString, schemeFrom, schemeTo); + var _getPage = createAETestPageUsingRefferer.bind( + null, + metaPolicy, + attributePolicy, + newAttributePolicy, + name, + rel + ); + var _getAnchorPage = _getPage.bind( + null, + buildAnchorString, + schemeFrom, + schemeTo + ); var _getAreaPage = _getPage.bind(null, buildAreaString, schemeFrom, schemeTo); // aMetaPolicy, aAttributePolicy, aNewAttributePolicy, aName, aChangingMethod, aStringBuilder @@ -405,13 +598,29 @@ return; } if (action === "generate-anchor-target-blank-policy-test") { - response.write(createTargetBlankRefferer(metaPolicy, name, schemeFrom, schemeTo, referrerPolicyHeader)); + response.write( + createTargetBlankRefferer( + metaPolicy, + name, + schemeFrom, + schemeTo, + referrerPolicyHeader + ) + ); return; } // iframe - _getPage = createIframeTestPageUsingRefferer.bind(null, metaPolicy, attributePolicy, newAttributePolicy, name, "", - schemeFrom, schemeTo); + _getPage = createIframeTestPageUsingRefferer.bind( + null, + metaPolicy, + attributePolicy, + newAttributePolicy, + name, + "", + schemeFrom, + schemeTo + ); // aMetaPolicy, aAttributePolicy, aNewAttributePolicy, aName, aChangingMethod if (action === "generate-iframe-policy-test") { @@ -433,12 +642,28 @@ return; } if (action === "generate-iframe-redirect-policy-test") { - response.write(createIframeTestPageUsingRefferer(metaPolicy, attributePolicy, newAttributePolicy, name, params, - schemeFrom, schemeTo)); + response.write( + createIframeTestPageUsingRefferer( + metaPolicy, + attributePolicy, + newAttributePolicy, + name, + params, + schemeFrom, + schemeTo + ) + ); return; } - var _getPage = createLinkPageUsingRefferer.bind(null, metaPolicy, attributePolicy, newAttributePolicy, name, rel); + var _getPage = createLinkPageUsingRefferer.bind( + null, + metaPolicy, + attributePolicy, + newAttributePolicy, + name, + rel + ); var _getLinkPage = _getPage.bind(null, buildLinkString, schemeFrom, schemeTo); // link @@ -456,7 +681,9 @@ } if (action === "generate-fetch-user-control-policy-test") { - response.write(createFetchUserControlRPTestCase(name, schemeFrom, schemeTo, crossOrigin)); + response.write( + createFetchUserControlRPTestCase(name, schemeFrom, schemeTo, crossOrigin) + ); return; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/script-1_bug597345.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/script-1_bug597345.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/base/test/script-1_bug597345.sjs 2021-10-17 14:42:34.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/base/test/script-1_bug597345.sjs 2021-10-20 19:28:16.000000000 +0000 @@ -1,16 +1,22 @@ // timer has to be alive so it can't be eaten by the GC. var timer; -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setHeader("Cache-Control", "no-cache", false); response.setHeader("Content-Type", "text/javascript", false); // The "stray" open comment at the end of the write is important! - response.write("document.write(\"PASS\r\n'; + var r = + "HTTP/1.1 200 OK\r\n" + + "Content-Type: text/html\r\n" + + 'Link: < \014>; rel="stylesheet"\r\n' + + "\r\n" + + "PASS\r\n"; response.bodyOutputStream.write(r, r.length); response.bodyOutputStream.flush(); response.finish(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/html/test/bug649134/file_bug649134-2.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/html/test/bug649134/file_bug649134-2.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/html/test/bug649134/file_bug649134-2.sjs 2021-10-17 14:42:35.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/html/test/bug649134/file_bug649134-2.sjs 2021-10-20 19:28:18.000000000 +0000 @@ -1,11 +1,11 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.seizePower(); - var r = 'HTTP/1.1 200 OK\r\n' + - 'Content-Type: text/html\r\n' + - 'Link: < \014>; rel="stylesheet",\r\n' + - '\r\n' + - 'PASS\r\n'; + var r = + "HTTP/1.1 200 OK\r\n" + + "Content-Type: text/html\r\n" + + 'Link: < \014>; rel="stylesheet",\r\n' + + "\r\n" + + "PASS\r\n"; response.bodyOutputStream.write(r, r.length); response.bodyOutputStream.flush(); response.finish(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/html/test/forms/save_restore_radio_groups.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/html/test/forms/save_restore_radio_groups.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/html/test/forms/save_restore_radio_groups.sjs 2021-10-17 14:42:35.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/html/test/forms/save_restore_radio_groups.sjs 2021-10-20 19:28:18.000000000 +0000 @@ -1,17 +1,17 @@ var pages = [ "" + - "" + - "
" + - "" + - "
" + - "", + "" + + "
" + + "" + + "
" + + "", "" + - "" + - "
" + - "" + - "
" + - "", - ]; + "" + + "
" + + "" + + "
" + + "", +]; /** * This SJS is going to send the same page the two first times it will be called @@ -21,8 +21,7 @@ * once. */ -function handleRequest(request, response) -{ +function handleRequest(request, response) { var counter = +getState("counter"); // convert to number; +"" === 0 response.setStatusLine(request.httpVersion, 200, "Ok"); @@ -47,4 +46,3 @@ setState("counter", "" + ++counter); } } - diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/html/test/forms/submit_invalid_file.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/html/test/forms/submit_invalid_file.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/html/test/forms/submit_invalid_file.sjs 2021-10-17 14:42:35.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/html/test/forms/submit_invalid_file.sjs 2021-10-20 19:28:18.000000000 +0000 @@ -1,5 +1,4 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setStatusLine(request.httpVersion, 200, "Ok"); response.setHeader("Content-Type", "text/html"); response.setHeader("Cache-Control", "no-cache"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/html/test/forms/test_submit_invalid_file.html firefox-trunk-95.0~a1~hg20211020r596404/dom/html/test/forms/test_submit_invalid_file.html --- firefox-trunk-95.0~a1~hg20211017r596111/dom/html/test/forms/test_submit_invalid_file.html 2021-10-17 14:42:35.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/html/test/forms/test_submit_invalid_file.html 2021-10-20 19:28:18.000000000 +0000 @@ -37,7 +37,7 @@ var file = FileUtils.getDir("TmpD", [], false); file.append("testfile"); - file.createUnique(SpecialPowers.Ci.nsIFile.NORMAL_FILE_TYPE, 0644); + file.createUnique(SpecialPowers.Ci.nsIFile.NORMAL_FILE_TYPE, 0o644); SpecialPowers.wrap(i).value = file.path; file.remove(/* recursive = */ false); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/html/test/form_submit_server.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/html/test/form_submit_server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/html/test/form_submit_server.sjs 2021-10-17 14:42:35.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/html/test/form_submit_server.sjs 2021-10-20 19:28:18.000000000 +0000 @@ -1,7 +1,9 @@ const CC = Components.Constructor; -const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", - "setInputStream"); +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); function utf8decode(s) { return decodeURIComponent(escape(s)); @@ -11,57 +13,70 @@ return unescape(encodeURIComponent(s)); } -function handleRequest(request, response) -{ +function handleRequest(request, response) { var bodyStream = new BinaryInputStream(request.bodyInputStream); var result = []; var requestBody = ""; - while ((bodyAvail = bodyStream.available()) > 0) + while ((bodyAvail = bodyStream.available()) > 0) { requestBody += bodyStream.readBytes(bodyAvail); + } if (request.method == "POST") { - var contentTypeParams = {}; - request.getHeader("Content-Type").split(/\s*\;\s*/).forEach(function(s) { - if (s.indexOf('=') >= 0) { - let [name, value] = s.split('='); - contentTypeParams[name] = value; - } - else { - contentTypeParams[''] = s; - } - }); - - if (contentTypeParams[''] == "multipart/form-data" && - request.queryString == "") { - requestBody.split("--" + contentTypeParams.boundary).slice(1, -1).forEach(function (s) { - - let headers = {}; - let headerEnd = s.indexOf("\r\n\r\n"); - s.substr(2, headerEnd-2).split("\r\n").forEach(function(s) { - // We're assuming UTF8 for now - let [name, value] = s.split(': '); - headers[name] = utf8decode(value); - }); - - let body = s.substring(headerEnd + 4, s.length - 2); - if (!headers["Content-Type"] || headers["Content-Type"] == "text/plain") { - // We're assuming UTF8 for now - body = utf8decode(body); + request + .getHeader("Content-Type") + .split(/\s*\;\s*/) + .forEach(function(s) { + if (s.indexOf("=") >= 0) { + let [name, value] = s.split("="); + contentTypeParams[name] = value; + } else { + contentTypeParams[""] = s; } - result.push({ headers: headers, body: body}); }); + + if ( + contentTypeParams[""] == "multipart/form-data" && + request.queryString == "" + ) { + requestBody + .split("--" + contentTypeParams.boundary) + .slice(1, -1) + .forEach(function(s) { + let headers = {}; + let headerEnd = s.indexOf("\r\n\r\n"); + s.substr(2, headerEnd - 2) + .split("\r\n") + .forEach(function(s) { + // We're assuming UTF8 for now + let [name, value] = s.split(": "); + headers[name] = utf8decode(value); + }); + + let body = s.substring(headerEnd + 4, s.length - 2); + if ( + !headers["Content-Type"] || + headers["Content-Type"] == "text/plain" + ) { + // We're assuming UTF8 for now + body = utf8decode(body); + } + result.push({ headers, body }); + }); } - if (contentTypeParams[''] == "text/plain" && - request.queryString == "plain") { + if ( + contentTypeParams[""] == "text/plain" && + request.queryString == "plain" + ) { result = utf8decode(requestBody); } - if (contentTypeParams[''] == "application/x-www-form-urlencoded" && - request.queryString == "url") { + if ( + contentTypeParams[""] == "application/x-www-form-urlencoded" && + request.queryString == "url" + ) { result = requestBody; } - } - else if (request.method == "GET") { + } else if (request.method == "GET") { result = request.queryString; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/html/test/test_bug658746.html firefox-trunk-95.0~a1~hg20211020r596404/dom/html/test/test_bug658746.html --- firefox-trunk-95.0~a1~hg20211017r596111/dom/html/test/test_bug658746.html 2021-10-17 14:42:35.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/html/test/test_bug658746.html 2021-10-20 19:28:18.000000000 +0000 @@ -68,8 +68,8 @@ SetGetDelete(0xa); // Octal numbers as properties. -SetGetDelete(03); -SetGetDelete(07); +SetGetDelete(0o3); +SetGetDelete(0o7); // String numbers as properties. SetGetDelete('0'); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/interfaces/base/nsIBrowser.idl firefox-trunk-95.0~a1~hg20211020r596404/dom/interfaces/base/nsIBrowser.idl --- firefox-trunk-95.0~a1~hg20211017r596111/dom/interfaces/base/nsIBrowser.idl 2021-10-17 14:42:35.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/interfaces/base/nsIBrowser.idl 2021-10-20 19:28:18.000000000 +0000 @@ -130,28 +130,6 @@ in AString aContentType); /** - * Determine what process switching behavior this browser element should have. - */ - cenum ProcessBehavior : 8 { - // Gecko won't automatically change which process this frame, or it's - // subframes, are loaded in. - PROCESS_BEHAVIOR_DISABLED, - - // If `useRemoteTabs` is enabled, Gecko will change which process this frame - // is loaded in automatically, without calling `performProcessSwitch`. - // When `useRemoteSubframes` is enabled, subframes will change processes. - PROCESS_BEHAVIOR_STANDARD, - - // Gecko won't automatically change which process this frame is loaded, but - // when `useRemoteSubframes` is enabled, subframes will change processes. - // - // NOTE: This configuration is included only for backwards compatibility, - // and will be removed, as it can easily lead to invalid behavior. - PROCESS_BEHAVIOR_SUBFRAME_ONLY, - }; - readonly attribute nsIBrowser_ProcessBehavior processSwitchBehavior; - - /** * Called to perform any async tasks which must be completed before changing * remoteness. Gecko will wait for the returned promise to resolve before * performing the process switch. diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/ipc/BrowserParent.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/ipc/BrowserParent.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/ipc/BrowserParent.cpp 2021-10-17 14:42:35.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/ipc/BrowserParent.cpp 2021-10-20 19:28:18.000000000 +0000 @@ -672,12 +672,9 @@ return IPC_OK(); } -mozilla::ipc::IPCResult BrowserParent::Recv__delete__() { +void BrowserParent::ActorDestroy(ActorDestroyReason why) { Manager()->NotifyTabDestroyed(mTabId, mMarkedDestroying); - return IPC_OK(); -} -void BrowserParent::ActorDestroy(ActorDestroyReason why) { ContentProcessManager::GetSingleton()->UnregisterRemoteFrame(mTabId); if (mRemoteLayerTreeOwner.IsInitialized()) { @@ -749,12 +746,22 @@ if (why == AbnormalShutdown) { frameLoader->MaybeNotifyCrashed(mBrowsingContext, Manager()->ChildID(), GetIPCChannel()); + } else if (why == ManagedEndpointDropped) { + // If we instead failed due to a constructor error, don't include process + // information, as the process did not crash. + frameLoader->MaybeNotifyCrashed(mBrowsingContext, ContentParentId{}, + nullptr); } } mFrameLoader = nullptr; - mBrowsingContext->BrowserParentDestroyed(this, why == AbnormalShutdown); + // If we were destroyed due to our ManagedEndpoints being dropped, make a + // point of showing the subframe crashed UI. We don't fire the full + // `MaybeNotifyCrashed` codepath, as the entire process hasn't crashed on us, + // and it may confuse the frontend. + mBrowsingContext->BrowserParentDestroyed( + this, why == AbnormalShutdown || why == ManagedEndpointDropped); } mozilla::ipc::IPCResult BrowserParent::RecvMoveFocus( diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/ipc/BrowserParent.h firefox-trunk-95.0~a1~hg20211020r596404/dom/ipc/BrowserParent.h --- firefox-trunk-95.0~a1~hg20211017r596111/dom/ipc/BrowserParent.h 2021-10-17 14:42:35.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/ipc/BrowserParent.h 2021-10-20 19:28:19.000000000 +0000 @@ -724,8 +724,6 @@ const nsString& aMessage, bool aSync, ipc::StructuredCloneData* aData, nsTArray* aJSONRetVal = nullptr); - virtual mozilla::ipc::IPCResult Recv__delete__() override; - virtual void ActorDestroy(ActorDestroyReason why) override; mozilla::ipc::IPCResult RecvRemoteIsReadyToHandleInputEvents(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/ipc/ContentChild.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/ipc/ContentChild.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/ipc/ContentChild.cpp 2021-10-17 14:42:35.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/ipc/ContentChild.cpp 2021-10-20 19:28:18.000000000 +0000 @@ -1680,12 +1680,14 @@ // is called. CGSShutdownServerConnections(); - // Actual security benefits are only acheived when we additionally deny - // future connections, however this currently breaks WebGL so it's not done - // by default. + // Actual security benefits are only achieved when we additionally deny + // future connections using the sandbox policy. WebGL must be remoted if + // the windowserver connections are blocked. WebGL remoting is disabled + // for some tests. if (aIsSandboxEnabled && Preferences::GetBool( - "security.sandbox.content.mac.disconnect-windowserver")) { + "security.sandbox.content.mac.disconnect-windowserver") && + Preferences::GetBool("webgl.out-of-process")) { CGError result = CGSSetDenyWindowServerConnections(true); MOZ_DIAGNOSTIC_ASSERT(result == kCGErrorSuccess); # if !MOZ_DIAGNOSTIC_ASSERT_ENABLED @@ -1783,9 +1785,32 @@ nsPrintfCString reason("%s initial %s BrowsingContext", browsingContext ? "discarded" : "missing", aIsTopLevel ? "top" : "frame"); + MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Warning, ("%s", reason.get())); + if (!aIsTopLevel) { + // Recover if the BrowsingContext is missing for a new subframe. The + // `ManagedEndpoint` instances will be automatically destroyed. + NS_WARNING(reason.get()); + return IPC_OK(); + } return IPC_FAIL(this, reason.get()); } + if (xpc::IsInAutomation() && + StaticPrefs:: + browser_tabs_remote_testOnly_failPBrowserCreation_enabled()) { + nsAutoCString idString; + if (NS_SUCCEEDED(Preferences::GetCString( + "browser.tabs.remote.testOnly.failPBrowserCreation.browsingContext", + idString))) { + nsresult rv = NS_OK; + uint64_t bcid = idString.ToInteger64(&rv); + if (NS_SUCCEEDED(rv) && bcid == browsingContext->Id()) { + NS_WARNING("Injecting artificial PBrowser creation failure"); + return IPC_OK(); + } + } + } + if (!aWindowInit.isInitialDocument() || !NS_IsAboutBlank(aWindowInit.documentURI())) { return IPC_FAIL(this, diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/ipc/ContentParent.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/ipc/ContentParent.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/ipc/ContentParent.cpp 2021-10-17 14:42:35.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/ipc/ContentParent.cpp 2021-10-20 19:28:18.000000000 +0000 @@ -2409,9 +2409,12 @@ info.hasAudio = true; } - // Windowserver access + // Window server access. If the disconnect-windowserver pref is not + // "true" or out-of-process WebGL is not enabled, allow window server + // access in the sandbox policy. if (!Preferences::GetBool( - "security.sandbox.content.mac.disconnect-windowserver")) { + "security.sandbox.content.mac.disconnect-windowserver") || + !Preferences::GetBool("webgl.out-of-process")) { info.hasWindowServer = true; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/ipc/tests/blob_verify.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/ipc/tests/blob_verify.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/ipc/tests/blob_verify.sjs 2021-10-17 14:42:35.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/ipc/tests/blob_verify.sjs 2021-10-20 19:28:18.000000000 +0000 @@ -1,16 +1,21 @@ const CC = Components.Constructor; -const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", - "setInputStream"); -const BinaryOutputStream = CC("@mozilla.org/binaryoutputstream;1", - "nsIBinaryOutputStream", - "setOutputStream"); +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); +const BinaryOutputStream = CC( + "@mozilla.org/binaryoutputstream;1", + "nsIBinaryOutputStream", + "setOutputStream" +); function handleRequest(request, response) { var bodyStream = new BinaryInputStream(request.bodyInputStream); var bodyBytes = []; - while ((bodyAvail = bodyStream.available()) > 0) + while ((bodyAvail = bodyStream.available()) > 0) { Array.prototype.push.apply(bodyBytes, bodyStream.readByteArray(bodyAvail)); + } var bos = new BinaryOutputStream(response.bodyOutputStream); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/ipc/tests/browser_crash_oopiframe.js firefox-trunk-95.0~a1~hg20211020r596404/dom/ipc/tests/browser_crash_oopiframe.js --- firefox-trunk-95.0~a1~hg20211017r596111/dom/ipc/tests/browser_crash_oopiframe.js 2021-10-17 14:42:35.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/ipc/tests/browser_crash_oopiframe.js 2021-10-20 19:28:19.000000000 +0000 @@ -229,10 +229,6 @@ "This test only makes sense of we can use OOP iframes." ); - await SpecialPowers.pushPrefEnv({ - set: [["dom.security.enforceIPCBasedPrincipalVetting", false]], - }); - // Create the crash reporting directory if it doesn't yet exist, otherwise, a failure // sometimes occurs. See bug 1687855 for fixing this. const uAppDataPath = Services.dirsvc.get("UAppData", Ci.nsIFile).path; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/ipc/tests/browser.ini firefox-trunk-95.0~a1~hg20211020r596404/dom/ipc/tests/browser.ini --- firefox-trunk-95.0~a1~hg20211017r596111/dom/ipc/tests/browser.ini 2021-10-17 14:42:35.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/ipc/tests/browser.ini 2021-10-20 19:28:19.000000000 +0000 @@ -50,3 +50,5 @@ skip-if = !fission # Test doesn't make sense without fission [browser_web_process_isolation.js] skip-if = !fission # Only relevant for fission +[browser_pbrowser_creation_failure.js] +skip-if = !fission diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/ipc/tests/browser_pbrowser_creation_failure.js firefox-trunk-95.0~a1~hg20211020r596404/dom/ipc/tests/browser_pbrowser_creation_failure.js --- firefox-trunk-95.0~a1~hg20211017r596111/dom/ipc/tests/browser_pbrowser_creation_failure.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/ipc/tests/browser_pbrowser_creation_failure.js 2021-10-20 19:28:18.000000000 +0000 @@ -0,0 +1,57 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function test_subframe_pbrowser_creation_failure() { + await BrowserTestUtils.withNewTab( + "https://example.com/document-builder.sjs?html=", + async browser => { + let bcid = await SpecialPowers.spawn(browser, [], () => { + return content.document.body.querySelector("iframe").browsingContext.id; + }); + + // We currently have no known way to trigger PBrowser creation failure, + // other than to use this custom pref for the purpose. + info(`enabling failPBrowserCreation for browsingContext: ${bcid}`); + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.tabs.remote.testOnly.failPBrowserCreation.enabled", true], + [ + "browser.tabs.remote.testOnly.failPBrowserCreation.browsingContext", + `${bcid}`, + ], + ], + }); + + let eventFiredPromise = BrowserTestUtils.waitForEvent( + browser, + "oop-browser-crashed" + ); + + info("triggering navigation which will fail pbrowser creation"); + await SpecialPowers.spawn(browser, [], () => { + content.document.body.querySelector("iframe").src = + "https://example.org/document-builder.sjs?html=frame"; + }); + + info("Waiting for oop-browser-crashed event."); + let event = await eventFiredPromise; + ok(!event.isTopFrame, "should be reporting subframe crash"); + ok( + event.childID == 0, + "childID should be zero, as no process actually crashed" + ); + is(event.browsingContextId, bcid, "bcid should match"); + + let { + subject: windowGlobal, + } = await BrowserUtils.promiseObserved("window-global-created", wgp => + wgp.documentURI.spec.startsWith("about:framecrashed") + ); + is(windowGlobal.browsingContext.id, bcid, "bcid is correct"); + + await SpecialPowers.popPrefEnv(); + } + ); +}); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/IPCUtils.h firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/IPCUtils.h --- firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/IPCUtils.h 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/IPCUtils.h 2021-10-20 19:28:18.000000000 +0000 @@ -0,0 +1,28 @@ +/* -*- 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/. */ + +#ifndef DOM_LOCKS_IPCUTILS_H_ +#define DOM_LOCKS_IPCUTILS_H_ + +#include "ipc/EnumSerializer.h" +#include "ipc/IPCMessageUtilsSpecializations.h" +#include "mozilla/dom/LockManagerBinding.h" + +namespace IPC { +using LockMode = mozilla::dom::LockMode; +template <> +struct ParamTraits + : public ContiguousEnumSerializerInclusive {}; + +DEFINE_IPC_SERIALIZER_WITH_FIELDS(mozilla::dom::LockInfo, mName, mMode, + mClientId); + +DEFINE_IPC_SERIALIZER_WITH_FIELDS(mozilla::dom::LockManagerSnapshot, mHeld, + mPending); +} // namespace IPC + +#endif // DOM_LOCKS_IPCUTILS_H_ diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/Lock.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/Lock.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/Lock.cpp 2021-10-17 14:42:35.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/Lock.cpp 2021-10-20 19:28:18.000000000 +0000 @@ -8,11 +8,12 @@ #include "mozilla/dom/LockBinding.h" #include "mozilla/dom/LockManager.h" #include "mozilla/dom/Promise.h" +#include "mozilla/dom/locks/LockRequestChild.h" namespace mozilla::dom { -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Lock, mOwner, mLockManager, - mWaitingPromise, mReleasedPromise) +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Lock, mOwner, mWaitingPromise, + mReleasedPromise) NS_IMPL_CYCLE_COLLECTING_ADDREF(Lock) NS_IMPL_CYCLE_COLLECTING_RELEASE(Lock) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Lock) @@ -20,16 +21,17 @@ NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END -Lock::Lock(nsIGlobalObject* aGlobal, const RefPtr& aLockManager, +Lock::Lock(nsIGlobalObject* aGlobal, + const WeakPtr& aLockRequestChild, const nsString& aName, LockMode aMode, const RefPtr& aReleasedPromise, ErrorResult& aRv) : mOwner(aGlobal), - mLockManager(aLockManager), + mLockRequestChild(aLockRequestChild), mName(aName), mMode(aMode), mWaitingPromise(Promise::Create(aGlobal, aRv)), mReleasedPromise(aReleasedPromise) { - MOZ_ASSERT(aLockManager); + MOZ_ASSERT(mLockRequestChild); MOZ_ASSERT(aReleasedPromise); } @@ -47,16 +49,18 @@ } void Lock::ResolvedCallback(JSContext* aCx, JS::Handle aValue) { - RefPtr pin = this; - // decrements the refcount, may delete this - mLockManager->ReleaseHeldLock(this); + if (mLockRequestChild) { + locks::PLockRequestChild::Send__delete__(mLockRequestChild, false); + mLockRequestChild = nullptr; + } mReleasedPromise->MaybeResolve(aValue); } void Lock::RejectedCallback(JSContext* aCx, JS::Handle aValue) { - RefPtr pin = this; - // decrements the refcount, may delete this - mLockManager->ReleaseHeldLock(this); + if (mLockRequestChild) { + locks::PLockRequestChild::Send__delete__(mLockRequestChild, false); + mLockRequestChild = nullptr; + } mReleasedPromise->MaybeReject(aValue); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/Lock.h firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/Lock.h --- firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/Lock.h 2021-10-17 14:42:35.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/Lock.h 2021-10-20 19:28:19.000000000 +0000 @@ -10,6 +10,7 @@ #include "js/TypeDecls.h" #include "mozilla/Attributes.h" #include "mozilla/ErrorResult.h" +#include "mozilla/WeakPtr.h" #include "mozilla/dom/BindingDeclarations.h" #include "mozilla/dom/LockManagerBinding.h" #include "mozilla/dom/PromiseNativeHandler.h" @@ -19,6 +20,9 @@ namespace mozilla::dom { class LockManager; +namespace locks { +class LockRequestChild; +} class Lock final : public PromiseNativeHandler, public nsWrapperCache { friend class LockManager; @@ -27,18 +31,11 @@ NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Lock) - Lock(nsIGlobalObject* aGlobal, const RefPtr& aLockManager, + Lock(nsIGlobalObject* aGlobal, + const WeakPtr& aLockRequestChild, const nsString& aName, LockMode aMode, const RefPtr& aReleasedPromise, ErrorResult& aRv); - bool operator==(const Lock& aOther) const { - // This operator is needed to remove released locks from the queue. - // This assumes each lock has a unique promise - MOZ_ASSERT(mReleasedPromise && aOther.mReleasedPromise, - "Promises are null when locks are unreleased??"); - return mReleasedPromise == aOther.mReleasedPromise; - } - protected: ~Lock() = default; @@ -62,7 +59,7 @@ private: nsCOMPtr mOwner; - RefPtr mLockManager; + WeakPtr mLockRequestChild; nsString mName; LockMode mMode; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/LockManagerChild.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/LockManagerChild.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/LockManagerChild.cpp 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/LockManagerChild.cpp 2021-10-20 19:28:18.000000000 +0000 @@ -0,0 +1,24 @@ +/* -*- 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 "LockManagerChild.h" +#include "LockRequestChild.h" + +namespace mozilla::dom::locks { + +NS_IMPL_CYCLE_COLLECTION(LockManagerChild, mOwner) +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(LockManagerChild, AddRef) +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(LockManagerChild, Release) + +void LockManagerChild::RequestLock(const LockRequest& aRequest, + const LockOptions& aOptions) { + auto requestActor = MakeRefPtr(aRequest, aOptions.mSignal); + SendPLockRequestConstructor( + requestActor, IPCLockRequest(nsString(aRequest.mName), aOptions.mMode, + aOptions.mIfAvailable, aOptions.mSteal)); +} + +} // namespace mozilla::dom::locks diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/LockManagerChild.h firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/LockManagerChild.h --- firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/LockManagerChild.h 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/LockManagerChild.h 2021-10-20 19:28:18.000000000 +0000 @@ -0,0 +1,37 @@ +/* -*- 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/. */ + +#ifndef DOM_LOCKS_LOCKMANAGERCHILD_H_ +#define DOM_LOCKS_LOCKMANAGERCHILD_H_ + +#include "mozilla/dom/locks/PLockManagerChild.h" +#include "mozilla/dom/Promise.h" +#include "nsIUUIDGenerator.h" + +namespace mozilla::dom::locks { + +struct LockRequest; + +class LockManagerChild final : public PLockManagerChild { + public: + NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(LockManagerChild) + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(LockManagerChild) + + explicit LockManagerChild(nsIGlobalObject* aOwner) : mOwner(aOwner){}; + + nsIGlobalObject* GetParentObject() const { return mOwner; }; + + void RequestLock(const LockRequest& aRequest, const LockOptions& aOptions); + + private: + ~LockManagerChild() = default; + + nsCOMPtr mOwner; +}; + +} // namespace mozilla::dom::locks + +#endif // DOM_LOCKS_LOCKMANAGERCHILD_H_ diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/LockManager.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/LockManager.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/LockManager.cpp 2021-10-17 14:42:35.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/LockManager.cpp 2021-10-20 19:28:18.000000000 +0000 @@ -5,30 +5,21 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/dom/LockManager.h" -#include "mozilla/AlreadyAddRefed.h" +#include "mozilla/dom/WorkerCommon.h" +#include "mozilla/dom/locks/LockManagerChild.h" +#include "mozilla/dom/locks/LockRequestChild.h" #include "mozilla/Assertions.h" -#include "mozilla/Attributes.h" #include "mozilla/ErrorResult.h" #include "mozilla/dom/LockManagerBinding.h" #include "mozilla/dom/Promise.h" -#include "mozilla/dom/Promise-inl.h" -#include "mozilla/dom/WorkerPrivate.h" -#include "nsIRunnable.h" -#include "nsIDUtils.h" +#include "mozilla/dom/locks/PLockManager.h" +#include "mozilla/ipc/BackgroundChild.h" +#include "mozilla/ipc/BackgroundUtils.h" +#include "mozilla/ipc/PBackgroundChild.h" namespace mozilla::dom { -NS_IMPL_CYCLE_COLLECTION_CLASS(LockManager) -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(LockManager) - tmp->Shutdown(); - NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner, mHeldLockSet) - NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER -NS_IMPL_CYCLE_COLLECTION_UNLINK_END -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(LockManager) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner, mHeldLockSet) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END -NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(LockManager) - +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(LockManager, mOwner, mActor) NS_IMPL_CYCLE_COLLECTING_ADDREF(LockManager) NS_IMPL_CYCLE_COLLECTING_RELEASE(LockManager) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LockManager) @@ -41,14 +32,38 @@ return LockManager_Binding::Wrap(aCx, this, aGivenProto); } -void LockManager::Shutdown() { - mQueueMap.Clear(); - // TODO: release the remaining locks and requests made in this instance when - // shared lock manager is implemented -} - -static nsString GetClientId(nsIGlobalObject* aGlobal) { - return NSID_TrimBracketsUTF16(aGlobal->GetClientInfo()->Id()); +LockManager::LockManager(nsIGlobalObject* aGlobal) : mOwner(aGlobal) { + Maybe clientInfo = aGlobal->GetClientInfo(); + if (!clientInfo) { + // TODO: https://github.com/WICG/web-locks/issues/78 + return; + } + + const mozilla::ipc::PrincipalInfo& principalInfo = + clientInfo->PrincipalInfo(); + + if (principalInfo.type() != + mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) { + return; + } + + mozilla::ipc::PBackgroundChild* backgroundActor = + mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(); + mActor = new locks::LockManagerChild(aGlobal); + backgroundActor->SendPLockManagerConstructor(mActor, principalInfo, + clientInfo->Id()); + + if (!NS_IsMainThread()) { + mWorkerRef = WeakWorkerRef::Create(GetCurrentThreadWorkerPrivate(), + [self = RefPtr(this)]() { + // Others may grab a strong reference + // and block immediate destruction. + // Shutdown early as we don't have to + // wait for them. + self->Shutdown(); + self->mWorkerRef = nullptr; + }); + } } static bool ValidateRequestArguments(const nsAString& name, @@ -89,33 +104,14 @@ return true; } -// XXX: should be MOZ_CAN_RUN_SCRIPT, but not sure how to call it from closures -MOZ_CAN_RUN_SCRIPT_BOUNDARY static void RunCallbackAndSettlePromise( - LockGrantedCallback& aCallback, mozilla::dom::Lock* lock, - Promise& aPromise) { - ErrorResult rv; - if (RefPtr result = aCallback.Call( - lock, rv, nullptr, CallbackObject::eRethrowExceptions)) { - aPromise.MaybeResolve(result); - } else if (rv.Failed() && !rv.IsUncatchableException()) { - aPromise.MaybeReject(std::move(rv)); - } else { - aPromise.MaybeResolveWithUndefined(); - } - // This is required even with no failure. IgnoredErrorResult is not an option - // since MaybeReject does not accept it. - rv.WouldReportJSException(); - MOZ_ASSERT(!rv.Failed()); -} - -already_AddRefed LockManager::Request(const nsAString& name, - LockGrantedCallback& callback, +already_AddRefed LockManager::Request(const nsAString& aName, + LockGrantedCallback& aCallback, ErrorResult& aRv) { - return Request(name, LockOptions(), callback, aRv); + return Request(aName, LockOptions(), aCallback, aRv); }; -already_AddRefed LockManager::Request(const nsAString& name, - const LockOptions& options, - LockGrantedCallback& callback, +already_AddRefed LockManager::Request(const nsAString& aName, + const LockOptions& aOptions, + LockGrantedCallback& aCallback, ErrorResult& aRv) { if (mOwner->GetStorageAccess() <= StorageAccess::eDeny) { // Step 4: If origin is an opaque origin, then return a promise rejected @@ -125,151 +121,64 @@ aRv.ThrowSecurityError("request() is not allowed in this context"); return nullptr; } - if (!ValidateRequestArguments(name, options, aRv)) { + if (!ValidateRequestArguments(aName, aOptions, aRv)) { return nullptr; } - if (options.mSignal.WasPassed()) { - aRv.ThrowNotSupportedError("AbortSignal support is not implemented yet"); + if (!mActor) { + // TODO: https://github.com/WICG/web-locks/issues/78 + aRv.ThrowInvalidStateError( + "The document of the lock manager is not fully active or web locks " + "aren't enabled for the document."); return nullptr; } RefPtr promise = Promise::Create(mOwner, aRv); - LockRequest request = {nsString(name), options.mMode, promise, &callback}; + if (aRv.Failed()) { + return nullptr; + } - nsCOMPtr runCallback(NS_NewRunnableFunction( - "RequestLock", - [self = RefPtr(this), request, options]() -> void { - nsTArray& queue = - self->mQueueMap.LookupOrInsert(request.mName); - if (options.mSteal) { - self->mHeldLockSet.RemoveIf([&request](Lock* lock) { - if (lock->mName == request.mName) { - lock->mReleasedPromise->MaybeRejectWithAbortError( - "The lock request is aborted"); - return true; - } - return false; - }); - queue.InsertElementAt(0, request); - } else if (options.mIfAvailable && - !self->IsGrantableRequest(request, queue)) { - // TODO: this must be asynchronous! "enqueue the following steps" - RunCallbackAndSettlePromise(*request.mCallback, nullptr, - *request.mPromise); - return; - } else { - queue.AppendElement(request); - } - self->ProcessRequestQueue(queue); - })); - Unused << NS_DispatchToCurrentThreadQueue(runCallback.forget(), - EventQueuePriority::Idle); - // TODO: AbortSignal support - // if (options.mSignal.WasPassed()) { - // options.mSignal.Value() - // } + mActor->RequestLock({nsString(aName), promise, &aCallback}, aOptions); return promise.forget(); }; already_AddRefed LockManager::Query(ErrorResult& aRv) { - RefPtr promise = Promise::Create(mOwner, aRv); - - // TODO: this should be retrieved via IPC - - LockManagerSnapshot snapshot; - snapshot.mHeld.Construct(); - snapshot.mPending.Construct(); - for (const auto& queueMapEntry : mQueueMap) { - for (const LockRequest& request : queueMapEntry.GetData()) { - LockInfo info; - info.mMode.Construct(request.mMode); - info.mName.Construct(request.mName); - info.mClientId.Construct( - GetClientId(request.mPromise->GetGlobalObject())); - if (!snapshot.mPending.Value().AppendElement(info, mozilla::fallible)) { - aRv.Throw(NS_ERROR_OUT_OF_MEMORY); - return nullptr; - }; - } - } - for (const Lock* lock : mHeldLockSet) { - LockInfo info; - info.mMode.Construct(lock->mMode); - info.mName.Construct(lock->mName); - info.mClientId.Construct(GetClientId(lock->mOwner)); - if (!snapshot.mHeld.Value().AppendElement(info, mozilla::fallible)) { - aRv.Throw(NS_ERROR_OUT_OF_MEMORY); - return nullptr; - }; + if (mOwner->GetStorageAccess() <= StorageAccess::eDeny) { + aRv.ThrowSecurityError("query() is not allowed in this context"); + return nullptr; } - promise->MaybeResolve(snapshot); - return promise.forget(); -}; -void LockManager::ReleaseHeldLock(Lock* aLock) { - MOZ_ASSERT(mHeldLockSet.Contains(aLock), "Lock not held??"); - mHeldLockSet.Remove(aLock); - if (auto queue = mQueueMap.Lookup(aLock->mName)) { - ProcessRequestQueue(queue.Data()); - if (queue.Data().IsEmpty()) { - // Remove if empty, to prevent the queue map from growing forever - mQueueMap.Remove(aLock->mName); - } + if (!mActor) { + // TODO: https://github.com/WICG/web-locks/issues/78 + aRv.ThrowInvalidStateError( + "The document of the lock manager is not fully active or web locks " + "aren't enabled for the document."); + return nullptr; } - // or else, the queue is removed during the previous lock release -} -void LockManager::ProcessRequestQueue( - nsTArray& aQueue) { - // use manual loop since range-loop is not safe for mutable arrays - for (uint32_t i = 0; i < aQueue.Length(); i++) { - auto& request = aQueue[i]; - if (IsGrantableRequest(request, aQueue)) { - RefPtr lock = new Lock(mOwner, this, request.mName, request.mMode, - request.mPromise, IgnoreErrors() /* ?? */); - mHeldLockSet.Insert(lock); - - lock->GetWaitingPromise().AppendNativeHandler(lock); - - RefPtr callback = request.mCallback; - aQueue.RemoveElement(request); - nsCOMPtr runCallback(NS_NewRunnableFunction( - "RunCallbackAndSettlePromise", [callback, lock]() -> void { - RunCallbackAndSettlePromise(*callback, lock, - lock->GetWaitingPromise()); - })); - // TODO: This should go to lock task queue - Unused << NS_DispatchToCurrentThreadQueue(runCallback.forget(), - EventQueuePriority::Idle); - } + RefPtr promise = Promise::Create(mOwner, aRv); + if (aRv.Failed()) { + return nullptr; } -} -bool LockManager::IsGrantableRequest( - const LockRequest& aRequest, nsTArray& aQueue) { - if (HasBlockingHeldLock(aRequest.mName, aRequest.mMode)) { - return false; - } - if (!aQueue.Length()) { - return true; // Possible as this check also happens before enqueuing - } - return aQueue[0] == aRequest; -} + mActor->SendQuery()->Then( + GetCurrentSerialEventTarget(), __func__, + [promise](locks::LockManagerChild::QueryPromise::ResolveOrRejectValue&& + aResult) { + if (aResult.IsResolve()) { + promise->MaybeResolve(aResult.ResolveValue()); + } else { + promise->MaybeRejectWithUnknownError("Query failed"); + } + }); + return promise.forget(); +}; -bool LockManager::HasBlockingHeldLock(const nsString& aName, LockMode aMode) { - for (const auto& lock : mHeldLockSet) { - if (lock->mName == aName) { - if (aMode == LockMode::Exclusive) { - return true; - } - MOZ_ASSERT(aMode == LockMode::Shared); - if (lock->mMode == LockMode::Exclusive) { - return true; - } - } +void LockManager::Shutdown() { + if (mActor) { + locks::PLockManagerChild::Send__delete__(mActor); + mActor = nullptr; } - return false; } } // namespace mozilla::dom diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/LockManager.h firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/LockManager.h --- firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/LockManager.h 2021-10-17 14:42:35.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/LockManager.h 2021-10-20 19:28:19.000000000 +0000 @@ -13,6 +13,7 @@ #include "mozilla/dom/BindingDeclarations.h" #include "mozilla/dom/Lock.h" #include "mozilla/dom/LockManagerBinding.h" +#include "mozilla/dom/WorkerRef.h" #include "nsCycleCollectionParticipant.h" #include "nsHashKeys.h" #include "nsTHashMap.h" @@ -25,69 +26,42 @@ class LockGrantedCallback; struct LockOptions; -class Promise; -struct LockRequest { - // agent; - // clientId; - // origin; - nsString mName; - LockMode mMode; - RefPtr mPromise; - RefPtr mCallback; - - bool operator==(const LockRequest& aOther) const { - // This operator is needed to remove aborted requests from the queue. - // This assumes each request has a unique promise, which should be true - // since each creates a new one. - MOZ_ASSERT(mPromise && aOther.mPromise, - "Promises are null when requests are still active??"); - return mPromise == aOther.mPromise; - } -}; +namespace locks { +class LockManagerChild; +} class LockManager final : public nsISupports, public nsWrapperCache { public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(LockManager) - explicit LockManager(nsIGlobalObject* aGlobal) : mOwner(aGlobal) {} - - protected: - ~LockManager() = default; + explicit LockManager(nsIGlobalObject* aGlobal); - public: nsIGlobalObject* GetParentObject() const { return mOwner; } JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; - void Shutdown(); - - already_AddRefed Request(const nsAString& name, - LockGrantedCallback& callback, + already_AddRefed Request(const nsAString& aName, + LockGrantedCallback& aCallback, ErrorResult& aRv); - already_AddRefed Request(const nsAString& name, - const LockOptions& options, - LockGrantedCallback& callback, + already_AddRefed Request(const nsAString& aName, + const LockOptions& aOptions, + LockGrantedCallback& aCallback, ErrorResult& aRv); already_AddRefed Query(ErrorResult& aRv); - void ReleaseHeldLock(Lock* aLock); + void Shutdown(); private: - void ProcessRequestQueue(nsTArray& aQueue); - bool IsGrantableRequest(const LockRequest& aRequest, - nsTArray& aQueue); - bool HasBlockingHeldLock(const nsString& aName, LockMode aMode); + ~LockManager() = default; nsCOMPtr mOwner; + RefPtr mActor; - // TODO: These should be behind IPC - // XXX: Before that these should be cycle collected or explicitly cleared - nsTHashSet> mHeldLockSet; - nsTHashMap> mQueueMap; + RefPtr mWorkerRef; }; } // namespace mozilla::dom diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/LockManagerParent.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/LockManagerParent.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/LockManagerParent.cpp 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/LockManagerParent.cpp 2021-10-20 19:28:18.000000000 +0000 @@ -0,0 +1,168 @@ +/* -*- 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 "LockManagerParent.h" +#include "LockRequestParent.h" + +#include "mozilla/ContentPrincipalInfoHashKey.h" +#include "mozilla/PrincipalHashKey.h" +#include "mozilla/RefPtr.h" +#include "mozilla/dom/locks/PLockManager.h" +#include "mozilla/media/MediaUtils.h" +#include "nsIDUtils.h" +#include "nsTHashMap.h" + +namespace mozilla::dom::locks { + +static nsTHashMap> + sManagedLocksMap; + +using IPCResult = mozilla::ipc::IPCResult; + +LockManagerParent::LockManagerParent( + const mozilla::ipc::ContentPrincipalInfo& aPrincipalInfo, + const nsID& aClientId) + : mClientId(NSID_TrimBracketsUTF16(aClientId)), + mPrincipalInfo(aPrincipalInfo) { + mManagedLocks = sManagedLocksMap.Get(aPrincipalInfo); + if (!mManagedLocks) { + mManagedLocks = new ManagedLocks(); + sManagedLocksMap.LookupOrInsert(aPrincipalInfo, mManagedLocks); + } +} + +void LockManagerParent::ActorDestroy(ActorDestroyReason aWhy) { + if (!mManagedLocks) { + return; + } + + nsTArray affectedResourceNames; + + mManagedLocks->mHeldLocks.RemoveElementsBy( + [this, &affectedResourceNames](const RefPtr& request) { + bool equals = request->Manager() == this; + if (equals) { + affectedResourceNames.AppendElement(request->Data().name()); + } + return equals; + }); + + for (auto& queue : mManagedLocks->mQueueMap) { + queue.GetModifiableData()->RemoveElementsBy( + [this, &name = queue.GetKey(), + &affectedResourceNames](const RefPtr& request) { + bool equals = request->Manager() == this; + if (equals) { + affectedResourceNames.AppendElement(name); + } + return equals; + }); + } + + for (const nsString& name : affectedResourceNames) { + if (auto queue = mManagedLocks->mQueueMap.Lookup(name)) { + ProcessRequestQueue(queue.Data()); + } + } + + mManagedLocks = nullptr; + // We just decreased the refcount and potentially deleted it, so check whether + // the weak pointer still points to anything and remove the entry if not. + if (!sManagedLocksMap.Get(mPrincipalInfo)) { + sManagedLocksMap.Remove(mPrincipalInfo); + } +} + +void LockManagerParent::ProcessRequestQueue( + nsTArray>& aQueue) { + while (aQueue.Length()) { + RefPtr first = aQueue[0]; + if (!IsGrantableRequest(first->Data())) { + break; + } + aQueue.RemoveElementAt(0); + mManagedLocks->mHeldLocks.AppendElement(first); + Unused << NS_WARN_IF(!first->SendResolve(first->Data().lockMode(), true)); + } +} + +bool LockManagerParent::IsGrantableRequest(const IPCLockRequest& aRequest) { + for (const auto& held : mManagedLocks->mHeldLocks) { + if (held->Data().name() == aRequest.name()) { + if (aRequest.lockMode() == LockMode::Exclusive) { + return false; + } + MOZ_ASSERT(aRequest.lockMode() == LockMode::Shared); + if (held->Data().lockMode() == LockMode::Exclusive) { + return false; + } + } + } + return true; +} + +IPCResult LockManagerParent::RecvQuery(QueryResolver&& aResolver) { + LockManagerSnapshot snapshot; + snapshot.mHeld.Construct(); + snapshot.mPending.Construct(); + for (const auto& queueMapEntry : mManagedLocks->mQueueMap) { + for (const RefPtr& request : queueMapEntry.GetData()) { + LockInfo info; + info.mMode.Construct(request->Data().lockMode()); + info.mName.Construct(request->Data().name()); + info.mClientId.Construct( + static_cast(request->Manager())->mClientId); + if (!snapshot.mPending.Value().AppendElement(info, mozilla::fallible)) { + return IPC_FAIL(this, "Out of memory"); + }; + } + } + for (const RefPtr& request : mManagedLocks->mHeldLocks) { + LockInfo info; + info.mMode.Construct(request->Data().lockMode()); + info.mName.Construct(request->Data().name()); + info.mClientId.Construct( + static_cast(request->Manager())->mClientId); + if (!snapshot.mHeld.Value().AppendElement(info, mozilla::fallible)) { + return IPC_FAIL(this, "Out of memory"); + }; + } + aResolver(snapshot); + return IPC_OK(); +}; + +already_AddRefed LockManagerParent::AllocPLockRequestParent( + const IPCLockRequest& aRequest) { + return MakeAndAddRef(aRequest); +} + +IPCResult LockManagerParent::RecvPLockRequestConstructor( + PLockRequestParent* aActor, const IPCLockRequest& aRequest) { + RefPtr actor = static_cast(aActor); + nsTArray>& queue = + mManagedLocks->mQueueMap.LookupOrInsert(aRequest.name()); + if (aRequest.steal()) { + mManagedLocks->mHeldLocks.RemoveElementsBy( + [&aRequest](const RefPtr& aHeld) { + if (aHeld->Data().name() == aRequest.name()) { + Unused << NS_WARN_IF(!aHeld->SendAbort()); + return true; + } + return false; + }); + queue.InsertElementAt(0, actor); + } else if (aRequest.ifAvailable() && + (!queue.IsEmpty() || !IsGrantableRequest(actor->Data()))) { + Unused << NS_WARN_IF(!aActor->SendResolve(aRequest.lockMode(), false)); + return IPC_OK(); + } else { + queue.AppendElement(actor); + } + ProcessRequestQueue(queue); + return IPC_OK(); +} + +} // namespace mozilla::dom::locks diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/LockManagerParent.h firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/LockManagerParent.h --- firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/LockManagerParent.h 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/LockManagerParent.h 2021-10-20 19:28:18.000000000 +0000 @@ -0,0 +1,61 @@ +/* -*- 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/. */ + +#ifndef DOM_LOCKS_LOCKMANAGERPARENT_H_ +#define DOM_LOCKS_LOCKMANAGERPARENT_H_ + +#include "mozilla/AlreadyAddRefed.h" +#include "mozilla/dom/locks/PLockManagerParent.h" +#include "mozilla/dom/locks/LockRequestParent.h" +#include "mozilla/ipc/PBackgroundSharedTypes.h" + +namespace mozilla::dom::locks { + +class ManagedLocks : public SupportsWeakPtr { + public: + NS_INLINE_DECL_REFCOUNTING(ManagedLocks) + + nsTArray> mHeldLocks; + nsTHashMap>> mQueueMap; + + private: + ~ManagedLocks() = default; +}; + +class LockManagerParent final : public PLockManagerParent { + using IPCResult = mozilla::ipc::IPCResult; + + public: + NS_INLINE_DECL_REFCOUNTING(LockManagerParent) + + LockManagerParent(const mozilla::ipc::ContentPrincipalInfo& aPrincipalInfo, + const nsID& aClientId); + + void ProcessRequestQueue(nsTArray>& aQueue); + bool IsGrantableRequest(const IPCLockRequest& aRequest); + + IPCResult RecvQuery(QueryResolver&& aResolver); + + already_AddRefed AllocPLockRequestParent( + const IPCLockRequest& aRequest); + IPCResult RecvPLockRequestConstructor(PLockRequestParent* aActor, + const IPCLockRequest& aRequest) final; + + ManagedLocks& Locks() { return *mManagedLocks; } + + private: + ~LockManagerParent() = default; + + void ActorDestroy(ActorDestroyReason aWhy) final; + + RefPtr mManagedLocks; + nsString mClientId; + mozilla::ipc::ContentPrincipalInfo mPrincipalInfo; +}; + +} // namespace mozilla::dom::locks + +#endif // DOM_LOCKS_LOCKMANAGERPARENT_H_ diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/LockRequestChild.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/LockRequestChild.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/LockRequestChild.cpp 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/LockRequestChild.cpp 2021-10-20 19:28:18.000000000 +0000 @@ -0,0 +1,97 @@ +/* -*- 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 "LockManagerChild.h" +#include "LockRequestChild.h" + +#include "mozilla/dom/Promise.h" +#include "mozilla/dom/WorkerPrivate.h" + +namespace mozilla::dom::locks { + +using IPCResult = mozilla::ipc::IPCResult; + +NS_IMPL_ISUPPORTS(LockRequestChild, nsISupports) + +// XXX: should be MOZ_CAN_RUN_SCRIPT, but not sure how to call it from closures +MOZ_CAN_RUN_SCRIPT_BOUNDARY static void RunCallbackAndSettlePromise( + LockGrantedCallback& aCallback, mozilla::dom::Lock* lock, + Promise& aPromise) { + ErrorResult rv; + if (RefPtr result = aCallback.Call( + lock, rv, nullptr, CallbackObject::eRethrowExceptions)) { + aPromise.MaybeResolve(result); + } else if (rv.Failed() && !rv.IsUncatchableException()) { + aPromise.MaybeReject(std::move(rv)); + return; + } else { + aPromise.MaybeResolveWithUndefined(); + } + // This is required even with no failure. IgnoredErrorResult is not an option + // since MaybeReject does not accept it. + rv.WouldReportJSException(); + if (NS_WARN_IF(rv.IsUncatchableException())) { + rv.SuppressException(); // XXX: Why does this happen anyway? + } + MOZ_ASSERT(!rv.Failed()); +} + +LockRequestChild::LockRequestChild( + const LockRequest& aRequest, + const Optional>& aSignal) + : mRequest(aRequest) { + if (aSignal.WasPassed()) { + Follow(&aSignal.Value()); + } + if (!NS_IsMainThread()) { + mWorkerRef = StrongWorkerRef::Create( + GetCurrentThreadWorkerPrivate(), "LockManager", + [self = RefPtr(this)]() { self->mWorkerRef = nullptr; }); + } +} + +IPCResult LockRequestChild::RecvResolve(const LockMode& aLockMode, + bool aIsAvailable) { + Unfollow(); + + RefPtr lock; + RefPtr promise; + if (aIsAvailable) { + IgnoredErrorResult err; + lock = + new Lock(static_cast(Manager())->GetParentObject(), + this, mRequest.mName, aLockMode, mRequest.mPromise, err); + if (MOZ_UNLIKELY(err.Failed())) { + mRequest.mPromise->MaybeRejectWithUnknownError( + "Failed to allocate a lock"); + return IPC_OK(); + } + lock->GetWaitingPromise().AppendNativeHandler(lock); + promise = &lock->GetWaitingPromise(); + } else { + // We are in `ifAvailable: true` mode and the lock is not available. + // There is no waitingPromise since there is no lock, so settle the promise + // from the request instead. + // This matches "If ifAvailable is true and request is not grantable" step. + promise = mRequest.mPromise; + } + + RunCallbackAndSettlePromise(*mRequest.mCallback, lock, *promise); + return IPC_OK(); +} + +IPCResult LockRequestChild::RecvAbort() { + Unfollow(); + mRequest.mPromise->MaybeRejectWithAbortError("The lock request is aborted"); + return IPC_OK(); +} + +void LockRequestChild::RunAbortAlgorithm() { + RecvAbort(); + Send__delete__(this, true); +} + +} // namespace mozilla::dom::locks diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/LockRequestChild.h firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/LockRequestChild.h --- firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/LockRequestChild.h 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/LockRequestChild.h 2021-10-20 19:28:19.000000000 +0000 @@ -0,0 +1,49 @@ +/* -*- 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/. */ + +#ifndef DOM_LOCKS_LOCKREQUESTCHILD_H_ +#define DOM_LOCKS_LOCKREQUESTCHILD_H_ + +#include "mozilla/dom/locks/PLockRequestChild.h" +#include "mozilla/dom/Lock.h" +#include "mozilla/dom/WorkerRef.h" +#include "nsISupportsImpl.h" + +namespace mozilla::dom::locks { + +struct LockRequest { + nsString mName; + RefPtr mPromise; + RefPtr mCallback; +}; + +class LockRequestChild final : public PLockRequestChild, + public AbortFollower, + public SupportsWeakPtr { + using IPCResult = mozilla::ipc::IPCResult; + + NS_DECL_ISUPPORTS + + public: + explicit LockRequestChild( + const LockRequest& aRequest, + const Optional>& aSignal); + + IPCResult RecvResolve(const LockMode& aLockMode, bool aIsAvailable); + IPCResult RecvAbort(); + + void RunAbortAlgorithm() final; + + private: + ~LockRequestChild() = default; + + LockRequest mRequest; + RefPtr mWorkerRef; +}; + +} // namespace mozilla::dom::locks + +#endif // DOM_LOCKS_LOCKREQUESTCHILD_H_ diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/LockRequestParent.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/LockRequestParent.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/LockRequestParent.cpp 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/LockRequestParent.cpp 2021-10-20 19:28:18.000000000 +0000 @@ -0,0 +1,38 @@ +/* -*- 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 "LockManagerParent.h" +#include "LockRequestParent.h" + +#include "mozilla/dom/Promise.h" + +namespace mozilla::dom::locks { + +mozilla::ipc::IPCResult LockRequestParent::Recv__delete__(bool aAborted) { + RefPtr manager = + static_cast(Manager()); + ManagedLocks& managed = manager->Locks(); + + DebugOnly unheld = managed.mHeldLocks.RemoveElement(this); + MOZ_ASSERT_IF(!aAborted, unheld); + + if (auto queue = managed.mQueueMap.Lookup(mRequest.name())) { + if (aAborted) { + DebugOnly dequeued = queue.Data().RemoveElement(this); + MOZ_ASSERT_IF(!unheld, dequeued); + } + manager->ProcessRequestQueue(queue.Data()); + if (queue.Data().IsEmpty()) { + // Remove if empty, to prevent the queue map from growing forever + queue.Remove(); + } + } + // or else, the queue is removed during the previous lock release (since + // multiple held locks are possible with `shared: true`) + return IPC_OK(); +} + +} // namespace mozilla::dom::locks diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/LockRequestParent.h firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/LockRequestParent.h --- firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/LockRequestParent.h 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/LockRequestParent.h 2021-10-20 19:28:19.000000000 +0000 @@ -0,0 +1,35 @@ +/* -*- 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/. */ + +#ifndef DOM_LOCKS_LOCKREQUESTPARENT_H_ +#define DOM_LOCKS_LOCKREQUESTPARENT_H_ + +#include "mozilla/dom/locks/PLockManager.h" +#include "mozilla/dom/locks/PLockRequestParent.h" +#include "nsISupportsImpl.h" + +namespace mozilla::dom::locks { + +class LockRequestParent final : public PLockRequestParent { + public: + NS_INLINE_DECL_REFCOUNTING(LockRequestParent) + + explicit LockRequestParent(const IPCLockRequest& aRequest) + : mRequest(aRequest){}; + + const IPCLockRequest& Data() { return mRequest; } + + mozilla::ipc::IPCResult Recv__delete__(bool aAborted); + + private: + ~LockRequestParent() = default; + + IPCLockRequest mRequest; +}; + +} // namespace mozilla::dom::locks + +#endif // DOM_LOCKS_LOCKREQUESTPARENT_H_ diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/moz.build firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/moz.build --- firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/moz.build 2021-10-17 14:42:35.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/moz.build 2021-10-20 19:28:18.000000000 +0000 @@ -7,14 +7,33 @@ with Files("**"): BUG_COMPONENT = ("Core", "DOM: Core & HTML") +MOCHITEST_MANIFESTS += ["test/mochitest.ini"] + EXPORTS.mozilla.dom += [ "Lock.h", "LockManager.h", ] +EXPORTS.mozilla.dom.locks += [ + "IPCUtils.h", + "LockManagerChild.h", + "LockManagerParent.h", + "LockRequestChild.h", + "LockRequestParent.h", +] + UNIFIED_SOURCES += [ "Lock.cpp", "LockManager.cpp", + "LockManagerChild.cpp", + "LockManagerParent.cpp", + "LockRequestChild.cpp", + "LockRequestParent.cpp", +] + +IPDL_SOURCES += [ + "PLockManager.ipdl", + "PLockRequest.ipdl", ] include("/ipc/chromium/chromium-config.mozbuild") diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/PLockManager.ipdl firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/PLockManager.ipdl --- firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/PLockManager.ipdl 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/PLockManager.ipdl 2021-10-20 19:28:18.000000000 +0000 @@ -0,0 +1,39 @@ +/* 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 protocol PBackground; +include protocol PLockRequest; + +include "mozilla/dom/locks/IPCUtils.h"; + +using mozilla::dom::LockMode from "mozilla/dom/LockManagerBinding.h"; +using mozilla::dom::LockManagerSnapshot from "mozilla/dom/LockManagerBinding.h"; + +namespace mozilla { +namespace dom { +namespace locks { + +struct IPCLockRequest { + nsString name; + LockMode lockMode; + bool ifAvailable; + bool steal; +}; + +[RefCounted] +protocol PLockManager { + manager PBackground; + manages PLockRequest; + + parent: + async Query() returns (LockManagerSnapshot snapshot); + + async PLockRequest(IPCLockRequest aRequest); + + async __delete__(); +}; + +} // namespace cache +} // namespace dom +} // namespace mozilla diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/PLockRequest.ipdl firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/PLockRequest.ipdl --- firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/PLockRequest.ipdl 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/PLockRequest.ipdl 2021-10-20 19:28:18.000000000 +0000 @@ -0,0 +1,29 @@ +/* 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 protocol PLockManager; + +include "mozilla/dom/locks/IPCUtils.h"; + +using mozilla::dom::LockMode from "mozilla/dom/LockManagerBinding.h"; + +namespace mozilla { +namespace dom { +namespace locks { + +[RefCounted] +protocol PLockRequest { + manager PLockManager; + + child: + async Resolve(LockMode aMode, bool aIsAvailable); + async Abort(); + + parent: + async __delete__(bool aAborted); +}; + +} // namespace cache +} // namespace dom +} // namespace mozilla diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/test/.eslintrc.js firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/test/.eslintrc.js --- firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/test/.eslintrc.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/test/.eslintrc.js 2021-10-20 19:28:18.000000000 +0000 @@ -0,0 +1,5 @@ +"use strict"; + +module.exports = { + extends: ["plugin:mozilla/mochitest-test"], +}; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/test/file_strongworker.js firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/test/file_strongworker.js --- firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/test/file_strongworker.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/test/file_strongworker.js 2021-10-20 19:28:18.000000000 +0000 @@ -0,0 +1,5 @@ +navigator.locks.request("exclusive", () => { + const channel = new BroadcastChannel("strongworker"); + channel.postMessage("lock acquired"); +}); +postMessage("onload"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/test/mochitest.ini firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/test/mochitest.ini --- firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/test/mochitest.ini 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/test/mochitest.ini 2021-10-20 19:28:19.000000000 +0000 @@ -0,0 +1,8 @@ +[DEFAULT] +scheme = https +prefs = + dom.weblocks.enabled=true + +[test_strongworker.html] +support-files = + file_strongworker.js diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/test/test_strongworker.html firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/test/test_strongworker.html --- firefox-trunk-95.0~a1~hg20211017r596111/dom/locks/test/test_strongworker.html 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/locks/test/test_strongworker.html 2021-10-20 19:28:18.000000000 +0000 @@ -0,0 +1,30 @@ + + + + + + diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/manifest/test/cookie_checker.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/manifest/test/cookie_checker.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/manifest/test/cookie_checker.sjs 2021-10-17 14:42:35.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/manifest/test/cookie_checker.sjs 2021-10-20 19:28:18.000000000 +0000 @@ -1,5 +1,5 @@ "use strict"; -Components.utils.import("resource://gre/modules/NetUtil.jsm"); +let { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm"); function handleRequest(request, response) { response.setStatusLine(request.httpVersion, 200); @@ -8,7 +8,9 @@ response.setHeader("Content-Type", "application/json", false); // CORS stuff - const origin = request.hasHeader("Origin") ? request.getHeader("Origin") : null; + const origin = request.hasHeader("Origin") + ? request.getHeader("Origin") + : null; if (origin) { response.setHeader("Access-Control-Allow-Origin", origin); response.setHeader("Access-Control-Allow-Credentials", "true"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/manifest/test/file_testserver.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/manifest/test/file_testserver.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/manifest/test/file_testserver.sjs 2021-10-17 14:42:35.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/manifest/test/file_testserver.sjs 2021-10-20 19:28:18.000000000 +0000 @@ -1,25 +1,26 @@ "use strict"; -Components.utils.import("resource://gre/modules/NetUtil.jsm"); +let { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm"); Components.utils.importGlobalProperties(["URLSearchParams"]); function loadHTMLFromFile(path) { // Load the HTML to return in the response from file. // Since it's relative to the cwd of the test runner, we start there and // append to get to the actual path of the file. - const testHTMLFile = - Components.classes["@mozilla.org/file/directory_service;1"]. - getService(Components.interfaces.nsIProperties). - get("CurWorkD", Components.interfaces.nsIFile); - - const testHTMLFileStream = - Components.classes["@mozilla.org/network/file-input-stream;1"]. - createInstance(Components.interfaces.nsIFileInputStream); + const testHTMLFile = Components.classes[ + "@mozilla.org/file/directory_service;1" + ] + .getService(Components.interfaces.nsIProperties) + .get("CurWorkD", Components.interfaces.nsIFile); + + const testHTMLFileStream = Components.classes[ + "@mozilla.org/network/file-input-stream;1" + ].createInstance(Components.interfaces.nsIFileInputStream); path .split("/") .filter(path => path) .reduce((file, path) => { - testHTMLFile.append(path) + testHTMLFile.append(path); return testHTMLFile; }, testHTMLFile); testHTMLFileStream.init(testHTMLFile, -1, 0, 0); @@ -34,17 +35,21 @@ response.setHeader("Cache-Control", "no-cache", false); // Deliver the CSP policy encoded in the URL - if(query.has("csp")){ + if (query.has("csp")) { response.setHeader("Content-Security-Policy", query.get("csp"), false); } // Deliver the CSPRO policy encoded in the URL - if(query.has("cspro")){ - response.setHeader("Content-Security-Policy-Report-Only", query.get("cspro"), false); + if (query.has("cspro")) { + response.setHeader( + "Content-Security-Policy-Report-Only", + query.get("cspro"), + false + ); } // Deliver the CORS header in the URL - if(query.has("cors")){ + if (query.has("cors")) { response.setHeader("Access-Control-Allow-Origin", query.get("cors"), false); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/manifest/test/resource.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/manifest/test/resource.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/manifest/test/resource.sjs 2021-10-17 14:42:35.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/manifest/test/resource.sjs 2021-10-20 19:28:18.000000000 +0000 @@ -19,64 +19,64 @@ *

hello

*/ //global handleRequest -'use strict'; +"use strict"; Components.utils.importGlobalProperties(["URLSearchParams"]); const HTTPStatus = new Map([ - [100, 'Continue'], - [101, 'Switching Protocol'], - [200, 'OK'], - [201, 'Created'], - [202, 'Accepted'], - [203, 'Non-Authoritative Information'], - [204, 'No Content'], - [205, 'Reset Content'], - [206, 'Partial Content'], - [300, 'Multiple Choice'], - [301, 'Moved Permanently'], - [302, 'Found'], - [303, 'See Other'], - [304, 'Not Modified'], - [305, 'Use Proxy'], - [306, 'unused'], - [307, 'Temporary Redirect'], - [308, 'Permanent Redirect'], - [400, 'Bad Request'], - [401, 'Unauthorized'], - [402, 'Payment Required'], - [403, 'Forbidden'], - [404, 'Not Found'], - [405, 'Method Not Allowed'], - [406, 'Not Acceptable'], - [407, 'Proxy Authentication Required'], - [408, 'Request Timeout'], - [409, 'Conflict'], - [410, 'Gone'], - [411, 'Length Required'], - [412, 'Precondition Failed'], - [413, 'Request Entity Too Large'], - [414, 'Request-URI Too Long'], - [415, 'Unsupported Media Type'], - [416, 'Requested Range Not Satisfiable'], - [417, 'Expectation Failed'], - [500, 'Internal Server Error'], - [501, 'Not Implemented'], - [502, 'Bad Gateway'], - [503, 'Service Unavailable'], - [504, 'Gateway Timeout'], - [505, 'HTTP Version Not Supported'] + [100, "Continue"], + [101, "Switching Protocol"], + [200, "OK"], + [201, "Created"], + [202, "Accepted"], + [203, "Non-Authoritative Information"], + [204, "No Content"], + [205, "Reset Content"], + [206, "Partial Content"], + [300, "Multiple Choice"], + [301, "Moved Permanently"], + [302, "Found"], + [303, "See Other"], + [304, "Not Modified"], + [305, "Use Proxy"], + [306, "unused"], + [307, "Temporary Redirect"], + [308, "Permanent Redirect"], + [400, "Bad Request"], + [401, "Unauthorized"], + [402, "Payment Required"], + [403, "Forbidden"], + [404, "Not Found"], + [405, "Method Not Allowed"], + [406, "Not Acceptable"], + [407, "Proxy Authentication Required"], + [408, "Request Timeout"], + [409, "Conflict"], + [410, "Gone"], + [411, "Length Required"], + [412, "Precondition Failed"], + [413, "Request Entity Too Large"], + [414, "Request-URI Too Long"], + [415, "Unsupported Media Type"], + [416, "Requested Range Not Satisfiable"], + [417, "Expectation Failed"], + [500, "Internal Server Error"], + [501, "Not Implemented"], + [502, "Bad Gateway"], + [503, "Service Unavailable"], + [504, "Gateway Timeout"], + [505, "HTTP Version Not Supported"], ]); function handleRequest(request, response) { const queryMap = new URLSearchParams(request.queryString); - if (queryMap.has('statusCode')) { - let statusCode = parseInt(queryMap.get('statusCode')); + if (queryMap.has("statusCode")) { + let statusCode = parseInt(queryMap.get("statusCode")); let statusText = HTTPStatus.get(statusCode); - queryMap.delete('statusCode'); - response.setStatusLine('1.1', statusCode, statusText); + queryMap.delete("statusCode"); + response.setStatusLine("1.1", statusCode, statusText); } - if (queryMap.has('body')) { - let body = queryMap.get('body') || ''; - queryMap.delete('body'); + if (queryMap.has("body")) { + let body = queryMap.get("body") || ""; + queryMap.delete("body"); response.write(decodeURIComponent(body)); } for (let [key, value] of queryMap.entries()) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/media/mediacapabilities/MediaCapabilities.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/media/mediacapabilities/MediaCapabilities.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/media/mediacapabilities/MediaCapabilities.cpp 2021-10-17 14:42:35.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/media/mediacapabilities/MediaCapabilities.cpp 2021-10-20 19:28:18.000000000 +0000 @@ -32,6 +32,7 @@ #include "mozilla/dom/WorkerRef.h" #include "mozilla/layers/KnowsCompositor.h" #include "nsContentUtils.h" +#include "WindowRenderer.h" static mozilla::LazyLogModule sMediaCapabilitiesLog("MediaCapabilities"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/media/MediaDecoder.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/media/MediaDecoder.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/media/MediaDecoder.cpp 2021-10-17 14:42:35.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/media/MediaDecoder.cpp 2021-10-20 19:28:18.000000000 +0000 @@ -35,6 +35,7 @@ #include "nsPrintfCString.h" #include "nsServiceManagerUtils.h" #include "nsTArray.h" +#include "WindowRenderer.h" #include #include diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/media/mediasink/AudioSink.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/media/mediasink/AudioSink.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/media/mediasink/AudioSink.cpp 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/media/mediasink/AudioSink.cpp 2021-10-20 19:28:18.000000000 +0000 @@ -291,7 +291,7 @@ mCurrentData->mTime.ToMicroseconds(), mCurrentData->Frames() - mCursor->Available(), framesToPop); - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled()) { mOwnerThread->Dispatch(NS_NewRunnableFunction( "AudioSink:AddMarker", [startTime = mCurrentData->mTime.ToMicroseconds(), diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/media/mediasink/DecodedStream.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/media/mediasink/DecodedStream.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/media/mediasink/DecodedStream.cpp 2021-10-17 14:42:35.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/media/mediasink/DecodedStream.cpp 2021-10-20 19:28:18.000000000 +0000 @@ -488,7 +488,7 @@ MOZ_ASSERT(mStartTime.isNothing(), "playback already started."); AUTO_PROFILER_LABEL(FUNCTION_SIGNATURE, MEDIA_PLAYBACK); - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled()) { nsPrintfCString markerString("StartTime=%" PRId64, aStartTime.ToMicroseconds()); PLAYBACK_PROFILER_MARKER(markerString); @@ -665,7 +665,7 @@ return; } - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled()) { nsPrintfCString markerString("Playing=%s", aPlaying ? "true" : "false"); PLAYBACK_PROFILER_MARKER(markerString); } @@ -675,7 +675,7 @@ void DecodedStream::SetVolume(double aVolume) { AssertOwnerThread(); - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled()) { nsPrintfCString markerString("Volume=%f", aVolume); PLAYBACK_PROFILER_MARKER(markerString); } @@ -690,7 +690,7 @@ void DecodedStream::SetPlaybackRate(double aPlaybackRate) { AssertOwnerThread(); - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled()) { nsPrintfCString markerString("PlaybackRate=%f", aPlaybackRate); PLAYBACK_PROFILER_MARKER(markerString); } @@ -705,7 +705,7 @@ void DecodedStream::SetPreservesPitch(bool aPreservesPitch) { AssertOwnerThread(); - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled()) { nsPrintfCString markerString("PreservesPitch=%s", aPreservesPitch ? "true" : "false"); PLAYBACK_PROFILER_MARKER(markerString); @@ -1088,7 +1088,7 @@ mLastOutputTime = time; auto currentTime = GetPosition(); - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled()) { nsPrintfCString markerString("OutputTime=%" PRId64, currentTime.ToMicroseconds()); PLAYBACK_PROFILER_MARKER(markerString); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/media/mediasource/TrackBuffersManager.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/media/mediasource/TrackBuffersManager.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/media/mediasource/TrackBuffersManager.cpp 2021-10-17 14:42:35.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/media/mediasource/TrackBuffersManager.cpp 2021-10-20 19:28:19.000000000 +0000 @@ -2123,7 +2123,7 @@ aSamples.Length(), aTrackData.mInfo->mMimeType.get(), aIntervals.GetStart().ToMicroseconds(), aIntervals.GetEnd().ToMicroseconds()); - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled()) { nsPrintfCString markerString( "Processing %zu %s frames(start:%" PRId64 " end:%" PRId64 ")", aSamples.Length(), aTrackData.mInfo->mMimeType.get(), @@ -2348,7 +2348,7 @@ lastRemovedIndex - firstRemovedIndex.ref() + 1, removedIntervals.GetStart().ToSeconds(), removedIntervals.GetEnd().ToSeconds()); - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled()) { nsPrintfCString markerString( "Removing frames from:%u (frames:%u) ([%f, %f))", firstRemovedIndex.ref(), lastRemovedIndex - firstRemovedIndex.ref() + 1, diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/media/platforms/wmf/WMFAudioMFTManager.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/media/platforms/wmf/WMFAudioMFTManager.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/media/platforms/wmf/WMFAudioMFTManager.cpp 2021-10-17 14:42:35.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/media/platforms/wmf/WMFAudioMFTManager.cpp 2021-10-20 19:28:19.000000000 +0000 @@ -185,6 +185,7 @@ HRESULT WMFAudioMFTManager::Input(MediaRawData* aSample) { + mLastInputTime = aSample->mTime; return mDecoder->Input(aSample->Data(), uint32_t(aSample->Size()), aSample->mTime.ToMicroseconds(), aSample->mDuration.ToMicroseconds()); @@ -222,6 +223,7 @@ RefPtr sample; HRESULT hr; int typeChangeCount = 0; + const auto oldAudioRate = mAudioRate; while (true) { hr = mDecoder->Output(&sample); if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { @@ -285,6 +287,11 @@ return S_OK; } + if (oldAudioRate != mAudioRate) { + LOG("Audio rate changed from %" PRIu32 " to %" PRIu32, oldAudioRate, + mAudioRate); + } + AlignedAudioBuffer audioData(numSamples); if (!audioData) { return E_OUTOFMEMORY; @@ -297,9 +304,18 @@ TimeUnit duration = FramesToTimeUnit(numFrames, mAudioRate); NS_ENSURE_TRUE(duration.IsValid(), E_FAIL); + const bool isAudioRateChangedToHigher = oldAudioRate < mAudioRate; + if (IsPartialOutput(duration, isAudioRateChangedToHigher)) { + LOG("Encounter a partial frame?! duration shrinks from %" PRId64 + " to %" PRId64, + mLastOutputDuration.ToMicroseconds(), duration.ToMicroseconds()); + return MF_E_TRANSFORM_NEED_MORE_INPUT; + } + aOutData = new AudioData(aStreamOffset, pts, std::move(audioData), mAudioChannels, mAudioRate, mChannelsMap); MOZ_DIAGNOSTIC_ASSERT(duration == aOutData->mDuration, "must be equal"); + mLastOutputDuration = aOutData->mDuration; #ifdef LOG_SAMPLE_DECODE LOG("Decoded audio sample! timestamp=%lld duration=%lld currentLength=%u", @@ -309,6 +325,26 @@ return S_OK; } +bool WMFAudioMFTManager::IsPartialOutput( + const media::TimeUnit& aNewOutputDuration, + const bool aIsRateChangedToHigher) const { + // This issue was found in Windows11, where AAC MFT decoder would incorrectly + // output partial output samples to us, even if MS's documentation said it + // won't happen [1]. More details are described in bug 1731430 comment 26. + // If the audio rate isn't changed to higher, which would result in shorter + // duration, but the new output duration is still shorter than the last one, + // then new output is possible an incorrect partial output. + // [1] + // https://docs.microsoft.com/en-us/windows/win32/medfound/mft-message-command-drain + if (mStreamType != AAC) { + return false; + } + if (mLastOutputDuration > aNewOutputDuration && !aIsRateChangedToHigher) { + return true; + } + return false; +} + void WMFAudioMFTManager::Shutdown() { mDecoder = nullptr; } } // namespace mozilla diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/media/platforms/wmf/WMFAudioMFTManager.h firefox-trunk-95.0~a1~hg20211020r596404/dom/media/platforms/wmf/WMFAudioMFTManager.h --- firefox-trunk-95.0~a1~hg20211017r596111/dom/media/platforms/wmf/WMFAudioMFTManager.h 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/media/platforms/wmf/WMFAudioMFTManager.h 2021-10-20 19:28:19.000000000 +0000 @@ -39,6 +39,9 @@ private: HRESULT UpdateOutputType(); + bool IsPartialOutput(const media::TimeUnit& aNewOutputDuration, + const bool aIsRateChangedToHigher) const; + uint32_t mAudioChannels; AudioConfig::ChannelLayout::ChannelMap mChannelsMap; uint32_t mAudioRate; @@ -50,6 +53,9 @@ const GUID& GetMFTGUID(); const GUID& GetMediaSubtypeGUID(); + media::TimeUnit mLastInputTime = media::TimeUnit::Zero(); + media::TimeUnit mLastOutputDuration = media::TimeUnit::Zero(); + bool mFirstFrame = true; }; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/media/test/allowed.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/media/test/allowed.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/media/test/allowed.sjs 2021-10-17 14:42:35.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/media/test/allowed.sjs 2021-10-20 19:28:18.000000000 +0000 @@ -1,13 +1,16 @@ function parseQuery(request, key) { - var params = request.queryString.split('&'); + var params = request.queryString.split("&"); for (var j = 0; j < params.length; ++j) { var p = params[j]; - if (p == key) - return true; - if (p.indexOf(key + "=") == 0) - return p.substring(key.length + 1); - if (p.indexOf("=") < 0 && key == "") - return p; + if (p == key) { + return true; + } + if (p.indexOf(key + "=") == 0) { + return p.substring(key.length + 1); + } + if (p.indexOf("=") < 0 && key == "") { + return p; + } } return false; } @@ -20,25 +23,26 @@ ogv: "video/ogg", oga: "audio/ogg", webm: "video/webm", - wav: "audio/x-wav" + wav: "audio/x-wav", }; // Return file with name as per the query string with access control // allow headers. -function handleRequest(request, response) -{ +function handleRequest(request, response) { var resource = parseQuery(request, ""); - var file = Components.classes["@mozilla.org/file/directory_service;1"]. - getService(Components.interfaces.nsIProperties). - get("CurWorkD", Components.interfaces.nsIFile); - var fis = Components.classes['@mozilla.org/network/file-input-stream;1']. - createInstance(Components.interfaces.nsIFileInputStream); - var bis = Components.classes["@mozilla.org/binaryinputstream;1"]. - createInstance(Components.interfaces.nsIBinaryInputStream); + var file = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("CurWorkD", Components.interfaces.nsIFile); + var fis = Components.classes[ + "@mozilla.org/network/file-input-stream;1" + ].createInstance(Components.interfaces.nsIFileInputStream); + var bis = Components.classes[ + "@mozilla.org/binaryinputstream;1" + ].createInstance(Components.interfaces.nsIBinaryInputStream); var paths = "tests/dom/media/test/" + resource; var split = paths.split("/"); - for(var i = 0; i < split.length; ++i) { + for (var i = 0; i < split.length; ++i) { file.append(split[i]); } fis.init(file, -1, -1, false); @@ -46,9 +50,12 @@ bis.setInputStream(fis); var bytes = bis.readBytes(bis.available()); response.setStatusLine(request.httpVersion, 206, "Partial Content"); - response.setHeader("Content-Range", "bytes 0-" + (bytes.length - 1) + "/" + bytes.length); - response.setHeader("Content-Length", ""+bytes.length, false); - var ext = resource.substring(resource.lastIndexOf(".")+1); + response.setHeader( + "Content-Range", + "bytes 0-" + (bytes.length - 1) + "/" + bytes.length + ); + response.setHeader("Content-Length", "" + bytes.length, false); + var ext = resource.substring(resource.lastIndexOf(".") + 1); response.setHeader("Content-Type", types[ext], false); response.setHeader("Access-Control-Allow-Origin", "*"); response.write(bytes, bytes.length); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/media/test/cancellable_request.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/media/test/cancellable_request.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/media/test/cancellable_request.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/media/test/cancellable_request.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -1,13 +1,16 @@ function parseQuery(request, key) { - var params = request.queryString.split('&'); + var params = request.queryString.split("&"); for (var j = 0; j < params.length; ++j) { var p = params[j]; - if (p == key) - return true; - if (p.indexOf(key + "=") == 0) - return p.substring(key.length + 1); - if (p.indexOf("=") < 0 && key == "") - return p; + if (p == key) { + return true; + } + if (p.indexOf(key + "=") == 0) { + return p.substring(key.length + 1); + } + if (p.indexOf("=") < 0 && key == "") { + return p; + } } return false; } @@ -16,18 +19,18 @@ array.push(String.fromCharCode((input >> 24) & 0xff)); array.push(String.fromCharCode((input >> 16) & 0xff)); array.push(String.fromCharCode((input >> 8) & 0xff)); - array.push(String.fromCharCode((input) & 0xff)); + array.push(String.fromCharCode(input & 0xff)); } function push32LE(array, input) { - array.push(String.fromCharCode((input) & 0xff)); + array.push(String.fromCharCode(input & 0xff)); array.push(String.fromCharCode((input >> 8) & 0xff)); array.push(String.fromCharCode((input >> 16) & 0xff)); array.push(String.fromCharCode((input >> 24) & 0xff)); } function push16LE(array, input) { - array.push(String.fromCharCode((input) & 0xff)); + array.push(String.fromCharCode(input & 0xff)); array.push(String.fromCharCode((input >> 8) & 0xff)); } @@ -60,19 +63,26 @@ const CC = Components.Constructor; const Timer = CC("@mozilla.org/timer;1", "nsITimer", "initWithCallback"); -const BinaryOutputStream = CC("@mozilla.org/binaryoutputstream;1", - "nsIBinaryOutputStream", - "setOutputStream"); +const BinaryOutputStream = CC( + "@mozilla.org/binaryoutputstream;1", + "nsIBinaryOutputStream", + "setOutputStream" +); function poll(f) { if (f()) { return; } - new Timer(function() { poll(f); }, 100, Ci.nsITimer.TYPE_ONE_SHOT); + new Timer( + function() { + poll(f); + }, + 100, + Ci.nsITimer.TYPE_ONE_SHOT + ); } -function handleRequest(request, response) -{ +function handleRequest(request, response) { var cancel = parseQuery(request, "cancelkey"); if (cancel) { setState(cancel[1], "cancelled"); @@ -89,47 +99,46 @@ var key = parseQuery(request, "key"); response.setHeader("Content-Type", "audio/x-wav"); - response.setHeader("Content-Length", ""+bytes.length, false); + response.setHeader("Content-Length", "" + bytes.length, false); var out = new BinaryOutputStream(response.bodyOutputStream); - var start = 0, end = bytes.length - 1; - if (request.hasHeader("Range")) - { + var start = 0, + end = bytes.length - 1; + if (request.hasHeader("Range")) { var rangeMatch = request.getHeader("Range").match(/^bytes=(\d+)?-(\d+)?$/); - if (rangeMatch[1] !== undefined) + if (rangeMatch[1] !== undefined) { start = parseInt(rangeMatch[1], 10); + } - if (rangeMatch[2] !== undefined) + if (rangeMatch[2] !== undefined) { end = parseInt(rangeMatch[2], 10); + } // No start given, so the end is really the count of bytes from the // end of the file. - if (start === undefined) - { + if (start === undefined) { start = Math.max(0, bytes.length - end); - end = bytes.length - 1; + end = bytes.length - 1; } // start and end are inclusive - if (end === undefined || end >= bytes.length) + if (end === undefined || end >= bytes.length) { end = bytes.length - 1; + } - if (end < start) - { + if (end < start) { response.setStatusLine(request.httpVersion, 200, "OK"); start = 0; end = bytes.length - 1; - } - else - { + } else { response.setStatusLine(request.httpVersion, 206, "Partial Content"); var contentRange = "bytes " + start + "-" + end + "/" + bytes.length; response.setHeader("Content-Range", contentRange); } } - + if (start > 0) { // Send all requested data out.write(bytes.slice(start, end + 1), end + 1 - start); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/media/test/contentType.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/media/test/contentType.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/media/test/contentType.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/media/test/contentType.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -1,15 +1,18 @@ // Parse the query string, and give us the value for a certain key, or false if // it does not exist. function parseQuery(request, key) { - var params = request.queryString.split('?')[0].split('&'); + var params = request.queryString.split("?")[0].split("&"); for (var j = 0; j < params.length; ++j) { var p = params[j]; - if (p == key) + if (p == key) { return true; - if (p.indexOf(key + "=") == 0) + } + if (p.indexOf(key + "=") == 0) { return p.substring(key.length + 1); - if (p.indexOf("=") < 0 && key == "") + } + if (p.indexOf("=") < 0 && key == "") { return p; + } } return false; } @@ -20,27 +23,31 @@ var filename = parseQuery(request, "file"); const CC = Components.Constructor; - const BinaryOutputStream = CC("@mozilla.org/binaryoutputstream;1", - "nsIBinaryOutputStream", - "setOutputStream"); - var file = Components.classes["@mozilla.org/file/directory_service;1"]. - getService(Components.interfaces.nsIProperties). - get("CurWorkD", Components.interfaces.nsIFile); - var fis = Components.classes['@mozilla.org/network/file-input-stream;1']. - createInstance(Components.interfaces.nsIFileInputStream); - var bis = Components.classes["@mozilla.org/binaryinputstream;1"]. - createInstance(Components.interfaces.nsIBinaryInputStream); + const BinaryOutputStream = CC( + "@mozilla.org/binaryoutputstream;1", + "nsIBinaryOutputStream", + "setOutputStream" + ); + var file = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("CurWorkD", Components.interfaces.nsIFile); + var fis = Components.classes[ + "@mozilla.org/network/file-input-stream;1" + ].createInstance(Components.interfaces.nsIFileInputStream); + var bis = Components.classes[ + "@mozilla.org/binaryinputstream;1" + ].createInstance(Components.interfaces.nsIBinaryInputStream); var paths = "tests/dom/media/test/" + filename; - dump(paths + '\n'); + dump(paths + "\n"); var split = paths.split("/"); - for(var i = 0; i < split.length; ++i) { + for (var i = 0; i < split.length; ++i) { file.append(split[i]); } fis.init(file, -1, -1, false); // handle range requests var partialstart = 0, - partialend = file.fileSize - 1; + partialend = file.fileSize - 1; if (request.hasHeader("Range")) { var range = request.getHeader("Range"); var parts = range.replace(/bytes=/, "").split("-"); @@ -50,7 +57,8 @@ partialend = file.fileSize - 1; } response.setStatusLine(request.httpVersion, 206, "Partial Content"); - var contentRange = "bytes " + partialstart + "-" + partialend + "/" + file.fileSize; + var contentRange = + "bytes " + partialstart + "-" + partialend + "/" + file.fileSize; response.setHeader("Content-Range", contentRange); } @@ -62,16 +70,16 @@ var contentType = parseQuery(request, "type"); if (contentType == false) { // This should not happen. - dump("No type specified without having \'nomime\' in parameters."); + dump("No type specified without having 'nomime' in parameters."); return; } response.setHeader("Content-Type", contentType, false); } - response.setHeader("Content-Length", ""+bis.available(), false); + response.setHeader("Content-Length", "" + bis.available(), false); var bytes = bis.readBytes(bis.available()); response.write(bytes, bytes.length); } catch (e) { - dump ("ERROR : " + e + "\n"); + dump("ERROR : " + e + "\n"); } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/media/test/dash_detect_stream_switch.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/media/test/dash_detect_stream_switch.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/media/test/dash_detect_stream_switch.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/media/test/dash_detect_stream_switch.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -15,55 +15,64 @@ var DEBUG = false; function parseQuery(request, key) { - var params = request.queryString.split('&'); + var params = request.queryString.split("&"); if (DEBUG) { - dump("DASH-SJS: request params = \"" + params + "\"\n"); + dump('DASH-SJS: request params = "' + params + '"\n'); } for (var j = 0; j < params.length; ++j) { var p = params[j]; - if (p == key) - return true; - if (p.indexOf(key + "=") === 0) - return p.substring(key.length + 1); - if (p.indexOf("=") < 0 && key === "") - return p; + if (p == key) { + return true; + } + if (p.indexOf(key + "=") === 0) { + return p.substring(key.length + 1); + } + if (p.indexOf("=") < 0 && key === "") { + return p; + } } return false; } -function handleRequest(request, response) -{ +function handleRequest(request, response) { try { var name = parseQuery(request, "name"); - var range = request.hasHeader("Range") ? request.getHeader("Range") - : undefined; - + var range = request.hasHeader("Range") + ? request.getHeader("Range") + : undefined; + // Should not get request for 1st subsegment from 2nd stream, nor 2nd // subsegment from 1st stream. - if (name == "dash-webm-video-320x180.webm" && range == "bytes=25514-32767" || - name == "dash-webm-video-428x240.webm" && range == "bytes=228-35852") - { + if ( + (name == "dash-webm-video-320x180.webm" && + range == "bytes=25514-32767") || + (name == "dash-webm-video-428x240.webm" && range == "bytes=228-35852") + ) { throw "Should not request " + name + " with byte-range " + range; } else { var rangeSplit = range.split("="); if (rangeSplit.length != 2) { - throw "DASH-SJS: ERROR: invalid number of tokens (" + rangeSplit.length + - ") delimited by \'=\' in \'Range\' header."; + throw "DASH-SJS: ERROR: invalid number of tokens (" + + rangeSplit.length + + ") delimited by '=' in 'Range' header."; } var offsets = rangeSplit[1].split("-"); if (offsets.length != 2) { - throw "DASH-SJS: ERROR: invalid number of tokens (" + offsets.length + - ") delimited by \'-\' in \'Range\' header."; + throw "DASH-SJS: ERROR: invalid number of tokens (" + + offsets.length + + ") delimited by '-' in 'Range' header."; } var startOffset = parseInt(offsets[0]); var endOffset = parseInt(offsets[1]); - var file = Components.classes["@mozilla.org/file/directory_service;1"]. - getService(Components.interfaces.nsIProperties). - get("CurWorkD", Components.interfaces.nsIFile); - var fis = Components.classes['@mozilla.org/network/file-input-stream;1']. - createInstance(Components.interfaces.nsIFileInputStream); - var bis = Components.classes["@mozilla.org/binaryinputstream;1"]. - createInstance(Components.interfaces.nsIBinaryInputStream); + var file = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("CurWorkD", Components.interfaces.nsIFile); + var fis = Components.classes[ + "@mozilla.org/network/file-input-stream;1" + ].createInstance(Components.interfaces.nsIFileInputStream); + var bis = Components.classes[ + "@mozilla.org/binaryinputstream;1" + ].createInstance(Components.interfaces.nsIBinaryInputStream); var paths = "tests/dom/media/test/" + name; var split = paths.split("/"); @@ -74,41 +83,58 @@ fis.init(file, -1, -1, false); // Exception: start offset should be within file bounds. if (startOffset > file.fileSize) { - throw "Starting offset [" + startOffset + "] is after end of file [" + - file.fileSize + "]."; + throw "Starting offset [" + + startOffset + + "] is after end of file [" + + file.fileSize + + "]."; } // End offset may be too large in the MPD. Real world HTTP servers just // return what data they can; do the same here - reduce the end offset. if (endOffset >= file.fileSize) { if (DEBUG) { - dump("DASH-SJS: reducing endOffset [" + endOffset + "] to fileSize [" + - (file.fileSize-1) + "]\n"); + dump( + "DASH-SJS: reducing endOffset [" + + endOffset + + "] to fileSize [" + + (file.fileSize - 1) + + "]\n" + ); } - endOffset = file.fileSize-1; + endOffset = file.fileSize - 1; } - fis.seek(Components.interfaces.nsISeekableStream.NS_SEEK_SET, startOffset); + fis.seek( + Components.interfaces.nsISeekableStream.NS_SEEK_SET, + startOffset + ); bis.setInputStream(fis); - + var byteLengthToRead = endOffset + 1 - startOffset; var totalBytesExpected = byteLengthToRead + startOffset; if (DEBUG) { - dump("DASH-SJS: byteLengthToRead = " + byteLengthToRead + - " byteLengthToRead+startOffset = " + totalBytesExpected + - " fileSize = " + file.fileSize + "\n"); + dump( + "DASH-SJS: byteLengthToRead = " + + byteLengthToRead + + " byteLengthToRead+startOffset = " + + totalBytesExpected + + " fileSize = " + + file.fileSize + + "\n" + ); } var bytes = bis.readBytes(byteLengthToRead); response.setStatusLine(request.httpVersion, 206, "Partial Content"); - response.setHeader("Content-Length", ""+bytes.length, false); + response.setHeader("Content-Length", "" + bytes.length, false); response.setHeader("Content-Type", "application/dash+xml", false); - var contentRange = "bytes " + startOffset + "-" + endOffset + "/" + - file.fileSize; + var contentRange = + "bytes " + startOffset + "-" + endOffset + "/" + file.fileSize; response.setHeader("Content-Range", contentRange, false); response.write(bytes, bytes.length); bis.close(); } } catch (e) { - dump ("DASH-SJS-ERROR: " + e + "\n"); + dump("DASH-SJS-ERROR: " + e + "\n"); response.setStatusLine(request.httpVersion, 404, "Not found"); } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/media/test/dynamic_resource.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/media/test/dynamic_resource.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/media/test/dynamic_resource.sjs 2021-10-17 14:42:35.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/media/test/dynamic_resource.sjs 2021-10-20 19:28:18.000000000 +0000 @@ -1,21 +1,23 @@ function parseQuery(request, key) { - var params = request.queryString.split('&'); + var params = request.queryString.split("&"); for (var j = 0; j < params.length; ++j) { var p = params[j]; - if (p == key) - return true; - if (p.indexOf(key + "=") == 0) - return p.substring(key.length + 1); - if (p.indexOf("=") < 0 && key == "") - return p; + if (p == key) { + return true; + } + if (p.indexOf(key + "=") == 0) { + return p.substring(key.length + 1); + } + if (p.indexOf("=") < 0 && key == "") { + return p; + } } return false; } // Return resource1 file content for the first request with a given key. // All subsequent requests return resource2. Both must be video/ogg. -function handleRequest(request, response) -{ +function handleRequest(request, response) { var key = parseQuery(request, "key"); var resource1 = parseQuery(request, "res1"); var resource2 = parseQuery(request, "res2"); @@ -23,16 +25,18 @@ var resource = getState(key) == "2" ? resource2 : resource1; setState(key, "2"); - var file = Components.classes["@mozilla.org/file/directory_service;1"]. - getService(Components.interfaces.nsIProperties). - get("CurWorkD", Components.interfaces.nsIFile); - var fis = Components.classes['@mozilla.org/network/file-input-stream;1']. - createInstance(Components.interfaces.nsIFileInputStream); - var bis = Components.classes["@mozilla.org/binaryinputstream;1"]. - createInstance(Components.interfaces.nsIBinaryInputStream); + var file = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("CurWorkD", Components.interfaces.nsIFile); + var fis = Components.classes[ + "@mozilla.org/network/file-input-stream;1" + ].createInstance(Components.interfaces.nsIFileInputStream); + var bis = Components.classes[ + "@mozilla.org/binaryinputstream;1" + ].createInstance(Components.interfaces.nsIBinaryInputStream); var paths = "tests/dom/media/test/" + resource; var split = paths.split("/"); - for(var i = 0; i < split.length; ++i) { + for (var i = 0; i < split.length; ++i) { file.append(split[i]); } fis.init(file, -1, -1, false); @@ -40,8 +44,11 @@ bis.setInputStream(fis); var bytes = bis.readBytes(bis.available()); response.setStatusLine(request.httpVersion, 206, "Partial Content"); - response.setHeader("Content-Range", "bytes 0-" + (bytes.length - 1) + "/" + bytes.length); - response.setHeader("Content-Length", ""+bytes.length, false); + response.setHeader( + "Content-Range", + "bytes 0-" + (bytes.length - 1) + "/" + bytes.length + ); + response.setHeader("Content-Length", "" + bytes.length, false); response.setHeader("Content-Type", "video/ogg", false); response.write(bytes, bytes.length); bis.close(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/media/test/gzipped_mp4.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/media/test/gzipped_mp4.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/media/test/gzipped_mp4.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/media/test/gzipped_mp4.sjs 2021-10-20 19:28:18.000000000 +0000 @@ -1,27 +1,25 @@ -function getGzippedFileBytes() -{ - var file; - getObjectState("SERVER_ROOT", function(serverRoot) { - file = serverRoot.getFile("tests/dom/media/test/short.mp4.gz"); - }); - var fileInputStream = - Components.classes['@mozilla.org/network/file-input-stream;1'] - .createInstance(Components.interfaces.nsIFileInputStream); - var binaryInputStream = - Components.classes["@mozilla.org/binaryinputstream;1"] - .createInstance(Components.interfaces.nsIBinaryInputStream); - fileInputStream.init(file, -1, -1, 0); - binaryInputStream.setInputStream(fileInputStream); - return binaryInputStream.readBytes(binaryInputStream.available()); -} - -function handleRequest(request, response) -{ - var bytes = getGzippedFileBytes(); - response.setHeader("Content-Length", String(bytes.length), false); - response.setHeader("Content-Type", "video/mp4", false); - response.setHeader("Access-Control-Allow-Origin", "*", false); - response.setHeader("Content-Encoding", "gzip", false); - response.setHeader("Cache-Control", "no-cache", false); - response.write(bytes, bytes.length); -} +function getGzippedFileBytes() { + var file; + getObjectState("SERVER_ROOT", function(serverRoot) { + file = serverRoot.getFile("tests/dom/media/test/short.mp4.gz"); + }); + var fileInputStream = Components.classes[ + "@mozilla.org/network/file-input-stream;1" + ].createInstance(Components.interfaces.nsIFileInputStream); + var binaryInputStream = Components.classes[ + "@mozilla.org/binaryinputstream;1" + ].createInstance(Components.interfaces.nsIBinaryInputStream); + fileInputStream.init(file, -1, -1, 0); + binaryInputStream.setInputStream(fileInputStream); + return binaryInputStream.readBytes(binaryInputStream.available()); +} + +function handleRequest(request, response) { + var bytes = getGzippedFileBytes(); + response.setHeader("Content-Length", String(bytes.length), false); + response.setHeader("Content-Type", "video/mp4", false); + response.setHeader("Access-Control-Allow-Origin", "*", false); + response.setHeader("Content-Encoding", "gzip", false); + response.setHeader("Cache-Control", "no-cache", false); + response.write(bytes, bytes.length); +} diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/media/test/midflight-redirect.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/media/test/midflight-redirect.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/media/test/midflight-redirect.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/media/test/midflight-redirect.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -1,5 +1,5 @@ function parseQuery(query, key) { - for (let p of query.split('&')) { + for (let p of query.split("&")) { if (p == key) { return true; } @@ -12,21 +12,22 @@ // Return the first few bytes in a short byte range response. When Firefox // requests subsequent bytes in a second range request, respond with a // redirect. Requests after the first redirected are serviced as expected. -function handleRequest(request, response) -{ +function handleRequest(request, response) { var query = request.queryString; var resource = parseQuery(query, "resource"); var type = parseQuery(query, "type") || "application/octet-stream"; var redirected = parseQuery(query, "redirected") || false; var useCors = parseQuery(query, "cors") || false; - var file = Components.classes["@mozilla.org/file/directory_service;1"]. - getService(Components.interfaces.nsIProperties). - get("CurWorkD", Components.interfaces.nsIFile); - var fis = Components.classes['@mozilla.org/network/file-input-stream;1']. - createInstance(Components.interfaces.nsIFileInputStream); - var bis = Components.classes["@mozilla.org/binaryinputstream;1"]. - createInstance(Components.interfaces.nsIBinaryInputStream); + var file = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("CurWorkD", Components.interfaces.nsIFile); + var fis = Components.classes[ + "@mozilla.org/network/file-input-stream;1" + ].createInstance(Components.interfaces.nsIFileInputStream); + var bis = Components.classes[ + "@mozilla.org/binaryinputstream;1" + ].createInstance(Components.interfaces.nsIBinaryInputStream); var paths = "tests/dom/media/test/" + resource; var split = paths.split("/"); for (var i = 0; i < split.length; ++i) { @@ -36,13 +37,21 @@ bis.setInputStream(fis); var bytes = bis.readBytes(bis.available()); - let [from, to] = request.getHeader("range").split("=")[1].split("-").map(s => parseInt(s)); + let [from, to] = request + .getHeader("range") + .split("=")[1] + .split("-") + .map(s => parseInt(s)); if (!redirected && from > 0) { - var origin = request.host == "mochi.test" ? "example.org" : "mochi.test:8888"; + var origin = + request.host == "mochi.test" ? "example.org" : "mochi.test:8888"; response.setStatusLine(request.httpVersion, 303, "See Other"); - let url = "http://" + origin + - "/tests/dom/media/test/midflight-redirect.sjs?redirected&" + query; + let url = + "http://" + + origin + + "/tests/dom/media/test/midflight-redirect.sjs?redirected&" + + query; response.setHeader("Location", url); response.setHeader("Content-Type", "text/html"); return; @@ -53,7 +62,8 @@ } if (from == 0 && !redirected) { - to = parseInt(parseQuery(query, "redirectAt")) || Math.floor(bytes.length / 4); + to = + parseInt(parseQuery(query, "redirectAt")) || Math.floor(bytes.length / 4); } to = Math.min(to, bytes.length - 1); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/media/test/redirect.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/media/test/redirect.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/media/test/redirect.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/media/test/redirect.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -1,26 +1,35 @@ function parseQuery(request, key) { - var params = request.queryString.split('&'); + var params = request.queryString.split("&"); for (var j = 0; j < params.length; ++j) { var p = params[j]; - if (p == key) - return true; - if (p.indexOf(key + "=") == 0) - return p.substring(key.length + 1); - if (p.indexOf("=") < 0 && key == "") - return p; + if (p == key) { + return true; + } + if (p.indexOf(key + "=") == 0) { + return p.substring(key.length + 1); + } + if (p.indexOf("=") < 0 && key == "") { + return p; + } } return false; } // Return file content for the first request with a given key. // All subsequent requests return a redirect to a different-origin resource. -function handleRequest(request, response) -{ +function handleRequest(request, response) { var domain = parseQuery(request, "domain"); var file = parseQuery(request, "file"); var allowed = parseQuery(request, "allowed"); response.setStatusLine(request.httpVersion, 303, "See Other"); - response.setHeader("Location", "http://" + domain + "/tests/dom/media/test/" + (allowed ? "allowed.sjs?" : "") + file); + response.setHeader( + "Location", + "http://" + + domain + + "/tests/dom/media/test/" + + (allowed ? "allowed.sjs?" : "") + + file + ); response.setHeader("Content-Type", "text/html"); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/media/test/referer.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/media/test/referer.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/media/test/referer.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/media/test/referer.sjs 2021-10-20 19:28:18.000000000 +0000 @@ -1,45 +1,51 @@ function parseQuery(request, key) { - var params = request.queryString.split('&'); + var params = request.queryString.split("&"); for (var j = 0; j < params.length; ++j) { var p = params[j]; - if (p == key) - return true; - if (p.indexOf(key + "=") == 0) - return p.substring(key.length + 1); - if (p.indexOf("=") < 0 && key == "") - return p; + if (p == key) { + return true; + } + if (p.indexOf(key + "=") == 0) { + return p.substring(key.length + 1); + } + if (p.indexOf("=") < 0 && key == "") { + return p; + } } return false; } -function handleRequest(request, response) -{ - var referer = request.hasHeader("Referer") ? request.getHeader("Referer") - : undefined; - if (referer == "http://mochi.test:8888/tests/dom/media/test/test_referer.html") { +function handleRequest(request, response) { + var referer = request.hasHeader("Referer") + ? request.getHeader("Referer") + : undefined; + if ( + referer == "http://mochi.test:8888/tests/dom/media/test/test_referer.html" + ) { var name = parseQuery(request, "name"); - var type = parseQuery(request, "type"); - var file = Components.classes["@mozilla.org/file/directory_service;1"]. - getService(Components.interfaces.nsIProperties). - get("CurWorkD", Components.interfaces.nsIFile); - var fis = Components.classes['@mozilla.org/network/file-input-stream;1']. - createInstance(Components.interfaces.nsIFileInputStream); - var bis = Components.classes["@mozilla.org/binaryinputstream;1"]. - createInstance(Components.interfaces.nsIBinaryInputStream); + var type = parseQuery(request, "type"); + var file = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("CurWorkD", Components.interfaces.nsIFile); + var fis = Components.classes[ + "@mozilla.org/network/file-input-stream;1" + ].createInstance(Components.interfaces.nsIFileInputStream); + var bis = Components.classes[ + "@mozilla.org/binaryinputstream;1" + ].createInstance(Components.interfaces.nsIBinaryInputStream); var paths = "tests/dom/media/test/" + name; var split = paths.split("/"); - for(var i = 0; i < split.length; ++i) { + for (var i = 0; i < split.length; ++i) { file.append(split[i]); } fis.init(file, -1, -1, false); bis.setInputStream(fis); var bytes = bis.readBytes(bis.available()); - response.setHeader("Content-Length", ""+bytes.length, false); + response.setHeader("Content-Length", "" + bytes.length, false); response.setHeader("Content-Type", type, false); response.write(bytes, bytes.length); bis.close(); - } - else { + } else { response.setStatusLine(request.httpVersion, 404, "Not found"); } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/media/test/seekLies.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/media/test/seekLies.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/media/test/seekLies.sjs 2021-10-17 14:42:35.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/media/test/seekLies.sjs 2021-10-20 19:28:18.000000000 +0000 @@ -1,21 +1,22 @@ -function handleRequest(request, response) -{ - var file = Components.classes["@mozilla.org/file/directory_service;1"]. - getService(Components.interfaces.nsIProperties). - get("CurWorkD", Components.interfaces.nsIFile); - var fis = Components.classes['@mozilla.org/network/file-input-stream;1']. - createInstance(Components.interfaces.nsIFileInputStream); - var bis = Components.classes["@mozilla.org/binaryinputstream;1"]. - createInstance(Components.interfaces.nsIBinaryInputStream); +function handleRequest(request, response) { + var file = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("CurWorkD", Components.interfaces.nsIFile); + var fis = Components.classes[ + "@mozilla.org/network/file-input-stream;1" + ].createInstance(Components.interfaces.nsIFileInputStream); + var bis = Components.classes[ + "@mozilla.org/binaryinputstream;1" + ].createInstance(Components.interfaces.nsIBinaryInputStream); var paths = "tests/dom/media/test/seek.ogv"; var split = paths.split("/"); - for(var i = 0; i < split.length; ++i) { + for (var i = 0; i < split.length; ++i) { file.append(split[i]); } fis.init(file, -1, -1, false); bis.setInputStream(fis); var bytes = bis.readBytes(bis.available()); - response.setHeader("Content-Length", ""+bytes.length, false); + response.setHeader("Content-Length", "" + bytes.length, false); response.setHeader("Content-Type", "video/ogg", false); response.setHeader("Accept-Ranges", "bytes", false); response.write(bytes, bytes.length); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/media/utils/PerformanceRecorder.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/media/utils/PerformanceRecorder.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/media/utils/PerformanceRecorder.cpp 2021-10-17 14:42:35.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/media/utils/PerformanceRecorder.cpp 2021-10-20 19:28:19.000000000 +0000 @@ -55,7 +55,7 @@ /* static */ bool PerformanceRecorder::IsMeasurementEnabled() { - return profiler_can_accept_markers() || + return profiler_thread_is_being_profiled() || PerformanceRecorder::sEnableMeasurementForTesting; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/media/webaudio/test/corsServer.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/media/webaudio/test/corsServer.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/media/webaudio/test/corsServer.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/media/webaudio/test/corsServer.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -1,22 +1,23 @@ -function handleRequest(request, response) -{ - var file = Components.classes["@mozilla.org/file/directory_service;1"]. - getService(Components.interfaces.nsIProperties). - get("CurWorkD", Components.interfaces.nsIFile); - var fis = Components.classes['@mozilla.org/network/file-input-stream;1']. - createInstance(Components.interfaces.nsIFileInputStream); - var bis = Components.classes["@mozilla.org/binaryinputstream;1"]. - createInstance(Components.interfaces.nsIBinaryInputStream); +function handleRequest(request, response) { + var file = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("CurWorkD", Components.interfaces.nsIFile); + var fis = Components.classes[ + "@mozilla.org/network/file-input-stream;1" + ].createInstance(Components.interfaces.nsIFileInputStream); + var bis = Components.classes[ + "@mozilla.org/binaryinputstream;1" + ].createInstance(Components.interfaces.nsIBinaryInputStream); var paths = "tests/dom/media/webaudio/test/small-shot.ogg"; var split = paths.split("/"); - for(var i = 0; i < split.length; ++i) { + for (var i = 0; i < split.length; ++i) { file.append(split[i]); } fis.init(file, -1, -1, false); bis.setInputStream(fis); var bytes = bis.readBytes(bis.available()); response.setHeader("Content-Type", "video/ogg", false); - response.setHeader("Content-Length", ""+ bytes.length, false); + response.setHeader("Content-Length", "" + bytes.length, false); response.setHeader("Access-Control-Allow-Origin", "*", false); response.write(bytes, bytes.length); response.processAsync(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/media/webrtc/tests/mochitests/head.js firefox-trunk-95.0~a1~hg20211020r596404/dom/media/webrtc/tests/mochitests/head.js --- firefox-trunk-95.0~a1~hg20211017r596111/dom/media/webrtc/tests/mochitests/head.js 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/media/webrtc/tests/mochitests/head.js 2021-10-20 19:28:19.000000000 +0000 @@ -445,9 +445,7 @@ ], }; - const isAndroid = !!navigator.userAgent.includes("Android"); - - if (isAndroid) { + if (navigator.userAgent.includes("Android")) { defaultMochitestPrefs.set.push( ["media.navigator.video.default_width", 320], ["media.navigator.video.default_height", 240], @@ -456,16 +454,14 @@ ); } - // All platforms but Linux support MediaDataEncoder and the tryserver Windows - // machine doesn't have HW H.264 encoder. In these cases, Fake GMP encoder is - // used so the pref needs to stay disabled. - // [TODO] re-enable after bug 1509012 is done or platform encoder available. - const alwaysHasHW264 = - !!navigator.userAgent.includes("Android") || - !!navigator.userAgent.includes("Mac OS X"); + // Platform codec prefs should be matched because fake H.264 GMP codec doesn't + // produce/consume real bitstreams. [TODO] remove after bug 1509012 is fixed. + const platformEncoderEnabled = SpecialPowers.getBoolPref( + "media.webrtc.platformencoder" + ); defaultMochitestPrefs.set.push([ "media.navigator.mediadatadecoder_h264_enabled", - alwaysHasHW264, + platformEncoderEnabled, ]); // Running as a Mochitest. @@ -478,6 +474,19 @@ SpecialPowers.exactGC(); } +// [TODO] remove after bug 1509012 is fixed. +async function matchPlatformH264CodecPrefs() { + const hasHW264 = + SpecialPowers.getBoolPref("media.webrtc.platformencoder") && + (navigator.userAgent.includes("Android") || + navigator.userAgent.includes("Mac OS X")); + + await pushPrefs( + ["media.webrtc.platformencoder", hasHW264], + ["media.navigator.mediadatadecoder_h264_enabled", hasHW264] + ); +} + async function runTestWhenReady(testFunc) { setupEnvironment(); const options = await testConfigured; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/media/webrtc/tests/mochitests/identity/idp.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/media/webrtc/tests/mochitests/identity/idp.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/media/webrtc/tests/mochitests/identity/idp.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/media/webrtc/tests/mochitests/identity/idp.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -1,18 +1,18 @@ function handleRequest(request, response) { - var key = '/.well-known/idp-proxy/' + request.queryString; - dump(getState(key) + '\n'); - if (request.method === 'GET') { + var key = "/.well-known/idp-proxy/" + request.queryString; + dump(getState(key) + "\n"); + if (request.method === "GET") { if (getState(key)) { - response.setStatusLine(request.httpVersion, 200, 'OK'); + response.setStatusLine(request.httpVersion, 200, "OK"); } else { - response.setStatusLine(request.httpVersion, 404, 'Not Found'); + response.setStatusLine(request.httpVersion, 404, "Not Found"); } - } else if (request.method === 'PUT') { - setState(key, 'OK'); - response.setStatusLine(request.httpVersion, 200, 'OK'); + } else if (request.method === "PUT") { + setState(key, "OK"); + response.setStatusLine(request.httpVersion, 200, "OK"); } else { - response.setStatusLine(request.httpVersion, 406, 'Method Not Allowed'); + response.setStatusLine(request.httpVersion, 406, "Method Not Allowed"); } - response.setHeader('Content-Type', 'text/plain;charset=UTF-8'); - response.write('OK'); + response.setHeader("Content-Type", "text/plain;charset=UTF-8"); + response.write("OK"); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/media/webrtc/tests/mochitests/test_peerConnection_basicH264Video.html firefox-trunk-95.0~a1~hg20211020r596404/dom/media/webrtc/tests/mochitests/test_peerConnection_basicH264Video.html --- firefox-trunk-95.0~a1~hg20211017r596111/dom/media/webrtc/tests/mochitests/test_peerConnection_basicH264Video.html 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/media/webrtc/tests/mochitests/test_peerConnection_basicH264Video.html 2021-10-20 19:28:18.000000000 +0000 @@ -13,11 +13,7 @@ var test; runNetworkTest(async function (options) { - const alwaysHasHW264 = - navigator.userAgent.includes("Android") || - navigator.userAgent.includes("Mac OS X"); - await pushPrefs(["media.webrtc.platformencoder", alwaysHasHW264]); - + matchPlatformH264CodecPrefs(); options = options || { }; options.h264 = true; test = new PeerConnectionTest(options); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/media/webrtc/tests/mochitests/test_peerConnection_maxFsConstraint.html firefox-trunk-95.0~a1~hg20211020r596404/dom/media/webrtc/tests/mochitests/test_peerConnection_maxFsConstraint.html --- firefox-trunk-95.0~a1~hg20211017r596111/dom/media/webrtc/tests/mochitests/test_peerConnection_maxFsConstraint.html 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/media/webrtc/tests/mochitests/test_peerConnection_maxFsConstraint.html 2021-10-20 19:28:18.000000000 +0000 @@ -88,6 +88,7 @@ runNetworkTest(async () => { await pushPrefs(['media.peerconnection.video.lock_scaling', true]); if (await checkForH264Support()) { + await matchPlatformH264CodecPrefs(); await testScale("H264"); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/media/webrtc/tests/mochitests/test_peerConnection_scaleResolution.html firefox-trunk-95.0~a1~hg20211020r596404/dom/media/webrtc/tests/mochitests/test_peerConnection_scaleResolution.html --- firefox-trunk-95.0~a1~hg20211017r596111/dom/media/webrtc/tests/mochitests/test_peerConnection_scaleResolution.html 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/media/webrtc/tests/mochitests/test_peerConnection_scaleResolution.html 2021-10-20 19:28:18.000000000 +0000 @@ -78,12 +78,10 @@ } runNetworkTest(async () => { + await matchPlatformH264CodecPrefs(); await pushPrefs(['media.peerconnection.video.lock_scaling', true]); await testScale("VP8"); - if (!navigator.appVersion.includes("Android")) { - // No support for H.264 on Android in automation, see Bug 1355786 - await testScale("H264"); - } + await testScale("H264"); }); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/media/webrtc/tests/mochitests/test_peerConnection_setParameters_scaleResolutionDownBy.html firefox-trunk-95.0~a1~hg20211020r596404/dom/media/webrtc/tests/mochitests/test_peerConnection_setParameters_scaleResolutionDownBy.html --- firefox-trunk-95.0~a1~hg20211017r596111/dom/media/webrtc/tests/mochitests/test_peerConnection_setParameters_scaleResolutionDownBy.html 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/media/webrtc/tests/mochitests/test_peerConnection_setParameters_scaleResolutionDownBy.html 2021-10-20 19:28:19.000000000 +0000 @@ -39,7 +39,8 @@ // Android platform encoders don't support small (e.g., // 80x60) or non-even (e.g., 20x15) resolutions. // [TODO] re-enable after SW fallback is implemented. - ["media.webrtc.platformencoder", false]); + ["media.webrtc.platformencoder", false], + ["media.navigator.mediadatadecoder_h264_enabled", false]); } let test = new PeerConnectionTest(options); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/media/webrtc/tests/mochitests/test_peerConnection_videoCodecs.html firefox-trunk-95.0~a1~hg20211020r596404/dom/media/webrtc/tests/mochitests/test_peerConnection_videoCodecs.html --- firefox-trunk-95.0~a1~hg20211017r596111/dom/media/webrtc/tests/mochitests/test_peerConnection_videoCodecs.html 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/media/webrtc/tests/mochitests/test_peerConnection_videoCodecs.html 2021-10-20 19:28:19.000000000 +0000 @@ -77,26 +77,26 @@ for (let codec of codecs) { info(`Testing video for codec ${codec.name} offset ${codec.offset}`); try { - // Force fake GMP codec for H.264 mode 0 because not all platforms - // support slice size control. Re-enable this after SW encoder fallback - // support (bug 1726617) and returning valid bitstream from fake GMP - // encoder (bug 1509012). - let disablePlatformCodec = codec.name == "H264" && codec.offset; let enc = SpecialPowers.getBoolPref('media.webrtc.platformencoder'); let dec = SpecialPowers.getBoolPref('media.navigator.mediadatadecoder_h264_enabled'); - if (disablePlatformCodec) { - await pushPrefs( - ['media.webrtc.platformencoder', false], - ['media.navigator.mediadatadecoder_h264_enabled', false], - ); + if (codec.name == "H264") { + await matchPlatformH264CodecPrefs(); + if (codec.offset == 1) { + // Force fake GMP codec for H.264 mode 0 because not all platforms + // support slice size control. Re-enable it after + // a. SW encoder fallback support (bug 1726617), and + // b. returning valid bitstream from fake GMP encoder (bug 1509012). + await pushPrefs( + ['media.webrtc.platformencoder', false], + ['media.navigator.mediadatadecoder_h264_enabled', false], + ); + } } await testVideoCodec(options, codec); - if (disablePlatformCodec) { - await pushPrefs( - ['media.webrtc.platformencoder', enc], - ['media.navigator.mediadatadecoder_h264_enabled', dec], - ); - } + await pushPrefs( + ['media.webrtc.platformencoder', enc], + ['media.navigator.mediadatadecoder_h264_enabled', dec], + ); } catch(e) { ok(false, `Error in test for codec ${codec.name}: ${e}\n${e.stack}`); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/media/webrtc/WebrtcGlobal.h firefox-trunk-95.0~a1~hg20211020r596404/dom/media/webrtc/WebrtcGlobal.h --- firefox-trunk-95.0~a1~hg20211017r596111/dom/media/webrtc/WebrtcGlobal.h 2021-10-17 14:42:35.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/media/webrtc/WebrtcGlobal.h 2021-10-20 19:28:18.000000000 +0000 @@ -39,20 +39,6 @@ namespace IPC { -template -struct ParamTraits> { - typedef mozilla::dom::Sequence paramType; - - static void Write(Message* aMsg, const paramType& aParam) { - WriteParam(aMsg, static_cast&>(aParam)); - } - - static bool Read(const Message* aMsg, PickleIterator* aIter, - paramType* aResult) { - return ReadParam(aMsg, aIter, static_cast*>(aResult)); - } -}; - template <> struct ParamTraits : public ContiguousEnumSerializer { request = v }); - return request; -} - -function handleRequest(request, response) { - response.processAsync(); - if (request.queryString == "save") { - // Get the context structure and finish the old request - getObjectState("context", function(obj) { - savedCtx = obj.wrappedJSObject; - request = savedCtx.request; - - response.setHeader("Content-Type", "application/octet-stream", false); - response.setHeader("Access-Control-Allow-Origin", "*", false); - response.setHeader("Cache-Control", "no-cache", false); - response.setStatusLine(request.httpVersion, 200, "OK"); - - const input = request.bodyInputStream; - const output = response.bodyOutputStream; - let bodyAvail; - while ((bodyAvail = input.available()) > 0) { - output.writeFrom(input, bodyAvail); - } - response.finish(); - }); - return; - } else if (request.queryString == "malformedresult=1" || request.queryString == "emptyresult=1") { - jsonOK = request.queryString == "malformedresult=1" ? '{"status":"ok","dat' : '{"status":"ok","data":[]}' - response.setHeader("Content-Length", String(jsonOK.length), false); - response.setHeader("Content-Type", "application/json", false); - response.setHeader("Access-Control-Allow-Origin", "*", false); - response.setHeader("Cache-Control", "no-cache", false); - response.setStatusLine(request.httpVersion, 200, "OK"); - response.write(jsonOK, jsonOK.length); - response.finish(); - } else if (request.queryString == "hangup=1") { - response.finish(); - } else if (request.queryString == "return400=1") { - jsonOK = "{'message':'Bad header:accept-language-stt'}"; - response.setHeader("Content-Length", String(jsonOK.length), false); - response.setHeader("Content-Type", "application/json", false); - response.setHeader("Access-Control-Allow-Origin", "*", false); - response.setHeader("Cache-Control", "no-cache", false); - response.setStatusLine(request.httpVersion, 400, "Bad Request"); - response.write(jsonOK, jsonOK.length); - response.finish(); - } - else { - ctx.wrappedJSObject = ctx; - ctx.request = request; - setObjectState("context", ctx); - jsonOK = '{"status":"ok","data":[{"confidence":0.9085610,"text":"hello"}]}'; - response.setHeader("Content-Length", String(jsonOK.length), false); - response.setHeader("Content-Type", "application/json", false); - response.setHeader("Access-Control-Allow-Origin", "*", false); - response.setHeader("Cache-Control", "no-cache", false); - response.setStatusLine(request.httpVersion, 200, "OK"); - response.write(jsonOK, jsonOK.length); - response.finish(); - } -} +const CC = Components.Constructor; + +// Context structure - we need to set this up properly to pass to setObjectState +const ctx = { + QueryInterface(iid) { + if (iid.equals(Components.interfaces.nsISupports)) { + return this; + } + throw Components.Exception("", Components.results.NS_ERROR_NO_INTERFACE); + }, +}; + +function setRequest(request) { + setObjectState(key, request); +} +function getRequest() { + let request; + getObjectState(v => { + request = v; + }); + return request; +} + +function handleRequest(request, response) { + response.processAsync(); + if (request.queryString == "save") { + // Get the context structure and finish the old request + getObjectState("context", function(obj) { + savedCtx = obj.wrappedJSObject; + request = savedCtx.request; + + response.setHeader("Content-Type", "application/octet-stream", false); + response.setHeader("Access-Control-Allow-Origin", "*", false); + response.setHeader("Cache-Control", "no-cache", false); + response.setStatusLine(request.httpVersion, 200, "OK"); + + const input = request.bodyInputStream; + const output = response.bodyOutputStream; + let bodyAvail; + while ((bodyAvail = input.available()) > 0) { + output.writeFrom(input, bodyAvail); + } + response.finish(); + }); + return; + } else if ( + request.queryString == "malformedresult=1" || + request.queryString == "emptyresult=1" + ) { + jsonOK = + request.queryString == "malformedresult=1" + ? '{"status":"ok","dat' + : '{"status":"ok","data":[]}'; + response.setHeader("Content-Length", String(jsonOK.length), false); + response.setHeader("Content-Type", "application/json", false); + response.setHeader("Access-Control-Allow-Origin", "*", false); + response.setHeader("Cache-Control", "no-cache", false); + response.setStatusLine(request.httpVersion, 200, "OK"); + response.write(jsonOK, jsonOK.length); + response.finish(); + } else if (request.queryString == "hangup=1") { + response.finish(); + } else if (request.queryString == "return400=1") { + jsonOK = "{'message':'Bad header:accept-language-stt'}"; + response.setHeader("Content-Length", String(jsonOK.length), false); + response.setHeader("Content-Type", "application/json", false); + response.setHeader("Access-Control-Allow-Origin", "*", false); + response.setHeader("Cache-Control", "no-cache", false); + response.setStatusLine(request.httpVersion, 400, "Bad Request"); + response.write(jsonOK, jsonOK.length); + response.finish(); + } else { + ctx.wrappedJSObject = ctx; + ctx.request = request; + setObjectState("context", ctx); + jsonOK = '{"status":"ok","data":[{"confidence":0.9085610,"text":"hello"}]}'; + response.setHeader("Content-Length", String(jsonOK.length), false); + response.setHeader("Content-Type", "application/json", false); + response.setHeader("Access-Control-Allow-Origin", "*", false); + response.setHeader("Cache-Control", "no-cache", false); + response.setStatusLine(request.httpVersion, 200, "OK"); + response.write(jsonOK, jsonOK.length); + response.finish(); + } +} diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/payments/PaymentRequest.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/payments/PaymentRequest.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/payments/PaymentRequest.cpp 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/payments/PaymentRequest.cpp 2021-10-20 19:28:19.000000000 +0000 @@ -95,7 +95,7 @@ } nsAutoCString locale; LocaleService::GetInstance()->GetAppLocaleAsBCP47(locale); - mozilla::intl::Locale loc = mozilla::intl::Locale(locale); + mozilla::intl::MozLocale loc = mozilla::intl::MozLocale(locale); if (!(loc.GetLanguage() == "en" && loc.GetRegion() == "US")) { return false; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/performance/Performance.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/performance/Performance.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/performance/Performance.cpp 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/performance/Performance.cpp 2021-10-20 19:28:19.000000000 +0000 @@ -330,7 +330,7 @@ new PerformanceMark(GetParentObject(), aName, Now()); InsertUserEntry(performanceMark); - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled()) { Maybe innerWindowId; if (GetOwner()) { innerWindowId = Some(GetOwner()->WindowID()); @@ -408,7 +408,7 @@ new PerformanceMeasure(GetParentObject(), aName, startTime, endTime); InsertUserEntry(performanceMeasure); - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled()) { TimeStamp startTimeStamp = CreationTimeStamp() + TimeDuration::FromMilliseconds(startTime); TimeStamp endTimeStamp = diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/performance/tests/serverTiming.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/performance/tests/serverTiming.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/performance/tests/serverTiming.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/performance/tests/serverTiming.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -1,21 +1,30 @@ - -var responseServerTiming = [{metric:"metric1", duration:"123.4", description:"description1"}, - {metric:"metric2", duration:"456.78", description:"description2"}]; -var trailerServerTiming = [{metric:"metric3", duration:"789.11", description:"description3"}, - {metric:"metric4", duration:"1112.13", description:"description4"}]; +var responseServerTiming = [ + { metric: "metric1", duration: "123.4", description: "description1" }, + { metric: "metric2", duration: "456.78", description: "description2" }, +]; +var trailerServerTiming = [ + { metric: "metric3", duration: "789.11", description: "description3" }, + { metric: "metric4", duration: "1112.13", description: "description4" }, +]; function createServerTimingHeader(headerData) { var header = ""; for (var i = 0; i < headerData.length; i++) { - header += "Server-Timing:" + headerData[i].metric + ";" + - "dur=" + headerData[i].duration + ";" + - "desc=" + headerData[i].description + "\r\n"; + header += + "Server-Timing:" + + headerData[i].metric + + ";" + + "dur=" + + headerData[i].duration + + ";" + + "desc=" + + headerData[i].description + + "\r\n"; } return header; } -function handleRequest(request, response) -{ +function handleRequest(request, response) { var body = "c\r\ndata reached\r\n3\r\nhej\r\n0\r\n"; response.seizePower(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/performance/tests/test_worker_performance_entries.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/performance/tests/test_worker_performance_entries.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/performance/tests/test_worker_performance_entries.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/performance/tests/test_worker_performance_entries.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -1,5 +1,4 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setHeader("Content-Type", "text/html"); if (request.queryString == "redirect") { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/plugins/test/mochitest/mixed_case_mime.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/plugins/test/mochitest/mixed_case_mime.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/plugins/test/mochitest/mixed_case_mime.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/plugins/test/mochitest/mixed_case_mime.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -1,5 +1,4 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.processAsync(); response.setHeader("Content-Type", "image/pNG", false); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/reporting/tests/delivering.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/reporting/tests/delivering.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/reporting/tests/delivering.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/reporting/tests/delivering.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -1,7 +1,9 @@ const CC = Components.Constructor; -const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", - "setInputStream"); +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); Components.utils.importGlobalProperties(["URLSearchParams"]); @@ -22,12 +24,15 @@ let body = { max_age: 1, - endpoints: [{ - url: "https://example.org/tests/dom/reporting/tests/delivering.sjs" + - (extraParams.length ? "?" + extraParams.join("&") : ""), - priority: 1, - weight: 1, - }] + endpoints: [ + { + url: + "https://example.org/tests/dom/reporting/tests/delivering.sjs" + + (extraParams.length ? "?" + extraParams.join("&") : ""), + priority: 1, + weight: 1, + }, + ], }; aResponse.setStatusLine(aRequest.httpVersion, 200, "OK"); @@ -68,26 +73,30 @@ var bytes = []; while ((avail = body.available()) > 0) { Array.prototype.push.apply(bytes, body.readByteArray(avail)); - } + } - let reports = getState("report"); - if (!reports) { - reports = []; - } else { - reports = JSON.parse(reports); - } + let reports = getState("report"); + if (!reports) { + reports = []; + } else { + reports = JSON.parse(reports); + } - const incoming_reports = JSON.parse(String.fromCharCode.apply(null, bytes)); - for (let report of incoming_reports) { + const incoming_reports = JSON.parse(String.fromCharCode.apply(null, bytes)); + for (let report of incoming_reports) { let data = { contentType: aRequest.getHeader("content-type"), origin: aRequest.getHeader("origin"), body: report, - url: aRequest.scheme + "://" + aRequest.host + aRequest.path + - (aRequest.queryString ? "&" + aRequest.queryString : ""), - } + url: + aRequest.scheme + + "://" + + aRequest.host + + aRequest.path + + (aRequest.queryString ? "&" + aRequest.queryString : ""), + }; reports.push(data); - } + } setState("report", JSON.stringify(reports)); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/script/ScriptLoader.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/script/ScriptLoader.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/script/ScriptLoader.cpp 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/script/ScriptLoader.cpp 2021-10-20 19:28:19.000000000 +0000 @@ -687,8 +687,10 @@ if (module) { JS::RootedValue privateValue(cx); JS::RootedScript moduleScript(cx, JS::GetModuleScript(module)); - if (!JS::UpdateDebugMetadata(cx, moduleScript, options, privateValue, - nullptr, introductionScript, nullptr)) { + JS::InstantiateOptions instantiateOptions(options); + if (!JS::UpdateDebugMetadata(cx, moduleScript, instantiateOptions, + privateValue, nullptr, introductionScript, + nullptr)) { return NS_ERROR_OUT_OF_MEMORY; } } @@ -2935,10 +2937,10 @@ } if (aRequest->IsModuleRequest()) { - aOptions->hideScriptFromDebugger = true; + aOptions->setHideScriptFromDebugger(true); } - aOptions->setdeferDebugMetadata(true); + aOptions->setDeferDebugMetadata(true); aOptions->borrowBuffer = true; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/nsHTTPSOnlyUtils.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/security/nsHTTPSOnlyUtils.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/nsHTTPSOnlyUtils.cpp 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/nsHTTPSOnlyUtils.cpp 2021-10-20 19:28:19.000000000 +0000 @@ -113,6 +113,20 @@ return; } + // HTTPS-First only applies to standard ports but HTTPS-Only brute forces + // all http connections to be https and overrules HTTPS-First. In case + // HTTPS-First is enabled, but HTTPS-Only is not enabled, we might return + // early if attempting to send a background request to a non standard port. + if (IsHttpsFirstModeEnabled(isPrivateWin) && + !IsHttpsOnlyModeEnabled(isPrivateWin)) { + int32_t port = 0; + nsresult rv = channelURI->GetPort(&port); + int defaultPortforScheme = NS_GetDefaultPort("http"); + if (NS_SUCCEEDED(rv) && port != defaultPortforScheme && port != -1) { + return; + } + } + // Check for general exceptions if (OnionException(channelURI) || LoopbackOrLocalException(channelURI)) { return; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/cors/bug1456721.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/cors/bug1456721.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/cors/bug1456721.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/cors/bug1456721.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -1,4 +1,3 @@ - function handleRequest(request, response) { response.setHeader("Cache-Control", "no-cache", false); let queryStr = request.queryString; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/cors/file_CrossSiteXHR_cache_server.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/cors/file_CrossSiteXHR_cache_server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/cors/file_CrossSiteXHR_cache_server.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/cors/file_CrossSiteXHR_cache_server.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -1,16 +1,17 @@ Cu.import("resource://gre/modules/Services.jsm"); -function handleRequest(request, response) -{ +function handleRequest(request, response) { var query = {}; - request.queryString.split('&').forEach(function (val) { - var [name, value] = val.split('='); + request.queryString.split("&").forEach(function(val) { + var [name, value] = val.split("="); query[name] = unescape(value); }); if ("setState" in query) { - setState("test/dom/security/test_CrossSiteXHR_cache:secData", - query.setState); + setState( + "test/dom/security/test_CrossSiteXHR_cache:secData", + query.setState + ); response.setHeader("Cache-Control", "no-cache", false); response.setHeader("Content-Type", "text/plain", false); @@ -23,24 +24,33 @@ // Send response - secData = - JSON.parse(getState("test/dom/security/test_CrossSiteXHR_cache:secData")); + secData = JSON.parse( + getState("test/dom/security/test_CrossSiteXHR_cache:secData") + ); - if (secData.allowOrigin) + if (secData.allowOrigin) { response.setHeader("Access-Control-Allow-Origin", secData.allowOrigin); + } - if (secData.withCred) + if (secData.withCred) { response.setHeader("Access-Control-Allow-Credentials", "true"); + } if (isPreflight) { - if (secData.allowHeaders) + if (secData.allowHeaders) { response.setHeader("Access-Control-Allow-Headers", secData.allowHeaders); + } - if (secData.allowMethods) + if (secData.allowMethods) { response.setHeader("Access-Control-Allow-Methods", secData.allowMethods); + } - if (secData.cacheTime) - response.setHeader("Access-Control-Max-Age", secData.cacheTime.toString()); + if (secData.cacheTime) { + response.setHeader( + "Access-Control-Max-Age", + secData.cacheTime.toString() + ); + } return; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/cors/file_CrossSiteXHR_inner_data.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/cors/file_CrossSiteXHR_inner_data.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/cors/file_CrossSiteXHR_inner_data.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/cors/file_CrossSiteXHR_inner_data.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -1,4 +1,5 @@ -var data = '\n\ +var data = + '\n\ \n\ \n\ \n\ @@ -92,10 +93,9 @@ \n\ Inner page\n\ \n\ -' +'; -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setStatusLine(null, 302, "Follow me"); response.setHeader("Location", "data:text/html," + escape(data)); response.setHeader("Content-Type", "text/plain"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/cors/file_CrossSiteXHR_server.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/cors/file_CrossSiteXHR_server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/cors/file_CrossSiteXHR_server.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/cors/file_CrossSiteXHR_server.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -1,15 +1,16 @@ const CC = Components.Constructor; -const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", - "setInputStream"); +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); Cu.import("resource://gre/modules/Services.jsm"); Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true); -function handleRequest(request, response) -{ +function handleRequest(request, response) { var query = {}; - request.queryString.split('&').forEach(function (val) { - var [name, value] = val.split('='); + request.queryString.split("&").forEach(function(val) { + var [name, value] = val.split("="); query[name] = unescape(value); }); @@ -17,11 +18,13 @@ var bodyStream = new BinaryInputStream(request.bodyInputStream); var bodyBytes = []; - while ((bodyAvail = bodyStream.available()) > 0) + while ((bodyAvail = bodyStream.available()) > 0) { Array.prototype.push.apply(bodyBytes, bodyStream.readByteArray(bodyAvail)); + } var body = decodeURIComponent( - escape(String.fromCharCode.apply(null, bodyBytes))); + escape(String.fromCharCode.apply(null, bodyBytes)) + ); if (query.hop) { query.hop = parseInt(query.hop, 10); @@ -44,74 +47,112 @@ // Check that request was correct if (!isPreflight && query.body && body != query.body) { - sendHttp500(response, "Wrong body. Expected " + query.body + " got " + - body); + sendHttp500( + response, + "Wrong body. Expected " + query.body + " got " + body + ); return; } if (!isPreflight && "headers" in query) { headers = JSON.parse(query.headers); - for(headerName in headers) { - // Content-Type is changed if there was a body - if (!(headerName == "Content-Type" && body) && - (!request.hasHeader(headerName) || - request.getHeader(headerName) != headers[headerName])) { - var actual = request.hasHeader(headerName) ? request.getHeader(headerName) - : ""; - sendHttp500(response, - "Header " + headerName + " had wrong value. Expected " + - headers[headerName] + " got " + actual); + for (headerName in headers) { + // Content-Type is changed if there was a body + if ( + !(headerName == "Content-Type" && body) && + (!request.hasHeader(headerName) || + request.getHeader(headerName) != headers[headerName]) + ) { + var actual = request.hasHeader(headerName) + ? request.getHeader(headerName) + : ""; + sendHttp500( + response, + "Header " + + headerName + + " had wrong value. Expected " + + headers[headerName] + + " got " + + actual + ); return; } } } - if (isPreflight && "requestHeaders" in query && - request.getHeader("Access-Control-Request-Headers") != query.requestHeaders) { - sendHttp500(response, + if ( + isPreflight && + "requestHeaders" in query && + request.getHeader("Access-Control-Request-Headers") != query.requestHeaders + ) { + sendHttp500( + response, "Access-Control-Request-Headers had wrong value. Expected " + - query.requestHeaders + " got " + - request.getHeader("Access-Control-Request-Headers")); + query.requestHeaders + + " got " + + request.getHeader("Access-Control-Request-Headers") + ); return; } - if (isPreflight && "requestMethod" in query && - request.getHeader("Access-Control-Request-Method") != query.requestMethod) { - sendHttp500(response, + if ( + isPreflight && + "requestMethod" in query && + request.getHeader("Access-Control-Request-Method") != query.requestMethod + ) { + sendHttp500( + response, "Access-Control-Request-Method had wrong value. Expected " + - query.requestMethod + " got " + - request.getHeader("Access-Control-Request-Method")); + query.requestMethod + + " got " + + request.getHeader("Access-Control-Request-Method") + ); return; } if ("origin" in query && request.getHeader("Origin") != query.origin) { - sendHttp500(response, - "Origin had wrong value. Expected " + query.origin + " got " + - request.getHeader("Origin")); + sendHttp500( + response, + "Origin had wrong value. Expected " + + query.origin + + " got " + + request.getHeader("Origin") + ); return; } if ("cookie" in query) { cookies = {}; - request.getHeader("Cookie").split(/ *; */).forEach(function (val) { - var [name, value] = val.split('='); - cookies[name] = unescape(value); - }); - - query.cookie.split(",").forEach(function (val) { - var [name, value] = val.split('='); + request + .getHeader("Cookie") + .split(/ *; */) + .forEach(function(val) { + var [name, value] = val.split("="); + cookies[name] = unescape(value); + }); + + query.cookie.split(",").forEach(function(val) { + var [name, value] = val.split("="); if (cookies[name] != value) { - sendHttp500(response, - "Cookie " + name + " had wrong value. Expected " + value + - " got " + cookies[name]); + sendHttp500( + response, + "Cookie " + + name + + " had wrong value. Expected " + + value + + " got " + + cookies[name] + ); return; } }); } if (query.noCookie && request.hasHeader("Cookie")) { - sendHttp500(response, - "Got cookies when didn't expect to: " + request.getHeader("Cookie")); + sendHttp500( + response, + "Got cookies when didn't expect to: " + request.getHeader("Cookie") + ); return; } @@ -123,24 +164,28 @@ if (isPreflight && query.preflightStatus) { response.setStatusLine(null, query.preflightStatus, "preflight status"); } - - if (query.allowOrigin && (!isPreflight || !query.noAllowPreflight)) + + if (query.allowOrigin && (!isPreflight || !query.noAllowPreflight)) { response.setHeader("Access-Control-Allow-Origin", query.allowOrigin); + } - if (query.allowCred) + if (query.allowCred) { response.setHeader("Access-Control-Allow-Credentials", "true"); + } - if (query.setCookie) + if (query.setCookie) { response.setHeader("Set-Cookie", query.setCookie + "; path=/"); + } if (isPreflight) { - if (query.allowHeaders) + if (query.allowHeaders) { response.setHeader("Access-Control-Allow-Headers", query.allowHeaders); + } - if (query.allowMethods) + if (query.allowMethods) { response.setHeader("Access-Control-Allow-Methods", query.allowMethods); - } - else { + } + } else { if (query.responseHeaders) { let responseHeaders = JSON.parse(query.responseHeaders); for (let responseHeader in responseHeaders) { @@ -148,14 +193,19 @@ } } - if (query.exposeHeaders) + if (query.exposeHeaders) { response.setHeader("Access-Control-Expose-Headers", query.exposeHeaders); + } } if (!isPreflight && query.hop && query.hop < hops.length) { - newURL = hops[query.hop].server + - "/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?" + - "hop=" + (query.hop + 1) + "&hops=" + escape(query.hops); + newURL = + hops[query.hop].server + + "/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?" + + "hop=" + + (query.hop + 1) + + "&hops=" + + escape(query.hops); if ("headers" in query) { newURL += "&headers=" + escape(query.headers); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_base_uri_server.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_base_uri_server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_base_uri_server.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_base_uri_server.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -10,7 +10,7 @@ Bug 1045897 - Test CSP base-uri directive`; -const REGULAR_POST_BASE =` +const REGULAR_POST_BASE = ` @@ -39,10 +39,8 @@ // Send HTML to test allowed/blocked behaviors response.setHeader("Content-Type", "text/html", false); response.write(PRE_BASE); - var base1 = - ""; - var base2 = - ""; + var base1 = ''; + var base2 = ''; response.write(base1 + base2); if (query.get("action") === "enforce-csp") { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_block_all_mcb.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_block_all_mcb.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_block_all_mcb.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_block_all_mcb.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -3,26 +3,26 @@ const HEAD = "" + - "" + + '' + "Bug 1122236 - CSP: Implement block-all-mixed-content" + ""; const CSP_ALLOW = - ""; + ''; const CSP_BLOCK = - ""; + ''; const BODY = "" + - "" + - "" + "" + @@ -32,24 +32,23 @@ // actual network load rather than loading the image from the cache. const BODY_CSPRO = "" + - "" + - "" + "" + ""; -function handleRequest(request, response) -{ +function handleRequest(request, response) { // avoid confusing cache behaviors response.setHeader("Cache-Control", "no-cache", false); - + var queryString = request.queryString; if (queryString === "csp-block") { @@ -66,11 +65,14 @@ } if (queryString === "cspro-block") { // CSP RO is not supported in meta tag, let's use the header - response.setHeader("Content-Security-Policy-Report-Only", "block-all-mixed-content", false); + response.setHeader( + "Content-Security-Policy-Report-Only", + "block-all-mixed-content", + false + ); response.write(HEAD + BODY_CSPRO); return; } // we should never get here but just in case return something unexpected response.write("do'h"); - } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_blocked_uri_in_violation_event_after_redirects.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_blocked_uri_in_violation_event_after_redirects.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_blocked_uri_in_violation_event_after_redirects.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_blocked_uri_in_violation_event_after_redirects.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -5,8 +5,7 @@ let REDIRECT_302_URI = "http://test1.example.com/tests/dom/security/test/csp/file_blocked_uri_in_violation_event_after_redirects.sjs?test1b#ref1b"; -let JS_REDIRECT = - ` +let JS_REDIRECT = ` " + - "" + - ""; + var html = + "" + + "" + + "" + + "Testpage for Bug 1036399" + + "" + + "" + + "
blocked
" + + "" + + "" + + ""; response.write(html); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_fontloader.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_fontloader.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_fontloader.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_fontloader.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -3,14 +3,14 @@ const PRE_HEAD = "" + - "" + + '' + "Bug 1195172 - CSP should block font from cache"; const CSP_BLOCK = - ""; + ''; const CSP_ALLOW = - ""; + ''; const CSS = "' + - '
test
'; + if (query.testid == "font-src") { + var resp = + '' + + '
test
'; response.write(resp); return; } // iframe that redirects to another site - if (query["testid"] == "frame-src") { - response.write(''); + if (query.testid == "frame-src") { + response.write( + '' + ); return; } // image that redirects to another site - if (query["testid"] == "img-src") { - response.write(''); + if (query.testid == "img-src") { + response.write( + '' + ); return; } // video content that redirects to another site - if (query["testid"] == "media-src") { - response.write(''); + if (query.testid == "media-src") { + response.write( + '' + ); return; } // object content that redirects to another site - if (query["testid"] == "object-src") { - response.write(''); + if (query.testid == "object-src") { + response.write( + '' + ); return; } // external script that redirects to another site - if (query["testid"] == "script-src") { - response.write(''); + if (query.testid == "script-src") { + response.write( + '' + ); return; } // external stylesheet that redirects to another site - if (query["testid"] == "style-src") { - response.write(''); + if (query.testid == "style-src") { + response.write( + '' + ); return; } // script that XHR's to a resource that redirects to another site - if (query["testid"] == "xhr-src") { - response.write(''); + if (query.testid == "xhr-src") { + response.write(''); return; } // for bug949706 - if (query["testid"] == "img-src-from-css") { + if (query.testid == "img-src-from-css") { // loads a stylesheet, which in turn loads an image that redirects. - response.write(''); + response.write( + '' + ); return; } - if (query["testid"] == "from-worker") { + if (query.testid == "from-worker") { // loads a script; launches a worker; that worker uses importscript; which then gets redirected // So it's: // '); + response.write( + '' + ); return; } - if (query["testid"] == "from-blob-worker") { + if (query.testid == "from-blob-worker") { // loads a script; launches a worker; that worker uses importscript; which then gets redirected // So it's: // '); + response.write( + '' + ); return; } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_redirects_resource.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_redirects_resource.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_redirects_resource.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_redirects_resource.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -1,11 +1,10 @@ // SJS file to serve resources for CSP redirect tests // This file mimics serving resources, e.g. fonts, images, etc., which a CSP // can include. The resource may redirect to a different resource, if specified. -function handleRequest(request, response) -{ +function handleRequest(request, response) { var query = {}; - request.queryString.split('&').forEach(function (val) { - var [name, value] = val.split('='); + request.queryString.split("&").forEach(function(val) { + var [name, value] = val.split("="); query[name] = unescape(value); }); @@ -16,16 +15,17 @@ response.setHeader("Cache-Control", "no-cache", false); // redirect to a resource on this site - if (query["redir"] == "same") { - var loc = thisSite+resource+"?res="+query["res"]+"&testid="+query["id"]; + if (query.redir == "same") { + var loc = thisSite + resource + "?res=" + query.res + "&testid=" + query.id; response.setStatusLine("1.1", 302, "Found"); response.setHeader("Location", loc, false); return; } // redirect to a resource on a different site - else if (query["redir"] == "other") { - var loc = otherSite+resource+"?res="+query["res"]+"&testid="+query["id"]; + else if (query.redir == "other") { + var loc = + otherSite + resource + "?res=" + query.res + "&testid=" + query.id; response.setStatusLine("1.1", 302, "Found"); response.setHeader("Location", loc, false); return; @@ -36,7 +36,7 @@ // the request for the content was sent or not. // downloadable font - if (query["res"] == "font") { + if (query.res == "font") { response.setHeader("Access-Control-Allow-Origin", "*", false); response.setHeader("Content-Type", "text/plain", false); response.write("font data..."); @@ -44,50 +44,50 @@ } // iframe with arbitrary content - if (query["res"] == "iframe") { + if (query.res == "iframe") { response.setHeader("Content-Type", "text/html", false); response.write("iframe content..."); return; } // image - if (query["res"] == "image") { + if (query.res == "image") { response.setHeader("Content-Type", "image/gif", false); response.write("image data..."); return; } // media content, e.g. Ogg video - if (query["res"] == "media") { + if (query.res == "media") { response.setHeader("Content-Type", "video/ogg", false); response.write("video data..."); return; } // plugin content, e.g. - if (query["res"] == "object") { + if (query.res == "object") { response.setHeader("Content-Type", "text/html", false); response.write("object data..."); return; } // script - if (query["res"] == "script") { + if (query.res == "script") { response.setHeader("Content-Type", "application/javascript", false); response.write("some script..."); return; } // external stylesheet - if (query["res"] == "style") { + if (query.res == "style") { response.setHeader("Content-Type", "text/css", false); response.write("css data..."); return; } // internal stylesheet that loads an image from an external site - if (query["res"] == "cssLoader") { - let bgURL = thisSite + resource + '?redir=other&res=image&id=' + query["id"]; + if (query.res == "cssLoader") { + let bgURL = thisSite + resource + "?redir=other&res=image&id=" + query.id; response.setHeader("Content-Type", "text/css", false); response.write("body { background:url('" + bgURL + "'); }"); return; @@ -95,9 +95,10 @@ // script that loads an internal worker that uses importScripts on a redirect // to an external script. - if (query["res"] == "loadWorkerThatMakesRequests") { + if (query.res == "loadWorkerThatMakesRequests") { // this creates a worker (same origin) that imports a redirecting script. - let workerURL = thisSite + resource + '?res=makeRequestsWorker&id=' + query["id"]; + let workerURL = + thisSite + resource + "?res=makeRequestsWorker&id=" + query.id; response.setHeader("Content-Type", "application/javascript", false); response.write("new Worker('" + workerURL + "');"); return; @@ -105,45 +106,67 @@ // script that loads an internal worker that uses importScripts on a redirect // to an external script. - if (query["res"] == "loadBlobWorkerThatMakesRequests") { + if (query.res == "loadBlobWorkerThatMakesRequests") { // this creates a worker (same origin) that imports a redirecting script. - let workerURL = thisSite + resource + '?res=makeRequestsWorker&id=' + query["id"]; + let workerURL = + thisSite + resource + "?res=makeRequestsWorker&id=" + query.id; response.setHeader("Content-Type", "application/javascript", false); - response.write("var x = new XMLHttpRequest(); x.open('GET', '" + workerURL + "'); "); + response.write( + "var x = new XMLHttpRequest(); x.open('GET', '" + workerURL + "'); " + ); response.write("x.responseType = 'blob'; x.send(); "); - response.write("x.onload = () => { new Worker(URL.createObjectURL(x.response)); };"); + response.write( + "x.onload = () => { new Worker(URL.createObjectURL(x.response)); };" + ); return; } // source for a worker that simply calls importScripts on a script that // redirects. - if (query["res"] == "makeRequestsWorker") { + if (query.res == "makeRequestsWorker") { // this is code for a worker that imports a redirected script. - let scriptURL = thisSite + resource + "?redir=other&res=script&id=script-src-redir-" + query["id"]; - let xhrURL = thisSite + resource + "?redir=other&res=xhr-resp&id=xhr-src-redir-" + query["id"]; - let fetchURL = thisSite + resource + "?redir=other&res=xhr-resp&id=fetch-src-redir-" + query["id"]; + let scriptURL = + thisSite + + resource + + "?redir=other&res=script&id=script-src-redir-" + + query.id; + let xhrURL = + thisSite + + resource + + "?redir=other&res=xhr-resp&id=xhr-src-redir-" + + query.id; + let fetchURL = + thisSite + + resource + + "?redir=other&res=xhr-resp&id=fetch-src-redir-" + + query.id; response.setHeader("Content-Type", "application/javascript", false); response.write("try { importScripts('" + scriptURL + "'); } catch(ex) {} "); - response.write("var x = new XMLHttpRequest(); x.open('GET', '" + xhrURL + "'); x.send();"); + response.write( + "var x = new XMLHttpRequest(); x.open('GET', '" + xhrURL + "'); x.send();" + ); response.write("fetch('" + fetchURL + "');"); return; } // script that invokes XHR - if (query["res"] == "xhr") { + if (query.res == "xhr") { response.setHeader("Content-Type", "application/javascript", false); - var resp = 'var x = new XMLHttpRequest();x.open("GET", "' + thisSite + - resource+'?redir=other&res=xhr-resp&id=xhr-src-redir", false);\n' + - 'x.send(null);'; + var resp = + 'var x = new XMLHttpRequest();x.open("GET", "' + + thisSite + + resource + + '?redir=other&res=xhr-resp&id=xhr-src-redir", false);\n' + + "x.send(null);"; response.write(resp); return; } // response to XHR - if (query["res"] == "xhr-resp") { + if (query.res == "xhr-resp") { response.setHeader("Access-Control-Allow-Origin", "*", false); response.setHeader("Content-Type", "text/html", false); - response.write('XHR response...'); + response.write("XHR response..."); return; } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_redirect_worker.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_redirect_worker.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_redirect_worker.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_redirect_worker.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -3,32 +3,33 @@ const THIS_SITE = "http://mochi.test:8888"; const OTHER_SITE = "http://example.com"; -function handleRequest(request, response) -{ +function handleRequest(request, response) { var query = {}; - request.queryString.split('&').forEach(function (val) { - var [name, value] = val.split('='); + request.queryString.split("&").forEach(function(val) { + var [name, value] = val.split("="); query[name] = unescape(value); }); - var resource = query['path']; + var resource = query.path; response.setHeader("Cache-Control", "no-cache", false); - var loc = ''; + var loc = ""; // redirect to a resource on this site - if (query["redir"] == "same") { - loc = THIS_SITE+resource+"#"+query['page_id'] + if (query.redir == "same") { + loc = THIS_SITE + resource + "#" + query.page_id; } // redirect to a resource on a different site - else if (query["redir"] == "other") { - loc = OTHER_SITE+resource+"#"+query['page_id'] + else if (query.redir == "other") { + loc = OTHER_SITE + resource + "#" + query.page_id; } response.setStatusLine("1.1", 302, "Found"); response.setHeader("Location", loc, false); - response.write(''); + response.write( + '' + ); return; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_reloadInFreshProcess.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_reloadInFreshProcess.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_reloadInFreshProcess.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_reloadInFreshProcess.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -1,7 +1,6 @@ "use strict"; -const TESTFRAME_QUERY_LARGE_ALLOCATION_WITH_NO_CSP = - ` +const TESTFRAME_QUERY_LARGE_ALLOCATION_WITH_NO_CSP = ` Bug 1555050 - Test CSP Navigation using ReloadInFreshProcess @@ -16,8 +15,7 @@ `; -const TESTFRAME_QUERY_LARGE_ALLOCATION_WITH_CSP = - ` +const TESTFRAME_QUERY_LARGE_ALLOCATION_WITH_CSP = ` Bug 1555050 - Test CSP Navigation using ReloadInFreshProcess @@ -32,8 +30,7 @@ `; -const LARGE_ALLOCATION = - ` +const LARGE_ALLOCATION = ` Bug 1555050 - Test CSP Navigation using ReloadInFreshProcess @@ -46,15 +43,18 @@ `; -function handleRequest(request, response) -{ +function handleRequest(request, response) { // avoid confusing cache behaviors response.setHeader("Cache-Control", "no-cache", false); - + var queryString = request.queryString; if (queryString == "testframe_with_csp") { - response.setHeader("Content-Security-Policy", "upgrade-insecure-requests", false); + response.setHeader( + "Content-Security-Policy", + "upgrade-insecure-requests", + false + ); response.write(TESTFRAME_QUERY_LARGE_ALLOCATION_WITH_NO_CSP); return; } @@ -65,7 +65,11 @@ } if (queryString == "largeAllocation_with_csp") { - response.setHeader("Content-Security-Policy", "upgrade-insecure-requests", false); + response.setHeader( + "Content-Security-Policy", + "upgrade-insecure-requests", + false + ); response.setHeader("Large-Allocation", "0", false); response.write(LARGE_ALLOCATION); return; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_report_for_import_server.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_report_for_import_server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_report_for_import_server.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_report_for_import_server.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -2,12 +2,13 @@ // Bug 1048048 - CSP violation report not sent for @import const CC = Components.Constructor; -const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", - "setInputStream"); +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); -function handleRequest(request, response) -{ +function handleRequest(request, response) { // avoid confusing cache behaviors response.setHeader("Cache-Control", "no-cache", false); response.setHeader("Content-Type", "text/html", false); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_scheme_relative_sources.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_scheme_relative_sources.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_scheme_relative_sources.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_scheme_relative_sources.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -3,36 +3,39 @@ * Bug 921493 - CSP: test allowlisting of scheme-relative sources */ -function handleRequest(request, response) -{ +function handleRequest(request, response) { Components.utils.importGlobalProperties(["URLSearchParams"]); let query = new URLSearchParams(request.queryString); let scheme = query.get("scheme"); let policy = query.get("policy"); - let linkUrl = scheme + - "://example.com/tests/dom/security/test/csp/file_scheme_relative_sources.js"; + let linkUrl = + scheme + + "://example.com/tests/dom/security/test/csp/file_scheme_relative_sources.js"; - let html = "" + - "" + - "" + - "test schemeless sources within CSP" + - "" + - " " + - "
blocked
" + - // try to load a scheme relative script - "" + - // have an inline script that reports back to the parent whether - // the script got loaded or not from within the sandboxed iframe. - "" + - "" + - ""; + let html = + "" + + "" + + "" + + "test schemeless sources within CSP" + + "" + + " " + + "
blocked
" + + // try to load a scheme relative script + "" + + // have an inline script that reports back to the parent whether + // the script got loaded or not from within the sandboxed iframe. + "" + + "" + + ""; response.setHeader("Cache-Control", "no-cache", false); response.setHeader("Content-Type", "text/html", false); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_svg_inline_style_server.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_svg_inline_style_server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_svg_inline_style_server.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_svg_inline_style_server.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -1,8 +1,6 @@ - "use strict"; -const SVG_IMG = - ` +const SVG_IMG = ` `; -const SVG_IMG_NO_INLINE_STYLE = - ` +const SVG_IMG_NO_INLINE_STYLE = ` `; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_testserver.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_testserver.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_testserver.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_testserver.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -7,20 +7,21 @@ // Load the HTML to return in the response from file. // Since it's relative to the cwd of the test runner, we start there and // append to get to the actual path of the file. - const testHTMLFile = - Components.classes["@mozilla.org/file/directory_service;1"]. - getService(Components.interfaces.nsIProperties). - get("CurWorkD", Components.interfaces.nsIFile); - - const testHTMLFileStream = - Components.classes["@mozilla.org/network/file-input-stream;1"]. - createInstance(Components.interfaces.nsIFileInputStream); + const testHTMLFile = Components.classes[ + "@mozilla.org/file/directory_service;1" + ] + .getService(Components.interfaces.nsIProperties) + .get("CurWorkD", Components.interfaces.nsIFile); + + const testHTMLFileStream = Components.classes[ + "@mozilla.org/network/file-input-stream;1" + ].createInstance(Components.interfaces.nsIFileInputStream); path .split("/") .filter(path => path) .reduce((file, path) => { - testHTMLFile.append(path) + testHTMLFile.append(path); return testHTMLFile; }, testHTMLFile); testHTMLFileStream.init(testHTMLFile, -1, 0, 0); @@ -35,17 +36,21 @@ response.setHeader("Cache-Control", "no-cache", false); // Deliver the CSP policy encoded in the URL - if(query.has("csp")){ + if (query.has("csp")) { response.setHeader("Content-Security-Policy", query.get("csp"), false); } // Deliver the CSP report-only policy encoded in the URI - if(query.has("cspRO")){ - response.setHeader("Content-Security-Policy-Report-Only", query.get("cspRO"), false); + if (query.has("cspRO")) { + response.setHeader( + "Content-Security-Policy-Report-Only", + query.get("cspRO"), + false + ); } // Deliver the CORS header in the URL - if(query.has("cors")){ + if (query.has("cors")) { response.setHeader("Access-Control-Allow-Origin", query.get("cors"), false); } @@ -56,7 +61,7 @@ } response.setHeader("Content-Type", type, false); - if(query.has("file")){ + if (query.has("file")) { response.write(loadHTMLFromFile(query.get("file"))); } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_upgrade_insecure_cors_server.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_upgrade_insecure_cors_server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_upgrade_insecure_cors_server.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_upgrade_insecure_cors_server.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -1,8 +1,7 @@ // Custom *.sjs file specifically for the needs of Bug: // Bug 1139297 - Implement CSP upgrade-insecure-requests directive -function handleRequest(request, response) -{ +function handleRequest(request, response) { // avoid confusing cache behaviors response.setHeader("Cache-Control", "no-cache", false); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_upgrade_insecure_docwrite_iframe.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_upgrade_insecure_docwrite_iframe.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_upgrade_insecure_docwrite_iframe.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_upgrade_insecure_docwrite_iframe.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -5,7 +5,8 @@ const IFRAME_URL = "http://example.com/tests/dom/security/test/csp/file_upgrade_insecure_docwrite_iframe.sjs?docwriteframe"; -const TEST_FRAME = ` +const TEST_FRAME = + ` TEST_FRAME @@ -13,12 +14,13 @@ `; - // doc.write(iframe) sends a post message to the parent indicating the current // location so the parent can make sure the request was upgraded to *https*. const DOC_WRITE_FRAME = ` @@ -30,8 +32,7 @@ `; -function handleRequest(request, response) -{ +function handleRequest(request, response) { // avoid confusing cache behaviors response.setHeader("Cache-Control", "no-cache", false); response.setHeader("Content-Type", "text/html", false); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_upgrade_insecure_loopback_server.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_upgrade_insecure_loopback_server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_upgrade_insecure_loopback_server.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_upgrade_insecure_loopback_server.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -1,8 +1,7 @@ // Custom *.sjs file specifically for the needs of Bug: // Bug 1447784 - Implement CSP upgrade-insecure-requests directive -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setHeader("Access-Control-Allow-Headers", "content-type", false); response.setHeader("Access-Control-Allow-Methods", "GET", false); response.setHeader("Access-Control-Allow-Origin", "*", false); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_upgrade_insecure_navigation_redirect.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_upgrade_insecure_navigation_redirect.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_upgrade_insecure_navigation_redirect.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_upgrade_insecure_navigation_redirect.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -36,8 +36,10 @@ return; } - if (query === "finaldoc_same_origin_redirect" || - query === "finaldoc_cross_origin_redirect") { + if ( + query === "finaldoc_same_origin_redirect" || + query === "finaldoc_cross_origin_redirect" + ) { response.write(FINAL_DOCUMENT); return; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_upgrade_insecure_reporting_server.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_upgrade_insecure_reporting_server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_upgrade_insecure_reporting_server.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_upgrade_insecure_reporting_server.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -6,35 +6,38 @@ // small red image const IMG_BYTES = atob( "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" + - "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="); + "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" +); -const REPORT_URI = "https://example.com/tests/dom/security/test/csp/file_upgrade_insecure_reporting_server.sjs?report"; +const REPORT_URI = + "https://example.com/tests/dom/security/test/csp/file_upgrade_insecure_reporting_server.sjs?report"; const POLICY = "upgrade-insecure-requests; default-src https: 'unsafe-inline'"; -const POLICY_RO = "default-src https: 'unsafe-inline'; report-uri " + REPORT_URI; +const POLICY_RO = + "default-src https: 'unsafe-inline'; report-uri " + REPORT_URI; function loadHTMLFromFile(path) { // Load the HTML to return in the response from file. // Since it's relative to the cwd of the test runner, we start there and // append to get to the actual path of the file. - var testHTMLFile = - Components.classes["@mozilla.org/file/directory_service;1"]. - getService(Components.interfaces.nsIProperties). - get("CurWorkD", Components.interfaces.nsIFile); + var testHTMLFile = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("CurWorkD", Components.interfaces.nsIFile); var dirs = path.split("/"); for (var i = 0; i < dirs.length; i++) { testHTMLFile.append(dirs[i]); } - var testHTMLFileStream = - Components.classes["@mozilla.org/network/file-input-stream;1"]. - createInstance(Components.interfaces.nsIFileInputStream); + var testHTMLFileStream = Components.classes[ + "@mozilla.org/network/file-input-stream;1" + ].createInstance(Components.interfaces.nsIFileInputStream); testHTMLFileStream.init(testHTMLFile, -1, 0, 0); - var testHTML = NetUtil.readInputStreamToString(testHTMLFileStream, testHTMLFileStream.available()); + var testHTML = NetUtil.readInputStreamToString( + testHTMLFileStream, + testHTMLFileStream.available() + ); return testHTML; } - -function handleRequest(request, response) -{ +function handleRequest(request, response) { // avoid confusing cache behaviors response.setHeader("Cache-Control", "no-cache", false); @@ -50,8 +53,12 @@ response.setHeader("Content-Security-Policy", POLICY, false); response.setHeader("Content-Security-Policy-Report-Only", POLICY_RO, false); response.setHeader("Content-Type", "text/html", false); - response.write(loadHTMLFromFile("tests/dom/security/test/csp/file_upgrade_insecure_reporting.html")); - return; + response.write( + loadHTMLFromFile( + "tests/dom/security/test/csp/file_upgrade_insecure_reporting.html" + ) + ); + return; } // (3) Return the image back to the client diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_upgrade_insecure_server.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_upgrade_insecure_server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/file_upgrade_insecure_server.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/file_upgrade_insecure_server.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -14,11 +14,21 @@ "" + ""; -const expectedQueries = [ "script", "style", "img", "iframe", "form", "xhr", - "media", "object", "font", "img-redir", "nested-img"]; +const expectedQueries = [ + "script", + "style", + "img", + "iframe", + "form", + "xhr", + "media", + "object", + "font", + "img-redir", + "nested-img", +]; -function handleRequest(request, response) -{ +function handleRequest(request, response) { // avoid confusing cache behaviors response.setHeader("Cache-Control", "no-cache", false); var queryString = request.queryString; @@ -50,7 +60,7 @@ } // make sure all the requested queries are indeed https - queryString += (request.scheme == "https") ? "-ok" : "-error"; + queryString += request.scheme == "https" ? "-ok" : "-error"; var receivedQueries = getState("receivedQueries"); @@ -83,7 +93,7 @@ // the iframe context in case of an error so we // can test both, using upgrade-insecure as well // as the base case of not using upgrade-insecure. - if ((queryString == "iframe-ok") || (queryString == "iframe-error")) { + if (queryString == "iframe-ok" || queryString == "iframe-error") { response.write(IFRAME_CONTENT); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/referrerdirective.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/referrerdirective.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/referrerdirective.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/referrerdirective.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -1,36 +1,40 @@ // Used for bug 965727 to serve up really simple scripts reflecting the // referrer sent to load this back to the loader. - function handleRequest(request, response) { // skip speculative loads. - var splits = request.queryString.split('&'); + var splits = request.queryString.split("&"); var params = {}; splits.forEach(function(v) { - let parts = v.split('='); + let parts = v.split("="); params[parts[0]] = unescape(parts[1]); }); - var loadType = params['type']; - var referrerLevel = 'error'; + var loadType = params.type; + var referrerLevel = "error"; - if (request.hasHeader('Referer')) { - var referrer = request.getHeader('Referer'); + if (request.hasHeader("Referer")) { + var referrer = request.getHeader("Referer"); if (referrer.indexOf("file_testserver.sjs") > -1) { referrerLevel = "full"; } else { referrerLevel = "origin"; } } else { - referrerLevel = 'none'; + referrerLevel = "none"; } - var theScript = 'window.postResult("' + loadType + '", "' + referrerLevel + '");'; - response.setHeader('Content-Type', 'application/javascript; charset=utf-8', false); - response.setHeader('Cache-Control', 'no-cache', false); + var theScript = + 'window.postResult("' + loadType + '", "' + referrerLevel + '");'; + response.setHeader( + "Content-Type", + "application/javascript; charset=utf-8", + false + ); + response.setHeader("Cache-Control", "no-cache", false); if (request.method != "OPTIONS") { - response.write(theScript); + response.write(theScript); } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/worker.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/worker.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/csp/worker.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/csp/worker.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -2,13 +2,11 @@ const SJS = "http://mochi.test:8888/tests/dom/security/test/csp/worker.sjs"; -function createFetchWorker(url) -{ - return `fetch("${url}");`; +function createFetchWorker(url) { + return `fetch("${url}");`; } -function createXHRWorker(url) -{ +function createXHRWorker(url) { return ` try { var xhr = new XMLHttpRequest(); @@ -18,8 +16,7 @@ `; } -function createImportScriptsWorker(url) -{ +function createImportScriptsWorker(url) { return ` try { importScripts("${url}"); @@ -27,14 +24,12 @@ `; } -function createChildWorkerURL(params) -{ +function createChildWorkerURL(params) { let url = SJS + "?" + params.toString(); return `new Worker("${url}");`; } -function createChildWorkerBlob(params) -{ +function createChildWorkerBlob(params) { let url = SJS + "?" + params.toString(); return ` try { @@ -48,8 +43,7 @@ `; } -function handleRequest(request, response) -{ +function handleRequest(request, response) { let params = new URLSearchParams(request.queryString); let id = params.get("id"); @@ -61,8 +55,12 @@ response.setHeader("Content-Type", "application/javascript"); // Deliver the CSP policy encoded in the URL - if(params.has("csp")) { - response.setHeader("Content-Security-Policy", unescape(params.get("csp")), false); + if (params.has("csp")) { + response.setHeader( + "Content-Security-Policy", + unescape(params.get("csp")), + false + ); } if (child) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/general/file_block_script_wrong_mime_server.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/general/file_block_script_wrong_mime_server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/general/file_block_script_wrong_mime_server.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/general/file_block_script_wrong_mime_server.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -27,7 +27,11 @@ response.write(WORKER); break; case "worker-import": - response.write(`importScripts("file_block_script_wrong_mime_server.sjs?type=script&mime=${query.get("mime")}");`); + response.write( + `importScripts("file_block_script_wrong_mime_server.sjs?type=script&mime=${query.get( + "mime" + )}");` + ); response.write(WORKER); break; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/general/file_block_subresource_redir_to_data.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/general/file_block_subresource_redir_to_data.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/general/file_block_subresource_redir_to_data.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/general/file_block_subresource_redir_to_data.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -1,22 +1,30 @@ "use strict"; let SCRIPT_DATA = "alert('this alert should be blocked');"; -let WORKER_DATA = "onmessage = function(event) { postMessage('worker-loaded'); }"; +let WORKER_DATA = + "onmessage = function(event) { postMessage('worker-loaded'); }"; -function handleRequest(request, response) -{ +function handleRequest(request, response) { const query = request.queryString; response.setHeader("Cache-Control", "no-cache", false); response.setStatusLine("1.1", 302, "Found"); if (query === "script" || query === "modulescript") { - response.setHeader("Location", "data:text/javascript," + escape(SCRIPT_DATA), false); + response.setHeader( + "Location", + "data:text/javascript," + escape(SCRIPT_DATA), + false + ); return; } if (query === "worker") { - response.setHeader("Location", "data:text/javascript," + escape(WORKER_DATA), false); + response.setHeader( + "Location", + "data:text/javascript," + escape(WORKER_DATA), + false + ); return; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/general/file_block_toplevel_data_redirect.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/general/file_block_toplevel_data_redirect.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/general/file_block_toplevel_data_redirect.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/general/file_block_toplevel_data_redirect.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -4,8 +4,7 @@ var DATA_URI = "toplevel data: URI navigations after redirect should be blocked"; -function handleRequest(request, response) -{ +function handleRequest(request, response) { // avoid confusing cache behaviors response.setHeader("Cache-Control", "no-cache", false); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/general/file_cache_splitting_isloaded.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/general/file_cache_splitting_isloaded.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/general/file_cache_splitting_isloaded.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/general/file_cache_splitting_isloaded.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -32,5 +32,4 @@ queryResponse.write("1"); queryResponse.finish(); }); - return; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/general/file_contentpolicytype_targeted_link_iframe.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/general/file_contentpolicytype_targeted_link_iframe.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/general/file_contentpolicytype_targeted_link_iframe.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/general/file_contentpolicytype_targeted_link_iframe.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -22,8 +22,7 @@ `; -function handleRequest(request, response) -{ +function handleRequest(request, response) { // avoid confusing cache behaviors response.setHeader("Cache-Control", "no-cache", false); response.setHeader("Content-Type", "text/html", false); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/general/file_framing_error_pages.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/general/file_framing_error_pages.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/general/file_framing_error_pages.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/general/file_framing_error_pages.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -1,7 +1,6 @@ "use strict"; function handleRequest(request, response) { - response.setHeader("Cache-Control", "no-cache", false); response.setHeader("Content-Type", "text/html", false); @@ -13,7 +12,11 @@ } if (query === "csp") { - response.setHeader("content-security-policy", "frame-ancestors 'none'", false); + response.setHeader( + "content-security-policy", + "frame-ancestors 'none'", + false + ); response.write("csp test loaded"); return; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/general/file_gpc_server.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/general/file_gpc_server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/general/file_gpc_server.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/general/file_gpc_server.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -8,8 +8,7 @@ if (gpc === "1") { response.write("true"); - } - else { + } else { response.write("false"); } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/general/file_nosniff_navigation_garbage.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/general/file_nosniff_navigation_garbage.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/general/file_nosniff_navigation_garbage.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/general/file_nosniff_navigation_garbage.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -3,31 +3,30 @@ // small red image const IMG = atob( "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" + - "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="); + "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" +); -function getSniffableContent(selector){ - switch(selector){ - case "xml": - return ``; - case "html": - return ` Test test `; - case 'js': - return `` - case "css": - return `*{ color: pink !important; }`; - case 'json': +function getSniffableContent(selector) { + switch (selector) { + case "xml": + return ``; + case "html": + return ` Test test `; + case "js": + return ``; + case "css": + return `*{ color: pink !important; }`; + case "json": return `{ 'test':'yes' }`; - case 'img': + case "img": return IMG; } return "Basic UTF-8 Text"; } -function handleRequest(request, response) -{ +function handleRequest(request, response) { // avoid confusing cache behaviors - response.setHeader('X-Content-Type-Options', 'nosniff'); // Disable Sniffing - response.setHeader("Content-Type","garbage/garbage"); // Try Browser to force sniffing. + response.setHeader("X-Content-Type-Options", "nosniff"); // Disable Sniffing + response.setHeader("Content-Type", "garbage/garbage"); // Try Browser to force sniffing. response.write(getSniffableContent(request.queryString)); - return; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/general/file_nosniff_navigation_mismatch.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/general/file_nosniff_navigation_mismatch.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/general/file_nosniff_navigation_mismatch.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/general/file_nosniff_navigation_mismatch.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -3,31 +3,30 @@ // small red image const IMG = atob( "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" + - "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="); + "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" +); -function getSniffableContent(selector){ - switch(selector){ - case "xml": - return ``; - case "html": - return ` Test test `; - case 'js': - return `` - case "css": - return `*{ color: pink !important; }`; - case 'json': +function getSniffableContent(selector) { + switch (selector) { + case "xml": + return ``; + case "html": + return ` Test test `; + case "js": + return ``; + case "css": + return `*{ color: pink !important; }`; + case "json": return `{ 'test':'yes' }`; - case 'img': + case "img": return IMG; } return "Basic UTF-8 Text"; } -function handleRequest(request, response) -{ +function handleRequest(request, response) { // avoid confusing cache behaviors - response.setHeader('X-Content-Type-Options', 'nosniff'); // Disable Sniffing - response.setHeader("Content-Type","image/png"); // Send a wrong mime type + response.setHeader("X-Content-Type-Options", "nosniff"); // Disable Sniffing + response.setHeader("Content-Type", "image/png"); // Send a wrong mime type response.write(getSniffableContent(request.queryString)); - return; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/general/file_nosniff_navigation.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/general/file_nosniff_navigation.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/general/file_nosniff_navigation.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/general/file_nosniff_navigation.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -3,30 +3,28 @@ // small red image const IMG = atob( "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" + - "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="); + "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" +); -function getSniffableContent(selector){ - switch(selector){ - case "xml": - return ``; - case "html": - return ` Test test `; - case "css": - return `*{ color: pink !important; }`; - case 'json': +function getSniffableContent(selector) { + switch (selector) { + case "xml": + return ``; + case "html": + return ` Test test `; + case "css": + return `*{ color: pink !important; }`; + case "json": return `{ 'test':'yes' }`; - case 'img': + case "img": return IMG; } return "Basic UTF-8 Text"; } -function handleRequest(request, response) -{ +function handleRequest(request, response) { // avoid confusing cache behaviors - response.setHeader('X-Content-Type-Options', 'nosniff'); // Disable Sniffing - response.setHeader("Content-Type","*/*"); // Try Browser to force sniffing. + response.setHeader("X-Content-Type-Options", "nosniff"); // Disable Sniffing + response.setHeader("Content-Type", "*/*"); // Try Browser to force sniffing. response.write(getSniffableContent(request.queryString)); - return; } - diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/general/file_nosniff_testserver.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/general/file_nosniff_testserver.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/general/file_nosniff_testserver.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/general/file_nosniff_testserver.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -7,7 +7,8 @@ // small red image const IMG = atob( "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" + - "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="); + "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" +); function handleRequest(request, response) { const query = new URLSearchParams(request.queryString); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/general/file_same_site_cookies_about.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/general/file_same_site_cookies_about.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/general/file_same_site_cookies_about.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/general/file_same_site_cookies_about.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -3,19 +3,22 @@ // small red image const IMG_BYTES = atob( "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" + - "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="); + "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" +); -const IFRAME_INC = - ``; +const IFRAME_INC = ``; -function handleRequest(request, response) -{ +function handleRequest(request, response) { // avoid confusing cache behaviors response.setHeader("Cache-Control", "no-cache", false); // using startsWith and discard the math random if (request.queryString.startsWith("setSameSiteCookie")) { - response.setHeader("Set-Cookie", "myKey=mySameSiteAboutCookie; samesite=strict", true); + response.setHeader( + "Set-Cookie", + "myKey=mySameSiteAboutCookie; samesite=strict", + true + ); response.setHeader("Content-Type", "image/png"); response.write(IMG_BYTES); return; @@ -42,22 +45,27 @@ // inclusion tets if (request.queryString.includes("loadsrcdocframeInc")) { - response.write(""); + response.write(''); return; } if (request.queryString.includes("loadblankframeInc")) { - let FRAME = ` + let FRAME = + ` \n\ + "\n\ \n\ - '; + " + ); } // Creates the following test cases for the specified referrer // policy combination: // with referrer function createTest(aPolicy, aImgPolicy, aName) { - var headString = ''; + var headString = ""; if (aPolicy) { headString += ''; } - headString += ''; + headString += ""; return createTestPage(headString, aImgPolicy, aName); } @@ -50,249 +67,271 @@ // testing regular load img with referrer policy // speculative parser should not kick in here function createTest2(aImgPolicy, name) { - return createTestPage('', aImgPolicy, name); + return createTestPage("", aImgPolicy, name); } function createTest3(aImgPolicy1, aImgPolicy2, aImgPolicy3, aName) { - return '\n\ + return ( + '\n\ \n\ \n\ - \n\ - \n\ - \n\ + \n\ + \n\ + \n\ \n\ + "\n\ \n\ - '; + " + ); } function createTestPage2(aHead, aPolicy, aName) { - return '\n\ - '+ - aHead + - '\n\ - \n\ + return ( + "\n\ + " + + aHead + + '\n\ + \n\ \n\ + "\n\ \n\ - '; + " + ); } function createTestPage3(aHead, aPolicy, aName) { - return '\n\ - '+ - aHead + - '\n\ - \n\ + "\n\ \n\ - '; + " + ); } function createTestPage4(aHead, aPolicy, aName) { - return '\n\ - '+ - aHead + - '\n\ - \n\ + "\n\ \n\ - '; + " + ); } function createSetAttributeTest1(aPolicy, aImgPolicy, aName) { - var headString = ''; + var headString = ""; headString += ''; - headString += ''; + headString += ""; return createTestPage3(headString, aImgPolicy, aName); } function createSetAttributeTest2(aPolicy, aImgPolicy, aName) { - var headString = ''; + var headString = ""; headString += ''; - headString += ''; + headString += ""; return createTestPage4(headString, aImgPolicy, aName); } - function createTest4(aPolicy, aName) { - var headString = ''; + var headString = ""; headString += ''; - headString += ''; + headString += ""; return createTestPage2(headString, aPolicy, aName); } function createTest5(aPolicy, aName) { - var headString = ''; + var headString = ""; headString += ''; return createTestPage2(headString, aPolicy, aName); } function handleRequest(request, response) { - var sharedKey = 'img_referrer_testserver.sjs'; - var params = request.queryString.split('&'); - var action = params[0].split('=')[1]; + var sharedKey = "img_referrer_testserver.sjs"; + var params = request.queryString.split("&"); + var action = params[0].split("=")[1]; - response.setHeader('Cache-Control', 'no-cache', false); - response.setHeader('Content-Type', 'text/html; charset=utf-8', false); + response.setHeader("Cache-Control", "no-cache", false); + response.setHeader("Content-Type", "text/html; charset=utf-8", false); - if (action === 'resetState') { + if (action === "resetState") { var state = getSharedState(sharedKey); state = {}; setSharedState(sharedKey, JSON.stringify(state)); response.write(""); return; } - if (action === 'test') { + if (action === "test") { // ?action=test&policy=origin&name=name&content=content - var policy = params[1].split('=')[1]; - var name = params[2].split('=')[1]; - var content = params[3].split('=')[1]; + var policy = params[1].split("=")[1]; + var name = params[2].split("=")[1]; + var content = params[3].split("=")[1]; var result = getSharedState(sharedKey); - if (result === '') { + if (result === "") { result = {}; } else { result = JSON.parse(result); } - if (!result["tests"]) { - result["tests"] = {}; + if (!result.tests) { + result.tests = {}; } var referrerLevel = "none"; - var test = {} - if (request.hasHeader('Referer')) { - let referrer = request.getHeader('Referer'); + var test = {}; + if (request.hasHeader("Referer")) { + let referrer = request.getHeader("Referer"); if (referrer.indexOf("img_referrer_testserver") > 0) { referrerLevel = "full"; } else if (referrer == "http://mochi.test:8888/") { referrerLevel = "origin"; } - test.referrer = request.getHeader('Referer'); + test.referrer = request.getHeader("Referer"); } else { - test.referrer = ''; + test.referrer = ""; } test.policy = referrerLevel; test.expected = policy; - result["tests"][name] = test; + result.tests[name] = test; setSharedState(sharedKey, JSON.stringify(result)); - if (content === 'image') { + if (content === "image") { response.setHeader("Content-Type", "image/png"); response.write(IMG_BYTES); } return; } - if (action === 'get-test-results') { + if (action === "get-test-results") { // ?action=get-result response.write(getSharedState(sharedKey)); return; } - if (action === 'generate-img-policy-test') { + if (action === "generate-img-policy-test") { // ?action=generate-img-policy-test&imgPolicy=b64-encoded-string&name=name&policy=b64-encoded-string - var imgPolicy = unescape(params[1].split('=')[1]); - var name = unescape(params[2].split('=')[1]); - var metaPolicy = ''; + var imgPolicy = unescape(params[1].split("=")[1]); + var name = unescape(params[2].split("=")[1]); + var metaPolicy = ""; if (params[3]) { - metaPolicy = params[3].split('=')[1]; + metaPolicy = params[3].split("=")[1]; } response.write(createTest(metaPolicy, imgPolicy, name)); return; } - if (action === 'generate-img-policy-test2') { + if (action === "generate-img-policy-test2") { // ?action=generate-img-policy-test2&imgPolicy=b64-encoded-string&name=name - var imgPolicy = unescape(params[1].split('=')[1]); - var name = unescape(params[2].split('=')[1]); + var imgPolicy = unescape(params[1].split("=")[1]); + var name = unescape(params[2].split("=")[1]); response.write(createTest2(imgPolicy, name)); return; } - if (action === 'generate-img-policy-test3') { + if (action === "generate-img-policy-test3") { // ?action=generate-img-policy-test3&imgPolicy1=b64-encoded-string&imgPolicy2=b64-encoded-string&imgPolicy3=b64-encoded-string&name=name - var imgPolicy1 = unescape(params[1].split('=')[1]); - var imgPolicy2 = unescape(params[2].split('=')[1]); - var imgPolicy3 = unescape(params[3].split('=')[1]); - var name = unescape(params[4].split('=')[1]); + var imgPolicy1 = unescape(params[1].split("=")[1]); + var imgPolicy2 = unescape(params[2].split("=")[1]); + var imgPolicy3 = unescape(params[3].split("=")[1]); + var name = unescape(params[4].split("=")[1]); response.write(createTest3(imgPolicy1, imgPolicy2, imgPolicy3, name)); return; } - if (action === 'generate-img-policy-test4') { + if (action === "generate-img-policy-test4") { // ?action=generate-img-policy-test4&imgPolicy=b64-encoded-string&name=name - var policy = unescape(params[1].split('=')[1]); - var name = unescape(params[2].split('=')[1]); + var policy = unescape(params[1].split("=")[1]); + var name = unescape(params[2].split("=")[1]); response.write(createTest4(policy, name)); return; } - if (action === 'generate-img-policy-test5') { + if (action === "generate-img-policy-test5") { // ?action=generate-img-policy-test5&policy=b64-encoded-string&name=name - var policy = unescape(params[1].split('=')[1]); - var name = unescape(params[2].split('=')[1]); + var policy = unescape(params[1].split("=")[1]); + var name = unescape(params[2].split("=")[1]); response.write(createTest5(policy, name)); return; } - if (action === 'generate-setAttribute-test1') { + if (action === "generate-setAttribute-test1") { // ?action=generate-setAttribute-test1&policy=b64-encoded-string&name=name - var imgPolicy = unescape(params[1].split('=')[1]); - var policy = unescape(params[2].split('=')[1]); - var name = unescape(params[3].split('=')[1]); + var imgPolicy = unescape(params[1].split("=")[1]); + var policy = unescape(params[2].split("=")[1]); + var name = unescape(params[3].split("=")[1]); response.write(createSetAttributeTest1(policy, imgPolicy, name)); return; } - if (action === 'generate-setAttribute-test2') { + if (action === "generate-setAttribute-test2") { // ?action=generate-setAttribute-test2&policy=b64-encoded-string&name=name - var imgPolicy = unescape(params[1].split('=')[1]); - var policy = unescape(params[2].split('=')[1]); - var name = unescape(params[3].split('=')[1]); + var imgPolicy = unescape(params[1].split("=")[1]); + var policy = unescape(params[2].split("=")[1]); + var name = unescape(params[3].split("=")[1]); response.write(createSetAttributeTest2(policy, imgPolicy, name)); return; } - response.write("I don't know action "+action); - return; + response.write("I don't know action " + action); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/referrer-policy/referrer_header.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/referrer-policy/referrer_header.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/referrer-policy/referrer_header.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/referrer-policy/referrer_header.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -1,6 +1,6 @@ - function handleRequest(request, response) { response.setHeader("Referrer-Policy", "same-origin"); - response.write('Loaded'); + response.write( + 'Loaded' + ); } - diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/referrer-policy/referrer_page.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/referrer-policy/referrer_page.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/referrer-policy/referrer_page.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/referrer-policy/referrer_page.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -6,7 +6,6 @@ let metaReferrerPolicy = params.get("meta") || ""; let showReferrer = params.has("show"); - if (referrerPolicyHeader) { response.setHeader("Referrer-Policy", referrerPolicyHeader, false); } @@ -20,7 +19,9 @@ if (showReferrer) { if (request.hasHeader("Referer")) { - resultString = `Referer Header: ${request.getHeader("Referer")}`; + resultString = `Referer Header: ${request.getHeader( + "Referer" + )}`; } else { resultString = `Referer Header: `; } @@ -35,5 +36,6 @@ ${resultString} - `); + ` + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/referrer-policy/referrer_testserver.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/referrer-policy/referrer_testserver.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/referrer-policy/referrer_testserver.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/referrer-policy/referrer_testserver.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -1,14 +1,14 @@ /* -* Test server for iframe, anchor, and area referrer attributes. -* https://bugzilla.mozilla.org/show_bug.cgi?id=1175736 -* Also server for further referrer tests such as redirecting tests -* bug 1174913, bug 1175736, bug 1184781 -*/ + * Test server for iframe, anchor, and area referrer attributes. + * https://bugzilla.mozilla.org/show_bug.cgi?id=1175736 + * Also server for further referrer tests such as redirecting tests + * bug 1174913, bug 1175736, bug 1184781 + */ Components.utils.importGlobalProperties(["URLSearchParams"]); const SJS = "referrer_testserver.sjs?"; const SJS_PATH = "/tests/dom/security/test/referrer-policy/"; -const BASE_ORIGIN = "example.com" +const BASE_ORIGIN = "example.com"; const BASE_URL = BASE_ORIGIN + SJS_PATH + SJS; const SHARED_KEY = SJS; const SAME_ORIGIN = "mochi.test:8888" + SJS_PATH + SJS; @@ -16,29 +16,57 @@ const IMG_BYTES = atob( "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" + - "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="); + "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" +); -function createTestUrl(aPolicy, aAction, aName, aType, aSchemeFrom, aSchemeTo, - crossOrigin, referrerPolicyHeader) { +function createTestUrl( + aPolicy, + aAction, + aName, + aType, + aSchemeFrom, + aSchemeTo, + crossOrigin, + referrerPolicyHeader +) { var schemeTo = aSchemeTo || "http"; var schemeFrom = aSchemeFrom || "http"; var rpHeader = referrerPolicyHeader || ""; var url = schemeTo + "://"; - url += (crossOrigin ? CROSS_ORIGIN_URL : BASE_URL); + url += crossOrigin ? CROSS_ORIGIN_URL : BASE_URL; url += - "ACTION=" + aAction + "&" + - "policy=" + aPolicy + "&" + - "NAME=" + aName + "&" + - "type=" + aType + "&" + - "RP_HEADER=" + rpHeader + "&" + - "SCHEME_FROM=" + schemeFrom; + "ACTION=" + + aAction + + "&" + + "policy=" + + aPolicy + + "&" + + "NAME=" + + aName + + "&" + + "type=" + + aType + + "&" + + "RP_HEADER=" + + rpHeader + + "&" + + "SCHEME_FROM=" + + schemeFrom; return url; - } +} // test page using iframe referrer attribute // if aParams are set this creates a test where the iframe url is a redirect -function createIframeTestPageUsingRefferer(aMetaPolicy, aAttributePolicy, aNewAttributePolicy, aName, aParams, - aSchemeFrom, aSchemeTo, aChangingMethod) { +function createIframeTestPageUsingRefferer( + aMetaPolicy, + aAttributePolicy, + aNewAttributePolicy, + aName, + aParams, + aSchemeFrom, + aSchemeTo, + aChangingMethod +) { var metaString = ""; if (aMetaPolicy) { metaString = ``; @@ -49,14 +77,23 @@ } else if (aChangingMethod === "property") { changeString = `document.getElementById("myframe").referrerPolicy = "${aNewAttributePolicy}"`; } - var iFrameString = ``; + var iFrameString = ``; var iframeUrl = ""; if (aParams) { aParams.delete("ACTION"); aParams.append("ACTION", "redirectIframe"); iframeUrl = "http://" + CROSS_ORIGIN_URL + aParams.toString(); } else { - iframeUrl = createTestUrl(aAttributePolicy, "test", aName, "iframe", aSchemeFrom, aSchemeTo); + iframeUrl = createTestUrl( + aAttributePolicy, + "test", + aName, + "iframe", + aSchemeFrom, + aSchemeTo + ); } return ` @@ -79,20 +116,62 @@ `; } -function buildAnchorString(aMetaPolicy, aReferrerPolicy, aName, aRelString, aSchemeFrom, aSchemeTo){ +function buildAnchorString( + aMetaPolicy, + aReferrerPolicy, + aName, + aRelString, + aSchemeFrom, + aSchemeTo +) { if (aReferrerPolicy) { - return `${aReferrerPolicy}`; - } - return `link`; + return `${aReferrerPolicy}`; + } + return `link`; } -function buildAreaString(aMetaPolicy, aReferrerPolicy, aName, aRelString, aSchemeFrom, aSchemeTo){ +function buildAreaString( + aMetaPolicy, + aReferrerPolicy, + aName, + aRelString, + aSchemeFrom, + aSchemeTo +) { var result = `image`; result += ``; if (aReferrerPolicy) { - result += `theArea`; + result += `theArea`; } else { - result += `theArea`; + result += `theArea`; } result += ``; @@ -100,7 +179,17 @@ } // test page using anchor or area referrer attribute -function createAETestPageUsingRefferer(aMetaPolicy, aAttributePolicy, aNewAttributePolicy, aName, aRel, aStringBuilder, aSchemeFrom, aSchemeTo, aChangingMethod) { +function createAETestPageUsingRefferer( + aMetaPolicy, + aAttributePolicy, + aNewAttributePolicy, + aName, + aRel, + aStringBuilder, + aSchemeFrom, + aSchemeTo, + aChangingMethod +) { var metaString = ""; if (aMetaPolicy) { metaString = ``; @@ -115,7 +204,14 @@ if (aRel) { relString = `rel="noreferrer"`; } - var elementString = aStringBuilder(aMetaPolicy, aAttributePolicy, aName, relString, aSchemeFrom, aSchemeTo); + var elementString = aStringBuilder( + aMetaPolicy, + aAttributePolicy, + aName, + relString, + aSchemeFrom, + aSchemeTo + ); return ` @@ -133,13 +229,26 @@ } // test page using anchor target=_blank rel=noopener -function createTargetBlankRefferer(aMetaPolicy, aName, aSchemeFrom, - aSchemeTo, aRpHeader) { +function createTargetBlankRefferer( + aMetaPolicy, + aName, + aSchemeFrom, + aSchemeTo, + aRpHeader +) { var metaString = ""; if (aMetaPolicy) { metaString = ``; } - var elementString = `link`; + var elementString = `link`; return ` @@ -161,7 +270,9 @@ function createRedirectImgTestCase(aParams, aAttributePolicy) { var metaString = ""; if (aParams.has("META_POLICY")) { - metaString = ``; + metaString = ``; } aParams.delete("ACTION"); aParams.append("ACTION", "redirectImg"); @@ -175,7 +286,9 @@ Test referrer policies on redirect (img) - + '); + response.write( + '' + ); return; } - if (action === "redirectImg"){ + if (action === "redirectImg") { params.delete("ACTION"); params.append("ACTION", "test"); params.append("type", "img"); // 302 found, 301 Moved Permanently, 303 See Other, 307 Temporary Redirect response.setStatusLine("1.1", 302, "found"); - response.setHeader("Location", "http://" + CROSS_ORIGIN_URL + params.toString(), false); + response.setHeader( + "Location", + "http://" + CROSS_ORIGIN_URL + params.toString(), + false + ); return; } - if (action === "redirectIframe"){ + if (action === "redirectIframe") { params.delete("ACTION"); params.append("ACTION", "test"); params.append("type", "iframe"); // 302 found, 301 Moved Permanently, 303 See Other, 307 Temporary Redirect response.setStatusLine("1.1", 302, "found"); - response.setHeader("Location", "http://" + CROSS_ORIGIN_URL + params.toString(), false); + response.setHeader( + "Location", + "http://" + CROSS_ORIGIN_URL + params.toString(), + false + ); return; } if (action === "test") { @@ -322,7 +503,7 @@ result = result ? JSON.parse(result) : {}; var referrerLevel = "none"; - var test = {} + var test = {}; if (request.hasHeader("Referer")) { var referrer = request.getHeader("Referer"); if (referrer.indexOf("referrer_testserver") > 0) { @@ -375,8 +556,20 @@ var name = params.get("NAME"); // anchor & area - var _getPage = createAETestPageUsingRefferer.bind(null, metaPolicy, attributePolicy, newAttributePolicy, name, rel); - var _getAnchorPage = _getPage.bind(null, buildAnchorString, schemeFrom, schemeTo); + var _getPage = createAETestPageUsingRefferer.bind( + null, + metaPolicy, + attributePolicy, + newAttributePolicy, + name, + rel + ); + var _getAnchorPage = _getPage.bind( + null, + buildAnchorString, + schemeFrom, + schemeTo + ); var _getAreaPage = _getPage.bind(null, buildAreaString, schemeFrom, schemeTo); // aMetaPolicy, aAttributePolicy, aNewAttributePolicy, aName, aChangingMethod, aStringBuilder @@ -405,13 +598,29 @@ return; } if (action === "generate-anchor-target-blank-policy-test") { - response.write(createTargetBlankRefferer(metaPolicy, name, schemeFrom, schemeTo, referrerPolicyHeader)); + response.write( + createTargetBlankRefferer( + metaPolicy, + name, + schemeFrom, + schemeTo, + referrerPolicyHeader + ) + ); return; } // iframe - _getPage = createIframeTestPageUsingRefferer.bind(null, metaPolicy, attributePolicy, newAttributePolicy, name, "", - schemeFrom, schemeTo); + _getPage = createIframeTestPageUsingRefferer.bind( + null, + metaPolicy, + attributePolicy, + newAttributePolicy, + name, + "", + schemeFrom, + schemeTo + ); // aMetaPolicy, aAttributePolicy, aNewAttributePolicy, aName, aChangingMethod if (action === "generate-iframe-policy-test") { @@ -433,12 +642,28 @@ return; } if (action === "generate-iframe-redirect-policy-test") { - response.write(createIframeTestPageUsingRefferer(metaPolicy, attributePolicy, newAttributePolicy, name, params, - schemeFrom, schemeTo)); + response.write( + createIframeTestPageUsingRefferer( + metaPolicy, + attributePolicy, + newAttributePolicy, + name, + params, + schemeFrom, + schemeTo + ) + ); return; } - var _getPage = createLinkPageUsingRefferer.bind(null, metaPolicy, attributePolicy, newAttributePolicy, name, rel); + var _getPage = createLinkPageUsingRefferer.bind( + null, + metaPolicy, + attributePolicy, + newAttributePolicy, + name, + rel + ); var _getLinkPage = _getPage.bind(null, buildLinkString, schemeFrom, schemeTo); // link @@ -456,10 +681,11 @@ } if (action === "generate-fetch-user-control-policy-test") { - response.write(createFetchUserControlRPTestCase(name, schemeFrom, schemeTo, crossOrigin)); + response.write( + createFetchUserControlRPTestCase(name, schemeFrom, schemeTo, crossOrigin) + ); return; } response.write("I don't know action " + action); - return; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/sec-fetch/file_no_cache.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/sec-fetch/file_no_cache.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/security/test/sec-fetch/file_no_cache.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/security/test/sec-fetch/file_no_cache.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -1,4 +1,4 @@ -const MESSAGE_PAGE = function (msg) { +const MESSAGE_PAGE = function(msg) { return ` `); } else { - let baseURI = "https://example.org/" + request.path.replace(/[a-z-]*\.sjs/, "mimeme.sjs?type="); + let baseURI = + "https://example.org/" + + request.path.replace(/[a-z-]*\.sjs/, "mimeme.sjs?type="); response.write(` diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/tests/browser/test_largeAllocationFormSubmit.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/tests/browser/test_largeAllocationFormSubmit.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/tests/browser/test_largeAllocationFormSubmit.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/tests/browser/test_largeAllocationFormSubmit.sjs 2021-10-20 19:28:20.000000000 +0000 @@ -1,6 +1,8 @@ -const BinaryInputStream = Components.Constructor("@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", - "setInputStream"); +const BinaryInputStream = Components.Constructor( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); function handleRequest(request, response) { response.setHeader("Large-Allocation", "0", false); @@ -16,8 +18,9 @@ var avail; var bytes = []; - while ((avail = body.available()) > 0) + while ((avail = body.available()) > 0) { Array.prototype.push.apply(bytes, body.readByteArray(avail)); + } var data = String.fromCharCode.apply(null, bytes); response.bodyOutputStream.write(data, data.length); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/tests/mochitest/beacon/beacon-handler.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/tests/mochitest/beacon/beacon-handler.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/tests/mochitest/beacon/beacon-handler.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/tests/mochitest/beacon/beacon-handler.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -5,17 +5,23 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ const CC = Components.Constructor; -const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", - "setInputStream"); +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); -function DEBUG(str) -{ +function DEBUG(str) { // dump("********** " + str + "\n"); } function setOurState(data) { - x = { data: data, QueryInterface: function(iid) { return this } }; + x = { + data, + QueryInterface(iid) { + return this; + }, + }; x.wrappedJSObject = x; setObjectState("beacon-handler", x); DEBUG("our state is " + data); @@ -43,8 +49,8 @@ DEBUG("GET was sending : " + data + "\n"); DEBUG("GET was sending : " + mimetype + "\n"); var result = { - "data": data, - "mimetype": mimetype, + data, + mimetype, }; response.write(JSON.stringify(result)); setOurState(null); @@ -54,26 +60,30 @@ DEBUG(" ------------ GET --------------- "); response.setHeader("Content-Type", "application/json", false); switch (request.queryString) { - case "getLastBeaconCors": - // Allow CORS responses of the last beacon - var originHeader = request.getHeader("origin"); - response.setHeader("Access-Control-Allow-Headers", "content-type", false); - response.setHeader("Access-Control-Allow-Methods", "POST, GET", false); - response.setHeader("Access-Control-Allow-Origin", originHeader, false); - response.setHeader("Access-Control-Allow-Credentials", "true", false); - case "getLastBeacon": - var state = getOurState(); - if (state === "unblocked") { - finishControlResponse(response); - } else { - DEBUG("GET has arrived, but POST has not, blocking response!"); - setOurState(response); - response.processAsync(); - } - break; - default: - response.setStatusLine(request.httpVersion, 400, "Bad Request"); - break; + case "getLastBeaconCors": + // Allow CORS responses of the last beacon + var originHeader = request.getHeader("origin"); + response.setHeader( + "Access-Control-Allow-Headers", + "content-type", + false + ); + response.setHeader("Access-Control-Allow-Methods", "POST, GET", false); + response.setHeader("Access-Control-Allow-Origin", originHeader, false); + response.setHeader("Access-Control-Allow-Credentials", "true", false); + case "getLastBeacon": + var state = getOurState(); + if (state === "unblocked") { + finishControlResponse(response); + } else { + DEBUG("GET has arrived, but POST has not, blocking response!"); + setOurState(response); + response.processAsync(); + } + break; + default: + response.setStatusLine(request.httpVersion, 400, "Bad Request"); + break; } return; } @@ -89,9 +99,11 @@ } var data = ""; - for (var i=0; i < bytes.length; i++) { + for (var i = 0; i < bytes.length; i++) { // We are only passing strings at this point. - if (bytes[i] < 32) continue; + if (bytes[i] < 32) { + continue; + } var charcode = String.fromCharCode(bytes[i]); data += charcode; } @@ -103,7 +115,6 @@ // check to see if this is form data. if (mimetype.indexOf("multipart/form-data") != -1) { - // trim the mime type to make testing easier. mimetype = "multipart/form-data"; // Extract only the form-data name. @@ -118,10 +129,10 @@ setState("beaconMimetype", mimetype); response.setHeader("Content-Type", "text/plain", false); - response.write('ok'); + response.write("ok"); var blockedResponse = getOurState(); - if (typeof(blockedResponse) == "object" && blockedResponse) { + if (typeof blockedResponse == "object" && blockedResponse) { DEBUG("GET is already pending, finishing!"); finishControlResponse(blockedResponse); blockedResponse.finish(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/tests/mochitest/beacon/beacon-originheader-handler.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/tests/mochitest/beacon/beacon-originheader-handler.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/tests/mochitest/beacon/beacon-originheader-handler.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/tests/mochitest/beacon/beacon-originheader-handler.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -3,8 +3,7 @@ * Bug 1280692 - navigator.sendBeacon() should not send origin header */ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setHeader("Cache-Control", "no-cache", false); response.setHeader("Content-Type", "text/plain", false); @@ -29,13 +28,11 @@ var header = "reset"; try { header = request.getHeader("origin"); - } - catch(e) { + } catch (e) { header = "no-header"; } setState("originHeader", header); - // if there is an xhr-request waiting, return the header now. getObjectState("xhr-response", function(xhrResponse) { if (!xhrResponse) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/tests/mochitest/beacon/beacon-preflight-handler.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/tests/mochitest/beacon/beacon-preflight-handler.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/tests/mochitest/beacon/beacon-preflight-handler.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/tests/mochitest/beacon/beacon-preflight-handler.sjs 2021-10-20 19:28:20.000000000 +0000 @@ -1,39 +1,38 @@ -function handleRequest(request, response) -{ - response.setHeader("Cache-Control", "no-cache, must-revalidate", false); - - if (request.queryString === "verify") { - var preflightState = getState("preflight"); - response.write(preflightState === "done" ? "green" : "red"); - return; - } - - var originHeader = request.getHeader("origin"); - response.setHeader("Access-Control-Allow-Headers", "content-type", false); - response.setHeader("Access-Control-Allow-Methods", "POST, GET", false); - response.setHeader("Access-Control-Allow-Origin", originHeader, false); - response.setHeader("Access-Control-Allow-Credentials", "true", false); - - if (request.queryString === "beacon") { - if (request.method == "OPTIONS") { - setState("preflight", "done"); - response.setStatusLine(null, 200, "OK"); - return; - } - response.setStatusLine(null, 200, "OK"); - response.write("DONE"); - return; - } - - if (request.queryString === "fail") { - if (request.method == "OPTIONS") { - setState("preflight", "done"); - response.setStatusLine(null, 400, "Bad Request"); - return; - } - setState("preflight", "oops"); - response.setStatusLine(null, 200, "OK"); - response.write("DONE"); - return; - } -} +function handleRequest(request, response) { + response.setHeader("Cache-Control", "no-cache, must-revalidate", false); + + if (request.queryString === "verify") { + var preflightState = getState("preflight"); + response.write(preflightState === "done" ? "green" : "red"); + return; + } + + var originHeader = request.getHeader("origin"); + response.setHeader("Access-Control-Allow-Headers", "content-type", false); + response.setHeader("Access-Control-Allow-Methods", "POST, GET", false); + response.setHeader("Access-Control-Allow-Origin", originHeader, false); + response.setHeader("Access-Control-Allow-Credentials", "true", false); + + if (request.queryString === "beacon") { + if (request.method == "OPTIONS") { + setState("preflight", "done"); + response.setStatusLine(null, 200, "OK"); + return; + } + response.setStatusLine(null, 200, "OK"); + response.write("DONE"); + return; + } + + if (request.queryString === "fail") { + if (request.method == "OPTIONS") { + setState("preflight", "done"); + response.setStatusLine(null, 400, "Bad Request"); + return; + } + setState("preflight", "oops"); + response.setStatusLine(null, 200, "OK"); + response.write("DONE"); + return; + } +} diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/tests/mochitest/beacon/beacon-redirect-handler.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/tests/mochitest/beacon/beacon-redirect-handler.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/tests/mochitest/beacon/beacon-redirect-handler.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/tests/mochitest/beacon/beacon-redirect-handler.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -1,47 +1,46 @@ -/* - * TestSever customized specifically for the needs of: - * Bug 1280692 - sendBeacon() should follow 30x redirect - * - * Here is a sequence of the test: - * [1] sendBeacon (identified by the queryString 'beacon') which gets redirected - * [2] redirected sendBeacon (identified by the queryString 'redirected') which - * updates the state idniciating that redirected sendBeacon succeeds. - * [3] xhr request (identified by the queryString 'verifyRedirectDidSucceed') - * which checks if the state was not changed from 'reset' to 'gree'. If the channel - * woulnd't be blocked correctly the redirected channel would set the state to 'red'. - * - */ - -function handleRequest(request, response) -{ - response.setHeader("Cache-Control", "no-cache, must-revalidate", false); - - // [Sequence 3] - if (request.queryString === "verifyRedirectDidSucceed") { - var redirectState = getState("redirectState"); - response.write(redirectState); - return; - } - - // [Sequence 1] - if (request.queryString === "beacon") { - setState("redirectState", "reset"); - var newLocation = - "http://mochi.test:8888/tests/dom/tests/mochitest/beacon/beacon-redirect-handler.sjs?redirected"; - response.setStatusLine("1.1", 302, "Found"); - response.setHeader("Location", newLocation, false); - return; - } - - // [Sequence 2] - if (request.queryString === "redirected") { - setState("redirectState", "green"); - response.setStatusLine(null, 200, "OK"); - return; - } - - // we should never get here, but just in case let's - // set the state to something unexpected - setState("redirectState", "red"); - response.setStatusLine(null, 200, "OK"); -} +/* + * TestSever customized specifically for the needs of: + * Bug 1280692 - sendBeacon() should follow 30x redirect + * + * Here is a sequence of the test: + * [1] sendBeacon (identified by the queryString 'beacon') which gets redirected + * [2] redirected sendBeacon (identified by the queryString 'redirected') which + * updates the state idniciating that redirected sendBeacon succeeds. + * [3] xhr request (identified by the queryString 'verifyRedirectDidSucceed') + * which checks if the state was not changed from 'reset' to 'gree'. If the channel + * woulnd't be blocked correctly the redirected channel would set the state to 'red'. + * + */ + +function handleRequest(request, response) { + response.setHeader("Cache-Control", "no-cache, must-revalidate", false); + + // [Sequence 3] + if (request.queryString === "verifyRedirectDidSucceed") { + var redirectState = getState("redirectState"); + response.write(redirectState); + return; + } + + // [Sequence 1] + if (request.queryString === "beacon") { + setState("redirectState", "reset"); + var newLocation = + "http://mochi.test:8888/tests/dom/tests/mochitest/beacon/beacon-redirect-handler.sjs?redirected"; + response.setStatusLine("1.1", 302, "Found"); + response.setHeader("Location", newLocation, false); + return; + } + + // [Sequence 2] + if (request.queryString === "redirected") { + setState("redirectState", "green"); + response.setStatusLine(null, 200, "OK"); + return; + } + + // we should never get here, but just in case let's + // set the state to something unexpected + setState("redirectState", "red"); + response.setStatusLine(null, 200, "OK"); +} diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/tests/mochitest/beacon/beacon-set-cookie.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/tests/mochitest/beacon/beacon-set-cookie.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/tests/mochitest/beacon/beacon-set-cookie.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/tests/mochitest/beacon/beacon-set-cookie.sjs 2021-10-20 19:28:20.000000000 +0000 @@ -1,6 +1,8 @@ -function handleRequest(request, response) -{ - response.setHeader("Set-Cookie", "cookie="+ request.host + "~" + Math.random()); +function handleRequest(request, response) { + response.setHeader( + "Set-Cookie", + "cookie=" + request.host + "~" + Math.random() + ); response.setHeader("Content-Type", "text/plain", false); response.setHeader("Cache-Control", "no-cache", false); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/tests/mochitest/bugs/bug289714.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/tests/mochitest/bugs/bug289714.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/tests/mochitest/bugs/bug289714.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/tests/mochitest/bugs/bug289714.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -2,12 +2,12 @@ // large enough to ensure that OnDataAvailable is called more than once (and so // the XHR will be triggered to send more than one "loading" event if desired). -function handleRequest(request, response) -{ +function handleRequest(request, response) { // Send 81920 bytes of こんにちは in Shift-JIS encoding, framed in XML. - let data = "" + - (new Array(1 << 13)).join("\x82\xb1\x82\xf1\x82\xc9\x82\xbf\x82\xcd") + - ""; + let data = + "" + + new Array(1 << 13).join("\x82\xb1\x82\xf1\x82\xc9\x82\xbf\x82\xcd") + + ""; response.processAsync(); response.setHeader("Content-Type", "text/xml", false); response.setHeader("Content-Length", "" + data.length, false); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/tests/mochitest/bugs/test_bug346659.html firefox-trunk-95.0~a1~hg20211020r596404/dom/tests/mochitest/bugs/test_bug346659.html --- firefox-trunk-95.0~a1~hg20211017r596111/dom/tests/mochitest/bugs/test_bug346659.html 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/tests/mochitest/bugs/test_bug346659.html 2021-10-20 19:28:19.000000000 +0000 @@ -192,12 +192,15 @@ is(location.host, "mochi.test:8888", "Unexpected host"); -var baseurl = window.location.href.replace(/mochi\.test:8888/, "example.com"); -wins[7] = window.open(r(baseurl, 'bug346659-opener.html?{"load":7}')); -wins[9] = window.open(r(baseurl, 'bug346659-parent.html?{"load":9}')); +SpecialPowers.pushPrefEnv({"set": [["dom.security.https_first", false]]}, function() { + var baseurl = window.location.href.replace(/mochi\.test:8888/, "example.com"); + wins[7] = window.open(r(baseurl, 'bug346659-opener.html?{"load":7}')); + wins[9] = window.open(r(baseurl, 'bug346659-parent.html?{"load":9}')); + + wins[11] = window.open(r(baseurl, 'bug346659-opener.html?{"load":11,"xsite":true}')); + wins[12] = window.open(r(baseurl, 'bug346659-parent.html?{"load":12,"xsite":true}')); +}); -wins[11] = window.open(r(baseurl, 'bug346659-opener.html?{"load":11,"xsite":true}')); -wins[12] = window.open(r(baseurl, 'bug346659-parent.html?{"load":12,"xsite":true}')); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/tests/mochitest/fetch/slow.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/tests/mochitest/fetch/slow.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/tests/mochitest/fetch/slow.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/tests/mochitest/fetch/slow.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -1,11 +1,15 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.processAsync(); - timer = Components.classes["@mozilla.org/timer;1"]. - createInstance(Components.interfaces.nsITimer); - timer.init(function() { - response.write("Here the content. But slowly."); - response.finish(); - }, 1000, Components.interfaces.nsITimer.TYPE_ONE_SHOT); + timer = Components.classes["@mozilla.org/timer;1"].createInstance( + Components.interfaces.nsITimer + ); + timer.init( + function() { + response.write("Here the content. But slowly."); + response.finish(); + }, + 1000, + Components.interfaces.nsITimer.TYPE_ONE_SHOT + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/tests/mochitest/general/frameStorageNullprincipal.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/tests/mochitest/general/frameStorageNullprincipal.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/tests/mochitest/general/frameStorageNullprincipal.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/tests/mochitest/general/frameStorageNullprincipal.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -7,15 +7,19 @@ // Get the nsIFile for frameStoragePrevented.html var file; getObjectState("SERVER_ROOT", function(serverRoot) { - file = serverRoot.getFile("/tests/dom/tests/mochitest/general/frameStoragePrevented.html"); + file = serverRoot.getFile( + "/tests/dom/tests/mochitest/general/frameStoragePrevented.html" + ); }); // Set up the file streams to read in the file as UTF-8 - let fstream = Components.classes["@mozilla.org/network/file-input-stream;1"]. - createInstance(Components.interfaces.nsIFileInputStream); + let fstream = Components.classes[ + "@mozilla.org/network/file-input-stream;1" + ].createInstance(Components.interfaces.nsIFileInputStream); fstream.init(file, -1, 0, 0); - let cstream = Components.classes["@mozilla.org/intl/converter-input-stream;1"]. - createInstance(Components.interfaces.nsIConverterInputStream); + let cstream = Components.classes[ + "@mozilla.org/intl/converter-input-stream;1" + ].createInstance(Components.interfaces.nsIConverterInputStream); cstream.init(fstream, "UTF-8", 0, 0); // Read in the file, and concatenate it onto the data string @@ -28,6 +32,9 @@ } while (read != 0); // Write out the file as a data: URI, and redirect to it - response.setStatusLine('1.1', 302, 'Found'); - response.setHeader('Location', 'data:text/html,' + encodeURIComponent(data) + "#nullprincipal"); + response.setStatusLine("1.1", 302, "Found"); + response.setHeader( + "Location", + "data:text/html," + encodeURIComponent(data) + "#nullprincipal" + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/tests/mochitest/general/generateCss.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/tests/mochitest/general/generateCss.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/tests/mochitest/general/generateCss.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/tests/mochitest/general/generateCss.sjs 2021-10-20 19:28:20.000000000 +0000 @@ -5,38 +5,43 @@ let gResponses = { // 1 - "A": "@import 'generateCss.sjs?B';", - "B": "", + A: "@import 'generateCss.sjs?B';", + B: "", // 2 - "C": "@import 'generateCss.sjs?D';", - "D": "", + C: "@import 'generateCss.sjs?D';", + D: "", // 3 - "E": "@import 'generateCss.sjs?F';", - "F": "", + E: "@import 'generateCss.sjs?F';", + F: "", // 4 - "G": "@import 'generateCss.sjs?H'; @import 'http://example.org/tests/dom/tests/mochitest/general/generateCss.sjs?K';", - "H": "@import 'http://example.com/tests/dom/tests/mochitest/general/generateCss.sjs?I';", - "I": "@import 'generateCss.sjs?J", - "J": "", - "K": "@import 'generateCss.sjs?L';", - "L": "@import 'generateCss.sjs?M", - "M": "", + G: + "@import 'generateCss.sjs?H'; @import 'http://example.org/tests/dom/tests/mochitest/general/generateCss.sjs?K';", + H: + "@import 'http://example.com/tests/dom/tests/mochitest/general/generateCss.sjs?I';", + I: "@import 'generateCss.sjs?J", + J: "", + K: "@import 'generateCss.sjs?L';", + L: "@import 'generateCss.sjs?M", + M: "", // 5 - "N": ".c1 { background-image: -moz-image-rect(url('/image/test/mochitest/blue.png'), 0, 0, 200, 200);}", + N: + ".c1 { background-image: -moz-image-rect(url('/image/test/mochitest/blue.png'), 0, 0, 200, 200);}", // 6 - "O": ".c2 { background-image: url('/image/test/mochitest/red.png');}", + O: ".c2 { background-image: url('/image/test/mochitest/red.png');}", // 7 - "P": "@font-face { font-family: Ahem; src: url('/tests/dom/base/test/Ahem.ttf'); } .c3 { font-family: Ahem; font-size: 20px; }", + P: + "@font-face { font-family: Ahem; src: url('/tests/dom/base/test/Ahem.ttf'); } .c3 { font-family: Ahem; font-size: 20px; }", // 8 - "Q": ".c4 { cursor: url('/image/test/mochitest/over.png') 2 2, auto; } ", + Q: ".c4 { cursor: url('/image/test/mochitest/over.png') 2 2, auto; } ", // 9 - "R": "#image { mask: url('/tests/dom/base/test/file_use_counter_svg_fill_pattern_data.svg'); }", + R: + "#image { mask: url('/tests/dom/base/test/file_use_counter_svg_fill_pattern_data.svg'); }", }; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/tests/mochitest/geolocation/network_geolocation.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/tests/mochitest/geolocation/network_geolocation.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/tests/mochitest/geolocation/network_geolocation.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/tests/mochitest/geolocation/network_geolocation.sjs 2021-10-20 19:28:20.000000000 +0000 @@ -1,57 +1,55 @@ -function parseQueryString(str) -{ - if (str == "") +function parseQueryString(str) { + if (str == "") { return {}; + } var paramArray = str.split("&"); var regex = /^([^=]+)=(.*)$/; var params = {}; - for (var i = 0, sz = paramArray.length; i < sz; i++) - { + for (var i = 0, sz = paramArray.length; i < sz; i++) { var match = regex.exec(paramArray[i]); - if (!match) + if (!match) { throw "Bad parameter in queryString! '" + paramArray[i] + "'"; + } params[decodeURIComponent(match[1])] = decodeURIComponent(match[2]); } return params; } -function getPosition(action) -{ +function getPosition(action) { var response = { status: "OK", location: { lat: 37.41857, lng: -122.08769, }, - accuracy: (action == "worse-accuracy") ? 100 : 42, + accuracy: action == "worse-accuracy" ? 100 : 42, }; return JSON.stringify(response); } var timer; -function handleRequest(request, response) -{ +function handleRequest(request, response) { var params = parseQueryString(request.queryString); if (params.action == "stop-responding") { - response.processAsync(); - return; + response.processAsync(); + return; } var position = getPosition(params.action); if (params.action == "respond-garbage") { - // better way? + // better way? var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz"; position = ""; var len = Math.floor(Math.random() * 5000); - for (var i=0; i< len; i++) { - var c = Math.floor(Math.random() * chars.length); - position += chars.substring(c, c+1); + for (var i = 0; i < len; i++) { + var c = Math.floor(Math.random() * chars.length); + position += chars.substring(c, c + 1); } } @@ -62,17 +60,22 @@ response.setHeader("Content-Type", "aplication/x-javascript", false); var delay = 0; - if ('delay' in params) { + if ("delay" in params) { delay = params.delay; } if (params.action === "send404") { response.setStatusLine("1.0", 404, "Not Found"); - position = ''; + position = ""; } - timer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer); - timer.initWithCallback(function() { - response.write(position); - response.finish(); - }, delay, timer.TYPE_ONE_SHOT); + timer = Components.classes["@mozilla.org/timer;1"].createInstance( + Components.interfaces.nsITimer + ); + timer.initWithCallback( + function() { + response.write(position); + response.finish(); + }, + delay, + timer.TYPE_ONE_SHOT + ); } - diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/tests/mochitest/script/file_blocked_script.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/tests/mochitest/script/file_blocked_script.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/tests/mochitest/script/file_blocked_script.sjs 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/tests/mochitest/script/file_blocked_script.sjs 2021-10-20 19:28:20.000000000 +0000 @@ -4,15 +4,18 @@ * 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/. */ -function setGlobalState(data, key) -{ - x = { data: data, QueryInterface: function(iid) { return this } }; +function setGlobalState(data, key) { + x = { + data, + QueryInterface(iid) { + return this; + }, + }; x.wrappedJSObject = x; setObjectState(key, x); } -function getGlobalState(key) -{ +function getGlobalState(key) { var data; getObjectState(key, function(x) { data = x && x.wrappedJSObject.data; @@ -20,8 +23,7 @@ return data; } -function finishBlockedRequest(request, response, query) -{ +function finishBlockedRequest(request, response, query) { response.setStatusLine(request.httpVersion, 200, "OK"); response.setHeader("Cache-Control", "no-cache", false); response.setHeader("Content-Type", "application/javascript", false); @@ -31,9 +33,8 @@ setGlobalState(undefined, query[1]); } -function handleRequest(request, response) -{ - var query = request.queryString.split('&'); +function handleRequest(request, response) { + var query = request.queryString.split("&"); switch (query[0]) { case "blocked": var alreadyUnblocked = getGlobalState(query[1]); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/tests/mochitest/sessionstorage/test_sessionStorageClone.html firefox-trunk-95.0~a1~hg20211020r596404/dom/tests/mochitest/sessionstorage/test_sessionStorageClone.html --- firefox-trunk-95.0~a1~hg20211017r596111/dom/tests/mochitest/sessionstorage/test_sessionStorageClone.html 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/tests/mochitest/sessionstorage/test_sessionStorageClone.html 2021-10-20 19:28:19.000000000 +0000 @@ -39,7 +39,7 @@ slave.close(); // Open a window from a different origin and check data // are NOT copied and not modified on our side - slaveOrigin = "http://example.com"; + slaveOrigin = "https://example.com"; slave = window.open(slaveOrigin + slavePath + "frameNotEqual.html"); break; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/vr/XRNativeOriginLocalFloor.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/vr/XRNativeOriginLocalFloor.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/vr/XRNativeOriginLocalFloor.cpp 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/vr/XRNativeOriginLocalFloor.cpp 2021-10-20 19:28:19.000000000 +0000 @@ -4,6 +4,7 @@ * 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 "mozilla/StaticPrefs_dom.h" #include "XRNativeOriginLocalFloor.h" #include "VRDisplayClient.h" @@ -15,13 +16,11 @@ : mDisplay(aDisplay), mInitialPositionValid(false) { MOZ_ASSERT(aDisplay); - // To avoid fingerprinting, we offset the floor height by 5cm. - // This will always result in the floor being lower than the + // To avoid fingerprinting, we offset the floor height. + // This should result in the floor being higher than the // real floor in order to avoid breaking content that expects // you to pick objects up off the floor. - // - // TODO (Bug 1616394) - Convert this constant to a pref. - const double kFloorFuzz = 0.05f; // Meters + const double kFloorFuzz = StaticPrefs::dom_vr_webxr_quantization(); // Meters mFloorRandom = double(rand()) / double(RAND_MAX) * kFloorFuzz; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/webidl/History.webidl firefox-trunk-95.0~a1~hg20211020r596404/dom/webidl/History.webidl --- firefox-trunk-95.0~a1~hg20211017r596111/dom/webidl/History.webidl 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/webidl/History.webidl 2021-10-20 19:28:19.000000000 +0000 @@ -21,7 +21,7 @@ attribute ScrollRestoration scrollRestoration; [Throws] readonly attribute any state; - [Throws, NeedsCallerType] + [Throws, NeedsSubjectPrincipal] void go(optional long delta = 0); [Throws, NeedsCallerType] void back(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/test/404_server.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/test/404_server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/test/404_server.sjs 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/test/404_server.sjs 2021-10-20 19:28:20.000000000 +0000 @@ -1,10 +1,9 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setStatusLine(request.httpVersion, 404, "Not found"); // Any valid JS. - if (request.queryString == 'js') { + if (request.queryString == "js") { response.setHeader("Content-Type", "text/javascript", false); - response.write('4 + 4'); + response.write("4 + 4"); } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/test/browser_fileURL.js firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/test/browser_fileURL.js --- firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/test/browser_fileURL.js 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/test/browser_fileURL.js 2021-10-20 19:28:20.000000000 +0000 @@ -3,108 +3,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; -const WORKER_BODY = "postMessage(42);\n"; - -// file:// tests. -add_task(async function() { - await SpecialPowers.pushPrefEnv({ - set: [["privacy.file_unique_origin", false]], - }); - - info("Creating the tmp directory."); - let parent = Cc["@mozilla.org/file/directory_service;1"] - .getService(Ci.nsIDirectoryService) - .QueryInterface(Ci.nsIProperties) - .get("TmpD", Ci.nsIFile); - parent.append("worker-dir-test"); - parent.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0o700); - - let dir_a = parent.clone(); - dir_a.append("a"); - dir_a.create(Ci.nsIFile.DIRECTORY_TYPE, 0o700); - - let page_a = dir_a.clone(); - page_a.append("empty.html"); - page_a.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600); - - let url_a = Services.io.newFileURI(page_a); - - let worker = dir_a.clone(); - worker.append("worker.js"); - - let stream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance( - Ci.nsIFileOutputStream - ); - stream.init( - worker, - 0x02 | 0x08 | 0x20, // write, create, truncate - 0o666, - 0 - ); - stream.write(WORKER_BODY, WORKER_BODY.length); - stream.close(); - - let url_worker = Services.io.newFileURI(worker); - - let dir_b = parent.clone(); - dir_b.append("b"); - dir_b.create(Ci.nsIFile.DIRECTORY_TYPE, 0o700); - - let page_b = dir_b.clone(); - page_b.append("empty.html"); - page_b.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600); - - let url_b = Services.io.newFileURI(page_b); - - let tab = BrowserTestUtils.addTab(gBrowser, url_a.spec); - gBrowser.selectedTab = tab; - - let browser = gBrowser.getBrowserForTab(tab); - await BrowserTestUtils.browserLoaded(browser); - - await SpecialPowers.spawn(browser, [url_worker.spec], function(spec) { - return new content.Promise((resolve, reject) => { - let w = new content.window.Worker(spec); - w.onerror = _ => { - reject(); - }; - w.onmessage = _ => { - resolve(); - }; - }); - }); - ok(true, "The worker is loaded when the script is on the same directory."); - - BrowserTestUtils.removeTab(tab); - - tab = BrowserTestUtils.addTab(gBrowser, url_b.spec); - gBrowser.selectedTab = tab; - - browser = gBrowser.getBrowserForTab(tab); - await BrowserTestUtils.browserLoaded(browser); - - await SpecialPowers.spawn(browser, [url_worker.spec], function(spec) { - return new content.Promise((resolve, reject) => { - let w = new content.window.Worker(spec); - w.onerror = _ => { - resolve(); - }; - w.onmessage = _ => { - reject(); - }; - }); - }); - ok( - true, - "The worker is not loaded when the script is on a different directory." - ); - - BrowserTestUtils.removeTab(tab); - - info("Removing the tmp directory."); - parent.remove(true); -}); - const EMPTY_URL = "/browser/dom/workers/test/empty.html"; const WORKER_URL = "/browser/dom/workers/test/empty_worker.js"; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/test/bug1063538.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/test/bug1063538.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/test/bug1063538.sjs 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/test/bug1063538.sjs 2021-10-20 19:28:20.000000000 +0000 @@ -1,6 +1,7 @@ function handleRequest(request, response) { response.processAsync(); response.write("Hello"); - setTimeout(function() { response.finish(); }, 100000); // wait 100 seconds. + setTimeout(function() { + response.finish(); + }, 100000); // wait 100 seconds. } - diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/test/redirect_to_foreign.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/test/redirect_to_foreign.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/test/redirect_to_foreign.sjs 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/test/redirect_to_foreign.sjs 2021-10-20 19:28:20.000000000 +0000 @@ -1,4 +1,7 @@ function handleRequest(request, response) { response.setStatusLine("1.1", 302, "Found"); - response.setHeader("Location", "http://example.org/tests/dom/workers/test/foreign.js"); + response.setHeader( + "Location", + "http://example.org/tests/dom/workers/test/foreign.js" + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/test/referrer.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/test/referrer.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/test/referrer.sjs 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/test/referrer.sjs 2021-10-20 19:28:20.000000000 +0000 @@ -1,5 +1,4 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { if (request.queryString == "result") { response.write(getState("referer")); setState("referer", "INVALID"); @@ -7,10 +6,9 @@ response.setHeader("Content-Type", "text/javascript", false); response.write("onmessage = function() { postMessage(42); }"); setState("referer", request.getHeader("referer")); - } else if (request.queryString == 'import') { + } else if (request.queryString == "import") { setState("referer", request.getHeader("referer")); response.setHeader("Content-Type", "text/javascript", false); response.write("'hello world'"); } } - diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/test/referrer_test_server.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/test/referrer_test_server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/test/referrer_test_server.sjs 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/test/referrer_test_server.sjs 2021-10-20 19:28:20.000000000 +0000 @@ -22,7 +22,7 @@ return url + searchParams.toString(); } -function createWorker (aRequestType, aPolicy) { +function createWorker(aRequestType, aPolicy) { return ` onmessage = function() { fetch("${createUrl(aRequestType, aPolicy)}").then(function () { @@ -97,5 +97,3 @@ return; } } - - diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/test/server_fetch_synthetic.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/test/server_fetch_synthetic.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/test/server_fetch_synthetic.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/test/server_fetch_synthetic.sjs 2021-10-20 19:28:20.000000000 +0000 @@ -38,7 +38,7 @@ blobContents = extractBlobFromMultipartFormData(requestBodyContents); } - log("Setting Headers") + log("Setting Headers"); response.setHeader("Content-Type", "text/html", false); response.setStatusLine(request.httpVersion, "200", "OK"); response.write(` diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/test/test_fileBlobPosting.xhtml firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/test/test_fileBlobPosting.xhtml --- firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/test/test_fileBlobPosting.xhtml 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/test/test_fileBlobPosting.xhtml 2021-10-20 19:28:19.000000000 +0000 @@ -40,7 +40,7 @@ var outStream = Cc["@mozilla.org/network/file-output-stream;1"] .createInstance(Ci.nsIFileOutputStream); outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate - 0666, 0); + 0o666, 0); outStream.write(fileData, fileData.length); outStream.close(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/test/test_fileBlobSubWorker.xhtml firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/test/test_fileBlobSubWorker.xhtml --- firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/test/test_fileBlobSubWorker.xhtml 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/test/test_fileBlobSubWorker.xhtml 2021-10-20 19:28:19.000000000 +0000 @@ -41,7 +41,7 @@ var outStream = Cc["@mozilla.org/network/file-output-stream;1"] .createInstance(Ci.nsIFileOutputStream); outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate - 0666, 0); + 0o666, 0); outStream.write(fileData, fileData.length); outStream.close(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/test/test_filePosting.xhtml firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/test/test_filePosting.xhtml --- firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/test/test_filePosting.xhtml 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/test/test_filePosting.xhtml 2021-10-20 19:28:20.000000000 +0000 @@ -40,7 +40,7 @@ var outStream = Cc["@mozilla.org/network/file-output-stream;1"] .createInstance(Ci.nsIFileOutputStream); outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate - 0666, 0); + 0o666, 0); outStream.write(fileData, fileData.length); outStream.close(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/test/test_fileReaderSyncErrors.xhtml firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/test/test_fileReaderSyncErrors.xhtml --- firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/test/test_fileReaderSyncErrors.xhtml 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/test/test_fileReaderSyncErrors.xhtml 2021-10-20 19:28:20.000000000 +0000 @@ -40,7 +40,7 @@ var outStream = Cc["@mozilla.org/network/file-output-stream;1"] .createInstance(Ci.nsIFileOutputStream); outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate - 0666, 0); + 0o666, 0); outStream.write(fileData, fileData.length); outStream.close(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/test/test_fileReaderSync.xhtml firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/test/test_fileReaderSync.xhtml --- firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/test/test_fileReaderSync.xhtml 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/test/test_fileReaderSync.xhtml 2021-10-20 19:28:20.000000000 +0000 @@ -41,7 +41,7 @@ var outStream = Cc["@mozilla.org/network/file-output-stream;1"] .createInstance(Ci.nsIFileOutputStream); outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate - 0666, 0); + 0o666, 0); outStream.write(fileData, fileData.length); outStream.close(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/test/test_fileReadSlice.xhtml firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/test/test_fileReadSlice.xhtml --- firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/test/test_fileReadSlice.xhtml 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/test/test_fileReadSlice.xhtml 2021-10-20 19:28:20.000000000 +0000 @@ -44,7 +44,7 @@ var outStream = Cc["@mozilla.org/network/file-output-stream;1"] .createInstance(Ci.nsIFileOutputStream); outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate - 0666, 0); + 0o666, 0); outStream.write(fileData, fileData.length); outStream.close(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/test/test_fileSlice.xhtml firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/test/test_fileSlice.xhtml --- firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/test/test_fileSlice.xhtml 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/test/test_fileSlice.xhtml 2021-10-20 19:28:20.000000000 +0000 @@ -41,7 +41,7 @@ var outStream = Cc["@mozilla.org/network/file-output-stream;1"] .createInstance(Ci.nsIFileOutputStream); outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate - 0666, 0); + 0o666, 0); outStream.write(fileData, fileData.length); outStream.close(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/test/test_fileSubWorker.xhtml firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/test/test_fileSubWorker.xhtml --- firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/test/test_fileSubWorker.xhtml 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/test/test_fileSubWorker.xhtml 2021-10-20 19:28:19.000000000 +0000 @@ -41,7 +41,7 @@ var outStream = Cc["@mozilla.org/network/file-output-stream;1"] .createInstance(Ci.nsIFileOutputStream); outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate - 0666, 0); + 0o666, 0); outStream.write(fileData, fileData.length); outStream.close(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/test/test_file.xhtml firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/test/test_file.xhtml --- firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/test/test_file.xhtml 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/test/test_file.xhtml 2021-10-20 19:28:20.000000000 +0000 @@ -41,7 +41,7 @@ var outStream = Cc["@mozilla.org/network/file-output-stream;1"] .createInstance(Ci.nsIFileOutputStream); outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate - 0666, 0); + 0o666, 0); outStream.write(fileData, fileData.length); outStream.close(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/WorkerScope.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/WorkerScope.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/workers/WorkerScope.cpp 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/workers/WorkerScope.cpp 2021-10-20 19:28:20.000000000 +0000 @@ -454,7 +454,7 @@ { AUTO_PROFILER_MARKER_TEXT( "ImportScripts", JS, MarkerStack::Capture(), - profiler_can_accept_markers() + profiler_thread_is_being_profiled() ? StringJoin(","_ns, aScriptURLs, [](nsACString& dest, const auto& scriptUrl) { AppendUTF16toUTF8(scriptUrl, dest); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/worklet/tests/server_import_with_cache.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/worklet/tests/server_import_with_cache.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/worklet/tests/server_import_with_cache.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/worklet/tests/server_import_with_cache.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -1,12 +1,11 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setHeader("Content-Type", "text/javascript", false); var state = getState("alreadySent"); if (!state) { setState("alreadySent", "1"); } else { - response.setStatusLine('1.1', 404, "Not Found"); + response.setStatusLine("1.1", 404, "Not Found"); } response.write("42"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/xhr/tests/echo.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/xhr/tests/echo.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/xhr/tests/echo.sjs 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/xhr/tests/echo.sjs 2021-10-20 19:28:20.000000000 +0000 @@ -1,21 +1,26 @@ const CC = Components.Constructor; -const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", - "setInputStream"); +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setHeader("Content-Type", "text/plain"); if (request.method == "GET") { - response.write(request.queryString); - return; + response.write(request.queryString); + return; } var bodyStream = new BinaryInputStream(request.bodyInputStream); var body = ""; var bodyAvail; - while ((bodyAvail = bodyStream.available()) > 0) - body += String.fromCharCode.apply(null, bodyStream.readByteArray(bodyAvail)); - + while ((bodyAvail = bodyStream.available()) > 0) { + body += String.fromCharCode.apply( + null, + bodyStream.readByteArray(bodyAvail) + ); + } + response.write(body); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/xhr/tests/file_html_in_xhr.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/xhr/tests/file_html_in_xhr.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/xhr/tests/file_html_in_xhr.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/xhr/tests/file_html_in_xhr.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -1,15 +1,19 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setHeader("Content-Type", "text/javascript", false); if (request.queryString.indexOf("report") != -1) { if (getState("loaded") == "loaded") { - response.write("ok(false, 'This script was not supposed to get fetched.'); continueAfterReport();"); + response.write( + "ok(false, 'This script was not supposed to get fetched.'); continueAfterReport();" + ); } else { - response.write("ok(true, 'This script was not supposed to get fetched.'); continueAfterReport();"); + response.write( + "ok(true, 'This script was not supposed to get fetched.'); continueAfterReport();" + ); } } else { setState("loaded", "loaded"); - response.write('document.documentElement.setAttribute("data-fail", "FAIL");'); + response.write( + 'document.documentElement.setAttribute("data-fail", "FAIL");' + ); } } - diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/xhr/tests/file_XHR_anon.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/xhr/tests/file_XHR_anon.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/xhr/tests/file_XHR_anon.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/xhr/tests/file_XHR_anon.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -4,10 +4,10 @@ if (request.queryString == "expectAuth=true") { if (request.hasHeader("Authorization")) { - headers["authorization"] = request.getHeader("Authorization"); + headers.authorization = request.getHeader("Authorization"); } else { response.setStatusLine(null, 401, "Authentication required"); - response.setHeader("WWW-Authenticate", "basic realm=\"testrealm\"", true); + response.setHeader("WWW-Authenticate", 'basic realm="testrealm"', true); } } else { invalidHeaders.push("Authorization"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/xhr/tests/file_XHRDocURI.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/xhr/tests/file_XHRDocURI.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/xhr/tests/file_XHRDocURI.sjs 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/xhr/tests/file_XHRDocURI.sjs 2021-10-20 19:28:20.000000000 +0000 @@ -1,5 +1,4 @@ -function handleRequest(aRequest, aResponse) -{ +function handleRequest(aRequest, aResponse) { var url = aRequest.queryString.match(/\burl=([^#&]*)/); if (!url) { aResponse.setStatusLine(aRequest.httpVersion, 404, "Not Found"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/xhr/tests/file_XHR_header.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/xhr/tests/file_XHR_header.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/xhr/tests/file_XHR_header.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/xhr/tests/file_XHR_header.sjs 2021-10-20 19:28:20.000000000 +0000 @@ -1,9 +1,8 @@ -// SJS file for getAllResponseRequests vs getResponseRequest -function handleRequest(request, response) -{ - // Header strings are interpreted by truncating the characters in them to - // bytes, so U+2026 HORIZONTAL ELLIPSIS here must be encoded manually: using - // "…" as the string would write a \x26 byte. - response.setHeader("X-Custom-Header-Bytes", "\xE2\x80\xA6", false); - response.write("42"); -} +// SJS file for getAllResponseRequests vs getResponseRequest +function handleRequest(request, response) { + // Header strings are interpreted by truncating the characters in them to + // bytes, so U+2026 HORIZONTAL ELLIPSIS here must be encoded manually: using + // "…" as the string would write a \x26 byte. + response.setHeader("X-Custom-Header-Bytes", "\xE2\x80\xA6", false); + response.write("42"); +} diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/xhr/tests/file_XHRResponseURL.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/xhr/tests/file_XHRResponseURL.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/xhr/tests/file_XHRResponseURL.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/xhr/tests/file_XHRResponseURL.sjs 2021-10-20 19:28:20.000000000 +0000 @@ -1,5 +1,4 @@ -function handleRequest(aRequest, aResponse) -{ +function handleRequest(aRequest, aResponse) { var url = aRequest.queryString.match(/\burl=([^#&]*)/); if (!url) { aResponse.setStatusLine(aRequest.httpVersion, 404, "Not Found"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/xhr/tests/file_XHRSendData.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/xhr/tests/file_XHRSendData.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/xhr/tests/file_XHRSendData.sjs 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/xhr/tests/file_XHRSendData.sjs 2021-10-20 19:28:20.000000000 +0000 @@ -1,21 +1,26 @@ const CC = Components.Constructor; -const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", - "setInputStream"); +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); -function handleRequest(request, response) -{ - if (request.hasHeader("Content-Type")) - response.setHeader("Result-Content-Type", - request.getHeader("Content-Type")); +function handleRequest(request, response) { + if (request.hasHeader("Content-Type")) { + response.setHeader( + "Result-Content-Type", + request.getHeader("Content-Type") + ); + } response.setHeader("Content-Type", "text/plain; charset=ISO-8859-1"); var body = new BinaryInputStream(request.bodyInputStream); var avail; var bytes = []; - while ((avail = body.available()) > 0) + while ((avail = body.available()) > 0) { Array.prototype.push.apply(bytes, body.readByteArray(avail)); + } var data = String.fromCharCode.apply(null, bytes); response.setHeader("Result-Content-Length", "" + data.length); @@ -23,8 +28,7 @@ var newURL = "http://" + data.split("&url=")[1]; response.setStatusLine(null, 307, "redirect"); response.setHeader("Location", newURL, false); - } - else { + } else { response.write(data); } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/xhr/tests/file_XHR_timeout.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/xhr/tests/file_XHR_timeout.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/xhr/tests/file_XHR_timeout.sjs 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/xhr/tests/file_XHR_timeout.sjs 2021-10-20 19:28:20.000000000 +0000 @@ -1,15 +1,18 @@ var timer = null; -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.processAsync(); - timer = Components.classes["@mozilla.org/timer;1"] - .createInstance(Components.interfaces.nsITimer); - timer.initWithCallback(function() - { - response.setStatusLine(null, 200, "OK"); - response.setHeader("Content-Type", "text/plain", false); - response.write("hello"); - response.finish(); - }, 3000 /* milliseconds */, Components.interfaces.nsITimer.TYPE_ONE_SHOT); + timer = Components.classes["@mozilla.org/timer;1"].createInstance( + Components.interfaces.nsITimer + ); + timer.initWithCallback( + function() { + response.setStatusLine(null, 200, "OK"); + response.setHeader("Content-Type", "text/plain", false); + response.write("hello"); + response.finish(); + }, + 3000 /* milliseconds */, + Components.interfaces.nsITimer.TYPE_ONE_SHOT + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/xhr/tests/progressserver.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/xhr/tests/progressserver.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/xhr/tests/progressserver.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/xhr/tests/progressserver.sjs 2021-10-20 19:28:19.000000000 +0000 @@ -1,7 +1,9 @@ const CC = Components.Constructor; -const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", - "setInputStream"); +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); function setReq(req) { setObjectState("dom/xhr/tests/progressserver", req); @@ -15,25 +17,28 @@ return req; } -function handleRequest(request, response) -{ - var pairs = request.queryString.split('&'); +function handleRequest(request, response) { + var pairs = request.queryString.split("&"); var command = pairs.shift(); dump("received '" + command + "' command\n"); var bodyStream = new BinaryInputStream(request.bodyInputStream); var body = ""; var bodyAvail; - while ((bodyAvail = bodyStream.available()) > 0) - body += String.fromCharCode.apply(null, bodyStream.readByteArray(bodyAvail)); + while ((bodyAvail = bodyStream.available()) > 0) { + body += String.fromCharCode.apply( + null, + bodyStream.readByteArray(bodyAvail) + ); + } if (command == "open") { response.processAsync(); setReq(response); response.setHeader("Cache-Control", "no-cache", false); - pairs.forEach(function (val) { - var [name, value] = val.split('='); + pairs.forEach(function(val) { + var [name, value] = val.split("="); response.setHeader(name, unescape(value), false); }); response.write(body); @@ -42,8 +47,7 @@ if (command == "send") { getReq().write(body); - } - else if (command == "close") { + } else if (command == "close") { getReq().finish(); setReq(null); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/xhr/tests/responseIdentical.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/xhr/tests/responseIdentical.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/xhr/tests/responseIdentical.sjs 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/xhr/tests/responseIdentical.sjs 2021-10-20 19:28:20.000000000 +0000 @@ -1,16 +1,19 @@ const CC = Components.Constructor; -const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", - "setInputStream"); +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); // Simply sending back the same data that is received -function handleRequest(request, response) -{ +function handleRequest(request, response) { var body = ""; var bodyStream = new BinaryInputStream(request.bodyInputStream); - var bytes = [], avail = 0; - while ((avail = bodyStream.available()) > 0) + var bytes = [], + avail = 0; + while ((avail = bodyStream.available()) > 0) { body += String.fromCharCode.apply(String, bodyStream.readByteArray(avail)); + } response.setHeader("Content-Type", "application/octet-stream", false); response.write(body); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/xhr/tests/slow.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/xhr/tests/slow.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/xhr/tests/slow.sjs 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/xhr/tests/slow.sjs 2021-10-20 19:28:20.000000000 +0000 @@ -1,11 +1,15 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.processAsync(); - timer = Components.classes["@mozilla.org/timer;1"]. - createInstance(Components.interfaces.nsITimer); - timer.init(function() { - response.write("Here the content. But slowly."); - response.finish(); - }, 5000, Components.interfaces.nsITimer.TYPE_ONE_SHOT); + timer = Components.classes["@mozilla.org/timer;1"].createInstance( + Components.interfaces.nsITimer + ); + timer.init( + function() { + response.write("Here the content. But slowly."); + response.finish(); + }, + 5000, + Components.interfaces.nsITimer.TYPE_ONE_SHOT + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/xhr/tests/sync_xhr_unload.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/xhr/tests/sync_xhr_unload.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/xhr/tests/sync_xhr_unload.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/xhr/tests/sync_xhr_unload.sjs 2021-10-20 19:28:20.000000000 +0000 @@ -1,15 +1,18 @@ var timer = null; -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.processAsync(); - timer = Components.classes["@mozilla.org/timer;1"] - .createInstance(Components.interfaces.nsITimer); - timer.initWithCallback(function() - { - response.setStatusLine(null, 200, "OK"); - response.setHeader("Content-Type", "text/plain", false); - response.write("hello"); - response.finish(); - }, 30000 /* milliseconds */, Components.interfaces.nsITimer.TYPE_ONE_SHOT); + timer = Components.classes["@mozilla.org/timer;1"].createInstance( + Components.interfaces.nsITimer + ); + timer.initWithCallback( + function() { + response.setStatusLine(null, 200, "OK"); + response.setHeader("Content-Type", "text/plain", false); + response.write("hello"); + response.finish(); + }, + 30000 /* milliseconds */, + Components.interfaces.nsITimer.TYPE_ONE_SHOT + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/xhr/tests/temporaryFileBlob.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/xhr/tests/temporaryFileBlob.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/xhr/tests/temporaryFileBlob.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/xhr/tests/temporaryFileBlob.sjs 2021-10-20 19:28:20.000000000 +0000 @@ -1,20 +1,23 @@ const CC = Components.Constructor; -const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", - "setInputStream"); -const BinaryOutputStream = CC("@mozilla.org/binaryoutputstream;1", - "nsIBinaryOutputStream", - "setOutputStream"); -const Timer = CC("@mozilla.org/timer;1", - "nsITimer", - "initWithCallback"); +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); +const BinaryOutputStream = CC( + "@mozilla.org/binaryoutputstream;1", + "nsIBinaryOutputStream", + "setOutputStream" +); +const Timer = CC("@mozilla.org/timer;1", "nsITimer", "initWithCallback"); function handleRequest(request, response) { var bodyStream = new BinaryInputStream(request.bodyInputStream); var bodyBytes = []; - while ((bodyAvail = bodyStream.available()) > 0) + while ((bodyAvail = bodyStream.available()) > 0) { Array.prototype.push.apply(bodyBytes, bodyStream.readByteArray(bodyAvail)); + } var bos = new BinaryOutputStream(response.bodyOutputStream); @@ -23,11 +26,19 @@ var part = bodyBytes.splice(0, 256); bos.writeByteArray(part); - response.timer1 = new Timer(function(timer) { - bos.writeByteArray(bodyBytes); - }, 1000, Components.interfaces.nsITimer.TYPE_ONE_SHOT); - - response.timer2 = new Timer(function(timer) { - response.finish(); - }, 2000, Components.interfaces.nsITimer.TYPE_ONE_SHOT); + response.timer1 = new Timer( + function(timer) { + bos.writeByteArray(bodyBytes); + }, + 1000, + Components.interfaces.nsITimer.TYPE_ONE_SHOT + ); + + response.timer2 = new Timer( + function(timer) { + response.finish(); + }, + 2000, + Components.interfaces.nsITimer.TYPE_ONE_SHOT + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/xhr/tests/worker_xhr_cors_redirect.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/xhr/tests/worker_xhr_cors_redirect.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/xhr/tests/worker_xhr_cors_redirect.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/xhr/tests/worker_xhr_cors_redirect.sjs 2021-10-20 19:28:20.000000000 +0000 @@ -1,7 +1,7 @@ function handleRequest(request, response) { response.setHeader("Access-Control-Allow-Origin", "*"); - if (request.queryString == 'redirect') { + if (request.queryString == "redirect") { response.setStatusLine("1.1", 302, "Found"); response.setHeader("Location", "worker_xhr_cors_redirect.sjs"); } else { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/xhr/tests/worker_xhr_headers_server.sjs firefox-trunk-95.0~a1~hg20211020r596404/dom/xhr/tests/worker_xhr_headers_server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/dom/xhr/tests/worker_xhr_headers_server.sjs 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/xhr/tests/worker_xhr_headers_server.sjs 2021-10-20 19:28:20.000000000 +0000 @@ -9,7 +9,7 @@ case "POST": try { var optionsHost = request.getHeader("options-host"); - } catch(e) { } + } catch (e) {} var headerFound = false; if (optionsHost) { @@ -20,7 +20,7 @@ try { var emptyHeader = "nada" + request.getHeader("empty"); - } catch(e) { } + } catch (e) {} if (emptyHeader && emptyHeader == "nada") { setState("emptyHeader", "nada"); @@ -28,16 +28,16 @@ } if (headerFound) { return; - } else { - break; } + break; case "OPTIONS": if (getState("optionsHost") == request.host) { try { - var optionsHeader = - request.getHeader("Access-Control-Request-Headers"); - } catch(e) { } + var optionsHeader = request.getHeader( + "Access-Control-Request-Headers" + ); + } catch (e) {} setState("optionsHeader", "'" + optionsHeader + "'"); } break; @@ -46,12 +46,15 @@ response.setHeader("Cache-Control", "no-cache", false); response.setHeader("Content-Type", "text/plain", false); - if (getState("postHost") == request.host && - getState("emptyHeader") == "nada") { + if ( + getState("postHost") == request.host && + getState("emptyHeader") == "nada" + ) { var result = getState("optionsHeader"); if (result) { - response.write("Success: expected OPTIONS request with " + result + - " header"); + response.write( + "Success: expected OPTIONS request with " + result + " header" + ); } else if (getState("badGet") == 1) { response.write("Error: unexpected GET request"); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/dom/xul/nsXULElement.cpp firefox-trunk-95.0~a1~hg20211020r596404/dom/xul/nsXULElement.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/dom/xul/nsXULElement.cpp 2021-10-17 14:42:36.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/dom/xul/nsXULElement.cpp 2021-10-20 19:28:20.000000000 +0000 @@ -1580,12 +1580,6 @@ static nsresult WriteStencil(nsIObjectOutputStream* aStream, JSContext* aCx, const JS::ReadOnlyCompileOptions& aOptions, JS::Stencil* aStencil) { - uint8_t flags = 0; // We don't have flags anymore. - nsresult rv = aStream->Write8(flags); - if (NS_FAILED(rv)) { - return rv; - } - JS::TranscodeBuffer buffer; JS::TranscodeResult code; code = JS::EncodeStencil(aCx, aOptions, aStencil, buffer); @@ -1604,7 +1598,7 @@ if (size > UINT32_MAX) { return NS_ERROR_FAILURE; } - rv = aStream->Write32(size); + nsresult rv = aStream->Write32(size); if (NS_SUCCEEDED(rv)) { // Ideally we could just pass "buffer" here. See bug 1566574. rv = aStream->WriteBytes(Span(buffer.begin(), size)); @@ -1616,12 +1610,6 @@ static nsresult ReadStencil(nsIObjectInputStream* aStream, JSContext* aCx, const JS::ReadOnlyCompileOptions& aOptions, JS::Stencil** aStencilOut) { - uint8_t flags; - nsresult rv = aStream->Read8(&flags); - if (NS_FAILED(rv)) { - return rv; - } - // We don't serialize mutedError-ness of scripts, which is fine as long as // we only serialize system and XUL-y things. We can detect this by checking // where the caller wants us to deserialize. @@ -1633,7 +1621,7 @@ JS::CurrentGlobalOrNull(aCx) == loaderGlobal); uint32_t size; - rv = aStream->Read32(&size); + nsresult rv = aStream->Read32(&size); if (NS_FAILED(rv)) { return rv; } @@ -1984,10 +1972,8 @@ JS::CompileOptions options(aCx); FillCompileOptions(options); - // We don't need setIntroductionType and setFileAndLine here, unlike - // nsXULPrototypeScript::Compile. - // mStencil already contains the information. - aScript.set(JS::InstantiateGlobalStencil(aCx, options, mStencil)); + JS::InstantiateOptions instantiateOptions(options); + aScript.set(JS::InstantiateGlobalStencil(aCx, instantiateOptions, mStencil)); if (!aScript) { JS_ClearPendingException(aCx); return NS_ERROR_OUT_OF_MEMORY; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/editor/libeditor/tests/file_bug611182.sjs firefox-trunk-95.0~a1~hg20211020r596404/editor/libeditor/tests/file_bug611182.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/editor/libeditor/tests/file_bug611182.sjs 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/editor/libeditor/tests/file_bug611182.sjs 2021-10-20 19:28:20.000000000 +0000 @@ -40,11 +40,13 @@ }, { ct: "application/xhtml+xml", - val: 'fooz bar', + val: + 'fooz bar', }, { ct: "application/xhtml+xml", - val: 'fooz bar', + val: + 'fooz bar', }, { ct: "text/html", @@ -52,175 +54,189 @@ }, { ct: "text/html", - val: '", + }, + { + ct: "text/html", + val: + "", + }, + { + ct: "text/html", + val: + "", + }, + { + ct: "text/html", + val: + "", + }, + { + ct: "text/html", + val: + "", + }, + { + ct: "text/html", + val: + "", + }, + { + ct: "text/html", + val: + "", + }, + { + ct: "text/html", + val: + "", + }, + { + ct: "text/html", + val: + "", + }, + { + ct: "text/html", + val: + "data:text/html,", + }, + { + ct: "text/html", + val: + "", + }, + { + ct: "text/html", + val: + "", + }, + { + ct: "text/html", + val: + "", + }, + { + ct: "text/html", + val: + "", }, ]; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/editor/libeditor/tests/file_bug795418-2.sjs firefox-trunk-95.0~a1~hg20211020r596404/editor/libeditor/tests/file_bug795418-2.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/editor/libeditor/tests/file_bug795418-2.sjs 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/editor/libeditor/tests/file_bug795418-2.sjs 2021-10-20 19:28:20.000000000 +0000 @@ -4,5 +4,7 @@ function handleRequest(request, response) { response.setHeader("Cache-Control", "no-cache", false); response.setHeader("Content-Type", "application/xhtml+xml", false); - response.write("AB"); + response.write( + "AB" + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/editor/libeditor/tests/file_sanitizer_on_paste.sjs firefox-trunk-95.0~a1~hg20211020r596404/editor/libeditor/tests/file_sanitizer_on_paste.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/editor/libeditor/tests/file_sanitizer_on_paste.sjs 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/editor/libeditor/tests/file_sanitizer_on_paste.sjs 2021-10-20 19:28:20.000000000 +0000 @@ -1,16 +1,19 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { if (request.queryString.indexOf("report") != -1) { response.setHeader("Content-Type", "text/javascript", false); if (getState("loaded") == "loaded") { - response.write("ok(false, 'There was an attempt to preload the image.');"); + response.write( + "ok(false, 'There was an attempt to preload the image.');" + ); } else { - response.write("ok(true, 'There was no attempt to preload the image.');"); + response.write("ok(true, 'There was no attempt to preload the image.');"); } response.write("SimpleTest.finish();"); } else { setState("loaded", "loaded"); response.setHeader("Content-Type", "image/svg", false); - response.write("Not supposed to load this"); + response.write( + "Not supposed to load this" + ); } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/editor/spellchecker/EditorSpellCheck.cpp firefox-trunk-95.0~a1~hg20211020r596404/editor/spellchecker/EditorSpellCheck.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/editor/spellchecker/EditorSpellCheck.cpp 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/editor/spellchecker/EditorSpellCheck.cpp 2021-10-20 19:28:20.000000000 +0000 @@ -893,7 +893,7 @@ // Required dictionary was not available. Try to get a dictionary // matching at least language part of dictName. - mozilla::intl::Locale loc = mozilla::intl::Locale(dictName); + mozilla::intl::MozLocale loc = mozilla::intl::MozLocale(dictName); nsAutoCString langCode(loc.GetLanguage()); // Try dictionary.spellchecker preference, if it starts with langCode, @@ -916,7 +916,8 @@ // equlas applocation locale language. LocaleService::GetInstance()->GetAppLocaleAsBCP47(appLocaleStr); if (!appLocaleStr.IsEmpty()) { - mozilla::intl::Locale appLoc = mozilla::intl::Locale(appLocaleStr); + mozilla::intl::MozLocale appLoc = + mozilla::intl::MozLocale(appLocaleStr); if (langCode.Equals(appLoc.GetLanguage())) { BuildDictionaryList(appLocaleStr, dictList, DICT_COMPARE_CASE_INSENSITIVE, tryDictList); @@ -928,7 +929,8 @@ nsAutoCString sysLocaleStr; OSPreferences::GetInstance()->GetSystemLocale(sysLocaleStr); if (!sysLocaleStr.IsEmpty()) { - mozilla::intl::Locale sysLoc = mozilla::intl::Locale(sysLocaleStr); + mozilla::intl::MozLocale sysLoc = + mozilla::intl::MozLocale(sysLocaleStr); if (langCode.Equals(sysLoc.GetLanguage())) { BuildDictionaryList(sysLocaleStr, dictList, DICT_COMPARE_CASE_INSENSITIVE, tryDictList); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/.eslintignore firefox-trunk-95.0~a1~hg20211020r596404/.eslintignore --- firefox-trunk-95.0~a1~hg20211017r596111/.eslintignore 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/.eslintignore 2021-10-20 19:28:14.000000000 +0000 @@ -76,6 +76,8 @@ devtools/client/netmonitor/test/xhr_bundle.js devtools/client/webconsole/test/browser/code_bundle_nosource.js devtools/client/webconsole/test/browser/code_bundle_invalidmap.js +devtools/client/webconsole/test/browser/test-autocomplete-mapped.js +devtools/client/webconsole/test/browser/test-autocomplete-mapped.src.js devtools/server/tests/xpcshell/setBreakpoint* devtools/server/tests/xpcshell/sourcemapped.js diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/.eslintrc.js firefox-trunk-95.0~a1~hg20211020r596404/.eslintrc.js --- firefox-trunk-95.0~a1~hg20211017r596111/.eslintrc.js 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/.eslintrc.js 2021-10-20 19:28:14.000000000 +0000 @@ -355,7 +355,6 @@ "no-nested-ternary": "off", "no-new-object": "off", "no-new-wrappers": "off", - "no-octal": "off", "no-redeclare": "off", "no-return-await": "off", "no-restricted-globals": "off", @@ -474,7 +473,6 @@ "no-empty": "off", "no-eval": "off", "no-lone-blocks": "off", - "no-octal": "off", "no-redeclare": "off", "no-shadow": "off", "no-throw-literal": "off", diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/2d/MacIOSurface.cpp firefox-trunk-95.0~a1~hg20211020r596404/gfx/2d/MacIOSurface.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/2d/MacIOSurface.cpp 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/2d/MacIOSurface.cpp 2021-10-20 19:28:20.000000000 +0000 @@ -97,11 +97,11 @@ size_t CreatePlaneDictionary(CFTypeRefPtr& aDict, const gfx::IntSize& aSize, size_t aOffset, size_t aBytesPerPixel) { - size_t bytesPerRow = IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, + size_t bytesPerRow = IOSurfaceAlignProperty(kIOSurfacePlaneBytesPerRow, aSize.width * aBytesPerPixel); // Add a SIMD register worth of extra bytes to the end of the allocation for // SWGL. - size_t totalBytes = IOSurfaceAlignProperty(kIOSurfaceAllocSize, + size_t totalBytes = IOSurfaceAlignProperty(kIOSurfacePlaneSize, aSize.height * bytesPerRow + 16); aDict = CFTypeRefPtr::WrapUnderCreateRule( @@ -114,7 +114,7 @@ AddDictionaryInt(aDict, kIOSurfacePlaneBytesPerRow, bytesPerRow); AddDictionaryInt(aDict, kIOSurfacePlaneOffset, aOffset); AddDictionaryInt(aDict, kIOSurfacePlaneSize, totalBytes); - AddDictionaryInt(aDict, kIOSurfaceBytesPerElement, aBytesPerPixel); + AddDictionaryInt(aDict, kIOSurfacePlaneBytesPerElement, aBytesPerPixel); return totalBytes; } @@ -150,11 +150,15 @@ } CFTypeRefPtr planeProps[2]; - size_t planeTotalBytes = CreatePlaneDictionary(planeProps[0], aYSize, 0, 1); - planeTotalBytes += - CreatePlaneDictionary(planeProps[1], aCbCrSize, planeTotalBytes, 2); + size_t yPlaneBytes = CreatePlaneDictionary(planeProps[0], aYSize, 0, 1); + size_t cbCrOffset = + IOSurfaceAlignProperty(kIOSurfacePlaneOffset, yPlaneBytes); + size_t cbCrPlaneBytes = + CreatePlaneDictionary(planeProps[1], aCbCrSize, cbCrOffset, 2); + size_t totalBytes = + IOSurfaceAlignProperty(kIOSurfaceAllocSize, cbCrOffset + cbCrPlaneBytes); - AddDictionaryInt(props, kIOSurfaceAllocSize, planeTotalBytes); + AddDictionaryInt(props, kIOSurfaceAllocSize, totalBytes); auto array = CFTypeRefPtr::WrapUnderCreateRule( CFArrayCreate(kCFAllocatorDefault, (const void**)planeProps, 2, diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/config/gfxVars.h firefox-trunk-95.0~a1~hg20211020r596404/gfx/config/gfxVars.h --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/config/gfxVars.h 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/config/gfxVars.h 2021-10-20 19:28:21.000000000 +0000 @@ -30,7 +30,6 @@ _(BrowserTabsRemoteAutostart, bool, false) \ _(ContentBackend, BackendType, BackendType::NONE) \ _(SoftwareBackend, BackendType, BackendType::NONE) \ - _(TileSize, IntSize, IntSize(-1, -1)) \ _(OffscreenFormat, gfxImageFormat, \ mozilla::gfx::SurfaceFormat::X8R8G8B8_UINT32) \ _(RequiresAcceleratedGLContextForCompositorOGL, bool, false) \ @@ -63,6 +62,8 @@ _(GREDirectory, nsString, nsString()) \ _(ProfDirectory, nsString, nsString()) \ _(AllowD3D11KeyedMutex, bool, false) \ + _(SwapIntervalGLX, bool, false) \ + _(SwapIntervalEGL, bool, false) \ _(SystemTextQuality, int32_t, 5 /* CLEARTYPE_QUALITY */) \ _(SystemTextClearTypeLevel, float, 1.0f) \ _(SystemTextEnhancedContrast, float, 1.0f) \ diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/docs/AsyncPanZoom.rst firefox-trunk-95.0~a1~hg20211020r596404/gfx/docs/AsyncPanZoom.rst --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/docs/AsyncPanZoom.rst 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/docs/AsyncPanZoom.rst 2021-10-20 19:28:21.000000000 +0000 @@ -141,13 +141,13 @@ responsively. This means that we want independent asynchronous panning for both the top-level page and the iframe. In addition to iframes, elements that have the overflow:scroll CSS property set are also -scrollable. In the scrollable elements are arranged in a tree structure, -and in the APZ code we have a matching tree of AsyncPanZoomController -(APZC) objects, one for each scrollable element. To manage this tree of -APZC instances, we have a single APZCTreeManager object. Each APZC is -relatively independent and handles the scrolling for its associated -scrollable element, but there are some cases in which they need to -interact; these cases are described in the sections below. +scrollable. In the display list, scrollable elements are arranged in a +tree structure, and in the APZ code we have a matching tree of +AsyncPanZoomController (APZC) objects, one for each scrollable element. +To manage this tree of APZC instances, we have a single APZCTreeManager +object. Each APZC is relatively independent and handles the scrolling for +its associated scrollable element, but there are some cases in which they +need to interact; these cases are described in the sections below. Hit detection ~~~~~~~~~~~~~ @@ -187,6 +187,8 @@ “fling handoff” in the case where analogous behaviour results from the scrolling momentum of the page after the user has lifted their finger). +.. _input-event-untransformation: + Input event untransformation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -517,14 +519,15 @@ scroll normally. Note: with Fission (where inactive scroll frames would make it impossible -to target the correct process in all situations) and WebRender (which -makes displayports more lightweight as the actual rendering is offloaded -to the compositor and can be done on demand), inactive scroll frames are -being phased out, and we are moving towards a model where all scroll -frames with nonempty scroll ranges are active and get a displayport and -an APZC. To conserve memory, displayports for scroll frames which have -not been recently scrolled are kept to a "minimal" size equal to the -viewport size. +to target the correct process in all situations; see +:ref:`this section ` for more details) and WebRender +(which makes displayports more lightweight as the actual rendering is +offloaded to the compositor and can be done on demand), inactive scroll +frames are being phased out, and we are moving towards a model where all +scroll frames with nonempty scroll ranges are active and get a +displayport and an APZC. To conserve memory, displayports for scroll +frames which have not been recently scrolled are kept to a "minimal" size +equal to the viewport size. WebRender Integration ~~~~~~~~~~~~~~~~~~~~~ @@ -566,6 +569,11 @@ relationships are between APZCs, and what content is subject to what transforms. +An additional use of the HitTestingTree is to allow APZ to keep content +processes up to date about enclosing transforms that they are subject to. +See :ref:`this section ` for +more details. + (In the past, with the Layers backend, the HitTestingTree was also used for compositor hit testing, hence the name. This is no longer the case, and there may be opportunities to simplify the tree as a result.) @@ -579,7 +587,7 @@ metadata applying to a subtree of such layers). In the Layers backend, such content would be rendered into a single texture which could then be moved asynchronously at composite time. Since a layer of content can - be scrolled by multiple scroll (nested) scroll frames, a + be scrolled by multiple (nested) scroll frames, a WebRenderLayerScrollData may contain scroll metadata for more than one scroll frame. * WebRenderScrollDataWrapper, which wraps WebRenderLayerScrollData @@ -588,7 +596,7 @@ 1:1 correspondence with HitTestingTreeNodes. It's not clear whether the distinction between WebRenderLayerScrollData -and WebRenderScrollDataWrapper still useful in a WebRender-only world. +and WebRenderScrollDataWrapper is still useful in a WebRender-only world. The code could potentially be revised such that we directly build and store nodes of a single type with the behaviour of WebRenderScrollDataWrapper. @@ -684,6 +692,89 @@ animations to advance to the next timestep (usually the next vsync). This happens just before reading the APZ transforms. +Fission Integration +~~~~~~~~~~~~~~~~~~~ + +This section describes how APZ interacts with the Fission (Site Isolation) +project. + +Introduction +^^^^^^^^^^^^ + +Fission is an architectural change motivated by security considerations, +where web content from each origin is isolated in its own process. Since +a page can contain a mixture of content from different origins (for +example, the top level page can be content from origin A, and it can +contain an iframe with content from origin B), that means that rendering +and interacting with a page can now involve coordination between APZ and +multiple content processes. + +.. _fission-hit-testing: + +Content Process Selection for Input Events +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Input events are initially received in the browser's parent process. +With Fission, the browser needs to decide which of possibly several +content processes an event is targeting. + +Since process boundaries correspond to iframe (subdocument) boundaries, +and every (html) document has a root scroll frame, process boundaries are +therefore also scroll frame boundaries. Since APZ already needs a hit +test mechanism to be able to determine which scroll frame an event +targets, this hit test mechanism was a good fit to also use to determine +which content process an event targets. + +APZ's hit test was therefore expanded to serve this purpose as well. This +mostly required only minor modifications, such as making sure that APZ +knows about the root scroll frames of iframes even if they're not +scrollable. Since APZ already needs to process all input events to +potentially apply :ref:`untransformations ` +related to async scrolling, as part of this process it now also labels +input events with information identifying which content process they +target. + +Hit Testing Accuracy +^^^^^^^^^^^^^^^^^^^^ + +Prior to Fission, APZ's hit test could afford to be somewhat inaccurate, +as it could fall back on the dispatch-to-content mechanism to wait for +a more accurate answer from the main thread if necessary, suffering a +performance cost only (not a correctness cost). + +With Fission, an inaccurate compositor hit test now implies a correctness +cost, as there is no cross-process main-thread fallback mechanism. +(Such a mechanism was considered, but judged to require too much +complexity and IPC traffic to be worth it.) + +Luckily, with WebRender the compositor has much more detailed information +available to use for hit testing than it did with Layers. For example, +the compositor can perform accurate hit testing even in the presence of +irregular shapes such as rounded corners. + +APZ leverages WebRender's more accurate hit testing ability to aim to +accurately select the target process (and target scroll frame) for an +event in general. + +One consequence of this is that the dispatch-to-content mechanism is now +used less often than before (its primary remaining use is handling +`preventDefault()`). + +.. _sending-transforms-to-content-processes: + +Sending Transforms To Content Processes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Content processes sometimes need to be able to convert between screen +coordinates and their local coordinates. To do this, they need to know +about any transforms that their containing iframe and its ancestors are +subject to, including async transforms (particularly in cases where the +async transforms persist for more than just a few frames). + +APZ has information about these transforms in its HitTestingTree. With +Fission, APZ periodically sends content processes information about these +transforms so that they are kept relatively up to date. + Threading / Locking Overview ---------------------------- @@ -733,11 +824,11 @@ **The lock ordering is as follows**: 1. UI main -2. GPU main (only if GPU enabled) +2. GPU main (only if GPU process enabled) 3. Compositor thread -4. SceneBuilder thread (only if WR enabled) +4. SceneBuilder thread 5. **APZ tree lock** -6. RenderBackend thread (only if WR enabled) +6. RenderBackend thread 7. **APZC map lock** 8. **APZC instance lock** 9. **APZ test lock** diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/gl/GLContextProviderEGL.cpp firefox-trunk-95.0~a1~hg20211020r596404/gfx/gl/GLContextProviderEGL.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/gl/GLContextProviderEGL.cpp 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/gl/GLContextProviderEGL.cpp 2021-10-20 19:28:20.000000000 +0000 @@ -310,26 +310,34 @@ #ifdef MOZ_GTK_WAYLAND if (surface && GdkIsWaylandDisplay()) { // Make eglSwapBuffers() non-blocking on wayland - egl->fSwapInterval(0); + const int interval = gfxVars::SwapIntervalEGL() ? 1 : 0; + egl->fSwapInterval(interval); } #endif if (aHardwareWebRender && egl->mLib->IsANGLE()) { MOZ_ASSERT(doubleBuffered); - egl->fSwapInterval(0); + const int interval = gfxVars::SwapIntervalEGL() ? 1 : 0; + egl->fSwapInterval(interval); } return gl.forget(); } already_AddRefed GLContextEGLFactory::Create( EGLNativeWindowType aWindow, bool aHardwareWebRender) { - RefPtr glContext; -#if !defined(MOZ_WIDGET_ANDROID) - glContext = CreateImpl(aWindow, aHardwareWebRender, /* aUseGles */ false); -#endif // !defined(MOZ_WIDGET_ANDROID) + bool preferGles; +#if defined(MOZ_WIDGET_ANDROID) + preferGles = true; +#else + preferGles = StaticPrefs::gfx_egl_prefer_gles_enabled_AtStartup(); +#endif // defined(MOZ_WIDGET_ANDROID) + RefPtr glContext = + CreateImpl(aWindow, aHardwareWebRender, preferGles); +#if !defined(MOZ_WIDGET_ANDROID) if (!glContext) { - glContext = CreateImpl(aWindow, aHardwareWebRender, /* aUseGles */ true); + glContext = CreateImpl(aWindow, aHardwareWebRender, !preferGles); } +#endif // !defined(MOZ_WIDGET_ANDROID) return glContext.forget(); } @@ -494,8 +502,10 @@ MOZ_ASSERT(ok); #ifdef MOZ_GTK_WAYLAND if (mSurface && GdkIsWaylandDisplay()) { - // Make eglSwapBuffers() non-blocking on wayland - mEgl->fSwapInterval(0); + // The swap interval pref is false by default so that eglSwapBuffers() + // is non-blocking on wayland. + const int interval = gfxVars::SwapIntervalEGL() ? 1 : 0; + mEgl->fSwapInterval(interval); } #endif return ok; @@ -1179,12 +1189,21 @@ RefPtr GLContextEGL::CreateEGLPBufferOffscreenContext( const std::shared_ptr display, const GLContextCreateDesc& desc, const mozilla::gfx::IntSize& size, nsACString* const out_failureId) { + bool preferGles; +#if defined(MOZ_WIDGET_ANDROID) + preferGles = true; +#else + preferGles = StaticPrefs::gfx_egl_prefer_gles_enabled_AtStartup(); +#endif // defined(MOZ_WIDGET_ANDROID) + RefPtr gl = CreateEGLPBufferOffscreenContextImpl( - display, desc, size, /* useGles */ false, out_failureId); + display, desc, size, preferGles, out_failureId); +#if !defined(MOZ_WIDGET_ANDROID) if (!gl) { - gl = CreateEGLPBufferOffscreenContextImpl( - display, desc, size, /* useGles */ true, out_failureId); + gl = CreateEGLPBufferOffscreenContextImpl(display, desc, size, !preferGles, + out_failureId); } +#endif // !defined(MOZ_WIDGET_ANDROID) return gl; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/gl/GLContextProviderGLX.cpp firefox-trunk-95.0~a1~hg20211020r596404/gfx/gl/GLContextProviderGLX.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/gl/GLContextProviderGLX.cpp 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/gl/GLContextProviderGLX.cpp 2021-10-20 19:28:20.000000000 +0000 @@ -482,8 +482,10 @@ // Many GLX implementations default to blocking until the next // VBlank when calling glXSwapBuffers. We want to run unthrottled // in ASAP mode. See bug 1280744. + const bool swapInterval = gfxVars::SwapIntervalGLX(); const bool isASAP = (StaticPrefs::layout_frame_rate() == 0); - mGLX->fSwapInterval(*mDisplay, mDrawable, isASAP ? 0 : 1); + const int interval = (swapInterval && !isASAP) ? 1 : 0; + mGLX->fSwapInterval(*mDisplay, mDrawable, interval); } return succeeded; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/apz/src/HitTestingTreeNode.h firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/apz/src/HitTestingTreeNode.h --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/apz/src/HitTestingTreeNode.h 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/apz/src/HitTestingTreeNode.h 2021-10-20 19:28:20.000000000 +0000 @@ -13,8 +13,8 @@ #include "mozilla/layers/LayersTypes.h" // for EventRegions #include "mozilla/layers/ScrollableLayerGuid.h" // for ScrollableLayerGuid #include "mozilla/Maybe.h" // for Maybe +#include "mozilla/RecursiveMutex.h" // for RecursiveMutexAutoLock #include "mozilla/RefPtr.h" // for nsRefPtr - namespace mozilla { namespace layers { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/apz/test/mochitest/apz_test_utils.js firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/apz/test/mochitest/apz_test_utils.js --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/apz/test/mochitest/apz_test_utils.js 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/apz/test/mochitest/apz_test_utils.js 2021-10-20 19:28:21.000000000 +0000 @@ -775,14 +775,12 @@ // each time this function is called. // The computed information is an object with three fields: // utils: the nsIDOMWindowUtils instance for this window -// isWebRender: true if WebRender is enabled // isWindow: true if the platform is Windows // activateAllScrollFrames: true if prefs indicate all scroll frames are // activated with at least a minimal display port function getHitTestConfig() { if (!("hitTestConfig" in window)) { var utils = SpecialPowers.getDOMWindowUtils(window); - var isWebRender = utils.layerManagerType.startsWith("WebRender"); var isWindows = getPlatform() == "windows"; let activateAllScrollFrames = SpecialPowers.getBoolPref("apz.wr.activate_all_scroll_frames") || @@ -793,7 +791,6 @@ window.hitTestConfig = { utils, - isWebRender, isWindows, activateAllScrollFrames, }; @@ -941,27 +938,15 @@ // behaviour on different platforms which makes testing harder. var expectedHitInfo = APZHitResultFlags.VISIBLE | APZHitResultFlags.SCROLLBAR; if (params.expectThumb) { - // The thumb has listeners which are APZ-aware. With WebRender we are able - // to losslessly propagate this flag to APZ, but with non-WebRender the area - // ends up in the mDispatchToContentRegion which we then convert back to - // a IRREGULAR_AREA flag. This still works correctly since IRREGULAR_AREA - // will fall back to the main thread for everything. - if (config.isWebRender) { - expectedHitInfo |= APZHitResultFlags.APZ_AWARE_LISTENERS; - if ( - !config.activateAllScrollFrames && - params.layerState == LayerState.INACTIVE - ) { - expectedHitInfo |= APZHitResultFlags.INACTIVE_SCROLLFRAME; - } - } else { - expectedHitInfo |= APZHitResultFlags.IRREGULAR_AREA; + // The thumb has listeners which are APZ-aware. + expectedHitInfo |= APZHitResultFlags.APZ_AWARE_LISTENERS; + var expectActive = + config.activateAllScrollFrames || params.layerState == LayerState.ACTIVE; + if (!expectActive) { + expectedHitInfo |= APZHitResultFlags.INACTIVE_SCROLLFRAME; } // We do not generate the layers for thumbs on inactive scrollframes. - if ( - params.layerState == LayerState.ACTIVE || - config.activateAllScrollFrames - ) { + if (expectActive) { expectedHitInfo |= APZHitResultFlags.SCROLLBAR_THUMB; } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/apz/test/mochitest/browser_test_group_fission.js firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/apz/test/mochitest/browser_test_group_fission.js --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/apz/test/mochitest/browser_test_group_fission.js 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/apz/test/mochitest/browser_test_group_fission.js 2021-10-20 19:28:20.000000000 +0000 @@ -22,9 +22,6 @@ ); } - var utils = SpecialPowers.getDOMWindowUtils(window); - var isWebRender = utils.layerManagerType.startsWith("WebRender"); - // Each of these subtests is a dictionary that contains: // file (required): filename of the subtest that will get opened in a new tab // in the top-level fission-enabled browser window. @@ -64,26 +61,12 @@ { file: "helper_fission_initial_displayport.html" }, { file: "helper_fission_checkerboard_severity.html" }, { file: "helper_fission_setResolution.html" }, + { file: "helper_fission_inactivescroller_positionedcontent.html" }, + { file: "helper_fission_irregular_areas.html" }, + // Bug 1576514: On WebRender this test casues an assertion. + // { file: "helper_fission_animation_styling_in_transformed_oopif.html", }, // add additional tests here ]; - // These tests are to ensure hit-testing works perfectly on the WR - // codepath. The layers codepath may need a main-thread fallback to get - // these working, but we can't use our synchronous hitTest(...) helpers - // for those anyway. - if (isWebRender) { - subtests = subtests.concat([ - { file: "helper_fission_inactivescroller_positionedcontent.html" }, - { file: "helper_fission_irregular_areas.html" }, - // add WebRender-specific tests here - ]); - } else { - subtests = subtests.concat([ - // Bug 1576514: On WebRender this test casues an assertion. - { - file: "helper_fission_animation_styling_in_transformed_oopif.html", - }, - ]); - } // ccov builds run slower and need longer, so let's scale up the timeout // by the number of tests we're running. diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/apz/test/mochitest/helper_check_dp_size.html firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/apz/test/mochitest/helper_check_dp_size.html --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/apz/test/mochitest/helper_check_dp_size.html 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/apz/test/mochitest/helper_check_dp_size.html 2021-10-20 19:28:20.000000000 +0000 @@ -100,13 +100,13 @@ // but it gives a good idea that the good and bad values are far apart so // this test should be robust, and provides good context in the future if // this test starts failing. - await testOne( 50, 5.2, "(height 50)"); // good wr 256 non-wr 256, bad wr 256 non-wr 384 - await testOne(128, 2.1, "(height128)"); // good wr 256 non-wr 256, bad wr 512 non-wr 640 - await testOne(200, 2.0, "(height200)"); // good wr 384 non-wr 384, bad wr 768 non-wr 896 - await testOne(256, 1.6, "(height256)"); // good wr 384 non-wr 384, bad wr 768 non-wr 1024 - await testOne(329, 1.6, "(height329)"); // good wr 512 non-wr 512, bad wr 896 non-wr 1280 - await testOne(500, 1.3, "(height500)"); // good wr 640 non-wr 640, bad wr 1280 non-wr 1920 - await testOne(640, getHitTestConfig().isWebRender ? 1.7 : 1.3, "(height640)"); // good wr 1024 non-wr 768, bad wr 1536 non-wr 2432 + await testOne( 50, 5.2, "(height 50)"); // good 256, bad 256 + await testOne(128, 2.1, "(height128)"); // good 256, bad 512 + await testOne(200, 2.0, "(height200)"); // good 384, bad 768 + await testOne(256, 1.6, "(height256)"); // good 384, bad 768 + await testOne(329, 1.6, "(height329)"); // good 512, bad 896 + await testOne(500, 1.3, "(height500)"); // good 640, bad 280 + await testOne(640, 1.7, "(height640)"); // good 1024, bad 1536 } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/apz/test/mochitest/helper_hittest_basic.html firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/apz/test/mochitest/helper_hittest_basic.html --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/apz/test/mochitest/helper_hittest_basic.html 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/apz/test/mochitest/helper_hittest_basic.html 2021-10-20 19:28:20.000000000 +0000 @@ -25,12 +25,8 @@ var apzaware = document.getElementById("apzaware"); let expectedHitInfo = APZHitResultFlags.VISIBLE; - if (config.isWebRender) { - if (!config.activateAllScrollFrames) { - expectedHitInfo |= APZHitResultFlags.INACTIVE_SCROLLFRAME; - } - } else { - expectedHitInfo |= APZHitResultFlags.IRREGULAR_AREA; + if (!config.activateAllScrollFrames) { + expectedHitInfo |= APZHitResultFlags.INACTIVE_SCROLLFRAME; } checkHitResult(hitTest(centerOf(scroller)), expectedHitInfo, @@ -66,13 +62,11 @@ await promiseApzFlushedRepaints(); var scrollY = scroller.scrollTopMax; utils.setAsyncScrollOffset(scroller, 0, scrollY); - if (config.isWebRender) { - // Tick the refresh driver once to make sure the compositor has applied the - // async scroll offset (for APZ hit-testing this doesn't matter, but for - // WebRender hit-testing we need to make sure WR has the latest info). - utils.advanceTimeAndRefresh(16); - utils.restoreNormalRefresh(); - } + // Tick the refresh driver once to make sure the compositor has applied the + // async scroll offset (for WebRender hit-testing we need to make sure WR has + // the latest info). + utils.advanceTimeAndRefresh(16); + utils.restoreNormalRefresh(); var scrollerViewId = utils.getViewId(scroller); @@ -88,8 +82,7 @@ apzawarePosition.y -= scrollY; // APZ position checkHitResult(hitTest(apzawarePosition), APZHitResultFlags.VISIBLE | - (config.isWebRender ? APZHitResultFlags.APZ_AWARE_LISTENERS - : APZHitResultFlags.IRREGULAR_AREA), + APZHitResultFlags.APZ_AWARE_LISTENERS, scrollerViewId, utils.getLayersId(), "active scrollframe - apzaware block"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/apz/test/mochitest/helper_hittest_checkerboard.html firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/apz/test/mochitest/helper_hittest_checkerboard.html --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/apz/test/mochitest/helper_hittest_checkerboard.html 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/apz/test/mochitest/helper_hittest_checkerboard.html 2021-10-20 19:28:21.000000000 +0000 @@ -31,13 +31,11 @@ await promiseApzFlushedRepaints(); var scrollY = scroller.scrollTopMax; utils.setAsyncScrollOffset(scroller, 0, scrollY); - if (config.isWebRender) { - // Tick the refresh driver once to make sure the compositor has applied the - // async scroll offset (for APZ hit-testing this doesn't matter, but for - // WebRender hit-testing we need to make sure WR has the latest info). - utils.advanceTimeAndRefresh(16); - utils.restoreNormalRefresh(); - } + // Tick the refresh driver once to make sure the compositor has applied the + // async scroll offset (for WebRender hit-testing we need to make sure WR has + // the latest info). + utils.advanceTimeAndRefresh(16); + utils.restoreNormalRefresh(); var scrollerViewId = utils.getViewId(scroller); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/apz/test/mochitest/helper_hittest_clippath.html firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/apz/test/mochitest/helper_hittest_clippath.html --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/apz/test/mochitest/helper_hittest_clippath.html 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/apz/test/mochitest/helper_hittest_clippath.html 2021-10-20 19:28:20.000000000 +0000 @@ -35,8 +35,6 @@ var config = getHitTestConfig(); var utils = config.utils; - const wrTag = config.isWebRender ? "WebRender" : "Layers"; - // layerize the iframe var subwindow = document.getElementById("sub").contentWindow; var subscroller = subwindow.document.scrollingElement; @@ -53,29 +51,27 @@ APZHitResultFlags.VISIBLE, iframeViewId, layersId, - `${wrTag} (simple) uninteresting point inside the iframe`); + `(simple) uninteresting point inside the iframe`); checkHitResult(hitTest({ x: 500, y: 10 }), APZHitResultFlags.VISIBLE, rootViewId, layersId, - `${wrTag} (simple) uninteresting point in the root scroller`); + `(simple) uninteresting point in the root scroller`); checkHitResult(hitTest({ x: 110, y: 110 }), APZHitResultFlags.VISIBLE, iframeViewId, layersId, - `${wrTag} (simple) point in the iframe behind overlaying div, but outside the bounding box of the clip path`); + `(simple) point in the iframe behind overlaying div, but outside the bounding box of the clip path`); checkHitResult(hitTest({ x: 160, y: 160 }), - config.isWebRender ? APZHitResultFlags.VISIBLE - : APZHitResultFlags.VISIBLE | APZHitResultFlags.IRREGULAR_AREA, - config.isWebRender ? iframeViewId : rootViewId, + APZHitResultFlags.VISIBLE, + iframeViewId, layersId, - `${wrTag} (simple) point in the iframe behind overlaying div, inside the bounding box of the clip path, but outside the actual clip shape`); + `(simple) point in the iframe behind overlaying div, inside the bounding box of the clip path, but outside the actual clip shape`); checkHitResult(hitTest({ x: 300, y: 200 }), - config.isWebRender ? APZHitResultFlags.VISIBLE - : APZHitResultFlags.VISIBLE | APZHitResultFlags.IRREGULAR_AREA, + APZHitResultFlags.VISIBLE, rootViewId, layersId, - `${wrTag} (simple) point inside the clip shape of the overlaying div`); + `(simple) point inside the clip shape of the overlaying div`); // Now we turn the "simple" clip-path that WR can handle into a more complex // one that needs a mask. Then run the checks again; the expected results for @@ -87,29 +83,27 @@ APZHitResultFlags.VISIBLE, iframeViewId, layersId, - `${wrTag} (complex) uninteresting point inside the iframe`); + `(complex) uninteresting point inside the iframe`); checkHitResult(hitTest({ x: 500, y: 10 }), APZHitResultFlags.VISIBLE, rootViewId, layersId, - `${wrTag} (complex) uninteresting point in the root scroller`); + `(complex) uninteresting point in the root scroller`); checkHitResult(hitTest({ x: 110, y: 110 }), APZHitResultFlags.VISIBLE, iframeViewId, layersId, - `${wrTag} (complex) point in the iframe behind overlaying div, but outside the bounding box of the clip path`); + `(complex) point in the iframe behind overlaying div, but outside the bounding box of the clip path`); checkHitResult(hitTest({ x: 160, y: 160 }), - config.isWebRender ? APZHitResultFlags.VISIBLE - : APZHitResultFlags.VISIBLE | APZHitResultFlags.IRREGULAR_AREA, - config.isWebRender ? iframeViewId : rootViewId, + APZHitResultFlags.VISIBLE, + iframeViewId, layersId, - `${wrTag} (complex) point in the iframe behind overlaying div, inside the bounding box of the clip path, but outside the actual clip shape`); + `(complex) point in the iframe behind overlaying div, inside the bounding box of the clip path, but outside the actual clip shape`); checkHitResult(hitTest({ x: 300, y: 200 }), - config.isWebRender ? APZHitResultFlags.VISIBLE - : APZHitResultFlags.VISIBLE | APZHitResultFlags.IRREGULAR_AREA, - config.isWebRender ? iframeViewId : rootViewId, + APZHitResultFlags.VISIBLE, + iframeViewId, layersId, - `${wrTag} (complex) point inside the clip shape of the overlaying div`); + `(complex) point inside the clip shape of the overlaying div`); } waitUntilApzStable() diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/apz/test/mochitest/helper_hittest_hoisted_scrollinfo.html firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/apz/test/mochitest/helper_hittest_hoisted_scrollinfo.html --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/apz/test/mochitest/helper_hittest_hoisted_scrollinfo.html 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/apz/test/mochitest/helper_hittest_hoisted_scrollinfo.html 2021-10-20 19:28:20.000000000 +0000 @@ -64,11 +64,8 @@ utils.setDisplayPortForElement(0, 0, 300, 500, scroller, 1); await promiseApzFlushedRepaints(); - // Inactive scrollframe flags will round-trip through the dispatch-to-content - // region and end up as IRREGULAR_AREA when WebRender is disabled. - var expectedHitFlags = config.isWebRender - ? APZHitResultFlags.VISIBLE | APZHitResultFlags.INACTIVE_SCROLLFRAME - : APZHitResultFlags.VISIBLE | APZHitResultFlags.IRREGULAR_AREA; + var expectedHitFlags = + APZHitResultFlags.VISIBLE | APZHitResultFlags.INACTIVE_SCROLLFRAME; checkHitResult(hitTest(centerOf(scroller)), expectedHitFlags, utils.getViewId(scroller), diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/apz/test/mochitest/helper_hittest_overscroll_subframe.html firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/apz/test/mochitest/helper_hittest_overscroll_subframe.html --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/apz/test/mochitest/helper_hittest_overscroll_subframe.html 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/apz/test/mochitest/helper_hittest_overscroll_subframe.html 2021-10-20 19:28:20.000000000 +0000 @@ -41,9 +41,9 @@ var config = getHitTestConfig(); var utils = config.utils; - // Subframe hit testing of overscrolled APZCs does not yet work with WebRender, - // so bail out early. - if (config.isWebRender) { + // Subframe hit testing of overscrolled APZCs does not yet work with WebRender + // (bug 1701831), so bail out early. + if (true) { SimpleTest.todo(false, "This test does not currently pass with WebRender"); return; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/apz/test/mochitest/helper_hittest_touchaction.html firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/apz/test/mochitest/helper_hittest_touchaction.html --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/apz/test/mochitest/helper_hittest_touchaction.html 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/apz/test/mochitest/helper_hittest_touchaction.html 2021-10-20 19:28:20.000000000 +0000 @@ -120,12 +120,6 @@ var scrollId = config.utils.getViewId(document.scrollingElement); var layersId = config.utils.getLayersId(); - // Elements with APZ aware listeners round-trip through the dispatch-to-content - // region and end up as IRREGULAR_AREA when WebRender is disabled. - var touchListenerFlag = config.isWebRender - ? APZHitResultFlags.APZ_AWARE_LISTENERS - : APZHitResultFlags.IRREGULAR_AREA; - checkHitResult( hitTest(centerOf("taNone")), APZHitResultFlags.VISIBLE | @@ -263,7 +257,7 @@ checkHitResult( hitTest(centerOf("taInnerManipListener")), APZHitResultFlags.VISIBLE | - touchListenerFlag | + APZHitResultFlags.APZ_AWARE_LISTENERS | APZHitResultFlags.DOUBLE_TAP_ZOOM_DISABLED, scrollId, layersId, @@ -272,14 +266,14 @@ checkHitResult( hitTest(centerOf("taListener")), APZHitResultFlags.VISIBLE | - touchListenerFlag, + APZHitResultFlags.APZ_AWARE_LISTENERS, scrollId, layersId, "div with touch listener"); checkHitResult( hitTest(centerOf("taInnerListenerPanX")), APZHitResultFlags.VISIBLE | - touchListenerFlag | + APZHitResultFlags.APZ_AWARE_LISTENERS | APZHitResultFlags.PAN_Y_DISABLED | APZHitResultFlags.PINCH_ZOOM_DISABLED | APZHitResultFlags.DOUBLE_TAP_ZOOM_DISABLED, @@ -351,14 +345,9 @@ "overflow:hidden div doesn't reset pan-x/pan-y from enclosing scroller"); } -if (!config.isWebRender) { - ok(true, "This test is WebRender-only because we get a bunch of dispatch-to-content regions without it and the test isn't very interesting."); - subtestDone(); -} else { - waitUntilApzStable() - .then(test) - .then(subtestDone, subtestFailed); -} +waitUntilApzStable() + .then(test) + .then(subtestDone, subtestFailed); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/apz/test/mochitest/helper_overscroll_behavior_bug1425603.html firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/apz/test/mochitest/helper_overscroll_behavior_bug1425603.html --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/apz/test/mochitest/helper_overscroll_behavior_bug1425603.html 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/apz/test/mochitest/helper_overscroll_behavior_bug1425603.html 2021-10-20 19:28:20.000000000 +0000 @@ -55,13 +55,11 @@ await promiseAllPaintsDone(); var scrollY = 300; utils.setAsyncScrollOffset(subframe, 0, scrollY); - if (config.isWebRender) { - // Tick the refresh driver once to make sure the compositor has applied the - // async scroll offset (for APZ hit-testing this doesn't matter, but for - // WebRender hit-testing we need to make sure WR has the latest info). - utils.advanceTimeAndRefresh(16); - utils.restoreNormalRefresh(); - } + // Tick the refresh driver once to make sure the compositor has applied the + // async scroll offset (for WebRender hit-testing we need to make sure WR has + // the latest info). + utils.advanceTimeAndRefresh(16); + utils.restoreNormalRefresh(); // Scroll over the subframe, and make sure that the page does not scroll, // i.e. overscroll-behavior is respected. diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/apz/test/mochitest/helper_scrollframe_activation_on_load.html firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/apz/test/mochitest/helper_scrollframe_activation_on_load.html --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/apz/test/mochitest/helper_scrollframe_activation_on_load.html 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/apz/test/mochitest/helper_scrollframe_activation_on_load.html 2021-10-20 19:28:20.000000000 +0000 @@ -51,12 +51,10 @@ let config = getHitTestConfig(); let heightMultiplier = SpecialPowers.getCharPref("apz.y_stationary_size_multiplier"); - if (config.isWebRender) { - // With WebRender, the effective height multiplier can be reduced - // for alignment reasons. The reduction should be no more than a - // factor of two. - heightMultiplier /= 2; - } + // With WebRender, the effective height multiplier can be reduced + // for alignment reasons. The reduction should be no more than a + // factor of two. + heightMultiplier /= 2; info("effective displayport height multipler is " + heightMultiplier); let rootDisplayPort = getLastContentDisplayportFor('root-element'); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/apz/test/mochitest/test_group_hittest-2.html firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/apz/test/mochitest/test_group_hittest-2.html --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/apz/test/mochitest/test_group_hittest-2.html 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/apz/test/mochitest/test_group_hittest-2.html 2021-10-20 19:28:21.000000000 +0000 @@ -33,6 +33,7 @@ ]; var subtests = [ + {"file": "helper_hittest_deep_scene_stack.html", "prefs": prefs}, {"file": "helper_hittest_pointerevents_svg.html", "prefs": prefs}, {"file": "helper_hittest_clippath.html", "prefs": prefs}, {"file": "helper_hittest_hoisted_scrollinfo.html", "prefs": prefs}, @@ -47,26 +48,10 @@ {"file": "helper_hittest_spam.html", "prefs": prefs}, ]; -function addConditionalTests(tests) { - // Add some more tests only useful with WebRender. Note that we do this in - // function run after loading, because trying to read layerManagerType can - // throw an NS_ERROR_FAILURE if called too early. - var utils = SpecialPowers.getDOMWindowUtils(window); - var isWebRender = utils.layerManagerType.startsWith("WebRender"); - if (isWebRender) { - // Add new tests at the beginning, to ensure the final test remains in - // the final position. - tests = [ - {"file": "helper_hittest_deep_scene_stack.html", "prefs": prefs}, - ].concat(tests); - } - return tests; -} - if (isApzEnabled()) { SimpleTest.waitForExplicitFinish(); window.onload = function() { - runSubtestsSeriallyInFreshWindows(addConditionalTests(subtests)) + runSubtestsSeriallyInFreshWindows(subtests) .then(SimpleTest.finish, SimpleTest.finishWithFailure); }; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/apz/test/mochitest/test_layerization.html firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/apz/test/mochitest/test_layerization.html --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/apz/test/mochitest/test_layerization.html 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/apz/test/mochitest/test_layerization.html 2021-10-20 19:28:20.000000000 +0000 @@ -64,12 +64,10 @@ let activateAllScrollFrames = config.activateAllScrollFrames; let heightMultiplier = SpecialPowers.getCharPref("apz.y_stationary_size_multiplier"); -if (config.isWebRender) { - // With WebRender, the effective height multiplier can be reduced - // for alignment reasons. The reduction should be no more than a - // factor of two. - heightMultiplier /= 2; -} +// With WebRender, the effective height multiplier can be reduced +// for alignment reasons. The reduction should be no more than a +// factor of two. +heightMultiplier /= 2; info("effective displayport height multipler is " + heightMultiplier); function hasNonZeroMarginDisplayPort(elementId, containingDoc = null) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/apz/test/reftest/reftest.list firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/apz/test/reftest/reftest.list --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/apz/test/reftest/reftest.list 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/apz/test/reftest/reftest.list 2021-10-20 19:28:20.000000000 +0000 @@ -32,8 +32,8 @@ # Test that position:fixed and position:sticky elements are attached to the # layout viewport. -pref(apz.allow_zooming,true) == pinch-zoom-position-fixed.html pinch-zoom-position-fixed-ref.html -pref(apz.allow_zooming,true) == pinch-zoom-position-sticky.html pinch-zoom-position-sticky-ref.html +fuzzy-if(winWidget&&isCoverageBuild,1-1,24-24) pref(apz.allow_zooming,true) == pinch-zoom-position-fixed.html pinch-zoom-position-fixed-ref.html +fuzzy-if(winWidget&&isCoverageBuild,1-1,24-24) pref(apz.allow_zooming,true) == pinch-zoom-position-sticky.html pinch-zoom-position-sticky-ref.html pref(apz.allow_zooming,true) == iframe-zoomed.html iframe-zoomed-ref.html pref(apz.allow_zooming,true) == scaled-iframe-zoomed.html scaled-iframe-zoomed-ref.html diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/composite/ImageComposite.cpp firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/composite/ImageComposite.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/composite/ImageComposite.cpp 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/composite/ImageComposite.cpp 2021-10-20 19:28:21.000000000 +0000 @@ -44,7 +44,8 @@ ? mImages[aImageIndex + 1].mTimeStamp : TimeStamp(); - if (profiler_can_accept_markers() && compositedImageTime && nextImageTime) { + if (profiler_thread_is_being_profiled() && compositedImageTime && + nextImageTime) { TimeDuration offsetCurrent = compositedImageTime - compositionTime; TimeDuration offsetNext = nextImageTime - compositionTime; nsPrintfCString str("current %.2lfms, next %.2lfms", @@ -181,7 +182,7 @@ // will never be shown. CountSkippedFrames(&aNewImages[0]); - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled()) { int len = aNewImages.Length(); const auto& first = aNewImages[0]; const auto& last = aNewImages.LastElement(); @@ -208,7 +209,7 @@ "Should only be called during a composition"); nsCString descr; - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled()) { nsCString relativeTimeString; if (image.mTimeStamp) { relativeTimeString.AppendPrintf( @@ -253,7 +254,7 @@ if (dropped > 0) { mDroppedFrames += dropped; - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled()) { const char* frameOrFrames = dropped == 1 ? "frame" : "frames"; nsPrintfCString text("%" PRId32 " %s dropped: %" PRId32 " -> %" PRId32 " (producer %" PRId32 ")", @@ -346,7 +347,7 @@ } void ImageComposite::DetectTimeStampJitter(const TimedImage* aNewImage) { - if (!profiler_can_accept_markers() || aNewImage->mTimeStamp.IsNull()) { + if (!profiler_thread_is_being_profiled() || aNewImage->mTimeStamp.IsNull()) { return; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/d3d11/ReadbackManagerD3D11.cpp firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/d3d11/ReadbackManagerD3D11.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/d3d11/ReadbackManagerD3D11.cpp 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/d3d11/ReadbackManagerD3D11.cpp 2021-10-20 19:28:20.000000000 +0000 @@ -5,7 +5,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ReadbackManagerD3D11.h" -#include "ReadbackLayer.h" #include "mozilla/layers/TextureClient.h" #include "mozilla/gfx/2D.h" diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/ipc/CompositorBridgeParent.cpp firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/ipc/CompositorBridgeParent.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/ipc/CompositorBridgeParent.cpp 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/ipc/CompositorBridgeParent.cpp 2021-10-20 19:28:20.000000000 +0000 @@ -1918,7 +1918,7 @@ double latencyNorm = latencyMs / aVsyncRate.ToMilliseconds(); int32_t fracLatencyNorm = lround(latencyNorm * 100.0); - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled()) { struct ContentFrameMarker { static constexpr Span MarkerTypeName() { return MakeStringSpan("CONTENT_FRAME_TIME"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/LayerManager.h firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/LayerManager.h --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/LayerManager.h 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/LayerManager.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,424 +0,0 @@ -/* -*- 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/. */ - -#ifndef GFX_LAYERMANAGER_H -#define GFX_LAYERMANAGER_H - -#include // for uint32_t, uint64_t, int32_t, uint8_t -#include // for stringstream -#include // for operator new -#include // for unordered_set -#include // for forward -#include "FrameMetrics.h" // for ScrollUpdatesMap -#include "ImageContainer.h" // for ImageContainer, ImageContainer::Mode, ImageContainer::SYNCHRONOUS -#include "WindowRenderer.h" -#include "mozilla/AlreadyAddRefed.h" // for already_AddRefed -#include "mozilla/Maybe.h" // for Maybe -#include "mozilla/RefPtr.h" // for RefPtr -#include "mozilla/TimeStamp.h" // for TimeStamp -#include "mozilla/UniquePtr.h" // for UniquePtr -#include "mozilla/dom/Animation.h" // for Animation -#include "mozilla/gfx/Point.h" // for IntSize -#include "mozilla/gfx/Types.h" // for SurfaceFormat -#include "mozilla/gfx/UserData.h" // for UserData, UserDataKey (ptr only) -#include "mozilla/layers/CompositorTypes.h" // for TextureFactoryIdentifier -#include "mozilla/layers/ScrollableLayerGuid.h" // for ScrollableLayerGuid, ScrollableLayerGuid::ViewID -#include "nsHashKeys.h" // for nsUint64HashKey -#include "nsISupports.h" // for NS_INLINE_DECL_REFCOUNTING -#include "nsIWidget.h" // for nsIWidget -#include "nsRefPtrHashtable.h" // for nsRefPtrHashtable -#include "nsRegion.h" // for nsIntRegion -#include "nsStringFwd.h" // for nsCString, nsAString -#include "nsTArray.h" // for nsTArray - -// XXX These includes could be avoided by moving function implementations to the -// cpp file -#include "mozilla/Assertions.h" // for AssertionConditionType, MOZ_ASSERT, MOZ_A... -#include "mozilla/layers/LayersTypes.h" // for CompositionPayload, LayersBackend, TransactionId, DrawRegionClip, LayersBackend:... - -class gfxContext; - -extern uint8_t gLayerManagerLayerBuilder; - -namespace mozilla { - -class FrameLayerBuilder; -class LogModule; -class ScrollPositionUpdate; - -namespace gfx { -class DrawTarget; -} // namespace gfx - -namespace layers { - -class AsyncPanZoomController; -class ClientLayerManager; -class Layer; -class CompositorBridgeChild; -class ReadbackProcessor; -class FocusTarget; -class KnowsCompositor; -class TransactionIdAllocator; -class FrameUniformityData; -class PersistentBufferProvider; -class GlyphArray; -class WebRenderLayerManager; -struct AnimData; - -// Defined in LayerUserData.h; please include that file instead. -class LayerUserData; - -class DidCompositeObserver { - public: - virtual void DidComposite() = 0; -}; - -/* - * Motivation: For truly smooth animation and video playback, we need to - * be able to compose frames and render them on a dedicated thread (i.e. - * off the main thread where DOM manipulation, script execution and layout - * induce difficult-to-bound latency). This requires Gecko to construct - * some kind of persistent scene structure (graph or tree) that can be - * safely transmitted across threads. We have other scenarios (e.g. mobile - * browsing) where retaining some rendered data between paints is desired - * for performance, so again we need a retained scene structure. - * - * Our retained scene structure is a layer tree. Each layer represents - * content which can be composited onto a destination surface; the root - * layer is usually composited into a window, and non-root layers are - * composited into their parent layers. Layers have attributes (e.g. - * opacity and clipping) that influence their compositing. - * - * We want to support a variety of layer implementations, including - * a simple "immediate mode" implementation that doesn't retain any - * rendered data between paints (i.e. uses cairo in just the way that - * Gecko used it before layers were introduced). But we also don't want - * to have bifurcated "layers"/"non-layers" rendering paths in Gecko. - * Therefore the layers API is carefully designed to permit maximally - * efficient implementation in an "immediate mode" style. See the - * BasicLayerManager for such an implementation. - */ - -/** - * A LayerManager controls a tree of layers. All layers in the tree - * must use the same LayerManager. - * - * All modifications to a layer tree must happen inside a transaction. - * Only the state of the layer tree at the end of a transaction is - * rendered. Transactions cannot be nested - * - * Each transaction has two phases: - * 1) Construction: layers are created, inserted, removed and have - * properties set on them in this phase. - * BeginTransaction and BeginTransactionWithTarget start a transaction in - * the Construction phase. - * 2) Drawing: PaintedLayers are rendered into in this phase, in tree - * order. When the client has finished drawing into the PaintedLayers, it should - * call EndTransaction to complete the transaction. - * - * All layer API calls happen on the main thread. - * - * Layers are refcounted. The layer manager holds a reference to the - * root layer, and each container layer holds a reference to its children. - */ -class LayerManager : public WindowRenderer { - protected: - typedef mozilla::gfx::DrawTarget DrawTarget; - typedef mozilla::gfx::IntSize IntSize; - typedef mozilla::gfx::SurfaceFormat SurfaceFormat; - - public: - LayerManager(); - - /** - * Release layers and resources held by this layer manager, and mark - * it as destroyed. Should do any cleanup necessary in preparation - * for its widget going away. After this call, only user data calls - * are valid on the layer manager. - */ - void Destroy() override; - bool IsDestroyed() { return mDestroyed; } - - virtual WebRenderLayerManager* AsWebRenderLayerManager() { return nullptr; } - - /** - * Returns true if this LayerManager is owned by an nsIWidget, - * and is used for drawing into the widget. - */ - virtual bool IsWidgetLayerManager() { return true; } - virtual bool IsInactiveLayerManager() { return false; } - - /** - * Start a new transaction. Nested transactions are not allowed so - * there must be no transaction currently in progress. - * This transaction will render the contents of the layer tree to - * the given target context. The rendering will be complete when - * EndTransaction returns. - */ - virtual bool BeginTransactionWithTarget( - gfxContext* aTarget, const nsCString& aURL = nsCString()) = 0; - - FrameLayerBuilder* GetLayerBuilder() { - return reinterpret_cast( - GetUserData(&gLayerManagerLayerBuilder)); - } - - /** - * Schedule a composition with the remote Compositor, if one exists - * for this LayerManager. Useful in conjunction with the - * END_NO_REMOTE_COMPOSITE flag to EndTransaction. - */ - virtual void ScheduleComposite() {} - - virtual void SetNeedsComposite(bool aNeedsComposite) {} - virtual bool NeedsComposite() const { return false; } - - virtual bool HasShadowManagerInternal() const { return false; } - bool HasShadowManager() const { return HasShadowManagerInternal(); } - virtual void StorePluginWidgetConfigurations( - const nsTArray& aConfigurations) {} - bool IsSnappingEffectiveTransforms() { return mSnapEffectiveTransforms; } - - /** - * Returns true if this LayerManager always requires an intermediate surface - * to render blend operations. - */ - virtual bool BlendingRequiresIntermediateSurface() { return false; } - - /** - * CONSTRUCTION PHASE ONLY - * Set the root layer. The root layer is initially null. If there is - * no root layer, EndTransaction won't draw anything. - */ - virtual void SetRoot(Layer* aLayer) = 0; - /** - * Can be called anytime - */ - Layer* GetRoot() { return nullptr; } - - /** - * CONSTRUCTION PHASE ONLY - * Called when a managee has mutated. - * Subclasses overriding this method must first call their - * superclass's impl - */ - virtual void Mutated(Layer* aLayer) {} - virtual void MutatedSimple(Layer* aLayer) {} - - /** - * Can be called anytime, from any thread. - * - * Creates an Image container which forwards its images to the compositor - * within layer transactions on the main thread or asynchronously using the - * ImageBridge IPDL protocol. In the case of asynchronous, If the protocol is - * not available, the returned ImageContainer will forward images within layer - * transactions. - */ - static already_AddRefed CreateImageContainer( - ImageContainer::Mode flag = ImageContainer::SYNCHRONOUS); - - /** - * Creates a DrawTarget which is optimized for inter-operating with this - * layer manager. - */ - virtual already_AddRefed CreateOptimalDrawTarget( - const IntSize& aSize, SurfaceFormat imageFormat); - - /** - * Creates a DrawTarget for alpha masks which is optimized for inter- - * operating with this layer manager. In contrast to CreateOptimalDrawTarget, - * this surface is optimised for drawing alpha only and we assume that - * drawing the mask is fairly simple. - */ - virtual already_AddRefed CreateOptimalMaskDrawTarget( - const IntSize& aSize); - - /** - * Creates a DrawTarget for use with canvas which is optimized for - * inter-operating with this layermanager. - */ - virtual already_AddRefed CreateDrawTarget( - const mozilla::gfx::IntSize& aSize, mozilla::gfx::SurfaceFormat aFormat); - - /** - * This setter can be used anytime. The user data for all keys is - * initially null. Ownership pases to the layer manager. - */ - void SetUserData(void* aKey, LayerUserData* aData) { - mUserData.Add(static_cast(aKey), aData, - LayerUserDataDestroy); - } - /** - * This can be used anytime. Ownership passes to the caller! - */ - UniquePtr RemoveUserData(void* aKey); - - /** - * This getter can be used anytime. - */ - bool HasUserData(void* aKey) { - return mUserData.Has(static_cast(aKey)); - } - /** - * This getter can be used anytime. Ownership is retained by the layer - * manager. - */ - LayerUserData* GetUserData(void* aKey) const { - return static_cast( - mUserData.Get(static_cast(aKey))); - } - - /** - * Must be called outside of a layers transaction. - * - * For the subtree rooted at |aSubtree|, this attempts to free up - * any free-able resources like retained buffers, but may do nothing - * at all. After this call, the layer tree is left in an undefined - * state; the layers in |aSubtree|'s subtree may no longer have - * buffers with valid content and may no longer be able to draw - * their visible and valid regions. - * - * In general, a painting or forwarding transaction on |this| must - * complete on the tree before it returns to a valid state. - * - * Resource freeing begins from |aSubtree| or |mRoot| if |aSubtree| - * is null. |aSubtree|'s manager must be this. - */ - virtual void ClearCachedResources(Layer* aSubtree = nullptr) {} - - /** - * Flag the next paint as the first for a document. - */ - virtual void SetIsFirstPaint() {} - virtual bool GetIsFirstPaint() const { return false; } - - /** - * Set the current focus target to be sent with the next paint. - */ - virtual void SetFocusTarget(const FocusTarget& aFocusTarget) {} - - virtual void SendInvalidRegion(const nsIntRegion& aRegion) {} - - virtual const char* Name() const { return "???"; } - - /** - * Dump information about this layer manager and its managed tree to - * aStream. - */ - void Dump(std::stringstream& aStream, const char* aPrefix = "", - bool aDumpHtml = false, bool aSorted = false); - /** - * Dump information about just this layer manager itself to aStream - */ - void DumpSelf(std::stringstream& aStream, const char* aPrefix = "", - bool aSorted = false); - void Dump(bool aSorted = false); - - /** - * Log information about this layer manager and its managed tree to - * the NSPR log (if enabled for "Layers"). - */ - void Log(const char* aPrefix = ""); - /** - * Log information about just this layer manager itself to the NSPR - * log (if enabled for "Layers"). - */ - void LogSelf(const char* aPrefix = ""); - - static bool IsLogEnabled(); - static mozilla::LogModule* GetLog(); - - bool IsInTransaction() const { return mInTransaction; } - - virtual void SetRegionToClear(const nsIntRegion& aRegion) { - mRegionToClear = aRegion; - } - - virtual float RequestProperty(const nsAString& property) { return -1; } - - virtual bool AsyncPanZoomEnabled() const { return false; } - - static void LayerUserDataDestroy(void* data); - - void AddPaintedPixelCount(int32_t aCount) { mPaintedPixelCount += aCount; } - - uint32_t GetAndClearPaintedPixelCount() { - uint32_t count = mPaintedPixelCount; - mPaintedPixelCount = 0; - return count; - } - - virtual void SetLayersObserverEpoch(LayersObserverEpoch aEpoch) {} - - virtual void DidComposite(TransactionId aTransactionId, - const mozilla::TimeStamp& aCompositeStart, - const mozilla::TimeStamp& aCompositeEnd) {} - - virtual void AddDidCompositeObserver(DidCompositeObserver* aObserver) { - MOZ_CRASH("GFX: LayerManager"); - } - virtual void RemoveDidCompositeObserver(DidCompositeObserver* aObserver) { - MOZ_CRASH("GFX: LayerManager"); - } - - virtual void UpdateTextureFactoryIdentifier( - const TextureFactoryIdentifier& aNewIdentifier) {} - - virtual TextureFactoryIdentifier GetTextureFactoryIdentifier() { - return TextureFactoryIdentifier(); - } - - virtual void SetTransactionIdAllocator(TransactionIdAllocator* aAllocator) {} - - virtual TransactionId GetLastTransactionId() { return TransactionId{0}; } - - void SetContainsSVG(bool aContainsSVG) { mContainsSVG = aContainsSVG; } - - protected: - gfx::UserData mUserData; - bool mDestroyed; - bool mSnapEffectiveTransforms; - - nsIntRegion mRegionToClear; - - // Protected destructor, to discourage deletion outside of Release(): - virtual ~LayerManager(); - - // Print interesting information about this into aStreamo. Internally - // used to implement Dump*() and Log*(). - virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix); - - uint64_t mId; - bool mInTransaction; - - // Used for tracking CONTENT_FRAME_TIME_WITH_SVG - bool mContainsSVG; - // The count of pixels that were painted in the current transaction. - uint32_t mPaintedPixelCount; - - public: - /* - * Methods to store/get/clear a "pending scroll info update" object on a - * per-scrollid basis. This is used for empty transactions that push over - * scroll position updates to the APZ code. - */ - virtual bool AddPendingScrollUpdateForNextTransaction( - ScrollableLayerGuid::ViewID aScrollId, - const ScrollPositionUpdate& aUpdateInfo) override; - Maybe> GetPendingScrollInfoUpdate( - ScrollableLayerGuid::ViewID aScrollId); - std::unordered_set - ClearPendingScrollInfoUpdate(); - - protected: - ScrollUpdatesMap mPendingScrollUpdates; -}; - -} // namespace layers -} // namespace mozilla - -#endif /* GFX_LAYERS_H */ diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/Layers.cpp firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/Layers.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/Layers.cpp 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/Layers.cpp 2021-10-20 19:28:20.000000000 +0000 @@ -14,9 +14,7 @@ #include // for char_traits, string, basic_string #include // for remove_reference<>::type #include "CompositableHost.h" // for CompositableHost -#include "GeckoProfiler.h" // for profiler_can_accept_markers, PROFILER_MARKER_TEXT -#include "LayerUserData.h" // for LayerUserData -#include "ReadbackLayer.h" // for ReadbackLayer +#include "LayerUserData.h" // for LayerUserData #include "TreeTraversal.h" // for ForwardIterator, ForEachNode, DepthFirstSearch, TraversalFlag, TraversalFl... #include "UnitTransforms.h" // for ViewAs, PixelCastJustification, PixelCastJustification::RenderTargetIsPare... #include "apz/src/AsyncPanZoomController.h" // for AsyncPanZoomController @@ -25,9 +23,9 @@ #include "gfxMatrix.h" // for gfxMatrix #include "gfxUtils.h" // for gfxUtils, gfxUtils::sDumpPaintFile #include "mozilla/ArrayIterator.h" // for ArrayIterator -#include "mozilla/BaseProfilerMarkersPrerequisites.h" // for MarkerTiming -#include "mozilla/DebugOnly.h" // for DebugOnly +#include "mozilla/DebugOnly.h" // for DebugOnly #include "mozilla/Logging.h" // for LogLevel, LogLevel::Debug, MOZ_LOG_TEST +#include "mozilla/ProfilerMarkers.h" // for profiler_thread_is_being_profiled, PROFILER_MARKER_TEXT #include "mozilla/ScrollPositionUpdate.h" // for ScrollPositionUpdate #include "mozilla/Telemetry.h" // for AccumulateTimeDelta #include "mozilla/TelemetryHistogramEnums.h" // for KEYPRESS_PRESENT_LATENCY, SCROLL_PRESENT_LATENCY @@ -78,10 +76,6 @@ } } -void WriteSnapshotToDumpFile(LayerManager* aManager, DataSourceSurface* aSurf) { - WriteSnapshotToDumpFile_internal(aManager, aSurf); -} - void WriteSnapshotToDumpFile(Compositor* aCompositor, DrawTarget* aTarget) { RefPtr surf = aTarget->Snapshot(); RefPtr dSurf = surf->GetDataSurface(); @@ -104,7 +98,7 @@ if (aPayloads.Length()) { TimeStamp presented = aCompositionEndTime; for (const CompositionPayload& payload : aPayloads) { - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled()) { MOZ_RELEASE_ASSERT(payload.mType <= kHighestCompositionPayloadType); nsAutoCString name( kCompositionPayloadTypeNames[uint8_t(payload.mType)]); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/Layers.h firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/Layers.h --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/Layers.h 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/Layers.h 2021-10-20 19:28:21.000000000 +0000 @@ -32,7 +32,6 @@ #include "mozilla/gfx/UserData.h" // for UserData, UserDataKey (ptr only) #include "mozilla/layers/AnimationInfo.h" // for AnimationInfo #include "mozilla/layers/LayerAttributes.h" // for SimpleLayerAttributes, ScrollbarData (ptr only) -#include "mozilla/layers/LayerManager.h" // for LayerManager #include "mozilla/layers/ScrollableLayerGuid.h" // for ScrollableLayerGuid, ScrollableLayerGuid::ViewID #include "mozilla/layers/BSPTree.h" #include "nsISupports.h" // for NS_INLINE_DECL_REFCOUNTING @@ -124,8 +123,6 @@ #ifdef MOZ_DUMP_PAINTING void WriteSnapshotToDumpFile(Layer* aLayer, gfx::DataSourceSurface* aSurf); -void WriteSnapshotToDumpFile(LayerManager* aManager, - gfx::DataSourceSurface* aSurf); void WriteSnapshotToDumpFile(Compositor* aCompositor, gfx::DrawTarget* aTarget); #endif diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/moz.build firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/moz.build --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/moz.build 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/moz.build 2021-10-20 19:28:21.000000000 +0000 @@ -25,7 +25,6 @@ "LayerUserData.h", "opengl/OGLShaderConfig.h", "opengl/OGLShaderProgram.h", - "ReadbackLayer.h", ] if CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows": @@ -177,7 +176,6 @@ "ipc/VideoBridgeParent.h", "ipc/VideoBridgeUtils.h", "LayerAttributes.h", - "LayerManager.h", "LayersTypes.h", "MemoryPressureObserver.h", "NativeLayer.h", diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/ProfilerScreenshots.cpp firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/ProfilerScreenshots.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/ProfilerScreenshots.cpp 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/ProfilerScreenshots.cpp 2021-10-20 19:28:20.000000000 +0000 @@ -84,7 +84,7 @@ originalSize, scaledSize, timeStamp]() { // Create a new surface that wraps backingSurface's data but has the // correct size. - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled(sourceThread)) { DataSourceSurface::ScopedMap scopedMap(backingSurface, DataSourceSurface::READ); RefPtr surf = diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/ReadbackLayer.h firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/ReadbackLayer.h --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/ReadbackLayer.h 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/ReadbackLayer.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,71 +0,0 @@ -/* -*- 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/. */ - -#ifndef GFX_READBACKLAYER_H -#define GFX_READBACKLAYER_H - -#include // for uint64_t -#include "Layers.h" // for Layer, etc -#include "mozilla/gfx/Rect.h" // for gfxRect -#include "mozilla/gfx/Point.h" // for IntPoint -#include "mozilla/mozalloc.h" // for operator delete -#include "nsCOMPtr.h" // for already_AddRefed -#include "nsDebug.h" // for NS_ASSERTION -#include "nsPoint.h" // for nsIntPoint -#include "nscore.h" // for nsACString - -class gfxContext; - -namespace mozilla { -namespace layers { - -class ReadbackProcessor; - -/** - * A ReadbackSink receives a stream of updates to a rectangle of pixels. - * These update callbacks are always called on the main thread, either during - * EndTransaction or from the event loop. - */ -class ReadbackSink { - public: - ReadbackSink() = default; - virtual ~ReadbackSink() = default; - - /** - * Sends an update to indicate that the background is currently unknown. - */ - virtual void SetUnknown(uint64_t aSequenceNumber) = 0; - /** - * Called by the layer system to indicate that the contents of part of - * the readback area are changing. - * @param aRect is the rectangle of content that is being updated, - * in the coordinate system of the ReadbackLayer. - * @param aSequenceNumber updates issued out of order should be ignored. - * Only use updates whose sequence counter is greater than all other updates - * seen so far. Return null when a non-fresh sequence value is given. - * @return a context into which the update should be drawn. This should be - * set up to clip to aRect. Zero should never be passed as a sequence number. - * If this returns null, EndUpdate should NOT be called. If it returns - * non-null, EndUpdate must be called. - * - * We don't support partially unknown backgrounds. Therefore, the - * first BeginUpdate after a SetUnknown will have the complete background. - */ - virtual already_AddRefed BeginUpdate( - const gfx::IntRect& aRect, uint64_t aSequenceNumber) = 0; - /** - * EndUpdate must be called immediately after BeginUpdate, without returning - * to the event loop. - * @param aContext the context returned by BeginUpdate - * Implicitly Restore()s the state of aContext. - */ - virtual void EndUpdate(const gfx::IntRect& aRect) = 0; -}; - -} // namespace layers -} // namespace mozilla - -#endif /* GFX_READBACKLAYER_H */ diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/wr/WebRenderBridgeParent.cpp firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/wr/WebRenderBridgeParent.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/wr/WebRenderBridgeParent.cpp 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/wr/WebRenderBridgeParent.cpp 2021-10-20 19:28:20.000000000 +0000 @@ -221,7 +221,7 @@ CompositorThread()->Dispatch(NS_NewRunnableFunction( "SceneBuiltNotificationRunnable", [parent, epoch, startTime]() { auto endTime = TimeStamp::Now(); - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled()) { PROFILER_MARKER("CONTENT_FULL_PAINT_TIME", GRAPHICS, MarkerTiming::Interval(startTime, endTime), ContentBuildMarker); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/wr/WebRenderBridgeParent.h firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/wr/WebRenderBridgeParent.h --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/wr/WebRenderBridgeParent.h 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/wr/WebRenderBridgeParent.h 2021-10-20 19:28:20.000000000 +0000 @@ -15,7 +15,6 @@ #include "mozilla/DataMutex.h" #include "mozilla/layers/CompositableTransactionParent.h" #include "mozilla/layers/CompositorVsyncSchedulerOwner.h" -#include "mozilla/layers/LayerManager.h" #include "mozilla/layers/PWebRenderBridgeParent.h" #include "mozilla/HashTable.h" #include "mozilla/Maybe.h" diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/wr/WebRenderCommandBuilder.cpp firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/wr/WebRenderCommandBuilder.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/wr/WebRenderCommandBuilder.cpp 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/wr/WebRenderCommandBuilder.cpp 2021-10-20 19:28:21.000000000 +0000 @@ -97,8 +97,6 @@ // during invalidations. std::vector> mExternalSurfaces; - IntRect mImageRect; - BlobItemData(DIGroup* aGroup, nsDisplayItem* aItem) : mUsed(false), mGroup(aGroup) { mInvalid = false; @@ -406,9 +404,7 @@ InvalidateRect(aData->mRect); aData->mInvalid = true; invalidated = true; - } else if (aData->mInvalid || - /* XXX: handle image load invalidation */ ( - aItem->IsInvalid(invalid) && invalid.IsEmpty())) { + } else if (aItem->IsInvalid(invalid) && invalid.IsEmpty()) { UniquePtr geometry( aItem->AllocateGeometry(aBuilder)); nsRect clippedBounds = clip.ApplyNonRoundedIntersection( @@ -543,7 +539,6 @@ } mActualBounds.OrWith(aData->mRect); aData->mClip = clip; - aData->mImageRect = mClippedImageBounds; GP("post mInvalidRect: %d %d %d %d\n", mInvalidRect.x, mInvalidRect.y, mInvalidRect.width, mInvalidRect.height); return invalidated; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/wr/WebRenderLayerManager.cpp firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/wr/WebRenderLayerManager.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/wr/WebRenderLayerManager.cpp 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/wr/WebRenderLayerManager.cpp 2021-10-20 19:28:21.000000000 +0000 @@ -602,11 +602,6 @@ mTransactionIdAllocator->NotifyTransactionCompleted(aTransactionId); } } - - // These observers fire whether or not we were in a transaction. - for (size_t i = 0; i < mDidCompositeObservers.Length(); i++) { - mDidCompositeObservers[i]->DidComposite(); - } } void WebRenderLayerManager::ClearCachedResources() { @@ -673,18 +668,6 @@ return mLatestTransactionId; } -void WebRenderLayerManager::AddDidCompositeObserver( - DidCompositeObserver* aObserver) { - if (!mDidCompositeObservers.Contains(aObserver)) { - mDidCompositeObservers.AppendElement(aObserver); - } -} - -void WebRenderLayerManager::RemoveDidCompositeObserver( - DidCompositeObserver* aObserver) { - mDidCompositeObservers.RemoveElement(aObserver); -} - void WebRenderLayerManager::FlushRendering(wr::RenderReasons aReasons) { CompositorBridgeChild* cBridge = GetCompositorBridgeChild(); if (!cBridge) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/wr/WebRenderLayerManager.h firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/wr/WebRenderLayerManager.h --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/wr/WebRenderLayerManager.h 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/wr/WebRenderLayerManager.h 2021-10-20 19:28:21.000000000 +0000 @@ -23,12 +23,12 @@ #include "mozilla/layers/CompositorTypes.h" // for TextureFactoryIdentifier #include "mozilla/layers/DisplayItemCache.h" // for DisplayItemCache #include "mozilla/layers/FocusTarget.h" // for FocusTarget -#include "mozilla/layers/LayerManager.h" // for DidCompositeObserver (ptr only), LayerManager::END_DEFAULT, LayerManager::En... #include "mozilla/layers/LayersTypes.h" // for TransactionId, LayersBackend, CompositionPayload (ptr only), LayersBackend::... #include "mozilla/layers/RenderRootStateManager.h" // for RenderRootStateManager #include "mozilla/layers/ScrollableLayerGuid.h" // for ScrollableLayerGuid, ScrollableLayerGuid::ViewID #include "mozilla/layers/WebRenderCommandBuilder.h" // for WebRenderCommandBuilder #include "mozilla/layers/WebRenderScrollData.h" // for WebRenderScrollData +#include "WindowRenderer.h" #include "nsHashKeys.h" // for nsRefPtrHashKey #include "nsRegion.h" // for nsIntRegion #include "nsStringFwd.h" // for nsCString, nsAString @@ -52,6 +52,8 @@ class PCompositorBridgeChild; class WebRenderBridgeChild; class WebRenderParentCommand; +class TransactionIdAllocator; +class LayerUserData; class WebRenderLayerManager final : public WindowRenderer { typedef nsTArray> LayerRefArray; @@ -107,9 +109,6 @@ void SetTransactionIdAllocator(TransactionIdAllocator* aAllocator); TransactionId GetLastTransactionId(); - void AddDidCompositeObserver(DidCompositeObserver* aObserver); - void RemoveDidCompositeObserver(DidCompositeObserver* aObserver); - void FlushRendering(wr::RenderReasons aReasons) override; void WaitOnTransactionProcessed() override; @@ -228,8 +227,6 @@ RefPtr mTransactionIdAllocator; TransactionId mLatestTransactionId; - nsTArray mDidCompositeObservers; - gfx::UserData mUserData; // This holds the scroll data that we need to send to the compositor for diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/wr/WebRenderUserData.cpp firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/wr/WebRenderUserData.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/layers/wr/WebRenderUserData.cpp 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/layers/wr/WebRenderUserData.cpp 2021-10-20 19:28:21.000000000 +0000 @@ -9,7 +9,6 @@ #include "mozilla/layers/AnimationHelper.h" #include "mozilla/layers/CompositorBridgeChild.h" #include "mozilla/layers/ImageClient.h" -#include "mozilla/layers/LayerManager.h" #include "mozilla/layers/WebRenderBridgeChild.h" #include "mozilla/layers/RenderRootStateManager.h" #include "mozilla/layers/WebRenderMessages.h" diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/tests/gtest/MockWidget.h firefox-trunk-95.0~a1~hg20211020r596404/gfx/tests/gtest/MockWidget.h --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/tests/gtest/MockWidget.h 2021-10-17 14:42:38.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/tests/gtest/MockWidget.h 2021-10-20 19:28:21.000000000 +0000 @@ -67,10 +67,6 @@ virtual void Enable(bool aState) override {} virtual bool IsEnabled() const override { return true; } virtual void SetFocus(Raise, mozilla::dom::CallerType aCallerType) override {} - virtual nsresult ConfigureChildren( - const nsTArray& aConfigurations) override { - return NS_OK; - } virtual void Invalidate(const LayoutDeviceIntRect& aRect) override {} virtual nsresult SetTitle(const nsAString& title) override { return NS_OK; } virtual LayoutDeviceIntPoint WidgetToScreenOffset() override { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/tests/gtest/moz.build firefox-trunk-95.0~a1~hg20211020r596404/gfx/tests/gtest/moz.build --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/tests/gtest/moz.build 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/tests/gtest/moz.build 2021-10-20 19:28:21.000000000 +0000 @@ -16,7 +16,6 @@ "TestConfigManager.cpp", "TestCoord.cpp", "TestGfxWidgets.cpp", - "TestLayers.cpp", "TestMatrix.cpp", "TestMoz2D.cpp", "TestPolygon.cpp", diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/tests/gtest/TestConfigManager.cpp firefox-trunk-95.0~a1~hg20211020r596404/gfx/tests/gtest/TestConfigManager.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/tests/gtest/TestConfigManager.cpp 2021-10-17 14:42:38.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/tests/gtest/TestConfigManager.cpp 2021-10-20 19:28:21.000000000 +0000 @@ -147,9 +147,6 @@ NS_IMETHOD GetIsHeadless(bool* aIsHeadless) override { return NS_ERROR_NOT_IMPLEMENTED; } - NS_IMETHOD GetUsesTiling(bool* aUsesTiling) override { - return NS_ERROR_NOT_IMPLEMENTED; - } NS_IMETHOD GetTargetFrameRate(uint32_t* aTargetFrameRate) override { return NS_ERROR_NOT_IMPLEMENTED; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/tests/gtest/TestLayers.cpp firefox-trunk-95.0~a1~hg20211020r596404/gfx/tests/gtest/TestLayers.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/tests/gtest/TestLayers.cpp 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/tests/gtest/TestLayers.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,39 +0,0 @@ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -#include "TestLayers.h" -#include "gfxPlatform.h" -#include "gtest/gtest.h" -#include "gmock/gmock.h" -#include "LayerUserData.h" -#include "mozilla/layers/CompositorBridgeParent.h" - -using namespace mozilla; -using namespace mozilla::gfx; -using namespace mozilla::layers; - -class TestLayerManager : public LayerManager { - public: - TestLayerManager() : LayerManager() {} - - virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) { - return false; - } - virtual void GetBackendName(nsAString& aName) {} - virtual LayersBackend GetBackendType() { return LayersBackend::LAYERS_BASIC; } - virtual bool BeginTransaction(const nsCString& = nsCString()) { return true; } - virtual void SetRoot(Layer* aLayer) {} - virtual bool BeginTransactionWithTarget(gfxContext* aTarget, - const nsCString& = nsCString()) { - return true; - } - virtual int32_t GetMaxTextureSize() const { return 0; } -}; - -class TestUserData : public LayerUserData { - public: - MOCK_METHOD0(Die, void()); - virtual ~TestUserData() { Die(); } -}; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/tests/gtest/TestLayers.h firefox-trunk-95.0~a1~hg20211020r596404/gfx/tests/gtest/TestLayers.h --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/tests/gtest/TestLayers.h 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/tests/gtest/TestLayers.h 2021-10-20 19:28:21.000000000 +0000 @@ -24,25 +24,4 @@ } // namespace layers } // namespace mozilla -/* Create layer tree from a simple layer tree description syntax. - * Each index is either the first letter of the layer type or - * a '(',')' to indicate the start/end of the child layers. - * The aim of this function is to remove hard to read - * layer tree creation code. - * - * Example "c(c(c(tt)t))" would yield: - * c - * | - * c - * / \ - * c t - * / \ - * t t - */ -already_AddRefed CreateLayerTree( - const char* aLayerTreeDescription, nsIntRegion* aVisibleRegions, - const mozilla::gfx::Matrix4x4* aTransforms, - RefPtr& aLayerManager, - nsTArray >& aLayersOut); - #endif diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/tests/reftest/reftest.list firefox-trunk-95.0~a1~hg20211020r596404/gfx/tests/reftest/reftest.list --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/tests/reftest/reftest.list 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/tests/reftest/reftest.list 2021-10-20 19:28:21.000000000 +0000 @@ -28,7 +28,7 @@ pref(apz.allow_zooming,true) fails-if(useDrawSnapshot) == 1662062-1-no-blurry.html 1662062-1-ref.html # Bug 1715676: nsBulletFrame has been removed and the new rendering does not use PushRoundedRect that this test is for: # == 1681610.html 1681610-ref.html -skip-if(geckoview) fuzzy-if(!useDrawSnapshot,0-255,0-667) fuzzy-if(useDrawSnapshot,0-255,0-3601) == 1687157-1.html 1687157-1-ref.html +skip-if(geckoview) fuzzy-if(!useDrawSnapshot,0-255,0-897) fuzzy-if(useDrawSnapshot,0-255,0-3601) == 1687157-1.html 1687157-1-ref.html fuzzy(64-99,512-523) == 1696439-1.html 1696439-1-ref.html random-if(gtkWidget) == 1722689-1.html 1722689-1-ref.html fuzzy-if(useDrawSnapshot,255-255,5-5) == 1724901-1.html 1724901-1-ref.html diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/thebes/gfxFontEntry.cpp firefox-trunk-95.0~a1~hg20211020r596404/gfx/thebes/gfxFontEntry.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/thebes/gfxFontEntry.cpp 2021-10-17 14:42:38.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/thebes/gfxFontEntry.cpp 2021-10-20 19:28:21.000000000 +0000 @@ -1754,7 +1754,7 @@ } nsCString charAndName; - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled()) { charAndName = nsPrintfCString("\\u%x %s", aMatchData->mCh, mName.get()); } AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("gfxFontFamily::FindFontForChar", diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/thebes/gfxPlatform.cpp firefox-trunk-95.0~a1~hg20211020r596404/gfx/thebes/gfxPlatform.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/thebes/gfxPlatform.cpp 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/thebes/gfxPlatform.cpp 2021-10-20 19:28:21.000000000 +0000 @@ -437,7 +437,6 @@ : mHasVariationFontSupport(false), mAzureCanvasBackendCollector(this, &gfxPlatform::GetAzureBackendInfo), mApzSupportCollector(this, &gfxPlatform::GetApzSupportInfo), - mTilesInfoCollector(this, &gfxPlatform::GetTilesSupportInfo), mFrameStatsCollector(this, &gfxPlatform::GetFrameStats), mCMSInfoCollector(this, &gfxPlatform::GetCMSSupportInfo), mDisplayInfoCollector(this, &gfxPlatform::GetDisplayInfo), @@ -482,6 +481,13 @@ #define WR_DEBUG_PREF "gfx.webrender.debug" +static void SwapIntervalPrefChangeCallback(const char* aPrefName, void*) { + bool egl = Preferences::GetBool("gfx.swap-interval.egl", false); + bool glx = Preferences::GetBool("gfx.swap-interval.glx", false); + gfxVars::SetSwapIntervalEGL(egl); + gfxVars::SetSwapIntervalGLX(glx); +} + static void WebRendeProfilerUIPrefChangeCallback(const char* aPrefName, void*) { nsCString uiString; if (NS_SUCCEEDED(Preferences::GetCString("gfx.webrender.debug.profiler-ui", @@ -940,8 +946,6 @@ InitLayersIPC(); - gPlatform->ComputeTileSize(); - gPlatform->mHasVariationFontSupport = gPlatform->CheckVariationFontSupport(); // This *create* the platform font list instance, but may not *initialize* it @@ -1545,34 +1549,6 @@ return result.forget(); } -void gfxPlatform::ComputeTileSize() { - // The tile size should be picked in the parent processes - // and sent to the child processes over IPDL GetTileSize. - if (!XRE_IsParentProcess()) { - return; - } - - int32_t w = StaticPrefs::layers_tile_width_AtStartup(); - int32_t h = StaticPrefs::layers_tile_height_AtStartup(); - - if (StaticPrefs::layers_tiles_adjust_AtStartup()) { - gfx::IntSize screenSize = GetScreenSize(); - if (screenSize.width > 0) { - // Choose a size so that there are between 2 and 4 tiles per screen width. - // FIXME: we should probably make sure this is within the max texture - // size, but I think everything should at least support 1024 - w = h = clamped(int32_t(RoundUpPow2(screenSize.width)) / 4, 256, 1024); - } - } - - // Don't allow changing the tile size after we've set it. - // Right now the code assumes that the tile size doesn't change. - MOZ_ASSERT(gfxVars::TileSize().width == -1 && - gfxVars::TileSize().height == -1); - - gfxVars::SetTileSize(IntSize(w, h)); -} - void gfxPlatform::PopulateScreenInfo() { nsCOMPtr manager = do_GetService("@mozilla.org/gfx/screenmanager;1"); @@ -2602,6 +2578,9 @@ gfxVars::SetUseSoftwareWebRender(!hasHardware && hasSoftware); + Preferences::RegisterPrefixCallbackAndCall(SwapIntervalPrefChangeCallback, + "gfx.swap-interval"); + // gfxFeature is not usable in the GPU process, so we use gfxVars to transmit // this feature if (hasWebRender) { @@ -2829,10 +2808,6 @@ return result; } -bool gfxPlatform::UsesTiling() const { - return StaticPrefs::layers_enable_tiles_AtStartup(); -} - /*** * The preference "layout.frame_rate" has 3 meanings depending on the value: * @@ -2976,16 +2951,6 @@ } } -void gfxPlatform::GetTilesSupportInfo(mozilla::widget::InfoObject& aObj) { - if (!StaticPrefs::layers_enable_tiles_AtStartup()) { - return; - } - - IntSize tileSize = gfxVars::TileSize(); - aObj.DefineProperty("TileHeight", tileSize.height); - aObj.DefineProperty("TileWidth", tileSize.width); -} - void gfxPlatform::GetFrameStats(mozilla::widget::InfoObject& aObj) { uint32_t i = 0; for (FrameStats& f : mFrameStats) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/thebes/gfxPlatformFontList.cpp firefox-trunk-95.0~a1~hg20211020r596404/gfx/thebes/gfxPlatformFontList.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/thebes/gfxPlatformFontList.cpp 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/thebes/gfxPlatformFontList.cpp 2021-10-20 19:28:21.000000000 +0000 @@ -50,8 +50,8 @@ #include using namespace mozilla; -using mozilla::intl::Locale; using mozilla::intl::LocaleService; +using mozilla::intl::MozLocale; using mozilla::intl::OSPreferences; #define LOG_FONTLIST(args) \ @@ -480,8 +480,9 @@ } else { NS_DispatchToMainThread( NS_NewRunnableFunction("font-info-updated notification callback", [] { - gfxPlatform::ForceGlobalReflow(gfxPlatform::NeedsReframe::Yes, - gfxPlatform::BroadcastToChildren::No); + gfxPlatform::ForceGlobalReflow( + gfxPlatform::NeedsReframe::Yes, + gfxPlatform::BroadcastToChildren::No); })); } @@ -504,6 +505,11 @@ ClearLangGroupPrefFonts(); CancelLoader(); + // Clear cached family records that will no longer be valid. + for (auto& f : mReplacementCharFallbackFamily) { + f = FontFamily(); + } + gfxFontUtils::GetPrefsFontList(kFontSystemWhitelistPref, mEnabledFontsList); } @@ -2165,7 +2171,7 @@ LocaleService::GetInstance()->GetAppLocaleAsBCP47(localeStr); { - Locale locale(localeStr); + MozLocale locale(localeStr); if (locale.GetLanguage().Equals("ja")) { AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese); } else if (locale.GetLanguage().Equals("zh")) { @@ -2197,7 +2203,7 @@ sysLocales, prefLocales, ""_ns, LocaleService::kLangNegStrategyFiltering, negLocales); for (const auto& localeStr : negLocales) { - Locale locale(localeStr); + MozLocale locale(localeStr); if (locale.GetLanguage().Equals("ja")) { AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/thebes/gfxPlatform.h firefox-trunk-95.0~a1~hg20211020r596404/gfx/thebes/gfxPlatform.h --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/thebes/gfxPlatform.h 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/thebes/gfxPlatform.h 2021-10-20 19:28:21.000000000 +0000 @@ -311,7 +311,6 @@ void GetAzureBackendInfo(mozilla::widget::InfoObject& aObj); void GetApzSupportInfo(mozilla::widget::InfoObject& aObj); - void GetTilesSupportInfo(mozilla::widget::InfoObject& aObj); void GetFrameStats(mozilla::widget::InfoObject& aObj); void GetCMSSupportInfo(mozilla::widget::InfoObject& aObj); void GetDisplayInfo(mozilla::widget::InfoObject& aObj); @@ -640,11 +639,6 @@ } /** - * Returns whether the current process should use tiling for layers. - */ - virtual bool UsesTiling() const; - - /** * Returns a logger if one is available and logging is enabled */ static mozilla::LogModule* GetLog(eGfxLog aWhichLog); @@ -961,14 +955,6 @@ static void ShutdownCMS(); /** - * Calling this function will compute and set the ideal tile size for the - * platform. This will only have an effect in the parent process; child - * processes should be updated via SetTileSize to match the value computed in - * the parent. - */ - void ComputeTileSize(); - - /** * This uses nsIScreenManager to determine the screen size and color depth */ void PopulateScreenInfo(); @@ -1002,7 +988,6 @@ mozilla::widget::GfxInfoCollector mAzureCanvasBackendCollector; mozilla::widget::GfxInfoCollector mApzSupportCollector; - mozilla::widget::GfxInfoCollector mTilesInfoCollector; mozilla::widget::GfxInfoCollector mFrameStatsCollector; mozilla::widget::GfxInfoCollector mCMSInfoCollector; mozilla::widget::GfxInfoCollector mDisplayInfoCollector; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/thebes/gfxPlatformMac.cpp firefox-trunk-95.0~a1~hg20211020r596404/gfx/thebes/gfxPlatformMac.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/thebes/gfxPlatformMac.cpp 2021-10-17 14:42:38.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/thebes/gfxPlatformMac.cpp 2021-10-20 19:28:21.000000000 +0000 @@ -230,12 +230,6 @@ return data; } -bool gfxPlatformMac::UsesTiling() const { - // The non-tiling ContentClient requires CrossProcessSemaphore which - // isn't implemented for OSX. - return true; -} - bool gfxPlatformMac::CreatePlatformFontList() { return gfxPlatformFontList::Initialize(new gfxMacPlatformFontList); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/thebes/gfxPlatformMac.h firefox-trunk-95.0~a1~hg20211020r596404/gfx/thebes/gfxPlatformMac.h --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/thebes/gfxPlatformMac.h 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/thebes/gfxPlatformMac.h 2021-10-20 19:28:21.000000000 +0000 @@ -37,8 +37,6 @@ return (gfxPlatformMac*)gfxPlatform::GetPlatform(); } - bool UsesTiling() const override; - already_AddRefed CreateOffscreenSurface( const IntSize& aSize, gfxImageFormat aFormat) override; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/gfx/webrender_bindings/RenderCompositorEGL.cpp firefox-trunk-95.0~a1~hg20211020r596404/gfx/webrender_bindings/RenderCompositorEGL.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/gfx/webrender_bindings/RenderCompositorEGL.cpp 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/gfx/webrender_bindings/RenderCompositorEGL.cpp 2021-10-20 19:28:21.000000000 +0000 @@ -191,8 +191,9 @@ const auto& gle = gl::GLContextEGL::Cast(gl()); const auto& egl = gle->mEgl; MakeCurrent(); - // Make eglSwapBuffers() non-blocking on wayland. - egl->fSwapInterval(0); + + const int interval = gfx::gfxVars::SwapIntervalEGL() ? 1 : 0; + egl->fSwapInterval(interval); } else { RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE); return false; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/.gitignore firefox-trunk-95.0~a1~hg20211020r596404/.gitignore --- firefox-trunk-95.0~a1~hg20211017r596111/.gitignore 2021-10-17 14:42:31.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/.gitignore 2021-10-20 19:28:14.000000000 +0000 @@ -163,7 +163,8 @@ # Ignore files created when running a reftest. lextab.py -# Ignore Visual Studio Code workspace files. +# Ignore Visual Studio/Visual Studio Code workspace files. +.vs/ .vscode/ !.vscode/extensions.json !.vscode/tasks.json diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/image/ImageFactory.cpp firefox-trunk-95.0~a1~hg20211020r596404/image/ImageFactory.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/image/ImageFactory.cpp 2021-10-17 14:42:38.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/image/ImageFactory.cpp 2021-10-20 19:28:21.000000000 +0000 @@ -114,7 +114,7 @@ } #endif - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled()) { static const size_t sMaxTruncatedLength = 1024; nsAutoCString spec; aURI->GetSpec(spec); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/image/Image.h firefox-trunk-95.0~a1~hg20211020r596404/image/Image.h --- firefox-trunk-95.0~a1~hg20211017r596111/image/Image.h 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/image/Image.h 2021-10-20 19:28:21.000000000 +0000 @@ -451,7 +451,7 @@ explicit AutoProfilerImagePaintMarker(ImageResource* self) : mStartTime(TimeStamp::Now()) { nsAutoCString spec; - if (self->mURI && profiler_can_accept_markers()) { + if (self->mURI && profiler_thread_is_being_profiled()) { static const size_t sMaxTruncatedLength = 1024; self->mURI->GetSpec(mSpec); if (mSpec.Length() >= sMaxTruncatedLength) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/image/RasterImage.cpp firefox-trunk-95.0~a1~hg20211020r596404/image/RasterImage.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/image/RasterImage.cpp 2021-10-17 14:42:38.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/image/RasterImage.cpp 2021-10-20 19:28:20.000000000 +0000 @@ -50,6 +50,7 @@ #include "nsProperties.h" #include "prenv.h" #include "prsystem.h" +#include "WindowRenderer.h" namespace mozilla { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/image/test/mochitest/bug1180105.sjs firefox-trunk-95.0~a1~hg20211020r596404/image/test/mochitest/bug1180105.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/image/test/mochitest/bug1180105.sjs 2021-10-17 14:42:38.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/image/test/mochitest/bug1180105.sjs 2021-10-20 19:28:22.000000000 +0000 @@ -8,8 +8,8 @@ function getFileAsInputStream(aFilename) { var file = Components.classes["@mozilla.org/file/directory_service;1"] - .getService(Components.interfaces.nsIProperties) - .get("CurWorkD", Components.interfaces.nsIFile); + .getService(Components.interfaces.nsIProperties) + .get("CurWorkD", Components.interfaces.nsIFile); file.append("tests"); file.append("image"); @@ -17,16 +17,19 @@ file.append("mochitest"); file.append(aFilename); - var fileStream = Components.classes['@mozilla.org/network/file-input-stream;1'] - .createInstance(Components.interfaces.nsIFileInputStream); + var fileStream = Components.classes[ + "@mozilla.org/network/file-input-stream;1" + ].createInstance(Components.interfaces.nsIFileInputStream); fileStream.init(file, 1, 0, false); return fileStream; } -function handleRequest(request, response) -{ - response.setHeader("Content-Type", - "multipart/x-mixed-replace;boundary=BOUNDARYOMG", false); +function handleRequest(request, response) { + response.setHeader( + "Content-Type", + "multipart/x-mixed-replace;boundary=BOUNDARYOMG", + false + ); response.setHeader("Cache-Control", "no-cache", false); response.setStatusLine(request.httpVersion, 200, "OK"); // We're sending parts off in a delayed fashion, to let the tests occur. @@ -42,8 +45,13 @@ return; } sendNextPart(response); - partTimer.initWithCallback(function() {sendParts(response);}, 1, - Components.interfaces.nsITimer.TYPE_ONE_SHOT); + partTimer.initWithCallback( + function() { + sendParts(response); + }, + 1, + Components.interfaces.nsITimer.TYPE_ONE_SHOT + ); } function sendClose(response) { @@ -60,4 +68,3 @@ // Toss in the boundary, so the browser can know this part is complete response.write("--BOUNDARYOMG\r\n"); } - diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/image/test/mochitest/bug1180105-waiter.sjs firefox-trunk-95.0~a1~hg20211020r596404/image/test/mochitest/bug1180105-waiter.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/image/test/mochitest/bug1180105-waiter.sjs 2021-10-17 14:42:38.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/image/test/mochitest/bug1180105-waiter.sjs 2021-10-20 19:28:21.000000000 +0000 @@ -18,7 +18,12 @@ response.write("done"); response.finish(); } else { - waitTimer.initWithCallback(function() {waitForFinish(response);}, 10, - Components.interfaces.nsITimer.TYPE_ONE_SHOT); + waitTimer.initWithCallback( + function() { + waitForFinish(response); + }, + 10, + Components.interfaces.nsITimer.TYPE_ONE_SHOT + ); } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/image/test/mochitest/bug468160.sjs firefox-trunk-95.0~a1~hg20211020r596404/image/test/mochitest/bug468160.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/image/test/mochitest/bug468160.sjs 2021-10-17 14:42:38.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/image/test/mochitest/bug468160.sjs 2021-10-20 19:28:21.000000000 +0000 @@ -1,5 +1,4 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setStatusLine("1.1", 302, "Found"); response.setHeader("Location", "red.png", false); response.setHeader("Cache-Control", "no-cache", false); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/image/test/mochitest/bug490949.sjs firefox-trunk-95.0~a1~hg20211020r596404/image/test/mochitest/bug490949.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/image/test/mochitest/bug490949.sjs 2021-10-17 14:42:38.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/image/test/mochitest/bug490949.sjs 2021-10-20 19:28:21.000000000 +0000 @@ -1,8 +1,7 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { var file = Components.classes["@mozilla.org/file/directory_service;1"] - .getService(Components.interfaces.nsIProperties) - .get("CurWorkD", Components.interfaces.nsIFile); + .getService(Components.interfaces.nsIProperties) + .get("CurWorkD", Components.interfaces.nsIFile); file.append("tests"); file.append("image"); @@ -11,19 +10,21 @@ var redirectstate = "/image/test/mochitest/bug490949.sjs"; if (getState(redirectstate) == "") { - file.append('blue.png'); + file.append("blue.png"); setState(redirectstate, "red"); } else { - file.append('red.png'); + file.append("red.png"); setState(redirectstate, ""); } response.setHeader("Cache-Control", "no-cache", false); - var fileStream = Components.classes['@mozilla.org/network/file-input-stream;1'] - .createInstance(Components.interfaces.nsIFileInputStream); + var fileStream = Components.classes[ + "@mozilla.org/network/file-input-stream;1" + ].createInstance(Components.interfaces.nsIFileInputStream); fileStream.init(file, 1, 0, false); - var binaryStream = Components.classes['@mozilla.org/binaryinputstream;1'] - .createInstance(Components.interfaces.nsIBinaryInputStream); + var binaryStream = Components.classes[ + "@mozilla.org/binaryinputstream;1" + ].createInstance(Components.interfaces.nsIBinaryInputStream); binaryStream.setInputStream(fileStream); response.bodyOutputStream.writeFrom(binaryStream, binaryStream.available()); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/image/test/mochitest/bug496292-1.sjs firefox-trunk-95.0~a1~hg20211020r596404/image/test/mochitest/bug496292-1.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/image/test/mochitest/bug496292-1.sjs 2021-10-17 14:42:38.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/image/test/mochitest/bug496292-1.sjs 2021-10-20 19:28:21.000000000 +0000 @@ -1,8 +1,7 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { var file = Components.classes["@mozilla.org/file/directory_service;1"] - .getService(Components.interfaces.nsIProperties) - .get("CurWorkD", Components.interfaces.nsIFile); + .getService(Components.interfaces.nsIProperties) + .get("CurWorkD", Components.interfaces.nsIFile); file.append("tests"); file.append("image"); @@ -10,19 +9,21 @@ file.append("mochitest"); if (request.getHeader("Accept") == "image/avif,image/webp,*/*") { - file.append('blue.png'); + file.append("blue.png"); } else { - file.append('red.png'); + file.append("red.png"); } response.setStatusLine(request.httpVersion, 200, "OK"); response.setHeader("Content-Type", "image/png", false); response.setHeader("Cache-Control", "no-cache", false); - var fileStream = Components.classes['@mozilla.org/network/file-input-stream;1'] - .createInstance(Components.interfaces.nsIFileInputStream); + var fileStream = Components.classes[ + "@mozilla.org/network/file-input-stream;1" + ].createInstance(Components.interfaces.nsIFileInputStream); fileStream.init(file, 1, 0, false); - var binaryStream = Components.classes['@mozilla.org/binaryinputstream;1'] - .createInstance(Components.interfaces.nsIBinaryInputStream); + var binaryStream = Components.classes[ + "@mozilla.org/binaryinputstream;1" + ].createInstance(Components.interfaces.nsIBinaryInputStream); binaryStream.setInputStream(fileStream); response.bodyOutputStream.writeFrom(binaryStream, binaryStream.available()); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/image/test/mochitest/bug496292-2.sjs firefox-trunk-95.0~a1~hg20211020r596404/image/test/mochitest/bug496292-2.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/image/test/mochitest/bug496292-2.sjs 2021-10-17 14:42:38.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/image/test/mochitest/bug496292-2.sjs 2021-10-20 19:28:21.000000000 +0000 @@ -1,8 +1,7 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { var file = Components.classes["@mozilla.org/file/directory_service;1"] - .getService(Components.interfaces.nsIProperties) - .get("CurWorkD", Components.interfaces.nsIFile); + .getService(Components.interfaces.nsIProperties) + .get("CurWorkD", Components.interfaces.nsIFile); file.append("tests"); file.append("image"); @@ -10,19 +9,21 @@ file.append("mochitest"); if (request.getHeader("Accept") == "image/png") { - file.append('blue.png'); + file.append("blue.png"); } else { - file.append('red.png'); + file.append("red.png"); } response.setStatusLine(request.httpVersion, 200, "OK"); response.setHeader("Content-Type", "image/png", false); response.setHeader("Cache-Control", "no-cache", false); - var fileStream = Components.classes['@mozilla.org/network/file-input-stream;1'] - .createInstance(Components.interfaces.nsIFileInputStream); + var fileStream = Components.classes[ + "@mozilla.org/network/file-input-stream;1" + ].createInstance(Components.interfaces.nsIFileInputStream); fileStream.init(file, 1, 0, false); - var binaryStream = Components.classes['@mozilla.org/binaryinputstream;1'] - .createInstance(Components.interfaces.nsIBinaryInputStream); + var binaryStream = Components.classes[ + "@mozilla.org/binaryinputstream;1" + ].createInstance(Components.interfaces.nsIBinaryInputStream); binaryStream.setInputStream(fileStream); response.bodyOutputStream.writeFrom(binaryStream, binaryStream.available()); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/image/test/mochitest/bug497665.sjs firefox-trunk-95.0~a1~hg20211020r596404/image/test/mochitest/bug497665.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/image/test/mochitest/bug497665.sjs 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/image/test/mochitest/bug497665.sjs 2021-10-20 19:28:21.000000000 +0000 @@ -1,8 +1,7 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { var file = Components.classes["@mozilla.org/file/directory_service;1"] - .getService(Components.interfaces.nsIProperties) - .get("CurWorkD", Components.interfaces.nsIFile); + .getService(Components.interfaces.nsIProperties) + .get("CurWorkD", Components.interfaces.nsIFile); file.append("tests"); file.append("image"); @@ -11,20 +10,22 @@ var redirectstate = "/image/test/mochitest/bug497665.sjs"; if (getState(redirectstate) == "") { - file.append('blue.png'); + file.append("blue.png"); setState(redirectstate, "red"); } else { - file.append('red.png'); + file.append("red.png"); setState(redirectstate, ""); } response.setHeader("Cache-Control", "max-age=3600", false); - var fileStream = Components.classes['@mozilla.org/network/file-input-stream;1'] - .createInstance(Components.interfaces.nsIFileInputStream); + var fileStream = Components.classes[ + "@mozilla.org/network/file-input-stream;1" + ].createInstance(Components.interfaces.nsIFileInputStream); fileStream.init(file, 1, 0, false); - var binaryStream = Components.classes['@mozilla.org/binaryinputstream;1'] - .createInstance(Components.interfaces.nsIBinaryInputStream); + var binaryStream = Components.classes[ + "@mozilla.org/binaryinputstream;1" + ].createInstance(Components.interfaces.nsIBinaryInputStream); binaryStream.setInputStream(fileStream); response.bodyOutputStream.writeFrom(binaryStream, binaryStream.available()); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/image/test/mochitest/bug552605.sjs firefox-trunk-95.0~a1~hg20211020r596404/image/test/mochitest/bug552605.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/image/test/mochitest/bug552605.sjs 2021-10-17 14:42:38.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/image/test/mochitest/bug552605.sjs 2021-10-20 19:28:21.000000000 +0000 @@ -1,5 +1,4 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { var redirectstate = "/image/test/mochitest/bug89419.sjs"; response.setStatusLine("1.1", 302, "Found"); if (getState(redirectstate) == "") { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/image/test/mochitest/bug657191.sjs firefox-trunk-95.0~a1~hg20211020r596404/image/test/mochitest/bug657191.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/image/test/mochitest/bug657191.sjs 2021-10-17 14:42:38.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/image/test/mochitest/bug657191.sjs 2021-10-20 19:28:21.000000000 +0000 @@ -1,23 +1,24 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { var file = Components.classes["@mozilla.org/file/directory_service;1"] - .getService(Components.interfaces.nsIProperties) - .get("CurWorkD", Components.interfaces.nsIFile); + .getService(Components.interfaces.nsIProperties) + .get("CurWorkD", Components.interfaces.nsIFile); file.append("tests"); file.append("image"); file.append("test"); file.append("mochitest"); - file.append('lime100x100.svg'); + file.append("lime100x100.svg"); response.setStatusLine("1.1", 500, "Internal Server Error"); response.setHeader("Content-Type", "image/svg+xml", false); - var fileStream = Components.classes['@mozilla.org/network/file-input-stream;1'] - .createInstance(Components.interfaces.nsIFileInputStream); + var fileStream = Components.classes[ + "@mozilla.org/network/file-input-stream;1" + ].createInstance(Components.interfaces.nsIFileInputStream); fileStream.init(file, 1, 0, false); - var binaryStream = Components.classes['@mozilla.org/binaryinputstream;1'] - .createInstance(Components.interfaces.nsIBinaryInputStream); + var binaryStream = Components.classes[ + "@mozilla.org/binaryinputstream;1" + ].createInstance(Components.interfaces.nsIBinaryInputStream); binaryStream.setInputStream(fileStream); response.bodyOutputStream.writeFrom(binaryStream, binaryStream.available()); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/image/test/mochitest/bug671906.sjs firefox-trunk-95.0~a1~hg20211020r596404/image/test/mochitest/bug671906.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/image/test/mochitest/bug671906.sjs 2021-10-17 14:42:38.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/image/test/mochitest/bug671906.sjs 2021-10-20 19:28:21.000000000 +0000 @@ -1,8 +1,7 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { var file = Components.classes["@mozilla.org/file/directory_service;1"] - .getService(Components.interfaces.nsIProperties) - .get("CurWorkD", Components.interfaces.nsIFile); + .getService(Components.interfaces.nsIProperties) + .get("CurWorkD", Components.interfaces.nsIFile); file.append("tests"); file.append("image"); @@ -11,10 +10,10 @@ var filestate = "/image/test/mochitest/bug671906.sjs"; if (getState(filestate) == "") { - file.append('blue.png'); + file.append("blue.png"); setState(filestate, "red"); } else { - file.append('red.png'); + file.append("red.png"); setState(filestate, ""); } @@ -24,8 +23,9 @@ date.setFullYear(date.getFullYear() + 1); response.setHeader("Expires", date.toUTCString(), false); - var fileStream = Components.classes['@mozilla.org/network/file-input-stream;1'] - .createInstance(Components.interfaces.nsIFileInputStream); + var fileStream = Components.classes[ + "@mozilla.org/network/file-input-stream;1" + ].createInstance(Components.interfaces.nsIFileInputStream); fileStream.init(file, 1, 0, false); response.bodyOutputStream.writeFrom(fileStream, fileStream.available()); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/image/test/mochitest/bug733553-informant.sjs firefox-trunk-95.0~a1~hg20211020r596404/image/test/mochitest/bug733553-informant.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/image/test/mochitest/bug733553-informant.sjs 2021-10-17 14:42:38.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/image/test/mochitest/bug733553-informant.sjs 2021-10-20 19:28:21.000000000 +0000 @@ -2,8 +2,7 @@ * http://creativecommons.org/publicdomain/zero/1.0/ */ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setHeader("Content-Type", "text/plain", false); response.setHeader("Cache-Control", "no-cache", false); response.setStatusLine(request.httpVersion, 200, "OK"); @@ -12,4 +11,3 @@ setSharedState("next-part", partName); response.write("OK!"); } - diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/image/test/mochitest/bug733553.sjs firefox-trunk-95.0~a1~hg20211020r596404/image/test/mochitest/bug733553.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/image/test/mochitest/bug733553.sjs 2021-10-17 14:42:38.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/image/test/mochitest/bug733553.sjs 2021-10-20 19:28:21.000000000 +0000 @@ -22,15 +22,15 @@ ["bad.jpg", "image/jpeg"], ["red.png", "image/png"], ["invalid.jpg", "image/jpeg"], - ["animated-gif2.gif", "image/gif"] + ["animated-gif2.gif", "image/gif"], ]; var timer = Components.classes["@mozilla.org/timer;1"]; var partTimer = timer.createInstance(Components.interfaces.nsITimer); function getFileAsInputStream(aFilename) { var file = Components.classes["@mozilla.org/file/directory_service;1"] - .getService(Components.interfaces.nsIProperties) - .get("CurWorkD", Components.interfaces.nsIFile); + .getService(Components.interfaces.nsIProperties) + .get("CurWorkD", Components.interfaces.nsIFile); file.append("tests"); file.append("image"); @@ -38,19 +38,22 @@ file.append("mochitest"); file.append(aFilename); - var fileStream = Components.classes['@mozilla.org/network/file-input-stream;1'] - .createInstance(Components.interfaces.nsIFileInputStream); + var fileStream = Components.classes[ + "@mozilla.org/network/file-input-stream;1" + ].createInstance(Components.interfaces.nsIFileInputStream); fileStream.init(file, 1, 0, false); return fileStream; } -function handleRequest(request, response) -{ +function handleRequest(request, response) { if (!getSharedState("next-part")) { setSharedState("next-part", "-1"); } - response.setHeader("Content-Type", - "multipart/x-mixed-replace;boundary=BOUNDARYOMG", false); + response.setHeader( + "Content-Type", + "multipart/x-mixed-replace;boundary=BOUNDARYOMG", + false + ); response.setHeader("Cache-Control", "no-cache", false); response.setStatusLine(request.httpVersion, 200, "OK"); // We're sending parts off in a delayed fashion, to let the tests occur. @@ -73,12 +76,16 @@ if (!wait) { callback = getSendNextPart(response); } else { - callback = function () { sendParts(response); }; + callback = function() { + sendParts(response); + }; } - partTimer.initWithCallback(callback, 1000, - Components.interfaces.nsITimer.TYPE_ONE_SHOT); - } - else { + partTimer.initWithCallback( + callback, + 1000, + Components.interfaces.nsITimer.TYPE_ONE_SHOT + ); + } else { sendClose(response); } } @@ -92,13 +99,12 @@ var part = bodyParts[bodyPartIndex]; var nextPartHead = "Content-Type: " + part[1] + "\r\n\r\n"; var inputStream = getFileAsInputStream(part[0]); - return function () { + return function() { response.bodyOutputStream.write(nextPartHead, nextPartHead.length); response.bodyOutputStream.writeFrom(inputStream, inputStream.available()); inputStream.close(); // Toss in the boundary, so the browser can know this part is complete response.write("--BOUNDARYOMG\r\n"); sendParts(response); - } + }; } - diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/image/test/mochitest/bug767779.sjs firefox-trunk-95.0~a1~hg20211020r596404/image/test/mochitest/bug767779.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/image/test/mochitest/bug767779.sjs 2021-10-17 14:42:38.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/image/test/mochitest/bug767779.sjs 2021-10-20 19:28:22.000000000 +0000 @@ -7,8 +7,8 @@ function getFileAsInputStream(aFilename) { var file = Components.classes["@mozilla.org/file/directory_service;1"] - .getService(Components.interfaces.nsIProperties) - .get("CurWorkD", Components.interfaces.nsIFile); + .getService(Components.interfaces.nsIProperties) + .get("CurWorkD", Components.interfaces.nsIFile); file.append("tests"); file.append("image"); @@ -16,14 +16,14 @@ file.append("mochitest"); file.append(aFilename); - var fileStream = Components.classes['@mozilla.org/network/file-input-stream;1'] - .createInstance(Components.interfaces.nsIFileInputStream); + var fileStream = Components.classes[ + "@mozilla.org/network/file-input-stream;1" + ].createInstance(Components.interfaces.nsIFileInputStream); fileStream.init(file, 1, 0, false); return fileStream; } -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setHeader("Content-Type", "image/gif", false); response.setHeader("Cache-Control", "no-cache", false); response.setStatusLine(request.httpVersion, 200, "OK"); @@ -38,12 +38,15 @@ function sendParts(inputStream, response) { // 3744 left, send in 8 chunks of 468 each - partTimer.initWithCallback(getSendNextPart(inputStream, response), 500, - Components.interfaces.nsITimer.TYPE_ONE_SHOT); + partTimer.initWithCallback( + getSendNextPart(inputStream, response), + 500, + Components.interfaces.nsITimer.TYPE_ONE_SHOT + ); } function getSendNextPart(inputStream, response) { - return function () { + return function() { response.bodyOutputStream.writeFrom(inputStream, 468); if (!inputStream.available()) { inputStream.close(); @@ -53,4 +56,3 @@ } }; } - diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/image/test/mochitest/bug89419.sjs firefox-trunk-95.0~a1~hg20211020r596404/image/test/mochitest/bug89419.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/image/test/mochitest/bug89419.sjs 2021-10-17 14:42:38.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/image/test/mochitest/bug89419.sjs 2021-10-20 19:28:21.000000000 +0000 @@ -1,5 +1,4 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { var redirectstate = "/image/test/mochitest/bug89419.sjs"; response.setStatusLine("1.1", 302, "Found"); if (getState(redirectstate) == "") { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/image/test/mochitest/test_mq_dynamic_svg.html firefox-trunk-95.0~a1~hg20211020r596404/image/test/mochitest/test_mq_dynamic_svg.html --- firefox-trunk-95.0~a1~hg20211017r596111/image/test/mochitest/test_mq_dynamic_svg.html 2021-10-17 14:42:37.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/image/test/mochitest/test_mq_dynamic_svg.html 2021-10-20 19:28:21.000000000 +0000 @@ -23,13 +23,13 @@ async function run() { let loadedFrame1 = new Promise(resolve => f1.onload = resolve); let loadedFrame2 = new Promise(resolve => f2.onload = resolve); - await SpecialPowers.pushPrefEnv({ set: [["ui.systemUsesDarkTheme", 0]] }); + await SpecialPowers.pushPrefEnv({ set: [["layout.css.prefers-color-scheme.content-override", 1]] }); f1.src = "mq_dynamic_svg_test.html"; f2.src = "mq_dynamic_svg_ref.html"; await loadedFrame1; await loadedFrame2; ok(!snapshotsEqual(), "In light mode snapshot comparison should be false"); - await SpecialPowers.pushPrefEnv({ set: [["ui.systemUsesDarkTheme", 1]] }); + await SpecialPowers.pushPrefEnv({ set: [["layout.css.prefers-color-scheme.content-override", 0]] }); ok(snapshotsEqual(), "In dark mode snapshot comparison should be true"); SimpleTest.finish(); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/image/test/mochitest/webcam-simulacrum.sjs firefox-trunk-95.0~a1~hg20211020r596404/image/test/mochitest/webcam-simulacrum.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/image/test/mochitest/webcam-simulacrum.sjs 2021-10-17 14:42:38.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/image/test/mochitest/webcam-simulacrum.sjs 2021-10-20 19:28:21.000000000 +0000 @@ -3,14 +3,14 @@ */ var counter = 2; -var frames = ['red.gif', 'blue.gif']; +var frames = ["red.gif", "blue.gif"]; var timer = Components.classes["@mozilla.org/timer;1"]; var partTimer = timer.createInstance(Components.interfaces.nsITimer); function getFileAsInputStream(aFilename) { var file = Components.classes["@mozilla.org/file/directory_service;1"] - .getService(Components.interfaces.nsIProperties) - .get("CurWorkD", Components.interfaces.nsIFile); + .getService(Components.interfaces.nsIProperties) + .get("CurWorkD", Components.interfaces.nsIFile); file.append("tests"); file.append("image"); @@ -18,21 +18,24 @@ file.append("mochitest"); file.append(aFilename); - var fileStream = Components.classes['@mozilla.org/network/file-input-stream;1'] - .createInstance(Components.interfaces.nsIFileInputStream); + var fileStream = Components.classes[ + "@mozilla.org/network/file-input-stream;1" + ].createInstance(Components.interfaces.nsIFileInputStream); fileStream.init(file, 1, 0, false); return fileStream; } -function handleRequest(request, response) -{ - response.setHeader("Content-Type", - "multipart/x-mixed-replace;boundary=BOUNDARYOMG", false); +function handleRequest(request, response) { + response.setHeader( + "Content-Type", + "multipart/x-mixed-replace;boundary=BOUNDARYOMG", + false + ); response.setHeader("Cache-Control", "no-cache", false); response.setStatusLine(request.httpVersion, 200, "OK"); response.processAsync(); response.write("--BOUNDARYOMG\r\n"); - while (frames.length > 0) { + while (frames.length) { sendNextPart(response); } response.write("--BOUNDARYOMG--\r\n"); @@ -48,4 +51,3 @@ // Toss in the boundary, so the browser can know this part is complete response.write("--BOUNDARYOMG\r\n"); } - diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/image/test/reftest/downscaling/reftest.list firefox-trunk-95.0~a1~hg20211020r596404/image/test/reftest/downscaling/reftest.list --- firefox-trunk-95.0~a1~hg20211017r596111/image/test/reftest/downscaling/reftest.list 2021-10-17 14:42:38.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/image/test/reftest/downscaling/reftest.list 2021-10-20 19:28:22.000000000 +0000 @@ -98,9 +98,6 @@ == downscale-16px.html?ff-0RGB.ico downscale-16px.html?ff-0RGB.png fuzzy(0-1,0-1) fuzzy-if(gtkWidget&&swgl,1-1,1-1) == downscale-16px.html?ff-ARGB.ico downscale-16px.html?ff-ARGB.png -# Upside-down (negative height) BMP -== downscale-8px.html?top-to-bottom-16x16-24bpp.bmp downscale-8px.html?bmp-size-16x16-24bpp.png - # Test downscaling from all supported formats from 256 to 32. == downscale-32px.html?.bmp downscale-32px-ref.html == downscale-32px.html?.gif downscale-32px-ref.html diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/intl/components/gtest/TestDateTimeFormat.cpp firefox-trunk-95.0~a1~hg20211020r596404/intl/components/gtest/TestDateTimeFormat.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/intl/components/gtest/TestDateTimeFormat.cpp 2021-10-17 14:42:38.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/intl/components/gtest/TestDateTimeFormat.cpp 2021-10-20 19:28:21.000000000 +0000 @@ -179,6 +179,14 @@ ASSERT_TRUE(buffer.verboseMatches(u"yMd")); } +TEST(IntlDateTimePatternGenerator, GetPlaceholderPattern) +{ + auto gen = DateTimePatternGenerator::TryCreate("en").unwrap(); + auto span = gen->GetPlaceholderPattern(); + // The default date-time pattern for 'en' locale is u"{1}, {0}". + ASSERT_EQ(span, MakeStringSpan(u"{1}, {0}")); +} + // A utility function to help test the DateTimeFormat::ComponentsBag. [[nodiscard]] bool FormatComponents( TestBuffer& aBuffer, DateTimeFormat::ComponentsBag& aComponents, diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/intl/components/gtest/TestLocale.cpp firefox-trunk-95.0~a1~hg20211020r596404/intl/components/gtest/TestLocale.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/intl/components/gtest/TestLocale.cpp 2021-10-17 14:42:38.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/intl/components/gtest/TestLocale.cpp 2021-10-20 19:28:21.000000000 +0000 @@ -4,12 +4,85 @@ #include "gtest/gtest.h" #include "mozilla/intl/Locale.h" +#include "mozilla/Span.h" -#include -#include +#include "TestBuffer.h" namespace mozilla::intl { +TEST(IntlLocale, LocaleSettersAndGetters) +{ + Locale locale; + locale.setLanguage("fr"); + locale.setRegion("CA"); + locale.setScript("Latn"); + ASSERT_TRUE(locale.setUnicodeExtension("u-ca-gregory")); + ASSERT_TRUE(locale.language().equalTo("fr")); + ASSERT_TRUE(locale.region().equalTo("CA")); + ASSERT_TRUE(locale.script().equalTo("Latn")); + ASSERT_EQ(MakeStringSpan(locale.unicodeExtension()), + MakeStringSpan("u-ca-gregory")); + + TestBuffer buffer; + ASSERT_TRUE(locale.toString(buffer).isOk()); + ASSERT_TRUE(buffer.verboseMatches("fr-Latn-CA-u-ca-gregory")); + + // No setters for variants or other extensions... + Locale locale2; + ASSERT_TRUE(LocaleParser::tryParse( + MakeStringSpan("fr-CA-fonipa-t-es-AR-h0-hybrid"), locale2) + .isOk()); + ASSERT_EQ(MakeStringSpan(locale2.variants()[0].get()), + MakeStringSpan("fonipa")); + ASSERT_EQ(MakeStringSpan(locale2.extensions()[0].get()), + MakeStringSpan("t-es-AR-h0-hybrid")); + locale2.clearVariants(); + ASSERT_EQ(locale2.variants().length(), 0UL); +} + +TEST(IntlLocale, LocaleParser) +{ + const char* tags[] = { + "en-US", "en-GB", "es-AR", "it", "zh-Hans-CN", + "de-AT", "pl", "fr-FR", "de-AT", "sr-Cyrl-SR", + "nb-NO", "fr-FR", "mk", "uk", "und-PL", + "und-Latn-AM", "ug-Cyrl", "sr-ME", "mn-Mong", "lif-Limb", + "gan", "zh-Hant", "yue-Hans", "unr", "unr-Deva", + "und-Thai-CN", "ug-Cyrl", "en-Latn-DE", "pl-FR", "de-CH", + "tuq", "sr-ME", "ng", "klx", "kk-Arab", + "en-Cyrl", "und-Cyrl-UK", "und-Arab", "und-Arab-FO"}; + + Locale locale; + for (const auto* tag : tags) { + ASSERT_TRUE(LocaleParser::tryParse(MakeStringSpan(tag), locale).isOk()); + } +} + +TEST(IntlLocale, LikelySubtags) +{ + Locale locale; + ASSERT_TRUE(LocaleParser::tryParse(MakeStringSpan("zh"), locale).isOk()); + ASSERT_TRUE(locale.addLikelySubtags()); + TestBuffer buffer; + ASSERT_TRUE(locale.toString(buffer).isOk()); + ASSERT_TRUE(buffer.verboseMatches("zh-Hans-CN")); + ASSERT_TRUE(locale.removeLikelySubtags()); + buffer.clear(); + ASSERT_TRUE(locale.toString(buffer).isOk()); + ASSERT_TRUE(buffer.verboseMatches("zh")); +} + +TEST(IntlLocale, Canonicalize) +{ + Locale locale; + ASSERT_TRUE( + LocaleParser::tryParse(MakeStringSpan("nob-bokmal"), locale).isOk()); + ASSERT_TRUE(locale.canonicalize().isOk()); + TestBuffer buffer; + ASSERT_TRUE(locale.toString(buffer).isOk()); + ASSERT_TRUE(buffer.verboseMatches("nb")); +} + // These tests are dependent on the machine that this test is being run on. TEST(IntlLocale, SystemDependentTests) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/intl/components/moz.build firefox-trunk-95.0~a1~hg20211020r596404/intl/components/moz.build --- firefox-trunk-95.0~a1~hg20211017r596111/intl/components/moz.build 2021-10-17 14:42:38.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/intl/components/moz.build 2021-10-20 19:28:22.000000000 +0000 @@ -35,7 +35,9 @@ "src/ICU4CGlue.cpp", "src/ICU4CLibrary.cpp", "src/ListFormat.cpp", + "src/Locale.cpp", "src/LocaleCanonicalizer.cpp", + "src/LocaleGenerated.cpp", "src/MeasureUnit.cpp", "src/NumberFormat.cpp", "src/NumberFormatFields.cpp", diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/intl/components/src/DateTimePatternGenerator.h firefox-trunk-95.0~a1~hg20211020r596404/intl/components/src/DateTimePatternGenerator.h --- firefox-trunk-95.0~a1~hg20211017r596111/intl/components/src/DateTimePatternGenerator.h 2021-10-17 14:42:38.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/intl/components/src/DateTimePatternGenerator.h 2021-10-20 19:28:22.000000000 +0000 @@ -93,6 +93,28 @@ } /** + * Get a pattern of the form "{1} {0}" to combine separate date and time + * patterns into a single pattern. The "{0}" part is the placeholder for the + * time pattern and "{1}" is the placeholder for the date pattern. + * + * See dateTimeFormat from + * https://unicode.org/reports/tr35/tr35-dates.html#dateTimeFormat + * + * Note: + * In CLDR, it's called Date-Time Combined Format + * https://cldr.unicode.org/translation/date-time/datetime-patterns#h.x7ca7qwzh4m + * + * The naming 'placeholder pattern' is from ICU4X. + * https://unicode-org.github.io/icu4x-docs/doc/icu_pattern/index.html + */ + Span GetPlaceholderPattern() const { + int32_t length; + const char16_t* combined = + udatpg_getDateTimeFormat(mGenerator.GetConst(), &length); + return Span{combined, static_cast(length)}; + } + + /** * TODO(Bug 1686965) - Temporarily get the underlying ICU object while * migrating to the unified API. This should be removed when completing the * migration. diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/intl/components/src/Locale.cpp firefox-trunk-95.0~a1~hg20211020r596404/intl/components/src/Locale.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/intl/components/src/Locale.cpp 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/intl/components/src/Locale.cpp 2021-10-20 19:28:22.000000000 +0000 @@ -0,0 +1,1469 @@ +/* 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 "mozilla/intl/Locale.h" + +#include "mozilla/Assertions.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/MathAlgorithms.h" +#include "mozilla/Span.h" +#include "mozilla/TextUtils.h" +#include "mozilla/Variant.h" + +#include "ICU4CGlue.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "unicode/uloc.h" +#include "unicode/utypes.h" + +namespace mozilla::intl { + +using namespace intl::LanguageTagLimits; + +template +bool IsStructurallyValidLanguageTag(Span language) { + // unicode_language_subtag = alpha{2,3} | alpha{5,8}; + size_t length = language.size(); + const CharT* str = language.data(); + return ((2 <= length && length <= 3) || (5 <= length && length <= 8)) && + std::all_of(str, str + length, IsAsciiAlpha); +} + +template bool IsStructurallyValidLanguageTag(Span language); +template bool IsStructurallyValidLanguageTag(Span language); +template bool IsStructurallyValidLanguageTag(Span language); + +template +bool IsStructurallyValidScriptTag(Span script) { + // unicode_script_subtag = alpha{4} ; + size_t length = script.size(); + const CharT* str = script.data(); + return length == 4 && std::all_of(str, str + length, IsAsciiAlpha); +} + +template bool IsStructurallyValidScriptTag(Span script); +template bool IsStructurallyValidScriptTag(Span script); +template bool IsStructurallyValidScriptTag(Span script); + +template +bool IsStructurallyValidRegionTag(Span region) { + // unicode_region_subtag = (alpha{2} | digit{3}) ; + size_t length = region.size(); + const CharT* str = region.data(); + return (length == 2 && std::all_of(str, str + length, IsAsciiAlpha)) || + (length == 3 && std::all_of(str, str + length, IsAsciiDigit)); +} + +template bool IsStructurallyValidRegionTag(Span region); +template bool IsStructurallyValidRegionTag(Span region); +template bool IsStructurallyValidRegionTag(Span region); + +#ifdef DEBUG +bool IsStructurallyValidVariantTag(Span variant) { + // unicode_variant_subtag = (alphanum{5,8} | digit alphanum{3}) ; + size_t length = variant.size(); + const char* str = variant.data(); + return ((5 <= length && length <= 8) || + (length == 4 && IsAsciiDigit(str[0]))) && + std::all_of(str, str + length, IsAsciiAlphanumeric); +} + +bool IsStructurallyValidUnicodeExtensionTag(Span extension) { + return LocaleParser::canParseUnicodeExtension(extension).isOk(); +} + +static bool IsStructurallyValidExtensionTag(Span extension) { + // other_extensions = sep [alphanum-[tTuUxX]] (sep alphanum{2,8})+ ; + // NB: Allow any extension, including Unicode and Transform here, because + // this function is only used for an assertion. + + size_t length = extension.size(); + const char* str = extension.data(); + const char* const end = extension.data() + length; + if (length <= 2) { + return false; + } + if (!IsAsciiAlphanumeric(str[0]) || str[0] == 'x' || str[0] == 'X') { + return false; + } + str++; + if (*str++ != '-') { + return false; + } + while (true) { + const char* sep = + reinterpret_cast(memchr(str, '-', end - str)); + size_t len = (sep ? sep : end) - str; + if (len < 2 || len > 8 || + !std::all_of(str, str + len, IsAsciiAlphanumeric)) { + return false; + } + if (!sep) { + return true; + } + str = sep + 1; + } +} + +bool IsStructurallyValidPrivateUseTag(Span privateUse) { + // pu_extensions = sep [xX] (sep alphanum{1,8})+ ; + + size_t length = privateUse.size(); + const char* str = privateUse.data(); + const char* const end = privateUse.data() + length; + if (length <= 2) { + return false; + } + if (str[0] != 'x' && str[0] != 'X') { + return false; + } + str++; + if (*str++ != '-') { + return false; + } + while (true) { + const char* sep = + reinterpret_cast(memchr(str, '-', end - str)); + size_t len = (sep ? sep : end) - str; + if (len == 0 || len > 8 || + !std::all_of(str, str + len, IsAsciiAlphanumeric)) { + return false; + } + if (!sep) { + return true; + } + str = sep + 1; + } +} +#endif + +ptrdiff_t Locale::unicodeExtensionIndex() const { + // The extension subtags aren't necessarily sorted, so we can't use binary + // search here. + auto p = std::find_if( + extensions().begin(), extensions().end(), + [](const auto& ext) { return ext[0] == 'u' || ext[0] == 'U'; }); + if (p != extensions().end()) { + return std::distance(extensions().begin(), p); + } + return -1; +} + +const char* Locale::unicodeExtension() const { + ptrdiff_t index = unicodeExtensionIndex(); + if (index >= 0) { + return extensions()[index].get(); + } + return nullptr; +} + +bool Locale::setUnicodeExtension(const char* extension) { + MOZ_ASSERT(IsStructurallyValidUnicodeExtensionTag(MakeStringSpan(extension))); + + auto duplicated = DuplicateStringToUniqueChars(extension); + + // Replace the existing Unicode extension subtag or append a new one. + ptrdiff_t index = unicodeExtensionIndex(); + if (index >= 0) { + extensions_[index] = std::move(duplicated); + return true; + } + return extensions_.append(std::move(duplicated)); +} + +void Locale::clearUnicodeExtension() { + ptrdiff_t index = unicodeExtensionIndex(); + if (index >= 0) { + extensions_.erase(extensions_.begin() + index); + } +} + +template +static bool SortAlphabetically(Vector& subtags) { + size_t length = subtags.length(); + + // Zero or one element lists are already sorted. + if (length < 2) { + return true; + } + + // Handle two element lists inline. + if (length == 2) { + if (strcmp(subtags[0].get(), subtags[1].get()) > 0) { + subtags[0].swap(subtags[1]); + } + return true; + } + + Vector scratch; + if (!scratch.resizeUninitialized(length)) { + return false; + } + for (size_t i = 0; i < length; i++) { + scratch[i] = subtags[i].release(); + } + + std::stable_sort( + scratch.begin(), scratch.end(), + [](const char* a, const char* b) { return strcmp(a, b) < 0; }); + + for (size_t i = 0; i < length; i++) { + subtags[i] = UniqueChars(scratch[i]); + } + return true; +} + +Result Locale::canonicalizeBaseName() { + // Per 6.2.3 CanonicalizeUnicodeLocaleId, the very first step is to + // canonicalize the syntax by normalizing the case and ordering all subtags. + // The canonical syntax form is specified in UTS 35, 3.2.1. + + // Language codes need to be in lower case. "JA" -> "ja" + language_.toLowerCase(); + MOZ_ASSERT(IsStructurallyValidLanguageTag(language().span())); + + // The first character of a script code needs to be capitalized. + // "hans" -> "Hans" + script_.toTitleCase(); + MOZ_ASSERT(script().missing() || + IsStructurallyValidScriptTag(script().span())); + + // Region codes need to be in upper case. "bu" -> "BU" + region_.toUpperCase(); + MOZ_ASSERT(region().missing() || + IsStructurallyValidRegionTag(region().span())); + + // The canonical case for variant subtags is lowercase. + for (UniqueChars& variant : variants_) { + char* variantChars = variant.get(); + size_t variantLength = strlen(variantChars); + AsciiToLowerCase(variantChars, variantLength, variantChars); + + MOZ_ASSERT(IsStructurallyValidVariantTag({variantChars, variantLength})); + } + + // Extensions and privateuse subtags are case normalized in the + // |canonicalizeExtensions| method. + + // The second step in UTS 35, 3.2.1, is to order all subtags. + + if (variants_.length() > 1) { + // 1. Any variants are in alphabetical order. + if (!SortAlphabetically(variants_)) { + return Err(CanonicalizationError::OutOfMemory); + } + + // Reject the Locale identifier if a duplicate variant was found, e.g. + // "en-variant-Variant". + const UniqueChars* duplicate = std::adjacent_find( + variants().begin(), variants().end(), [](const auto& a, const auto& b) { + return strcmp(a.get(), b.get()) == 0; + }); + if (duplicate != variants().end()) { + return Err(CanonicalizationError::DuplicateVariant); + } + } + + // 2. Any extensions are in alphabetical order by their singleton. + // 3. All attributes are sorted in alphabetical order. + // 4. All keywords and tfields are sorted by alphabetical order of their keys, + // within their respective extensions. + // 5. Any type or tfield value "true" is removed. + // - A subsequent call to canonicalizeExtensions() will perform these steps. + + // 6.2.3 CanonicalizeUnicodeLocaleId, step 2 transforms the locale identifier + // into its canonical form per UTS 3.2.1. + + // 1. Use the bcp47 data to replace keys, types, tfields, and tvalues by their + // canonical forms. + // - A subsequent call to canonicalizeExtensions() will perform this step. + + // 2. Replace aliases in the unicode_language_id and tlang (if any). + // - tlang is handled in canonicalizeExtensions(). + + // Replace deprecated language, region, and variant subtags with their + // preferred mappings. + + if (!updateLegacyMappings()) { + return Err(CanonicalizationError::OutOfMemory); + } + + // Replace deprecated language subtags with their preferred values. + if (!languageMapping(language_) && complexLanguageMapping(language_)) { + performComplexLanguageMappings(); + } + + // Replace deprecated script subtags with their preferred values. + if (script().present()) { + scriptMapping(script_); + } + + // Replace deprecated region subtags with their preferred values. + if (region().present()) { + if (!regionMapping(region_) && complexRegionMapping(region_)) { + performComplexRegionMappings(); + } + } + + // Replace deprecated variant subtags with their preferred values. + if (!performVariantMappings()) { + return Err(CanonicalizationError::OutOfMemory); + } + + // No extension replacements are currently present. + // Private use sequences are left as is. + + // 3. Replace aliases in special key values. + // - A subsequent call to canonicalizeExtensions() will perform this step. + + return Ok(); +} + +#ifdef DEBUG +static bool IsAsciiLowercaseAlphanumericOrDash(Span span) { + const char* ptr = span.data(); + size_t length = span.size(); + return std::all_of(ptr, ptr + length, [](auto c) { + return IsAsciiLowercaseAlpha(c) || IsAsciiDigit(c) || c == '-'; + }); +} +#endif + +Result Locale::canonicalizeExtensions() { + // The canonical case for all extension subtags is lowercase. + for (UniqueChars& extension : extensions_) { + char* extensionChars = extension.get(); + size_t extensionLength = strlen(extensionChars); + AsciiToLowerCase(extensionChars, extensionLength, extensionChars); + + MOZ_ASSERT( + IsStructurallyValidExtensionTag({extensionChars, extensionLength})); + } + + // Any extensions are in alphabetical order by their singleton. + // "u-ca-chinese-t-zh-latn" -> "t-zh-latn-u-ca-chinese" + if (!SortAlphabetically(extensions_)) { + return Err(CanonicalizationError::OutOfMemory); + } + + for (UniqueChars& extension : extensions_) { + if (extension[0] == 'u') { + MOZ_TRY(canonicalizeUnicodeExtension(extension)); + } else if (extension[0] == 't') { + MOZ_TRY(canonicalizeTransformExtension(extension)); + } + + MOZ_ASSERT( + IsAsciiLowercaseAlphanumericOrDash(MakeStringSpan(extension.get()))); + } + + // The canonical case for privateuse subtags is lowercase. + if (char* privateuse = privateuse_.get()) { + size_t privateuseLength = strlen(privateuse); + AsciiToLowerCase(privateuse, privateuseLength, privateuse); + + MOZ_ASSERT( + IsStructurallyValidPrivateUseTag({privateuse, privateuseLength})); + } + return Ok(); +} + +/** + * CanonicalizeUnicodeExtension( attributes, keywords ) + * + * Canonical syntax per + * : + * + * - All attributes and keywords are in lowercase. + * - Note: The parser already converted keywords to lowercase. + * - All attributes are sorted in alphabetical order. + * - All keywords are sorted by alphabetical order of their keys. + * - Any type value "true" is removed. + * + * Canonical form: + * - All keys and types use the canonical form (from the name attribute; + * see Section 3.6.4 U Extension Data Files). + */ +Result Locale::canonicalizeUnicodeExtension( + UniqueChars& unicodeExtension) { + const char* const extension = unicodeExtension.get(); + MOZ_ASSERT(extension[0] == 'u'); + MOZ_ASSERT(extension[1] == '-'); + MOZ_ASSERT(IsStructurallyValidExtensionTag(MakeStringSpan(extension))); + + size_t length = strlen(extension); + + LocaleParser::AttributesVector attributes; + LocaleParser::KeywordsVector keywords; + + using Attribute = LocaleParser::AttributesVector::ElementType; + using Keyword = LocaleParser::KeywordsVector::ElementType; + + if (LocaleParser::parseUnicodeExtension(Span(extension, length), attributes, + keywords) + .isErr()) { + MOZ_ASSERT_UNREACHABLE("unexpected invalid Unicode extension subtag"); + return Err(CanonicalizationError::InternalError); + } + + auto attributesLess = [extension](const Attribute& a, const Attribute& b) { + const char* astr = a.begin(extension); + const char* bstr = b.begin(extension); + size_t alen = a.length(); + size_t blen = b.length(); + + if (int r = + std::char_traits::compare(astr, bstr, std::min(alen, blen))) { + return r < 0; + } + return alen < blen; + }; + + // All attributes are sorted in alphabetical order. + if (attributes.length() > 1) { + std::stable_sort(attributes.begin(), attributes.end(), attributesLess); + } + + auto keywordsLess = [extension](const Keyword& a, const Keyword& b) { + const char* astr = a.begin(extension); + const char* bstr = b.begin(extension); + MOZ_ASSERT(a.length() >= UnicodeKeyLength); + MOZ_ASSERT(b.length() >= UnicodeKeyLength); + + return std::char_traits::compare(astr, bstr, UnicodeKeyLength) < 0; + }; + + // All keywords are sorted by alphabetical order of keys. + if (keywords.length() > 1) { + // Using a stable sort algorithm, guarantees that two keywords using the + // same key are never reordered. That means for example + // when we have the input "u-nu-thai-kf-false-nu-latn", we are guaranteed to + // get the result "u-kf-false-nu-thai-nu-latn", i.e. "nu-thai" still occurs + // before "nu-latn". + // This is required so that deduplication below preserves the first keyword + // for a given key and discards the rest. + std::stable_sort(keywords.begin(), keywords.end(), keywordsLess); + } + + Vector sb; + if (!sb.append('u')) { + return Err(CanonicalizationError::OutOfMemory); + } + + // Append all Unicode extension attributes. + for (size_t i = 0; i < attributes.length(); i++) { + const auto& attribute = attributes[i]; + + // Skip duplicate attributes. + if (i > 0) { + const auto& lastAttribute = attributes[i - 1]; + if (attribute.length() == lastAttribute.length() && + std::char_traits::compare(attribute.begin(extension), + lastAttribute.begin(extension), + attribute.length()) == 0) { + continue; + } + MOZ_ASSERT(attributesLess(lastAttribute, attribute)); + } + + if (!sb.append('-')) { + return Err(CanonicalizationError::OutOfMemory); + } + if (!sb.append(attribute.begin(extension), attribute.length())) { + return Err(CanonicalizationError::OutOfMemory); + } + } + + static constexpr size_t UnicodeKeyWithSepLength = UnicodeKeyLength + 1; + + using StringSpan = Span; + + static auto isTrue = [](StringSpan type) { + static constexpr char True[] = "true"; + constexpr size_t TrueLength = std::char_traits::length(True); + return type.size() == TrueLength && + std::char_traits::compare(type.data(), True, TrueLength) == 0; + }; + + auto appendKey = [&sb, extension](const Keyword& keyword) { + MOZ_ASSERT(keyword.length() == UnicodeKeyLength); + return sb.append(keyword.begin(extension), UnicodeKeyLength); + }; + + auto appendKeyword = [&sb, extension](const Keyword& keyword, + StringSpan type) { + MOZ_ASSERT(keyword.length() > UnicodeKeyLength); + + // Elide the Unicode extension type "true". + if (isTrue(type)) { + return sb.append(keyword.begin(extension), UnicodeKeyLength); + } + // Otherwise append the complete Unicode extension keyword. + return sb.append(keyword.begin(extension), keyword.length()); + }; + + auto appendReplacement = [&sb, extension](const Keyword& keyword, + StringSpan replacement) { + MOZ_ASSERT(keyword.length() > UnicodeKeyLength); + + // Elide the type "true" if present in the replacement. + if (isTrue(replacement)) { + return sb.append(keyword.begin(extension), UnicodeKeyLength); + } + // Otherwise append the Unicode key (including the separator) and the + // replaced type. + return sb.append(keyword.begin(extension), UnicodeKeyWithSepLength) && + sb.append(replacement.data(), replacement.size()); + }; + + // Append all Unicode extension keywords. + for (size_t i = 0; i < keywords.length(); i++) { + const auto& keyword = keywords[i]; + + // Skip duplicate keywords. + if (i > 0) { + const auto& lastKeyword = keywords[i - 1]; + if (std::char_traits::compare(keyword.begin(extension), + lastKeyword.begin(extension), + UnicodeKeyLength) == 0) { + continue; + } + MOZ_ASSERT(keywordsLess(lastKeyword, keyword)); + } + + if (!sb.append('-')) { + return Err(CanonicalizationError::OutOfMemory); + } + + if (keyword.length() == UnicodeKeyLength) { + // Keyword without type value. + if (!appendKey(keyword)) { + return Err(CanonicalizationError::OutOfMemory); + } + } else { + StringSpan key(keyword.begin(extension), UnicodeKeyLength); + StringSpan type(keyword.begin(extension) + UnicodeKeyWithSepLength, + keyword.length() - UnicodeKeyWithSepLength); + + // Search if there's a replacement for the current Unicode keyword. + if (const char* replacement = replaceUnicodeExtensionType(key, type)) { + if (!appendReplacement(keyword, MakeStringSpan(replacement))) { + return Err(CanonicalizationError::OutOfMemory); + } + } else { + if (!appendKeyword(keyword, type)) { + return Err(CanonicalizationError::OutOfMemory); + } + } + } + } + + // We can keep the previous extension when canonicalization didn't modify it. + if (sb.length() != length || + std::char_traits::compare(sb.begin(), extension, length) != 0) { + // Null-terminate the new string and replace the previous extension. + if (!sb.append('\0')) { + return Err(CanonicalizationError::OutOfMemory); + } + UniqueChars canonical(sb.extractOrCopyRawBuffer()); + if (!canonical) { + return Err(CanonicalizationError::OutOfMemory); + } + unicodeExtension = std::move(canonical); + } + + return Ok(); +} + +template +static bool LocaleToString(const Locale& tag, Buffer& sb) { + auto appendSubtag = [&sb](const auto& subtag) { + auto span = subtag.span(); + MOZ_ASSERT(!span.empty()); + return sb.append(span.data(), span.size()); + }; + + auto appendSubtagZ = [&sb](const char* subtag) { + MOZ_ASSERT(strlen(subtag) > 0); + return sb.append(subtag, strlen(subtag)); + }; + + auto appendSubtagsZ = [&sb, &appendSubtagZ](const auto& subtags) { + for (const auto& subtag : subtags) { + if (!sb.append('-') || !appendSubtagZ(subtag.get())) { + return false; + } + } + return true; + }; + + // Append the language subtag. + if (!appendSubtag(tag.language())) { + return false; + } + + // Append the script subtag if present. + if (tag.script().present()) { + if (!sb.append('-') || !appendSubtag(tag.script())) { + return false; + } + } + + // Append the region subtag if present. + if (tag.region().present()) { + if (!sb.append('-') || !appendSubtag(tag.region())) { + return false; + } + } + + // Append the variant subtags if present. + if (!appendSubtagsZ(tag.variants())) { + return false; + } + + // Append the extensions subtags if present. + if (!appendSubtagsZ(tag.extensions())) { + return false; + } + + // Append the private-use subtag if present. + if (tag.privateuse()) { + if (!sb.append('-') || !appendSubtagZ(tag.privateuse())) { + return false; + } + } + + return true; +} + +/** + * CanonicalizeTransformExtension + * + * Canonical form per : + * + * - These subtags are all in lowercase (that is the canonical casing for these + * subtags), [...]. + * + * And per + * : + * + * - All keywords and tfields are sorted by alphabetical order of their keys, + * within their respective extensions. + */ +Result +Locale::canonicalizeTransformExtension(UniqueChars& transformExtension) { + const char* const extension = transformExtension.get(); + MOZ_ASSERT(extension[0] == 't'); + MOZ_ASSERT(extension[1] == '-'); + MOZ_ASSERT(IsStructurallyValidExtensionTag(MakeStringSpan(extension))); + + size_t length = strlen(extension); + + Locale tag; + LocaleParser::TFieldVector fields; + + using TField = LocaleParser::TFieldVector::ElementType; + + if (LocaleParser::parseTransformExtension(Span(extension, length), tag, + fields) + .isErr()) { + MOZ_ASSERT_UNREACHABLE("unexpected invalid transform extension subtag"); + return Err(CanonicalizationError::InternalError); + } + + auto tfieldLess = [extension](const TField& a, const TField& b) { + MOZ_ASSERT(a.length() > TransformKeyLength); + MOZ_ASSERT(b.length() > TransformKeyLength); + const char* astr = a.begin(extension); + const char* bstr = b.begin(extension); + return std::char_traits::compare(astr, bstr, TransformKeyLength) < 0; + }; + + // All tfields are sorted by alphabetical order of their keys. + if (fields.length() > 1) { + std::stable_sort(fields.begin(), fields.end(), tfieldLess); + } + + Vector sb; + if (!sb.append('t')) { + return Err(CanonicalizationError::OutOfMemory); + } + + // Append the language subtag if present. + // + // Replace aliases in tlang per + // . + if (tag.language().present()) { + if (!sb.append('-')) { + return Err(CanonicalizationError::OutOfMemory); + } + + MOZ_TRY(tag.canonicalizeBaseName()); + + // The canonical case for Transform extensions is lowercase per + // . Convert the two + // subtags which don't use lowercase for their canonical syntax. + tag.script_.toLowerCase(); + tag.region_.toLowerCase(); + + if (!LocaleToString(tag, sb)) { + return Err(CanonicalizationError::OutOfMemory); + } + } + + static constexpr size_t TransformKeyWithSepLength = TransformKeyLength + 1; + + using StringSpan = Span; + + // Append all fields. + // + // UTS 35, 3.2.1 specifies: + // - Any type or tfield value "true" is removed. + // + // But the `tvalue` subtag is mandatory in `tfield: tkey tvalue`, so ignore + // this apparently invalid part of the UTS 35 specification and simply + // append all `tfield` subtags. + for (const auto& field : fields) { + if (!sb.append('-')) { + return Err(CanonicalizationError::OutOfMemory); + } + + StringSpan key(field.begin(extension), TransformKeyLength); + StringSpan value(field.begin(extension) + TransformKeyWithSepLength, + field.length() - TransformKeyWithSepLength); + + // Search if there's a replacement for the current transform keyword. + if (const char* replacement = replaceTransformExtensionType(key, value)) { + if (!sb.append(field.begin(extension), TransformKeyWithSepLength)) { + return Err(CanonicalizationError::OutOfMemory); + } + if (!sb.append(replacement, strlen(replacement))) { + return Err(CanonicalizationError::OutOfMemory); + } + } else { + if (!sb.append(field.begin(extension), field.length())) { + return Err(CanonicalizationError::OutOfMemory); + } + } + } + + // We can keep the previous extension when canonicalization didn't modify it. + if (sb.length() != length || + std::char_traits::compare(sb.begin(), extension, length) != 0) { + // Null-terminate the new string and replace the previous extension. + if (!sb.append('\0')) { + return Err(CanonicalizationError::OutOfMemory); + } + UniqueChars canonical(sb.extractOrCopyRawBuffer()); + if (!canonical) { + return Err(CanonicalizationError::OutOfMemory); + } + transformExtension = std::move(canonical); + } + + return Ok(); +} + +// Zero-terminated ICU Locale ID. +using LocaleId = + Vector; + +enum class LikelySubtags : bool { Add, Remove }; + +// Return true iff the locale is already maximized resp. minimized. +static bool HasLikelySubtags(LikelySubtags likelySubtags, const Locale& tag) { + // The locale is already maximized if the language, script, and region + // subtags are present and no placeholder subtags ("und", "Zzzz", "ZZ") are + // used. + if (likelySubtags == LikelySubtags::Add) { + return !tag.language().equalTo("und") && + (tag.script().present() && !tag.script().equalTo("Zzzz")) && + (tag.region().present() && !tag.region().equalTo("ZZ")); + } + + // The locale is already minimized if it only contains a language + // subtag whose value is not the placeholder value "und". + return !tag.language().equalTo("und") && tag.script().missing() && + tag.region().missing(); +} + +// Create an ICU locale ID from the given locale. +static bool CreateLocaleForLikelySubtags(const Locale& tag, LocaleId& locale) { + MOZ_ASSERT(locale.length() == 0); + + auto appendSubtag = [&locale](const auto& subtag) { + auto span = subtag.span(); + MOZ_ASSERT(!span.empty()); + return locale.append(span.data(), span.size()); + }; + + // Append the language subtag. + if (!appendSubtag(tag.language())) { + return false; + } + + // Append the script subtag if present. + if (tag.script().present()) { + if (!locale.append('_') || !appendSubtag(tag.script())) { + return false; + } + } + + // Append the region subtag if present. + if (tag.region().present()) { + if (!locale.append('_') || !appendSubtag(tag.region())) { + return false; + } + } + + // Zero-terminated for use with ICU. + return locale.append('\0'); +} + +// Assign the language, script, and region subtags from an ICU locale ID. +// +// ICU provides |uloc_getLanguage|, |uloc_getScript|, and |uloc_getCountry| to +// retrieve these subtags, but unfortunately these functions are rather slow, so +// we use our own implementation. +static bool AssignFromLocaleId(LocaleId& localeId, Locale& tag) { + MOZ_ASSERT(localeId.back() == '\0', + "Locale ID should be zero-terminated for ICU"); + + // Replace the ICU locale ID separator. + std::replace(localeId.begin(), localeId.end(), '_', '-'); + + // ICU replaces "und" with the empty string, which means "und" becomes "" and + // "und-Latn" becomes "-Latn". Handle this case separately. + if (localeId[0] == '\0' || localeId[0] == '-') { + static constexpr char und[] = "und"; + constexpr size_t length = std::char_traits::length(und); + + // Insert "und" in front of the locale ID. + if (!localeId.growBy(length)) { + return false; + } + memmove(localeId.begin() + length, localeId.begin(), localeId.length()); + memmove(localeId.begin(), und, length); + } + + Span localeSpan(localeId.begin(), localeId.length() - 1); + + // Retrieve the language, script, and region subtags from the locale ID + Locale localeTag; + if (LocaleParser::tryParseBaseName(localeSpan, localeTag).isErr()) { + return false; + } + + tag.setLanguage(localeTag.language()); + tag.setScript(localeTag.script()); + tag.setRegion(localeTag.region()); + + return true; +} + +template +static bool CallLikelySubtags(const LocaleId& localeId, LocaleId& result) { + // Locale ID must be zero-terminated before passing it to ICU. + MOZ_ASSERT(localeId.back() == '\0'); + MOZ_ASSERT(result.length() == 0); + + // Ensure there's enough room for the result. + MOZ_ALWAYS_TRUE(result.resize(LocaleId::InlineLength)); + + if (FillBufferWithICUCall(result, [&localeId](char* chars, int32_t size, + UErrorCode* status) { + return likelySubtagsFn(localeId.begin(), chars, size, status); + }).isErr()) { + return false; + } + + // Zero-terminated for use with ICU. + return result.append('\0'); +} + +// The canonical way to compute the Unicode BCP 47 locale identifier with likely +// subtags is as follows: +// +// 1. Call uloc_forLanguageTag() to transform the locale identifer into an ICU +// locale ID. +// 2. Call uloc_addLikelySubtags() to add the likely subtags to the locale ID. +// 3. Call uloc_toLanguageTag() to transform the resulting locale ID back into +// a Unicode BCP 47 locale identifier. +// +// Since uloc_forLanguageTag() and uloc_toLanguageTag() are both kind of slow +// and we know, by construction, that the input Unicode BCP 47 locale identifier +// only contains valid language, script, and region subtags, we can avoid both +// calls if we implement them ourselves, see CreateLocaleForLikelySubtags() and +// AssignFromLocaleId(). (Where "slow" means about 50% of the execution time of +// |Intl.Locale.prototype.maximize|.) +static bool LikelySubtags(LikelySubtags likelySubtags, Locale& tag) { + // Return early if the input is already maximized/minimized. + if (HasLikelySubtags(likelySubtags, tag)) { + return true; + } + + // Create the locale ID for the input argument. + LocaleId locale; + if (!CreateLocaleForLikelySubtags(tag, locale)) { + return false; + } + + // Either add or remove likely subtags to/from the locale ID. + LocaleId localeLikelySubtags; + if (likelySubtags == LikelySubtags::Add) { + if (!CallLikelySubtags(locale, + localeLikelySubtags)) { + return false; + } + } else { + if (!CallLikelySubtags(locale, localeLikelySubtags)) { + return false; + } + } + + // Assign the language, script, and region subtags from the locale ID. + if (!AssignFromLocaleId(localeLikelySubtags, tag)) { + return false; + } + + // Update mappings in case ICU returned a non-canonical locale. + return tag.canonicalizeBaseName().isOk(); +} + +bool Locale::addLikelySubtags() { + return LikelySubtags(LikelySubtags::Add, *this); +} + +bool Locale::removeLikelySubtags() { + return LikelySubtags(LikelySubtags::Remove, *this); +} + +UniqueChars Locale::DuplicateStringToUniqueChars(const char* s) { + size_t length = strlen(s) + 1; + auto duplicate = MakeUnique(length); + memcpy(duplicate.get(), s, length); + return duplicate; +} + +size_t Locale::toStringCapacity() const { + // This is a bit awkward, the buffer class currently does not support + // being resized, so we need to calculate the required size up front and + // reserve it all at once. + auto lengthSubtag = [](const auto& subtag) { + auto span = subtag.span(); + MOZ_ASSERT(!span.empty()); + return span.size(); + }; + + auto lengthSubtagZ = [](const char* subtag) { + size_t length = strlen(subtag); + MOZ_ASSERT(length > 0); + return length; + }; + + auto lengthSubtagsZ = [&lengthSubtagZ](const auto& subtags) { + size_t length = 0; + for (const auto& subtag : subtags) { + length += lengthSubtagZ(subtag.get()) + 1; + } + return length; + }; + + // First calculate required capacity + size_t capacity = 0; + + capacity += lengthSubtag(language_); + + if (script_.present()) { + capacity += lengthSubtag(script_) + 1; + } + + if (region_.present()) { + capacity += lengthSubtag(region_) + 1; + } + + capacity += lengthSubtagsZ(variants_); + + capacity += lengthSubtagsZ(extensions_); + + if (privateuse_.get()) { + capacity += lengthSubtagZ(privateuse_.get()) + 1; + } + + return capacity; +} + +size_t Locale::toStringAppend(char* buffer) const { + // Current write position inside buffer. + size_t offset = 0; + + auto appendHyphen = [&offset, &buffer]() { + buffer[offset] = '-'; + offset += 1; + }; + + auto appendSubtag = [&offset, &buffer](const auto& subtag) { + auto span = subtag.span(); + memcpy(buffer + offset, span.data(), span.size()); + offset += span.size(); + }; + + auto appendSubtagZ = [&offset, &buffer](const char* subtag) { + size_t length = strlen(subtag); + memcpy(buffer + offset, subtag, length); + offset += length; + }; + + auto appendSubtagsZ = [&appendHyphen, &appendSubtagZ](const auto& subtags) { + for (const auto& subtag : subtags) { + appendHyphen(); + appendSubtagZ(subtag.get()); + } + }; + + // Append the language subtag. + appendSubtag(language_); + + // Append the script subtag if present. + if (script_.present()) { + appendHyphen(); + appendSubtag(script_); + } + + // Append the region subtag if present. + if (region_.present()) { + appendHyphen(); + appendSubtag(region_); + } + + // Append the variant subtags if present. + appendSubtagsZ(variants_); + + // Append the extensions subtags if present. + appendSubtagsZ(extensions_); + + // Append the private-use subtag if present. + if (privateuse_.get()) { + appendHyphen(); + appendSubtagZ(privateuse_.get()); + } + + return offset; +} + +LocaleParser::Token LocaleParser::nextToken() { + MOZ_ASSERT(index_ <= length_ + 1, "called after 'None' token was read"); + + TokenKind kind = TokenKind::None; + size_t tokenLength = 0; + for (size_t i = index_; i < length_; i++) { + // UTS 35, section 3.1. + // alpha = [A-Z a-z] ; + // digit = [0-9] ; + char c = charAt(i); + if (IsAsciiAlpha(c)) { + kind |= TokenKind::Alpha; + } else if (IsAsciiDigit(c)) { + kind |= TokenKind::Digit; + } else if (c == '-' && i > index_ && i + 1 < length_) { + break; + } else { + return {TokenKind::Error, 0, 0}; + } + tokenLength += 1; + } + + Token token{kind, index_, tokenLength}; + index_ += tokenLength + 1; + return token; +} + +UniqueChars LocaleParser::chars(size_t index, size_t length) const { + // Add +1 to null-terminate the string. + auto chars = MakeUnique(length + 1); + char* dest = chars.get(); + std::copy_n(locale_ + index, length, dest); + dest[length] = '\0'; + return chars; +} + +// Parse the `unicode_language_id` production. +// +// unicode_language_id = unicode_language_subtag +// (sep unicode_script_subtag)? +// (sep unicode_region_subtag)? +// (sep unicode_variant_subtag)* ; +// +// sep = "-" +// +// Note: Unicode CLDR locale identifier backward compatibility extensions +// removed from `unicode_language_id`. +// +// |tok| is the current token from |ts|. +// +// All subtags will be added unaltered to |tag|, without canonicalizing their +// case or, in the case of variant subtags, detecting and rejecting duplicate +// variants. Users must subsequently |canonicalizeBaseName| to perform these +// actions. +// +// Do not use this function directly: use |parseBaseName| or +// |parseTlangFromTransformExtension| instead. +Result LocaleParser::internalParseBaseName( + LocaleParser& ts, Locale& tag, Token& tok) { + if (ts.isLanguage(tok)) { + ts.copyChars(tok, tag.language_); + + tok = ts.nextToken(); + } else { + // The language subtag is mandatory. + return Err(ParserError::NotParseable); + } + + if (ts.isScript(tok)) { + ts.copyChars(tok, tag.script_); + + tok = ts.nextToken(); + } + + if (ts.isRegion(tok)) { + ts.copyChars(tok, tag.region_); + + tok = ts.nextToken(); + } + + auto& variants = tag.variants_; + MOZ_ASSERT(variants.length() == 0); + while (ts.isVariant(tok)) { + auto variant = ts.chars(tok); + if (!variants.append(std::move(variant))) { + return Err(ParserError::OutOfMemory); + } + + tok = ts.nextToken(); + } + + return Ok(); +} + +Result LocaleParser::tryParse( + mozilla::Span locale, Locale& tag) { + // unicode_locale_id = unicode_language_id + // extensions* + // pu_extensions? ; + + LocaleParser ts(locale); + Token tok = ts.nextToken(); + + MOZ_TRY(parseBaseName(ts, tag, tok)); + + // extensions = unicode_locale_extensions + // | transformed_extensions + // | other_extensions ; + + // Bit set of seen singletons. + uint64_t seenSingletons = 0; + + auto& extensions = tag.extensions_; + while (ts.isExtensionStart(tok)) { + char singleton = ts.singletonKey(tok); + + // Reject the input if a duplicate singleton was found. + uint64_t hash = 1ULL << (AsciiAlphanumericToNumber(singleton) + 1); + if (seenSingletons & hash) { + return Err(ParserError::NotParseable); + } + seenSingletons |= hash; + + Token start = tok; + tok = ts.nextToken(); + + // We'll check for missing non-singleton subtags after this block by + // comparing |startValue| with the then-current position. + size_t startValue = tok.index(); + + if (singleton == 'u') { + while (ts.isUnicodeExtensionPart(tok)) { + tok = ts.nextToken(); + } + } else if (singleton == 't') { + // transformed_extensions = sep [tT] + // ((sep tlang (sep tfield)*) + // | (sep tfield)+) ; + + // tlang = unicode_language_subtag + // (sep unicode_script_subtag)? + // (sep unicode_region_subtag)? + // (sep unicode_variant_subtag)* ; + if (ts.isLanguage(tok)) { + tok = ts.nextToken(); + + if (ts.isScript(tok)) { + tok = ts.nextToken(); + } + + if (ts.isRegion(tok)) { + tok = ts.nextToken(); + } + + while (ts.isVariant(tok)) { + tok = ts.nextToken(); + } + } + + // tfield = tkey tvalue; + while (ts.isTransformExtensionKey(tok)) { + tok = ts.nextToken(); + + size_t startTValue = tok.index(); + while (ts.isTransformExtensionPart(tok)) { + tok = ts.nextToken(); + } + + // `tfield` requires at least one `tvalue`. + if (tok.index() <= startTValue) { + return Err(ParserError::NotParseable); + } + } + } else { + while (ts.isOtherExtensionPart(tok)) { + tok = ts.nextToken(); + } + } + + // Singletons must be followed by a non-singleton subtag, "en-a-b" is not + // allowed. + if (tok.index() <= startValue) { + return Err(ParserError::NotParseable); + } + + UniqueChars extension = ts.extension(start, tok); + if (!extensions.append(std::move(extension))) { + return Err(ParserError::OutOfMemory); + } + } + + // Trailing `pu_extension` component of the `unicode_locale_id` production. + if (ts.isPrivateUseStart(tok)) { + Token start = tok; + tok = ts.nextToken(); + + size_t startValue = tok.index(); + while (ts.isPrivateUsePart(tok)) { + tok = ts.nextToken(); + } + + // There must be at least one subtag after the "-x-". + if (tok.index() <= startValue) { + return Err(ParserError::NotParseable); + } + + UniqueChars privateUse = ts.extension(start, tok); + tag.privateuse_ = std::move(privateUse); + } + + if (!tok.isNone()) { + return Err(ParserError::NotParseable); + } + + return Ok(); +} + +Result LocaleParser::tryParseBaseName( + Span locale, Locale& tag) { + LocaleParser ts(locale); + Token tok = ts.nextToken(); + + MOZ_TRY(parseBaseName(ts, tag, tok)); + if (!tok.isNone()) { + return Err(ParserError::NotParseable); + } + + return Ok(); +} + +// Parse |extension|, which must be a valid `transformed_extensions` subtag, and +// fill |tag| and |fields| from the `tlang` and `tfield` components. +Result LocaleParser::parseTransformExtension( + Span extension, Locale& tag, TFieldVector& fields) { + LocaleParser ts(extension); + Token tok = ts.nextToken(); + + if (!ts.isExtensionStart(tok) || ts.singletonKey(tok) != 't') { + return Err(ParserError::NotParseable); + } + + tok = ts.nextToken(); + + if (tok.isNone()) { + return Err(ParserError::NotParseable); + } + + if (ts.isLanguage(tok)) { + // We're parsing a possible `tlang` in a known-valid transform extension, so + // use the special-purpose function that takes advantage of this to compute + // lowercased |tag| contents in an optimal manner. + MOZ_TRY(parseTlangInTransformExtension(ts, tag, tok)); + + // After `tlang` we must have a `tfield` and its `tkey`, or we're at the end + // of the transform extension. + MOZ_ASSERT(ts.isTransformExtensionKey(tok) || tok.isNone()); + } else { + // If there's no `tlang` subtag, at least one `tfield` must be present. + MOZ_ASSERT(ts.isTransformExtensionKey(tok)); + } + + // Trailing `tfield` subtags. (Any other trailing subtags are an error, + // because we're guaranteed to only see a valid tranform extension here.) + while (ts.isTransformExtensionKey(tok)) { + size_t begin = tok.index(); + tok = ts.nextToken(); + + size_t startTValue = tok.index(); + while (ts.isTransformExtensionPart(tok)) { + tok = ts.nextToken(); + } + + // `tfield` requires at least one `tvalue`. + if (tok.index() <= startTValue) { + return Err(ParserError::NotParseable); + } + + size_t length = tok.index() - 1 - begin; + if (!fields.emplaceBack(begin, length)) { + return Err(ParserError::OutOfMemory); + } + } + + if (!tok.isNone()) { + return Err(ParserError::NotParseable); + } + + return Ok(); +} + +// Parse |extension|, which must be a valid `unicode_locale_extensions` subtag, +// and fill |attributes| and |keywords| from the `attribute` and `keyword` +// components. +Result LocaleParser::parseUnicodeExtension( + Span extension, AttributesVector& attributes, + KeywordsVector& keywords) { + LocaleParser ts(extension); + Token tok = ts.nextToken(); + + // unicode_locale_extensions = sep [uU] ((sep keyword)+ | + // (sep attribute)+ (sep keyword)*) ; + + if (!ts.isExtensionStart(tok) || ts.singletonKey(tok) != 'u') { + return Err(ParserError::NotParseable); + } + + tok = ts.nextToken(); + + if (tok.isNone()) { + return Err(ParserError::NotParseable); + } + + while (ts.isUnicodeExtensionAttribute(tok)) { + if (!attributes.emplaceBack(tok.index(), tok.length())) { + return Err(ParserError::OutOfMemory); + } + + tok = ts.nextToken(); + } + + // keyword = key (sep type)? ; + while (ts.isUnicodeExtensionKey(tok)) { + size_t begin = tok.index(); + tok = ts.nextToken(); + + while (ts.isUnicodeExtensionType(tok)) { + tok = ts.nextToken(); + } + + if (tok.isError()) { + return Err(ParserError::NotParseable); + } + + size_t length = tok.index() - 1 - begin; + if (!keywords.emplaceBack(begin, length)) { + return Err(ParserError::OutOfMemory); + } + } + + if (!tok.isNone()) { + return Err(ParserError::NotParseable); + } + + return Ok(); +} + +Result LocaleParser::canParseUnicodeExtension( + Span extension) { + LocaleParser ts(extension); + Token tok = ts.nextToken(); + + // unicode_locale_extensions = sep [uU] ((sep keyword)+ | + // (sep attribute)+ (sep keyword)*) ; + + if (!ts.isExtensionStart(tok) || ts.singletonKey(tok) != 'u') { + return Err(ParserError::NotParseable); + } + + tok = ts.nextToken(); + + if (tok.isNone()) { + return Err(ParserError::NotParseable); + } + + while (ts.isUnicodeExtensionAttribute(tok)) { + tok = ts.nextToken(); + } + + // keyword = key (sep type)? ; + while (ts.isUnicodeExtensionKey(tok)) { + tok = ts.nextToken(); + + while (ts.isUnicodeExtensionType(tok)) { + tok = ts.nextToken(); + } + + if (tok.isError()) { + return Err(ParserError::NotParseable); + } + } + + if (!tok.isNone()) { + return Err(ParserError::OutOfMemory); + } + + return Ok(); +} + +Result +LocaleParser::canParseUnicodeExtensionType(Span unicodeType) { + MOZ_ASSERT(!unicodeType.empty(), "caller must exclude empty strings"); + + LocaleParser ts(unicodeType); + Token tok = ts.nextToken(); + + while (ts.isUnicodeExtensionType(tok)) { + tok = ts.nextToken(); + } + + if (!tok.isNone()) { + return Err(ParserError::NotParseable); + } + + return Ok(); +} + +} // namespace mozilla::intl diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/intl/components/src/LocaleGenerated.cpp firefox-trunk-95.0~a1~hg20211020r596404/intl/components/src/LocaleGenerated.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/intl/components/src/LocaleGenerated.cpp 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/intl/components/src/LocaleGenerated.cpp 2021-10-20 19:28:21.000000000 +0000 @@ -0,0 +1,1111 @@ +// Generated by make_intl_data.py. DO NOT EDIT. +// Version: CLDR-39 +// URL: https://unicode.org/Public/cldr/39/core.zip + +#include "mozilla/Assertions.h" +#include "mozilla/Span.h" +#include "mozilla/TextUtils.h" + +#include +#include +#include +#include +#include +#include + +#include "mozilla/intl/Locale.h" + +using namespace mozilla::intl::LanguageTagLimits; + +template +static inline bool HasReplacement( + const char (&subtags)[Length][TagLength], + const mozilla::intl::LanguageTagSubtag& subtag) { + MOZ_ASSERT(subtag.length() == TagLength - 1, + "subtag must have the same length as the list of subtags"); + + const char* ptr = subtag.span().data(); + return std::binary_search(std::begin(subtags), std::end(subtags), ptr, + [](const char* a, const char* b) { + return memcmp(a, b, TagLength - 1) < 0; + }); +} + +template +static inline const char* SearchReplacement( + const char (&subtags)[Length][TagLength], const char* (&aliases)[Length], + const mozilla::intl::LanguageTagSubtag& subtag) { + MOZ_ASSERT(subtag.length() == TagLength - 1, + "subtag must have the same length as the list of subtags"); + + const char* ptr = subtag.span().data(); + auto p = std::lower_bound(std::begin(subtags), std::end(subtags), ptr, + [](const char* a, const char* b) { + return memcmp(a, b, TagLength - 1) < 0; + }); + if (p != std::end(subtags) && memcmp(*p, ptr, TagLength - 1) == 0) { + return aliases[std::distance(std::begin(subtags), p)]; + } + return nullptr; +} + +#ifdef DEBUG +static bool IsAsciiLowercaseAlphanumeric(char c) { + return mozilla::IsAsciiLowercaseAlpha(c) || mozilla::IsAsciiDigit(c); +} + +static bool IsAsciiLowercaseAlphanumericOrDash(char c) { + return IsAsciiLowercaseAlphanumeric(c) || c == '-'; +} + +static bool IsCanonicallyCasedLanguageTag(mozilla::Span span) { + return std::all_of(span.begin(), span.end(), + mozilla::IsAsciiLowercaseAlpha); +} + +static bool IsCanonicallyCasedScriptTag(mozilla::Span span) { + return mozilla::IsAsciiUppercaseAlpha(span[0]) && + std::all_of(span.begin() + 1, span.end(), + mozilla::IsAsciiLowercaseAlpha); +} + +static bool IsCanonicallyCasedRegionTag(mozilla::Span span) { + return std::all_of(span.begin(), span.end(), + mozilla::IsAsciiUppercaseAlpha) || + std::all_of(span.begin(), span.end(), mozilla::IsAsciiDigit); +} + +static bool IsCanonicallyCasedVariantTag(mozilla::Span span) { + return std::all_of(span.begin(), span.end(), IsAsciiLowercaseAlphanumeric); +} + +static bool IsCanonicallyCasedUnicodeKey(mozilla::Span key) { + return std::all_of(key.begin(), key.end(), IsAsciiLowercaseAlphanumeric); +} + +static bool IsCanonicallyCasedUnicodeType(mozilla::Span type) { + return std::all_of(type.begin(), type.end(), + IsAsciiLowercaseAlphanumericOrDash); +} + +static bool IsCanonicallyCasedTransformKey(mozilla::Span key) { + return std::all_of(key.begin(), key.end(), IsAsciiLowercaseAlphanumeric); +} + +static bool IsCanonicallyCasedTransformType(mozilla::Span type) { + return std::all_of(type.begin(), type.end(), + IsAsciiLowercaseAlphanumericOrDash); +} +#endif + +// Mappings from language subtags to preferred values. +// Derived from CLDR Supplemental Data, version 39. +// https://unicode.org/Public/cldr/39/core.zip +bool mozilla::intl::Locale::languageMapping(LanguageSubtag& language) { + MOZ_ASSERT(IsStructurallyValidLanguageTag(language.span())); + MOZ_ASSERT(IsCanonicallyCasedLanguageTag(language.span())); + + if (language.length() == 2) { + static const char languages[8][3] = { + "bh", "in", "iw", "ji", "jw", "mo", "tl", "tw", + }; + static const char* aliases[8] = { + "bho", "id", "he", "yi", "jv", "ro", "fil", "ak", + }; + + if (const char* replacement = SearchReplacement(languages, aliases, language)) { + language.set(mozilla::MakeStringSpan(replacement)); + return true; + } + return false; + } + + if (language.length() == 3) { + static const char languages[401][4] = { + "aam", "aar", "abk", "adp", "afr", "agp", "ais", "aju", "aka", "alb", + "als", "amh", "ara", "arb", "arg", "arm", "asd", "asm", "aue", "ava", + "ave", "aym", "ayr", "ayx", "aze", "azj", "bak", "bam", "baq", "baz", + "bcc", "bcl", "bel", "ben", "bgm", "bhk", "bih", "bis", "bjd", "bjq", + "bkb", "bod", "bos", "bre", "btb", "bul", "bur", "bxk", "bxr", "cat", + "ccq", "ces", "cha", "che", "chi", "chu", "chv", "cjr", "cka", "cld", + "cmk", "cmn", "cor", "cos", "coy", "cqu", "cre", "cwd", "cym", "cze", + "daf", "dan", "dap", "deu", "dgo", "dhd", "dik", "diq", "dit", "div", + "djl", "dkl", "drh", "drr", "dud", "duj", "dut", "dwl", "dzo", "ekk", + "ell", "elp", "emk", "eng", "epo", "esk", "est", "eus", "ewe", "fao", + "fas", "fat", "fij", "fin", "fra", "fre", "fry", "fuc", "ful", "gav", + "gaz", "gbc", "gbo", "geo", "ger", "gfx", "ggn", "ggo", "ggr", "gio", + "gla", "gle", "glg", "gli", "glv", "gno", "gre", "grn", "gti", "gug", + "guj", "guv", "gya", "hat", "hau", "hdn", "hea", "heb", "her", "him", + "hin", "hmo", "hrr", "hrv", "hun", "hye", "ibi", "ibo", "ice", "ido", + "iii", "ike", "iku", "ile", "ill", "ilw", "ina", "ind", "ipk", "isl", + "ita", "izi", "jar", "jav", "jeg", "jpn", "kal", "kan", "kas", "kat", + "kau", "kaz", "kdv", "kgc", "kgd", "kgh", "khk", "khm", "kik", "kin", + "kir", "kmr", "knc", "kng", "knn", "koj", "kom", "kon", "kor", "kpp", + "kpv", "krm", "ktr", "kua", "kur", "kvs", "kwq", "kxe", "kxl", "kzh", + "kzj", "kzt", "lao", "lat", "lav", "lbk", "leg", "lii", "lim", "lin", + "lit", "llo", "lmm", "ltz", "lub", "lug", "lvs", "mac", "mah", "mal", + "mao", "mar", "may", "meg", "mgx", "mhr", "mkd", "mlg", "mlt", "mnk", + "mnt", "mof", "mol", "mon", "mri", "msa", "mst", "mup", "mwd", "mwj", + "mya", "myd", "myt", "nad", "nau", "nav", "nbf", "nbl", "nbx", "ncp", + "nde", "ndo", "nep", "nld", "nln", "nlr", "nno", "nns", "nnx", "nob", + "noo", "nor", "npi", "nts", "nxu", "nya", "oci", "ojg", "oji", "ori", + "orm", "ory", "oss", "oun", "pan", "pbu", "pcr", "per", "pes", "pli", + "plt", "pmc", "pmu", "pnb", "pol", "por", "ppa", "ppr", "pry", "pus", + "puz", "que", "quz", "rmr", "rmy", "roh", "ron", "rum", "run", "rus", + "sag", "san", "sap", "sca", "scc", "scr", "sgl", "sin", "skk", "slk", + "slo", "slv", "sme", "smo", "sna", "snd", "som", "sot", "spa", "spy", + "sqi", "src", "srd", "srp", "ssw", "sul", "sum", "sun", "swa", "swe", + "swh", "tah", "tam", "tat", "tdu", "tel", "tgg", "tgk", "tgl", "tha", + "thc", "thw", "thx", "tib", "tid", "tie", "tir", "tkk", "tlw", "tmp", + "tne", "ton", "tsf", "tsn", "tso", "ttq", "tuk", "tur", "twi", "uig", + "ukr", "umu", "unp", "uok", "urd", "uzb", "uzn", "ven", "vie", "vol", + "wel", "wgw", "wit", "wiw", "wln", "wol", "xba", "xho", "xia", "xkh", + "xpe", "xrq", "xsj", "xsl", "ybd", "ydd", "yen", "yid", "yiy", "yma", + "ymt", "yor", "yos", "yuu", "zai", "zha", "zho", "zir", "zsm", "zul", + "zyb", + }; + static const char* aliases[401] = { + "aas", "aa", "ab", "dz", "af", "apf", "ami", "jrb", "ak", "sq", + "sq", "am", "ar", "ar", "an", "hy", "snz", "as", "ktz", "av", + "ae", "ay", "ay", "nun", "az", "az", "ba", "bm", "eu", "nvo", + "bal", "bik", "be", "bn", "bcg", "fbl", "bho", "bi", "drl", "bzc", + "ebk", "bo", "bs", "br", "beb", "bg", "my", "luy", "bua", "ca", + "rki", "cs", "ch", "ce", "zh", "cu", "cv", "mom", "cmr", "syr", + "xch", "zh", "kw", "co", "pij", "quh", "cr", "cr", "cy", "cs", + "dnj", "da", "njz", "de", "doi", "mwr", "din", "zza", "dif", "dv", + "dze", "aqd", "mn", "kzk", "uth", "dwu", "nl", "dbt", "dz", "et", + "el", "amq", "man", "en", "eo", "ik", "et", "eu", "ee", "fo", + "fa", "ak", "fj", "fi", "fr", "fr", "fy", "ff", "ff", "dev", + "om", "wny", "grb", "ka", "de", "vaj", "gvr", "esg", "gtu", "aou", + "gd", "ga", "gl", "kzk", "gv", "gon", "el", "gn", "nyc", "gn", + "gu", "duz", "gba", "ht", "ha", "hai", "hmn", "he", "hz", "srx", + "hi", "ho", "jal", "hr", "hu", "hy", "opa", "ig", "is", "io", + "ii", "iu", "iu", "ie", "ilm", "gal", "ia", "id", "ik", "is", + "it", "eza", "jgk", "jv", "oyb", "ja", "kl", "kn", "ks", "ka", + "kr", "kk", "zkd", "tdf", "ncq", "kml", "mn", "km", "ki", "rw", + "ky", "ku", "kr", "kg", "kok", "kwv", "kv", "kg", "ko", "jkm", + "kv", "bmf", "dtp", "kj", "ku", "gdj", "yam", "tvd", "kru", "dgl", + "dtp", "dtp", "lo", "la", "lv", "bnc", "enl", "raq", "li", "ln", + "lt", "ngt", "rmx", "lb", "lu", "lg", "lv", "mk", "mh", "ml", + "mi", "mr", "ms", "cir", "jbk", "chm", "mk", "mg", "mt", "man", + "wnn", "xnt", "ro", "mn", "mi", "ms", "mry", "raj", "dmw", "vaj", + "my", "aog", "mry", "xny", "na", "nv", "nru", "nr", "ekc", "kdz", + "nd", "ng", "ne", "nl", "azd", "nrk", "nn", "nbr", "ngv", "nb", + "dtd", "no", "ne", "pij", "bpp", "ny", "oc", "oj", "oj", "or", + "om", "or", "os", "vaj", "pa", "ps", "adx", "fa", "fa", "pi", + "mg", "huw", "phr", "lah", "pl", "pt", "bfy", "lcq", "prt", "ps", + "pub", "qu", "qu", "emx", "rom", "rm", "ro", "ro", "rn", "ru", + "sg", "sa", "aqt", "hle", "sr", "hr", "isk", "si", "oyb", "sk", + "sk", "sl", "se", "sm", "sn", "sd", "so", "st", "es", "kln", + "sq", "sc", "sc", "sr", "ss", "sgd", "ulw", "su", "sw", "sv", + "sw", "ty", "ta", "tt", "dtp", "te", "bjp", "tg", "fil", "th", + "tpo", "ola", "oyb", "bo", "itd", "ras", "ti", "twm", "weo", "tyj", + "kak", "to", "taj", "tn", "ts", "tmh", "tk", "tr", "ak", "ug", + "uk", "del", "wro", "ema", "ur", "uz", "uz", "ve", "vi", "vo", + "cy", "wgb", "nol", "nwo", "wa", "wo", "cax", "xh", "acn", "waw", + "kpe", "dmw", "suj", "den", "rki", "yi", "ynq", "yi", "yrm", "lrr", + "mtm", "yo", "zom", "yug", "zap", "za", "zh", "scv", "ms", "zu", + "za", + }; + + if (const char* replacement = SearchReplacement(languages, aliases, language)) { + language.set(mozilla::MakeStringSpan(replacement)); + return true; + } + return false; + } + + return false; +} + +// Language subtags with complex mappings. +// Derived from CLDR Supplemental Data, version 39. +// https://unicode.org/Public/cldr/39/core.zip +bool mozilla::intl::Locale::complexLanguageMapping(const LanguageSubtag& language) { + MOZ_ASSERT(IsStructurallyValidLanguageTag(language.span())); + MOZ_ASSERT(IsCanonicallyCasedLanguageTag(language.span())); + + if (language.length() == 2) { + return language.equalTo("sh"); + } + + if (language.length() == 3) { + static const char languages[6][4] = { + "cnr", "drw", "hbs", "prs", "swc", "tnf", + }; + + return HasReplacement(languages, language); + } + + return false; +} + +// Mappings from script subtags to preferred values. +// Derived from CLDR Supplemental Data, version 39. +// https://unicode.org/Public/cldr/39/core.zip +bool mozilla::intl::Locale::scriptMapping(ScriptSubtag& script) { + MOZ_ASSERT(IsStructurallyValidScriptTag(script.span())); + MOZ_ASSERT(IsCanonicallyCasedScriptTag(script.span())); + + { + if (script.equalTo("Qaai")) { + script.set(mozilla::MakeStringSpan("Zinh")); + return true; + } + return false; + } +} + +// Mappings from region subtags to preferred values. +// Derived from CLDR Supplemental Data, version 39. +// https://unicode.org/Public/cldr/39/core.zip +bool mozilla::intl::Locale::regionMapping(RegionSubtag& region) { + MOZ_ASSERT(IsStructurallyValidRegionTag(region.span())); + MOZ_ASSERT(IsCanonicallyCasedRegionTag(region.span())); + + if (region.length() == 2) { + static const char regions[23][3] = { + "BU", "CS", "CT", "DD", "DY", "FQ", "FX", "HV", "JT", "MI", + "NH", "NQ", "PU", "PZ", "QU", "RH", "TP", "UK", "VD", "WK", + "YD", "YU", "ZR", + }; + static const char* aliases[23] = { + "MM", "RS", "KI", "DE", "BJ", "AQ", "FR", "BF", "UM", "UM", + "VU", "AQ", "UM", "PA", "EU", "ZW", "TL", "GB", "VN", "UM", + "YE", "RS", "CD", + }; + + if (const char* replacement = SearchReplacement(regions, aliases, region)) { + region.set(mozilla::MakeStringSpan(replacement)); + return true; + } + return false; + } + + { + static const char regions[300][4] = { + "004", "008", "010", "012", "016", "020", "024", "028", "031", "032", + "036", "040", "044", "048", "050", "051", "052", "056", "060", "062", + "064", "068", "070", "072", "074", "076", "084", "086", "090", "092", + "096", "100", "104", "108", "112", "116", "120", "124", "132", "136", + "140", "144", "148", "152", "156", "158", "162", "166", "170", "174", + "175", "178", "180", "184", "188", "191", "192", "196", "203", "204", + "208", "212", "214", "218", "222", "226", "230", "231", "232", "233", + "234", "238", "239", "242", "246", "248", "249", "250", "254", "258", + "260", "262", "266", "268", "270", "275", "276", "278", "280", "288", + "292", "296", "300", "304", "308", "312", "316", "320", "324", "328", + "332", "334", "336", "340", "344", "348", "352", "356", "360", "364", + "368", "372", "376", "380", "384", "388", "392", "398", "400", "404", + "408", "410", "414", "417", "418", "422", "426", "428", "430", "434", + "438", "440", "442", "446", "450", "454", "458", "462", "466", "470", + "474", "478", "480", "484", "492", "496", "498", "499", "500", "504", + "508", "512", "516", "520", "524", "528", "531", "533", "534", "535", + "540", "548", "554", "558", "562", "566", "570", "574", "578", "580", + "581", "583", "584", "585", "586", "591", "598", "600", "604", "608", + "612", "616", "620", "624", "626", "630", "634", "638", "642", "643", + "646", "652", "654", "659", "660", "662", "663", "666", "670", "674", + "678", "682", "686", "688", "690", "694", "702", "703", "704", "705", + "706", "710", "716", "720", "724", "728", "729", "732", "736", "740", + "744", "748", "752", "756", "760", "762", "764", "768", "772", "776", + "780", "784", "788", "792", "795", "796", "798", "800", "804", "807", + "818", "826", "830", "831", "832", "833", "834", "840", "850", "854", + "858", "860", "862", "876", "882", "886", "887", "891", "894", "958", + "959", "960", "962", "963", "964", "965", "966", "967", "968", "969", + "970", "971", "972", "973", "974", "975", "976", "977", "978", "979", + "980", "981", "982", "983", "984", "985", "986", "987", "988", "989", + "990", "991", "992", "993", "994", "995", "996", "997", "998", "999", + }; + static const char* aliases[300] = { + "AF", "AL", "AQ", "DZ", "AS", "AD", "AO", "AG", "AZ", "AR", + "AU", "AT", "BS", "BH", "BD", "AM", "BB", "BE", "BM", "034", + "BT", "BO", "BA", "BW", "BV", "BR", "BZ", "IO", "SB", "VG", + "BN", "BG", "MM", "BI", "BY", "KH", "CM", "CA", "CV", "KY", + "CF", "LK", "TD", "CL", "CN", "TW", "CX", "CC", "CO", "KM", + "YT", "CG", "CD", "CK", "CR", "HR", "CU", "CY", "CZ", "BJ", + "DK", "DM", "DO", "EC", "SV", "GQ", "ET", "ET", "ER", "EE", + "FO", "FK", "GS", "FJ", "FI", "AX", "FR", "FR", "GF", "PF", + "TF", "DJ", "GA", "GE", "GM", "PS", "DE", "DE", "DE", "GH", + "GI", "KI", "GR", "GL", "GD", "GP", "GU", "GT", "GN", "GY", + "HT", "HM", "VA", "HN", "HK", "HU", "IS", "IN", "ID", "IR", + "IQ", "IE", "IL", "IT", "CI", "JM", "JP", "KZ", "JO", "KE", + "KP", "KR", "KW", "KG", "LA", "LB", "LS", "LV", "LR", "LY", + "LI", "LT", "LU", "MO", "MG", "MW", "MY", "MV", "ML", "MT", + "MQ", "MR", "MU", "MX", "MC", "MN", "MD", "ME", "MS", "MA", + "MZ", "OM", "NA", "NR", "NP", "NL", "CW", "AW", "SX", "BQ", + "NC", "VU", "NZ", "NI", "NE", "NG", "NU", "NF", "NO", "MP", + "UM", "FM", "MH", "PW", "PK", "PA", "PG", "PY", "PE", "PH", + "PN", "PL", "PT", "GW", "TL", "PR", "QA", "RE", "RO", "RU", + "RW", "BL", "SH", "KN", "AI", "LC", "MF", "PM", "VC", "SM", + "ST", "SA", "SN", "RS", "SC", "SL", "SG", "SK", "VN", "SI", + "SO", "ZA", "ZW", "YE", "ES", "SS", "SD", "EH", "SD", "SR", + "SJ", "SZ", "SE", "CH", "SY", "TJ", "TH", "TG", "TK", "TO", + "TT", "AE", "TN", "TR", "TM", "TC", "TV", "UG", "UA", "MK", + "EG", "GB", "JE", "GG", "JE", "IM", "TZ", "US", "VI", "BF", + "UY", "UZ", "VE", "WF", "WS", "YE", "YE", "RS", "ZM", "AA", + "QM", "QN", "QP", "QQ", "QR", "QS", "QT", "EU", "QV", "QW", + "QX", "QY", "QZ", "XA", "XB", "XC", "XD", "XE", "XF", "XG", + "XH", "XI", "XJ", "XK", "XL", "XM", "XN", "XO", "XP", "XQ", + "XR", "XS", "XT", "XU", "XV", "XW", "XX", "XY", "XZ", "ZZ", + }; + + if (const char* replacement = SearchReplacement(regions, aliases, region)) { + region.set(mozilla::MakeStringSpan(replacement)); + return true; + } + return false; + } +} + +// Region subtags with complex mappings. +// Derived from CLDR Supplemental Data, version 39. +// https://unicode.org/Public/cldr/39/core.zip +bool mozilla::intl::Locale::complexRegionMapping(const RegionSubtag& region) { + MOZ_ASSERT(IsStructurallyValidRegionTag(region.span())); + MOZ_ASSERT(IsCanonicallyCasedRegionTag(region.span())); + + if (region.length() == 2) { + return region.equalTo("AN") || + region.equalTo("NT") || + region.equalTo("PC") || + region.equalTo("SU"); + } + + { + static const char regions[8][4] = { + "172", "200", "530", "532", "536", "582", "810", "890", + }; + + return HasReplacement(regions, region); + } +} + +// Language subtags with complex mappings. +// Derived from CLDR Supplemental Data, version 39. +// https://unicode.org/Public/cldr/39/core.zip +void mozilla::intl::Locale::performComplexLanguageMappings() { + MOZ_ASSERT(IsStructurallyValidLanguageTag(language().span())); + MOZ_ASSERT(IsCanonicallyCasedLanguageTag(language().span())); + + if (language().equalTo("cnr")) { + setLanguage("sr"); + if (region().missing()) { + setRegion("ME"); + } + } + else if (language().equalTo("drw") || + language().equalTo("prs") || + language().equalTo("tnf")) { + setLanguage("fa"); + if (region().missing()) { + setRegion("AF"); + } + } + else if (language().equalTo("hbs") || + language().equalTo("sh")) { + setLanguage("sr"); + if (script().missing()) { + setScript("Latn"); + } + } + else if (language().equalTo("swc")) { + setLanguage("sw"); + if (region().missing()) { + setRegion("CD"); + } + } +} + +// Region subtags with complex mappings. +// Derived from CLDR Supplemental Data, version 39. +// https://unicode.org/Public/cldr/39/core.zip +void mozilla::intl::Locale::performComplexRegionMappings() { + MOZ_ASSERT(IsStructurallyValidLanguageTag(language().span())); + MOZ_ASSERT(IsCanonicallyCasedLanguageTag(language().span())); + MOZ_ASSERT(IsStructurallyValidRegionTag(region().span())); + MOZ_ASSERT(IsCanonicallyCasedRegionTag(region().span())); + + if (region().equalTo("172")) { + if (language().equalTo("hy") || + (language().equalTo("und") && script().equalTo("Armn"))) { + setRegion("AM"); + } + else if (language().equalTo("az") || + language().equalTo("tkr") || + language().equalTo("tly") || + language().equalTo("ttt")) { + setRegion("AZ"); + } + else if (language().equalTo("be")) { + setRegion("BY"); + } + else if (language().equalTo("ab") || + language().equalTo("ka") || + (language().equalTo("ku") && script().equalTo("Yezi")) || + language().equalTo("os") || + (language().equalTo("und") && script().equalTo("Geor")) || + (language().equalTo("und") && script().equalTo("Yezi")) || + language().equalTo("xmf")) { + setRegion("GE"); + } + else if (language().equalTo("ky")) { + setRegion("KG"); + } + else if (language().equalTo("kk") || + (language().equalTo("ug") && script().equalTo("Cyrl"))) { + setRegion("KZ"); + } + else if (language().equalTo("gag")) { + setRegion("MD"); + } + else if (language().equalTo("tg")) { + setRegion("TJ"); + } + else if (language().equalTo("tk")) { + setRegion("TM"); + } + else if (language().equalTo("crh") || + language().equalTo("got") || + language().equalTo("ji") || + language().equalTo("rue") || + language().equalTo("uk") || + (language().equalTo("und") && script().equalTo("Goth"))) { + setRegion("UA"); + } + else if (language().equalTo("kaa") || + language().equalTo("sog") || + (language().equalTo("und") && script().equalTo("Chrs")) || + (language().equalTo("und") && script().equalTo("Sogd")) || + (language().equalTo("und") && script().equalTo("Sogo")) || + language().equalTo("uz") || + language().equalTo("xco")) { + setRegion("UZ"); + } + else { + setRegion("RU"); + } + } + else if (region().equalTo("200")) { + if (language().equalTo("sk")) { + setRegion("SK"); + } + else { + setRegion("CZ"); + } + } + else if (region().equalTo("530") || + region().equalTo("532") || + region().equalTo("AN")) { + if (language().equalTo("vic")) { + setRegion("SX"); + } + else { + setRegion("CW"); + } + } + else if (region().equalTo("536") || + region().equalTo("NT")) { + if (language().equalTo("akk") || + language().equalTo("ckb") || + (language().equalTo("ku") && script().equalTo("Arab")) || + language().equalTo("syr") || + (language().equalTo("und") && script().equalTo("Syrc")) || + (language().equalTo("und") && script().equalTo("Xsux"))) { + setRegion("IQ"); + } + else { + setRegion("SA"); + } + } + else if (region().equalTo("582") || + region().equalTo("PC")) { + if (language().equalTo("mh")) { + setRegion("MH"); + } + else if (language().equalTo("pau")) { + setRegion("PW"); + } + else { + setRegion("FM"); + } + } + else if (region().equalTo("810") || + region().equalTo("SU")) { + if (language().equalTo("hy") || + (language().equalTo("und") && script().equalTo("Armn"))) { + setRegion("AM"); + } + else if (language().equalTo("az") || + language().equalTo("tkr") || + language().equalTo("tly") || + language().equalTo("ttt")) { + setRegion("AZ"); + } + else if (language().equalTo("be")) { + setRegion("BY"); + } + else if (language().equalTo("et") || + language().equalTo("vro")) { + setRegion("EE"); + } + else if (language().equalTo("ab") || + language().equalTo("ka") || + (language().equalTo("ku") && script().equalTo("Yezi")) || + language().equalTo("os") || + (language().equalTo("und") && script().equalTo("Geor")) || + (language().equalTo("und") && script().equalTo("Yezi")) || + language().equalTo("xmf")) { + setRegion("GE"); + } + else if (language().equalTo("ky")) { + setRegion("KG"); + } + else if (language().equalTo("kk") || + (language().equalTo("ug") && script().equalTo("Cyrl"))) { + setRegion("KZ"); + } + else if (language().equalTo("lt") || + language().equalTo("sgs")) { + setRegion("LT"); + } + else if (language().equalTo("ltg") || + language().equalTo("lv")) { + setRegion("LV"); + } + else if (language().equalTo("gag")) { + setRegion("MD"); + } + else if (language().equalTo("tg")) { + setRegion("TJ"); + } + else if (language().equalTo("tk")) { + setRegion("TM"); + } + else if (language().equalTo("crh") || + language().equalTo("got") || + language().equalTo("ji") || + language().equalTo("rue") || + language().equalTo("uk") || + (language().equalTo("und") && script().equalTo("Goth"))) { + setRegion("UA"); + } + else if (language().equalTo("kaa") || + language().equalTo("sog") || + (language().equalTo("und") && script().equalTo("Chrs")) || + (language().equalTo("und") && script().equalTo("Sogd")) || + (language().equalTo("und") && script().equalTo("Sogo")) || + language().equalTo("uz") || + language().equalTo("xco")) { + setRegion("UZ"); + } + else { + setRegion("RU"); + } + } + else if (region().equalTo("890")) { + if (language().equalTo("bs")) { + setRegion("BA"); + } + else if (language().equalTo("hr")) { + setRegion("HR"); + } + else if (language().equalTo("mk")) { + setRegion("MK"); + } + else if (language().equalTo("sl")) { + setRegion("SI"); + } + else { + setRegion("RS"); + } + } +} + +static const char* ToCharPointer(const char* str) { + return str; +} + +static const char* ToCharPointer(const mozilla::intl::UniqueChars& str) { + return str.get(); +} + +template +static bool IsLessThan(const T& a, const U& b) { + return strcmp(ToCharPointer(a), ToCharPointer(b)) < 0; +} + +// Mappings from variant subtags to preferred values. +// Derived from CLDR Supplemental Data, version 39. +// https://unicode.org/Public/cldr/39/core.zip +bool mozilla::intl::Locale::performVariantMappings() { + // The variant subtags need to be sorted for binary search. + MOZ_ASSERT(std::is_sorted(variants_.begin(), variants_.end(), + IsLessThan)); + + auto removeVariantAt = [&](size_t index) { + variants_.erase(variants_.begin() + index); + }; + + auto insertVariantSortedIfNotPresent = [&](const char* variant) { + auto* p = std::lower_bound( + variants_.begin(), variants_.end(), variant, + IsLessThan); + + // Don't insert the replacement when already present. + if (p != variants_.end() && strcmp(p->get(), variant) == 0) { + return true; + } + + // Insert the preferred variant in sort order. + auto preferred = DuplicateStringToUniqueChars(variant); + return !!variants_.insert(p, std::move(preferred)); + }; + + for (size_t i = 0; i < variants_.length();) { + const char* variant = variants_[i].get(); + MOZ_ASSERT(IsCanonicallyCasedVariantTag(mozilla::MakeStringSpan(variant))); + + if (strcmp(variant, "arevela") == 0 || + strcmp(variant, "arevmda") == 0 || + strcmp(variant, "bokmal") == 0 || + strcmp(variant, "hakka") == 0 || + strcmp(variant, "lojban") == 0 || + strcmp(variant, "nynorsk") == 0 || + strcmp(variant, "saaho") == 0 || + strcmp(variant, "xiang") == 0) { + removeVariantAt(i); + } + else if (strcmp(variant, "aaland") == 0) { + removeVariantAt(i); + setRegion("AX"); + } + else if (strcmp(variant, "heploc") == 0) { + removeVariantAt(i); + if (!insertVariantSortedIfNotPresent("alalc97")) { + return false; + } + } + else if (strcmp(variant, "polytoni") == 0) { + removeVariantAt(i); + if (!insertVariantSortedIfNotPresent("polyton")) { + return false; + } + } + else { + i++; + } + } + return true; +} + +// Canonicalize legacy locale identifiers. +// Derived from CLDR Supplemental Data, version 39. +// https://unicode.org/Public/cldr/39/core.zip +bool mozilla::intl::Locale::updateLegacyMappings() { + // We're mapping legacy tags to non-legacy form here. + // Other tags remain unchanged. + // + // Legacy tags are either sign language tags ("sgn") or have one or multiple + // variant subtags. Therefore we can quickly exclude most tags by checking + // these two subtags. + + MOZ_ASSERT(IsCanonicallyCasedLanguageTag(language().span())); + + if (!language().equalTo("sgn") && variants().length() == 0) { + return true; + } + + for ([[maybe_unused]] const auto& variant : variants()) { + MOZ_ASSERT( + IsStructurallyValidVariantTag(mozilla::MakeStringSpan(variant.get()))); + MOZ_ASSERT( + IsCanonicallyCasedVariantTag(mozilla::MakeStringSpan(variant.get()))); + } + + // The variant subtags need to be sorted for binary search. + MOZ_ASSERT(std::is_sorted(variants_.begin(), variants_.end(), + IsLessThan)); + + auto findVariant = [this](const char* variant) { + auto* p = std::lower_bound(variants_.begin(), variants_.end(), variant, + IsLessThan); + + if (p != variants_.end() && strcmp(p->get(), variant) == 0) { + return p; + } + return static_cast(nullptr); + }; + + auto insertVariantSortedIfNotPresent = [&](const char* variant) { + auto* p = std::lower_bound(variants_.begin(), variants_.end(), variant, + IsLessThan); + + // Don't insert the replacement when already present. + if (p != variants_.end() && strcmp(p->get(), variant) == 0) { + return true; + } + + // Insert the preferred variant in sort order. + auto preferred = DuplicateStringToUniqueChars(variant); + return !!variants_.insert(p, std::move(preferred)); + }; + + auto removeVariant = [&](auto* p) { + size_t index = std::distance(variants_.begin(), p); + variants_.erase(variants_.begin() + index); + }; + + auto removeVariants = [&](auto* p, auto* q) { + size_t pIndex = std::distance(variants_.begin(), p); + size_t qIndex = std::distance(variants_.begin(), q); + MOZ_ASSERT(pIndex < qIndex, "variant subtags are sorted"); + + variants_.erase(variants_.begin() + qIndex); + variants_.erase(variants_.begin() + pIndex); + }; + + if (variants().length() >= 2) { + if (auto* hepburn = findVariant("hepburn")) { + if (auto* heploc = findVariant("heploc")) { + removeVariants(hepburn, heploc); + + if (!insertVariantSortedIfNotPresent("alalc97")) { + return false; + } + } + } + } + + if (language().equalTo("sgn")) { + if (region().present() && signLanguageMapping(language_, region())) { + region_.set(mozilla::MakeStringSpan("")); + } + } + else if (language().equalTo("aa") || + language().equalTo("aar")) { + if (auto* saaho = findVariant("saaho")) { + removeVariant(saaho); + setLanguage("ssy"); + } + } + else if (language().equalTo("arm") || + language().equalTo("hy") || + language().equalTo("hye")) { + if (auto* arevmda = findVariant("arevmda")) { + removeVariant(arevmda); + setLanguage("hyw"); + } + } + else if (language().equalTo("art")) { + if (auto* lojban = findVariant("lojban")) { + removeVariant(lojban); + setLanguage("jbo"); + } + } + else if (language().equalTo("cel")) { + if (auto* gaulish = findVariant("gaulish")) { + removeVariant(gaulish); + setLanguage("xtg"); + } + } + else if (language().equalTo("chi") || + language().equalTo("cmn") || + language().equalTo("zh") || + language().equalTo("zho")) { + if (auto* guoyu = findVariant("guoyu")) { + if (auto* hakka = findVariant("hakka")) { + removeVariants(guoyu, hakka); + setLanguage("hak"); + return true; + } + } + if (auto* guoyu = findVariant("guoyu")) { + if (auto* xiang = findVariant("xiang")) { + removeVariants(guoyu, xiang); + setLanguage("hsn"); + return true; + } + } + if (auto* guoyu = findVariant("guoyu")) { + removeVariant(guoyu); + setLanguage("zh"); + } + else if (auto* hakka = findVariant("hakka")) { + removeVariant(hakka); + setLanguage("hak"); + } + else if (auto* xiang = findVariant("xiang")) { + removeVariant(xiang); + setLanguage("hsn"); + } + } + else if (language().equalTo("no") || + language().equalTo("nor")) { + if (auto* bokmal = findVariant("bokmal")) { + removeVariant(bokmal); + setLanguage("nb"); + } + else if (auto* nynorsk = findVariant("nynorsk")) { + removeVariant(nynorsk); + setLanguage("nn"); + } + } + + return true; +} + +// Mappings from legacy sign languages. +// Derived from CLDR Supplemental Data, version 39. +// https://unicode.org/Public/cldr/39/core.zip +bool mozilla::intl::Locale::signLanguageMapping(LanguageSubtag& language, + const RegionSubtag& region) { + MOZ_ASSERT(language.equalTo("sgn")); + MOZ_ASSERT(IsStructurallyValidRegionTag(region.span())); + MOZ_ASSERT(IsCanonicallyCasedRegionTag(region.span())); + + if (region.length() == 2) { + static const char regions[22][3] = { + "BR", "CO", "DD", "DE", "DK", "ES", "FR", "FX", "GB", "GR", + "IE", "IT", "JP", "MX", "NI", "NL", "NO", "PT", "SE", "UK", + "US", "ZA", + }; + static const char* aliases[22] = { + "bzs", "csn", "gsg", "gsg", "dsl", "ssp", "fsl", "fsl", "bfi", "gss", + "isg", "ise", "jsl", "mfs", "ncs", "dse", "nsi", "psr", "swl", "bfi", + "ase", "sfs", + }; + + if (const char* replacement = SearchReplacement(regions, aliases, region)) { + language.set(mozilla::MakeStringSpan(replacement)); + return true; + } + return false; + } + + { + static const char regions[22][4] = { + "076", "170", "208", "249", "250", "276", "278", "280", "300", "372", + "380", "392", "484", "528", "558", "578", "620", "710", "724", "752", + "826", "840", + }; + static const char* aliases[22] = { + "bzs", "csn", "dsl", "fsl", "fsl", "gsg", "gsg", "gsg", "gss", "isg", + "ise", "jsl", "mfs", "dse", "ncs", "nsi", "psr", "sfs", "ssp", "swl", + "bfi", "ase", + }; + + if (const char* replacement = SearchReplacement(regions, aliases, region)) { + language.set(mozilla::MakeStringSpan(replacement)); + return true; + } + return false; + } +} + +template +static inline bool IsUnicodeKey(mozilla::Span key, const char (&str)[Length]) { + static_assert(Length == UnicodeKeyLength + 1, + "Unicode extension key is two characters long"); + return memcmp(key.data(), str, Length - 1) == 0; +} + +template +static inline bool IsUnicodeType(mozilla::Span type, const char (&str)[Length]) { + static_assert(Length > UnicodeKeyLength + 1, + "Unicode extension type contains more than two characters"); + return type.size() == (Length - 1) && + memcmp(type.data(), str, Length - 1) == 0; +} + +static int32_t CompareUnicodeType(const char* a, mozilla::Span b) { + MOZ_ASSERT(!std::char_traits::find(b.data(), b.size(), '\0'), + "unexpected null-character in string"); + + using UnsignedChar = unsigned char; + for (size_t i = 0; i < b.size(); i++) { + // |a| is zero-terminated and |b| doesn't contain a null-terminator. So if + // we've reached the end of |a|, the below if-statement will always be true. + // That ensures we don't read past the end of |a|. + if (int32_t r = UnsignedChar(a[i]) - UnsignedChar(b[i])) { + return r; + } + } + + // Return zero if both strings are equal or a positive number if |b| is a + // prefix of |a|. + return int32_t(UnsignedChar(a[b.size()])); +} + +template +static inline const char* SearchUnicodeReplacement( + const char* (&types)[Length], const char* (&aliases)[Length], + mozilla::Span type) { + + auto p = std::lower_bound(std::begin(types), std::end(types), type, + [](const auto& a, const auto& b) { + return CompareUnicodeType(a, b) < 0; + }); + if (p != std::end(types) && CompareUnicodeType(*p, type) == 0) { + return aliases[std::distance(std::begin(types), p)]; + } + return nullptr; +} + +/** + * Mapping from deprecated BCP 47 Unicode extension types to their preferred + * values. + * + * Spec: https://www.unicode.org/reports/tr35/#Unicode_Locale_Extension_Data_Files + * Spec: https://www.unicode.org/reports/tr35/#t_Extension + */ +const char* mozilla::intl::Locale::replaceUnicodeExtensionType( + mozilla::Span key, mozilla::Span type) { + MOZ_ASSERT(key.size() == UnicodeKeyLength); + MOZ_ASSERT(IsCanonicallyCasedUnicodeKey(key)); + + MOZ_ASSERT(type.size() > UnicodeKeyLength); + MOZ_ASSERT(IsCanonicallyCasedUnicodeType(type)); + + if (IsUnicodeKey(key, "ca")) { + if (IsUnicodeType(type, "ethiopic-amete-alem")) { + return "ethioaa"; + } + if (IsUnicodeType(type, "islamicc")) { + return "islamic-civil"; + } + } + else if (IsUnicodeKey(key, "kb") || + IsUnicodeKey(key, "kc") || + IsUnicodeKey(key, "kh") || + IsUnicodeKey(key, "kk") || + IsUnicodeKey(key, "kn")) { + if (IsUnicodeType(type, "yes")) { + return "true"; + } + } + else if (IsUnicodeKey(key, "ks")) { + if (IsUnicodeType(type, "primary")) { + return "level1"; + } + if (IsUnicodeType(type, "tertiary")) { + return "level3"; + } + } + else if (IsUnicodeKey(key, "ms")) { + if (IsUnicodeType(type, "imperial")) { + return "uksystem"; + } + } + else if (IsUnicodeKey(key, "rg") || + IsUnicodeKey(key, "sd")) { + static const char* types[144] = { + "cn11" , "cn12" , "cn13" , "cn14" , "cn15" , "cn21" , "cn22" , + "cn23" , "cn31" , "cn32" , "cn33" , "cn34" , "cn35" , "cn36" , + "cn37" , "cn41" , "cn42" , "cn43" , "cn44" , "cn45" , "cn46" , + "cn50" , "cn51" , "cn52" , "cn53" , "cn54" , "cn61" , "cn62" , + "cn63" , "cn64" , "cn65" , "cn71" , "cn91" , "cn92" , "cz10a" , + "cz10b" , "cz10c" , "cz10d" , "cz10e" , "cz10f" , "cz611" , "cz612" , + "cz613" , "cz614" , "cz615" , "cz621" , "cz622" , "cz623" , "cz624" , + "cz626" , "cz627" , "czjc" , "czjm" , "czka" , "czkr" , "czli" , + "czmo" , "czol" , "czpa" , "czpl" , "czpr" , "czst" , "czus" , + "czvy" , "czzl" , "fi01" , "fra" , "frb" , "frbl" , "frc" , + "frcp" , "frd" , "fre" , "frf" , "frg" , "frgf" , "frgp" , + "frh" , "fri" , "frj" , "frk" , "frl" , "frm" , "frmf" , + "frmq" , "frn" , "frnc" , "fro" , "frp" , "frpf" , "frpm" , + "frq" , "frr" , "frre" , "frs" , "frt" , "frtf" , "fru" , + "frv" , "frwf" , "fryt" , "laxn" , "lud" , "lug" , "lul" , + "mrnkc" , "nlaw" , "nlcw" , "nlsx" , "no23" , "nzn" , "nzs" , + "omba" , "omsh" , "plds" , "plkp" , "pllb" , "plld" , "pllu" , + "plma" , "plmz" , "plop" , "plpd" , "plpk" , "plpm" , "plsk" , + "plsl" , "plwn" , "plwp" , "plzp" , "shta" , "tteto" , "ttrcm" , + "ttwto" , "twkhq" , "twtnq" , "twtpq" , "twtxq" , "usas" , "usgu" , + "usmp" , "uspr" , "usum" , "usvi" , + }; + static const char* aliases[144] = { + "cnbj" , "cntj" , "cnhe" , "cnsx" , "cnmn" , "cnln" , "cnjl" , + "cnhl" , "cnsh" , "cnjs" , "cnzj" , "cnah" , "cnfj" , "cnjx" , + "cnsd" , "cnha" , "cnhb" , "cnhn" , "cngd" , "cngx" , "cnhi" , + "cncq" , "cnsc" , "cngz" , "cnyn" , "cnxz" , "cnsn" , "cngs" , + "cnqh" , "cnnx" , "cnxj" , "twzzzz", "hkzzzz", "mozzzz", "cz110" , + "cz111" , "cz112" , "cz113" , "cz114" , "cz115" , "cz663" , "cz632" , + "cz633" , "cz634" , "cz635" , "cz641" , "cz642" , "cz643" , "cz644" , + "cz646" , "cz647" , "cz31" , "cz64" , "cz41" , "cz52" , "cz51" , + "cz80" , "cz71" , "cz53" , "cz32" , "cz10" , "cz20" , "cz42" , + "cz63" , "cz72" , "axzzzz", "frges" , "frnaq" , "blzzzz", "frara" , + "cpzzzz", "frbfc" , "frbre" , "frcvl" , "frges" , "gfzzzz", "gpzzzz", + "frcor" , "frbfc" , "fridf" , "frocc" , "frnaq" , "frges" , "mfzzzz", + "mqzzzz", "frocc" , "nczzzz", "frhdf" , "frnor" , "pfzzzz", "pmzzzz", + "frnor" , "frpdl" , "rezzzz", "frhdf" , "frnaq" , "tfzzzz", "frpac" , + "frara" , "wfzzzz", "ytzzzz", "laxs" , "lucl" , "luec" , "luca" , + "mr13" , "awzzzz", "cwzzzz", "sxzzzz", "no50" , "nzauk" , "nzcan" , + "ombj" , "omsj" , "pl02" , "pl04" , "pl08" , "pl10" , "pl06" , + "pl12" , "pl14" , "pl16" , "pl20" , "pl18" , "pl22" , "pl26" , + "pl24" , "pl28" , "pl30" , "pl32" , "tazzzz", "tttob" , "ttmrc" , + "tttob" , "twkhh" , "twtnn" , "twnwt" , "twtxg" , "aszzzz", "guzzzz", + "mpzzzz", "przzzz", "umzzzz", "vizzzz", + }; + return SearchUnicodeReplacement(types, aliases, type); + } + else if (IsUnicodeKey(key, "tz")) { + static const char* types[28] = { + "aqams" , "cnckg" , "cnhrb" , "cnkhg" , "cuba" , "egypt" , + "eire" , "est" , "gmt0" , "hongkong", "hst" , "iceland" , + "iran" , "israel" , "jamaica" , "japan" , "libya" , "mst" , + "navajo" , "poland" , "portugal", "prc" , "roc" , "rok" , + "turkey" , "uct" , "usnavajo", "zulu" , + }; + static const char* aliases[28] = { + "nzakl" , "cnsha" , "cnsha" , "cnurc" , "cuhav" , "egcai" , + "iedub" , "utcw05" , "gmt" , "hkhkg" , "utcw10" , "isrey" , + "irthr" , "jeruslm" , "jmkin" , "jptyo" , "lytip" , "utcw07" , + "usden" , "plwaw" , "ptlis" , "cnsha" , "twtpe" , "krsel" , + "trist" , "utc" , "usden" , "utc" , + }; + return SearchUnicodeReplacement(types, aliases, type); + } + return nullptr; +} + +template +static inline bool IsTransformKey(mozilla::Span key, const char (&str)[Length]) { + static_assert(Length == TransformKeyLength + 1, + "Transform extension key is two characters long"); + return memcmp(key.data(), str, Length - 1) == 0; +} + +template +static inline bool IsTransformType(mozilla::Span type, const char (&str)[Length]) { + static_assert(Length > TransformKeyLength + 1, + "Transform extension type contains more than two characters"); + return type.size() == (Length - 1) && + memcmp(type.data(), str, Length - 1) == 0; +} + +/** + * Mapping from deprecated BCP 47 Transform extension types to their preferred + * values. + * + * Spec: https://www.unicode.org/reports/tr35/#Unicode_Locale_Extension_Data_Files + * Spec: https://www.unicode.org/reports/tr35/#t_Extension + */ +const char* mozilla::intl::Locale::replaceTransformExtensionType( + mozilla::Span key, mozilla::Span type) { + MOZ_ASSERT(key.size() == TransformKeyLength); + MOZ_ASSERT(IsCanonicallyCasedTransformKey(key)); + + MOZ_ASSERT(type.size() > TransformKeyLength); + MOZ_ASSERT(IsCanonicallyCasedTransformType(type)); + + if (IsTransformKey(key, "d0")) { + if (IsTransformType(type, "name")) { + return "charname"; + } + } + else if (IsTransformKey(key, "m0")) { + if (IsTransformType(type, "names")) { + return "prprname"; + } + } + return nullptr; +} diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/intl/components/src/Locale.h firefox-trunk-95.0~a1~hg20211020r596404/intl/components/src/Locale.h --- firefox-trunk-95.0~a1~hg20211017r596111/intl/components/src/Locale.h 2021-10-17 14:42:38.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/intl/components/src/Locale.h 2021-10-20 19:28:21.000000000 +0000 @@ -2,22 +2,407 @@ * 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 intl_components_Locale_h_ -#define intl_components_Locale_h_ +/* Structured representation of Unicode locale IDs used with Intl functions. */ -#include "unicode/uloc.h" +#ifndef intl_components_Locale_h +#define intl_components_Locale_h +#include "mozilla/Assertions.h" +#include "mozilla/intl/ICUError.h" #include "mozilla/intl/ICU4CGlue.h" +#include "mozilla/Span.h" +#include "mozilla/TextUtils.h" +#include "mozilla/TypedEnumBits.h" +#include "mozilla/Variant.h" +#include "mozilla/Vector.h" +#include "mozilla/Result.h" + +#include +#include +#include +#include +#include + +#include "unicode/uloc.h" namespace mozilla::intl { -class Locale final { +/** + * Return true if |language| is a valid language subtag. + */ +template +bool IsStructurallyValidLanguageTag(mozilla::Span language); + +/** + * Return true if |script| is a valid script subtag. + */ +template +bool IsStructurallyValidScriptTag(mozilla::Span script); + +/** + * Return true if |region| is a valid region subtag. + */ +template +bool IsStructurallyValidRegionTag(mozilla::Span region); + +#ifdef DEBUG +/** + * Return true if |variant| is a valid variant subtag. + */ +bool IsStructurallyValidVariantTag(mozilla::Span variant); + +/** + * Return true if |extension| is a valid Unicode extension subtag. + */ +bool IsStructurallyValidUnicodeExtensionTag( + mozilla::Span extension); + +/** + * Return true if |privateUse| is a valid private-use subtag. + */ +bool IsStructurallyValidPrivateUseTag(mozilla::Span privateUse); + +#endif + +template +char AsciiToLowerCase(CharT c) { + MOZ_ASSERT(mozilla::IsAscii(c)); + return mozilla::IsAsciiUppercaseAlpha(c) ? (c + 0x20) : c; +} + +template +char AsciiToUpperCase(CharT c) { + MOZ_ASSERT(mozilla::IsAscii(c)); + return mozilla::IsAsciiLowercaseAlpha(c) ? (c - 0x20) : c; +} + +template +void AsciiToLowerCase(CharT* chars, size_t length, char* dest) { + char (&fn)(CharT) = AsciiToLowerCase; + std::transform(chars, chars + length, dest, fn); +} + +template +void AsciiToUpperCase(CharT* chars, size_t length, char* dest) { + char (&fn)(CharT) = AsciiToUpperCase; + std::transform(chars, chars + length, dest, fn); +} + +template +void AsciiToTitleCase(CharT* chars, size_t length, char* dest) { + if (length > 0) { + AsciiToUpperCase(chars, 1, dest); + AsciiToLowerCase(chars + 1, length - 1, dest + 1); + } +} + +// Constants for language subtag lengths. +namespace LanguageTagLimits { + +// unicode_language_subtag = alpha{2,3} | alpha{5,8} ; +static constexpr size_t LanguageLength = 8; + +// unicode_script_subtag = alpha{4} ; +static constexpr size_t ScriptLength = 4; + +// unicode_region_subtag = (alpha{2} | digit{3}) ; +static constexpr size_t RegionLength = 3; +static constexpr size_t AlphaRegionLength = 2; +static constexpr size_t DigitRegionLength = 3; + +// key = alphanum alpha ; +static constexpr size_t UnicodeKeyLength = 2; + +// tkey = alpha digit ; +static constexpr size_t TransformKeyLength = 2; + +} // namespace LanguageTagLimits + +// Fixed size language subtag which is stored inline in Locale. +template +class LanguageTagSubtag final { + uint8_t length_ = 0; + char chars_[Length] = {}; // zero initialize + + public: + LanguageTagSubtag() = default; + + LanguageTagSubtag(const LanguageTagSubtag&) = delete; + LanguageTagSubtag& operator=(const LanguageTagSubtag&) = delete; + + size_t length() const { return length_; } + bool missing() const { return length_ == 0; } + bool present() const { return length_ > 0; } + + mozilla::Span span() const { return {chars_, length_}; } + + template + void set(mozilla::Span str) { + MOZ_ASSERT(str.size() <= Length); + std::copy_n(str.data(), str.size(), chars_); + length_ = str.size(); + } + + // The toXYZCase() methods are using |Length| instead of |length()|, because + // current compilers (tested GCC and Clang) can't infer the maximum string + // length - even when using hints like |std::min| - and instead are emitting + // SIMD optimized code. Using a fixed sized length avoids emitting the SIMD + // code. (Emitting SIMD code doesn't make sense here, because the SIMD code + // only kicks in for long strings.) A fixed length will additionally ensure + // the compiler unrolls the loop in the case conversion code. + + void toLowerCase() { AsciiToLowerCase(chars_, Length, chars_); } + + void toUpperCase() { AsciiToUpperCase(chars_, Length, chars_); } + + void toTitleCase() { AsciiToTitleCase(chars_, Length, chars_); } + + template + bool equalTo(const char (&str)[N]) const { + static_assert(N - 1 <= Length, + "subtag literals must not exceed the maximum subtag length"); + + return length_ == N - 1 && memcmp(chars_, str, N - 1) == 0; + } +}; + +using LanguageSubtag = LanguageTagSubtag; +using ScriptSubtag = LanguageTagSubtag; +using RegionSubtag = LanguageTagSubtag; + +using Latin1Char = unsigned char; +using UniqueChars = UniquePtr; + +/** + * Object representing a Unicode BCP 47 locale identifier. + * + * All subtags are already in canonicalized case. + */ +class MOZ_STACK_CLASS Locale final { + LanguageSubtag language_ = {}; + ScriptSubtag script_ = {}; + RegionSubtag region_ = {}; + + using VariantsVector = Vector; + using ExtensionsVector = Vector; + + VariantsVector variants_; + ExtensionsVector extensions_; + UniqueChars privateuse_ = nullptr; + + friend class LocaleParser; + + public: + enum class CanonicalizationError : uint8_t { + DuplicateVariant, + InternalError, + OutOfMemory, + }; + + private: + Result canonicalizeUnicodeExtension( + UniqueChars& unicodeExtension); + + Result canonicalizeTransformExtension( + UniqueChars& transformExtension); + public: - Locale() = delete; + static bool languageMapping(LanguageSubtag& language); + static bool complexLanguageMapping(const LanguageSubtag& language); + + private: + static bool scriptMapping(ScriptSubtag& script); + static bool regionMapping(RegionSubtag& region); + static bool complexRegionMapping(const RegionSubtag& region); + + void performComplexLanguageMappings(); + void performComplexRegionMappings(); + [[nodiscard]] bool performVariantMappings(); + + [[nodiscard]] bool updateLegacyMappings(); + + static bool signLanguageMapping(LanguageSubtag& language, + const RegionSubtag& region); + + static const char* replaceTransformExtensionType( + mozilla::Span key, mozilla::Span type); + + public: + /** + * Given a Unicode key and type, return the null-terminated preferred + * replacement for that type if there is one, or null if there is none, e.g. + * in effect + * |replaceUnicodeExtensionType("ca", "islamicc") == "islamic-civil"| + * and + * |replaceUnicodeExtensionType("ca", "islamic-civil") == nullptr|. + */ + static const char* replaceUnicodeExtensionType( + mozilla::Span key, mozilla::Span type); + + public: + Locale() = default; + Locale(const Locale&) = delete; + Locale& operator=(const Locale&) = delete; + + const LanguageSubtag& language() const { return language_; } + const ScriptSubtag& script() const { return script_; } + const RegionSubtag& region() const { return region_; } + const auto& variants() const { return variants_; } + const auto& extensions() const { return extensions_; } + const char* privateuse() const { return privateuse_.get(); } + + /** + * Return the Unicode extension subtag or nullptr if not present. + */ + const char* unicodeExtension() const; + + private: + ptrdiff_t unicodeExtensionIndex() const; + + public: + /** + * Set the language subtag. The input must be a valid language subtag. + */ + template + void setLanguage(const char (&language)[N]) { + mozilla::Span span(language, N - 1); + MOZ_ASSERT(IsStructurallyValidLanguageTag(span)); + language_.set(span); + } + + /** + * Set the language subtag. The input must be a valid language subtag. + */ + void setLanguage(const LanguageSubtag& language) { + MOZ_ASSERT(IsStructurallyValidLanguageTag(language.span())); + language_.set(language.span()); + } + + /** + * Set the script subtag. The input must be a valid script subtag. + */ + template + void setScript(const char (&script)[N]) { + mozilla::Span span(script, N - 1); + MOZ_ASSERT(IsStructurallyValidScriptTag(span)); + script_.set(span); + } + + /** + * Set the script subtag. The input must be a valid script subtag or the empty + * string. + */ + void setScript(const ScriptSubtag& script) { + MOZ_ASSERT(script.missing() || IsStructurallyValidScriptTag(script.span())); + script_.set(script.span()); + } + + /** + * Set the region subtag. The input must be a valid region subtag. + */ + template + void setRegion(const char (®ion)[N]) { + mozilla::Span span(region, N - 1); + MOZ_ASSERT(IsStructurallyValidRegionTag(span)); + region_.set(span); + } + + /** + * Set the region subtag. The input must be a valid region subtag or the empty + * empty string. + */ + void setRegion(const RegionSubtag& region) { + MOZ_ASSERT(region.missing() || IsStructurallyValidRegionTag(region.span())); + region_.set(region.span()); + } + + /** + * Removes all variant subtags. + */ + void clearVariants() { variants_.clearAndFree(); } + + /** + * Set the Unicode extension subtag. The input must be a valid Unicode + * extension subtag. + */ + [[nodiscard]] bool setUnicodeExtension(const char* extension); + + /** + * Remove any Unicode extension subtag if present. + */ + void clearUnicodeExtension(); + + /** Canonicalize the base-name (language, script, region, variant) subtags. */ + Result canonicalizeBaseName(); + + /** + * Canonicalize all extension subtags. + */ + Result canonicalizeExtensions(); + + /** + * Canonicalizes the given structurally valid Unicode BCP 47 locale + * identifier, including regularized case of subtags. For example, the + * locale Zh-haNS-bu-variant2-Variant1-u-ca-chinese-t-Zh-laTN-x-PRIVATE, + * where + * + * Zh ; 2*3ALPHA + * -haNS ; ["-" script] + * -bu ; ["-" region] + * -variant2 ; *("-" variant) + * -Variant1 + * -u-ca-chinese ; *("-" extension) + * -t-Zh-laTN + * -x-PRIVATE ; ["-" privateuse] + * + * becomes zh-Hans-MM-variant1-variant2-t-zh-latn-u-ca-chinese-x-private + * + * Spec: ECMAScript Internationalization API Specification, 6.2.3. + */ + Result canonicalize() { + MOZ_TRY(canonicalizeBaseName()); + return canonicalizeExtensions(); + } + + /** + * Fill the buffer with a string representation of the locale. + */ + template + Result toString(B& buffer) const { + static_assert(std::is_same_v); + + size_t capacity = toStringCapacity(); + + // Attempt to reserve needed capacity + if (!buffer.reserve(capacity)) { + return Err(ICUError::OutOfMemory); + } + + size_t offset = toStringAppend(buffer.data()); + + MOZ_ASSERT(capacity == offset); + buffer.written(offset); + + return Ok(); + } + + /** + * Add likely-subtags to the locale. + * + * Spec: + */ + [[nodiscard]] bool addLikelySubtags(); + + /** + * Remove likely-subtags from the locale. + * + * Spec: + */ + [[nodiscard]] bool removeLikelySubtags(); /** * Returns the default locale as an ICU locale identifier. The returned string - * is NOT a valid BCP 47 language tag! + * is NOT a valid BCP 47 locale! * * Also see . */ @@ -35,8 +420,273 @@ return AvailableLocalesEnumeration(); } + + private: + static UniqueChars DuplicateStringToUniqueChars(const char* s); + size_t toStringCapacity() const; + size_t toStringAppend(char* buffer) const; +}; + +/** + * Parser for Unicode BCP 47 locale identifiers. + * + * + */ +class MOZ_STACK_CLASS LocaleParser final { + public: + enum class ParserError : uint8_t { + // Input was not parseable as a locale, subtag or extension. + NotParseable, + // Unable to allocate memory for the parser to operate. + OutOfMemory, + }; + + // Exposed as |public| for |MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS|. + enum class TokenKind : uint8_t { + None = 0b000, + Alpha = 0b001, + Digit = 0b010, + AlphaDigit = 0b011, + Error = 0b100 + }; + + private: + class Token final { + size_t index_; + size_t length_; + TokenKind kind_; + + public: + Token(TokenKind kind, size_t index, size_t length) + : index_(index), length_(length), kind_(kind) {} + + TokenKind kind() const { return kind_; } + size_t index() const { return index_; } + size_t length() const { return length_; } + + bool isError() const { return kind_ == TokenKind::Error; } + bool isNone() const { return kind_ == TokenKind::None; } + bool isAlpha() const { return kind_ == TokenKind::Alpha; } + bool isDigit() const { return kind_ == TokenKind::Digit; } + bool isAlphaDigit() const { return kind_ == TokenKind::AlphaDigit; } + }; + + const char* locale_; + size_t length_; + size_t index_ = 0; + + explicit LocaleParser(Span locale) + : locale_(locale.data()), length_(locale.size()) {} + + char charAt(size_t index) const { return locale_[index]; } + + // Copy the token characters into |subtag|. + template + void copyChars(const Token& tok, LanguageTagSubtag& subtag) const { + subtag.set(mozilla::Span(locale_ + tok.index(), tok.length())); + } + + // Create a string copy of |length| characters starting at |index|. + UniqueChars chars(size_t index, size_t length) const; + + // Create a string copy of the token characters. + UniqueChars chars(const Token& tok) const { + return chars(tok.index(), tok.length()); + } + + UniqueChars extension(const Token& start, const Token& end) const { + MOZ_ASSERT(start.index() < end.index()); + + size_t length = end.index() - 1 - start.index(); + return chars(start.index(), length); + } + + Token nextToken(); + + // unicode_language_subtag = alpha{2,3} | alpha{5,8} ; + // + // Four character language subtags are not allowed in Unicode BCP 47 locale + // identifiers. Also see the comparison to Unicode CLDR locale identifiers in + // . + bool isLanguage(const Token& tok) const { + return tok.isAlpha() && ((2 <= tok.length() && tok.length() <= 3) || + (5 <= tok.length() && tok.length() <= 8)); + } + + // unicode_script_subtag = alpha{4} ; + bool isScript(const Token& tok) const { + return tok.isAlpha() && tok.length() == 4; + } + + // unicode_region_subtag = (alpha{2} | digit{3}) ; + bool isRegion(const Token& tok) const { + return (tok.isAlpha() && tok.length() == 2) || + (tok.isDigit() && tok.length() == 3); + } + + // unicode_variant_subtag = (alphanum{5,8} | digit alphanum{3}) ; + bool isVariant(const Token& tok) const { + return (5 <= tok.length() && tok.length() <= 8) || + (tok.length() == 4 && mozilla::IsAsciiDigit(charAt(tok.index()))); + } + + // Returns the code unit of the first character at the given singleton token. + // Always returns the lower case form of an alphabetical character. + char singletonKey(const Token& tok) const { + MOZ_ASSERT(tok.length() == 1); + return AsciiToLowerCase(charAt(tok.index())); + } + + // extensions = unicode_locale_extensions | + // transformed_extensions | + // other_extensions ; + // + // unicode_locale_extensions = sep [uU] ((sep keyword)+ | + // (sep attribute)+ (sep keyword)*) ; + // + // transformed_extensions = sep [tT] ((sep tlang (sep tfield)*) | + // (sep tfield)+) ; + // + // other_extensions = sep [alphanum-[tTuUxX]] (sep alphanum{2,8})+ ; + bool isExtensionStart(const Token& tok) const { + return tok.length() == 1 && singletonKey(tok) != 'x'; + } + + // other_extensions = sep [alphanum-[tTuUxX]] (sep alphanum{2,8})+ ; + bool isOtherExtensionPart(const Token& tok) const { + return 2 <= tok.length() && tok.length() <= 8; + } + + // unicode_locale_extensions = sep [uU] ((sep keyword)+ | + // (sep attribute)+ (sep keyword)*) ; + // keyword = key (sep type)? ; + bool isUnicodeExtensionPart(const Token& tok) const { + return isUnicodeExtensionKey(tok) || isUnicodeExtensionType(tok) || + isUnicodeExtensionAttribute(tok); + } + + // attribute = alphanum{3,8} ; + bool isUnicodeExtensionAttribute(const Token& tok) const { + return 3 <= tok.length() && tok.length() <= 8; + } + + // key = alphanum alpha ; + bool isUnicodeExtensionKey(const Token& tok) const { + return tok.length() == 2 && mozilla::IsAsciiAlpha(charAt(tok.index() + 1)); + } + + // type = alphanum{3,8} (sep alphanum{3,8})* ; + bool isUnicodeExtensionType(const Token& tok) const { + return 3 <= tok.length() && tok.length() <= 8; + } + + // tkey = alpha digit ; + bool isTransformExtensionKey(const Token& tok) const { + return tok.length() == 2 && mozilla::IsAsciiAlpha(charAt(tok.index())) && + mozilla::IsAsciiDigit(charAt(tok.index() + 1)); + } + + // tvalue = (sep alphanum{3,8})+ ; + bool isTransformExtensionPart(const Token& tok) const { + return 3 <= tok.length() && tok.length() <= 8; + } + + // pu_extensions = sep [xX] (sep alphanum{1,8})+ ; + bool isPrivateUseStart(const Token& tok) const { + return tok.length() == 1 && singletonKey(tok) == 'x'; + } + + // pu_extensions = sep [xX] (sep alphanum{1,8})+ ; + bool isPrivateUsePart(const Token& tok) const { + return 1 <= tok.length() && tok.length() <= 8; + } + + // Helper function for use in |parseBaseName| and + // |parseTlangInTransformExtension|. Do not use this directly! + static Result internalParseBaseName(LocaleParser& ts, + Locale& tag, Token& tok); + + // Parse the `unicode_language_id` production, i.e. the + // language/script/region/variants portion of a locale, into |tag|. + // |tok| must be the current token. + static Result parseBaseName(LocaleParser& ts, Locale& tag, + Token& tok) { + return internalParseBaseName(ts, tag, tok); + } + + // Parse the `tlang` production within a parsed 't' transform extension. + // The precise requirements for "previously parsed" are: + // + // * the input begins from current token |tok| with a valid `tlang` + // * the `tlang` is wholly lowercase (*not* canonical case) + // * variant subtags in the `tlang` may contain duplicates and be + // unordered + // + // Return an error on internal failure. Otherwise, return a success value. If + // there was no `tlang`, then |tag.language().missing()|. But if there was a + // `tlang`, then |tag| is filled with subtags exactly as they appeared in the + // parse input. + static Result parseTlangInTransformExtension( + LocaleParser& ts, Locale& tag, Token& tok) { + MOZ_ASSERT(ts.isLanguage(tok)); + return internalParseBaseName(ts, tag, tok); + } + + friend class Locale; + + class Range final { + size_t begin_; + size_t length_; + + public: + Range(size_t begin, size_t length) : begin_(begin), length_(length) {} + + template + T* begin(T* ptr) const { + return ptr + begin_; + } + + size_t length() const { return length_; } + }; + + using TFieldVector = Vector; + using AttributesVector = Vector; + using KeywordsVector = Vector; + + // Parse |extension|, which must be a validated, fully lowercase + // `transformed_extensions` subtag, and fill |tag| and |fields| from the + // `tlang` and `tfield` components. Data in |tag| is lowercase, consistent + // with |extension|. + static Result parseTransformExtension( + mozilla::Span extension, Locale& tag, TFieldVector& fields); + + // Parse |extension|, which must be a validated, fully lowercase + // `unicode_locale_extensions` subtag, and fill |attributes| and |keywords| + // from the `attribute` and `keyword` components. + static Result parseUnicodeExtension( + mozilla::Span extension, AttributesVector& attributes, + KeywordsVector& keywords); + + public: + // Parse the input string as a locale. + static Result tryParse(Span locale, Locale& tag); + + // Parse the input string as the base-name parts (language, script, region, + // variants) of a locale. + static Result tryParseBaseName(Span locale, + Locale& tag); + + // Return Ok() iff |extension| can be parsed as a Unicode extension subtag. + static Result canParseUnicodeExtension( + Span extension); + + // Return Ok() iff |unicodeType| can be parsed as a Unicode extension type. + static Result canParseUnicodeExtensionType( + Span unicodeType); }; +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(LocaleParser::TokenKind) + } // namespace mozilla::intl -#endif +#endif /* intl_components_Locale_h */ diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/intl/docs/icu.rst firefox-trunk-95.0~a1~hg20211020r596404/intl/docs/icu.rst --- firefox-trunk-95.0~a1~hg20211017r596111/intl/docs/icu.rst 2021-10-17 14:42:38.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/intl/docs/icu.rst 2021-10-20 19:28:21.000000000 +0000 @@ -230,7 +230,7 @@ $ export PYTHONPATH="$topsrcdir/third_party/python/PyYAML/lib3/" $ python3 ./make_intl_data.py langtags -The CLDR version used will be printed in the header of CLDR-sensitive generated files. For example, ``js/src/builtin/intl/LanguageTagGenerated.cpp`` currently begins with: +The CLDR version used will be printed in the header of CLDR-sensitive generated files. For example, ``intl/components/src/LocaleGenerated.cpp`` currently begins with: .. code:: cpp diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/intl/locale/LocaleService.cpp firefox-trunk-95.0~a1~hg20211020r596404/intl/locale/LocaleService.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/intl/locale/LocaleService.cpp 2021-10-17 14:42:38.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/intl/locale/LocaleService.cpp 2021-10-20 19:28:22.000000000 +0000 @@ -303,8 +303,8 @@ bool LocaleService::LanguagesMatch(const nsACString& aRequested, const nsACString& aAvailable) { - Locale requested = Locale(aRequested); - Locale available = Locale(aAvailable); + MozLocale requested = MozLocale(aRequested); + MozLocale available = MozLocale(aAvailable); return requested.GetLanguage().Equals(available.GetLanguage()); } @@ -528,7 +528,7 @@ } MOZ_ASSERT( - aDefaultLocale.IsEmpty() || Locale(aDefaultLocale).IsWellFormed(), + aDefaultLocale.IsEmpty() || MozLocale(aDefaultLocale).IsWellFormed(), "If specified, default locale must be a well-formed BCP47 language tag."); if (aStrategy == kLangNegStrategyLookup && aDefaultLocale.IsEmpty()) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/intl/locale/MozLocale.cpp firefox-trunk-95.0~a1~hg20211020r596404/intl/locale/MozLocale.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/intl/locale/MozLocale.cpp 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/intl/locale/MozLocale.cpp 2021-10-20 19:28:22.000000000 +0000 @@ -8,43 +8,39 @@ using namespace mozilla::intl; using namespace mozilla::intl::ffi; -/** - * Note: The file name is `MozLocale` to avoid compilation problems on - * case-insensitive Windows. The class name is `Locale`. - */ -Locale::Locale(const nsACString& aLocale) +MozLocale::MozLocale(const nsACString& aLocale) : mRaw(unic_langid_new(&aLocale, &mIsWellFormed)) {} -const nsCString Locale::AsString() const { +const nsCString MozLocale::AsString() const { nsCString tag; unic_langid_as_string(mRaw.get(), &tag); return tag; } -const nsDependentCSubstring Locale::GetLanguage() const { +const nsDependentCSubstring MozLocale::GetLanguage() const { nsDependentCSubstring sub; unic_langid_get_language(mRaw.get(), &sub); return sub; } -const nsDependentCSubstring Locale::GetScript() const { +const nsDependentCSubstring MozLocale::GetScript() const { nsDependentCSubstring sub; unic_langid_get_script(mRaw.get(), &sub); return sub; } -const nsDependentCSubstring Locale::GetRegion() const { +const nsDependentCSubstring MozLocale::GetRegion() const { nsDependentCSubstring sub; unic_langid_get_region(mRaw.get(), &sub); return sub; } -void Locale::GetVariants(nsTArray& aRetVal) const { +void MozLocale::GetVariants(nsTArray& aRetVal) const { unic_langid_get_variants(mRaw.get(), &aRetVal); } -bool Locale::Matches(const Locale& aOther, bool aThisRange, - bool aOtherRange) const { +bool MozLocale::Matches(const MozLocale& aOther, bool aThisRange, + bool aOtherRange) const { if (!IsWellFormed() || !aOther.IsWellFormed()) { return false; } @@ -52,8 +48,8 @@ return unic_langid_matches(mRaw.get(), aOther.Raw(), aThisRange, aOtherRange); } -bool Locale::Maximize() { return unic_langid_maximize(mRaw.get()); } +bool MozLocale::Maximize() { return unic_langid_maximize(mRaw.get()); } -void Locale::ClearVariants() { unic_langid_clear_variants(mRaw.get()); } +void MozLocale::ClearVariants() { unic_langid_clear_variants(mRaw.get()); } -void Locale::ClearRegion() { unic_langid_clear_region(mRaw.get()); } +void MozLocale::ClearRegion() { unic_langid_clear_region(mRaw.get()); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/intl/locale/MozLocale.h firefox-trunk-95.0~a1~hg20211020r596404/intl/locale/MozLocale.h --- firefox-trunk-95.0~a1~hg20211017r596111/intl/locale/MozLocale.h 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/intl/locale/MozLocale.h 2021-10-20 19:28:23.000000000 +0000 @@ -41,17 +41,13 @@ * * Example: * - * Locale loc = Locale("de-at"); + * MozLocale loc = MozLocale("de-at"); * * ASSERT_TRUE(loc.GetLanguage().Equals("de")); * ASSERT_TRUE(loc.GetScript().IsEmpty()); * ASSERT_TRUE(loc.GetRegion().Equals("AT")); // canonicalized to upper case - * - * - * Note: The file name is `MozLocale` to avoid compilation problems on - * case-insensitive Windows. The class name is `Locale`. */ -class Locale { +class MozLocale { public: /** * The constructor expects the input to be a well-formed BCP47-style locale @@ -66,8 +62,9 @@ * created with its flag `mWellFormed` set to false which will make the Locale * never match. */ - explicit Locale(const nsACString& aLocale); - explicit Locale(const char* aLocale) : Locale(nsDependentCString(aLocale)){}; + explicit MozLocale(const nsACString& aLocale); + explicit MozLocale(const char* aLocale) + : MozLocale(nsDependentCString(aLocale)){}; const nsDependentCSubstring GetLanguage() const; const nsDependentCSubstring GetScript() const; @@ -102,7 +99,8 @@ * locale is being treated as a range and matches any region field * value including "US" of the other locale. */ - bool Matches(const Locale& aOther, bool aThisRange, bool aOtherRange) const; + bool Matches(const MozLocale& aOther, bool aThisRange, + bool aOtherRange) const; /** * This operation uses CLDR data to build a more specific version @@ -133,14 +131,14 @@ /** * Compares two locales expecting all fields to match each other. */ - bool operator==(const Locale& aOther) { + bool operator==(const MozLocale& aOther) { // Note: non-well-formed Locale objects are never // treated as equal to anything // (even other non-well-formed ones). return Matches(aOther, false, false); } - Locale(Locale&& aOther) + MozLocale(MozLocale&& aOther) : mIsWellFormed(aOther.mIsWellFormed), mRaw(std::move(aOther.mRaw)) {} ffi::LanguageIdentifier* Raw() { return mRaw.get(); } @@ -157,6 +155,6 @@ } // namespace intl } // namespace mozilla -MOZ_DECLARE_RELOCATE_USING_MOVE_CONSTRUCTOR(mozilla::intl::Locale) +MOZ_DECLARE_RELOCATE_USING_MOVE_CONSTRUCTOR(mozilla::intl::MozLocale) #endif /* mozilla_intl_MozLocale_h__ */ diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/intl/locale/nsLanguageAtomService.cpp firefox-trunk-95.0~a1~hg20211020r596404/intl/locale/nsLanguageAtomService.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/intl/locale/nsLanguageAtomService.cpp 2021-10-17 14:42:38.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/intl/locale/nsLanguageAtomService.cpp 2021-10-20 19:28:22.000000000 +0000 @@ -187,7 +187,7 @@ langStr.Truncate(start.get() - langStr.BeginReading()); } - Locale loc(langStr); + MozLocale loc(langStr); if (loc.IsWellFormed()) { // Fill in script subtag if not present. if (loc.GetScript().IsEmpty()) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/intl/locale/OSPreferences.cpp firefox-trunk-95.0~a1~hg20211020r596404/intl/locale/OSPreferences.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/intl/locale/OSPreferences.cpp 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/intl/locale/OSPreferences.cpp 2021-10-20 19:28:23.000000000 +0000 @@ -12,10 +12,11 @@ #include "OSPreferences.h" #include "mozilla/ClearOnShutdown.h" +#include "mozilla/intl/DateTimePatternGenerator.h" +#include "mozilla/intl/DateTimeFormat.h" +#include "mozilla/Result.h" #include "mozilla/Services.h" #include "nsIObserverService.h" -#include "unicode/udat.h" -#include "unicode/udatpg.h" using namespace mozilla::intl; @@ -97,60 +98,53 @@ } /** - * This method retrieves from ICU the best pattern for a given date/time style. + * This method retrieves from mozilla::intl the best pattern for a given + * date/time style. */ bool OSPreferences::GetDateTimePatternForStyle(DateTimeFormatStyle aDateStyle, DateTimeFormatStyle aTimeStyle, const nsACString& aLocale, nsACString& aRetVal) { - UDateFormatStyle timeStyle = UDAT_NONE; - UDateFormatStyle dateStyle = UDAT_NONE; + DateTimeFormat::StyleBag style; switch (aTimeStyle) { - case DateTimeFormatStyle::None: - timeStyle = UDAT_NONE; - break; case DateTimeFormatStyle::Short: - timeStyle = UDAT_SHORT; + style.time = Some(DateTimeFormat::Style::Short); break; case DateTimeFormatStyle::Medium: - timeStyle = UDAT_MEDIUM; + style.time = Some(DateTimeFormat::Style::Medium); break; case DateTimeFormatStyle::Long: - timeStyle = UDAT_LONG; + style.time = Some(DateTimeFormat::Style::Long); break; case DateTimeFormatStyle::Full: - timeStyle = UDAT_FULL; + style.time = Some(DateTimeFormat::Style::Full); break; + case DateTimeFormatStyle::None: case DateTimeFormatStyle::Invalid: - timeStyle = UDAT_NONE; + // Do nothing. break; } switch (aDateStyle) { - case DateTimeFormatStyle::None: - dateStyle = UDAT_NONE; - break; case DateTimeFormatStyle::Short: - dateStyle = UDAT_SHORT; + style.date = Some(DateTimeFormat::Style::Short); break; case DateTimeFormatStyle::Medium: - dateStyle = UDAT_MEDIUM; + style.date = Some(DateTimeFormat::Style::Medium); break; case DateTimeFormatStyle::Long: - dateStyle = UDAT_LONG; + style.date = Some(DateTimeFormat::Style::Long); break; case DateTimeFormatStyle::Full: - dateStyle = UDAT_FULL; + style.date = Some(DateTimeFormat::Style::Full); break; + case DateTimeFormatStyle::None: case DateTimeFormatStyle::Invalid: - dateStyle = UDAT_NONE; + // Do nothing. break; } - const int32_t kPatternMax = 160; - UChar pattern[kPatternMax]; - nsAutoCString locale; if (aLocale.IsEmpty()) { AutoTArray regionalPrefsLocales; @@ -160,24 +154,33 @@ locale.Assign(aLocale); } - UErrorCode status = U_ZERO_ERROR; - UDateFormat* df = udat_open(timeStyle, dateStyle, locale.get(), nullptr, -1, - nullptr, -1, &status); - if (U_FAILURE(status)) { + auto genResult = + DateTimePatternGenerator::TryCreate(PromiseFlatCString(aLocale).get()); + if (genResult.isErr()) { return false; } + auto generator = genResult.unwrap(); - int32_t patsize = udat_toPattern(df, false, pattern, kPatternMax, &status); - udat_close(df); - if (U_FAILURE(status)) { + auto dfResult = DateTimeFormat::TryCreateFromStyle( + MakeStringSpan(locale.get()), style, generator.get(), Nothing()); + if (dfResult.isErr()) { return false; } - aRetVal = NS_ConvertUTF16toUTF8(pattern, patsize); + auto df = dfResult.unwrap(); + + DateTimeFormat::PatternVector pattern; + auto patternResult = df->GetPattern(pattern); + if (patternResult.isErr()) { + return false; + } + + aRetVal = NS_ConvertUTF16toUTF8(pattern.begin(), pattern.length()); return true; } /** - * This method retrieves from ICU the best skeleton for a given date/time style. + * This method retrieves from mozilla::intl the best skeleton for a given + * date/time style. * * This is useful for cases where an OS does not provide its own patterns, * but provide ability to customize the skeleton, like alter hourCycle setting. @@ -193,20 +196,21 @@ return false; } - nsAutoString patternAsUtf16 = NS_ConvertUTF8toUTF16(pattern); - - const int32_t kSkeletonMax = 160; - UChar skeleton[kSkeletonMax]; + auto genResult = + DateTimePatternGenerator::TryCreate(PromiseFlatCString(aLocale).get()); + if (genResult.isErr()) { + return false; + } - UErrorCode status = U_ZERO_ERROR; - int32_t skelsize = udatpg_getSkeleton( - nullptr, (const UChar*)patternAsUtf16.BeginReading(), - patternAsUtf16.Length(), skeleton, kSkeletonMax, &status); - if (U_FAILURE(status)) { + nsAutoString patternAsUtf16 = NS_ConvertUTF8toUTF16(pattern); + DateTimeFormat::SkeletonVector skeleton; + auto generator = genResult.unwrap(); + auto skeletonResult = generator->GetSkeleton(patternAsUtf16, skeleton); + if (skeletonResult.isErr()) { return false; } - aRetVal = NS_ConvertUTF16toUTF8(skeleton, skelsize); + aRetVal = NS_ConvertUTF16toUTF8(skeleton.begin(), skeleton.length()); return true; } @@ -344,34 +348,22 @@ nsACString& aRetVal) { aRetVal.Truncate(); - UErrorCode status = U_ZERO_ERROR; - UDateTimePatternGenerator* pg = - udatpg_open(PromiseFlatCString(aLocale).get(), &status); - if (U_FAILURE(status)) { + auto genResult = + DateTimePatternGenerator::TryCreate(PromiseFlatCString(aLocale).get()); + if (genResult.isErr()) { return false; } nsAutoString skeletonAsUtf16 = NS_ConvertUTF8toUTF16(aSkeleton); - nsAutoString result; - - int32_t len = - udatpg_getBestPattern(pg, (const UChar*)skeletonAsUtf16.BeginReading(), - skeletonAsUtf16.Length(), nullptr, 0, &status); - if (status == U_BUFFER_OVERFLOW_ERROR) { // expected - result.SetLength(len); - status = U_ZERO_ERROR; - udatpg_getBestPattern(pg, (const UChar*)skeletonAsUtf16.BeginReading(), - skeletonAsUtf16.Length(), - (UChar*)result.BeginWriting(), len, &status); - } - - udatpg_close(pg); - - if (U_SUCCESS(status)) { - aRetVal = NS_ConvertUTF16toUTF8(result); + DateTimeFormat::PatternVector pattern; + auto generator = genResult.unwrap(); + auto patternResult = generator->GetBestPattern(skeletonAsUtf16, pattern); + if (patternResult.isErr()) { + return false; } - return U_SUCCESS(status); + aRetVal = NS_ConvertUTF16toUTF8(pattern.begin(), pattern.length()); + return true; } /** @@ -385,8 +377,6 @@ */ bool OSPreferences::GetDateTimeConnectorPattern(const nsACString& aLocale, nsACString& aRetVal) { - bool result = false; - // Check for a valid override pref and use that if present. nsAutoCString value; nsresult nr = Preferences::GetCString( @@ -397,19 +387,16 @@ return true; } - UErrorCode status = U_ZERO_ERROR; - UDateTimePatternGenerator* pg = - udatpg_open(PromiseFlatCString(aLocale).get(), &status); - if (U_SUCCESS(status)) { - int32_t resultSize; - const UChar* value = udatpg_getDateTimeFormat(pg, &resultSize); - MOZ_ASSERT(resultSize >= 0); - - aRetVal = NS_ConvertUTF16toUTF8(value, resultSize); - result = true; + auto genResult = + DateTimePatternGenerator::TryCreate(PromiseFlatCString(aLocale).get()); + if (genResult.isErr()) { + return false; } - udatpg_close(pg); - return result; + + auto generator = genResult.unwrap(); + Span result = generator->GetPlaceholderPattern(); + aRetVal = NS_ConvertUTF16toUTF8(result.data(), result.size()); + return true; } /** diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/intl/locale/Quotes.cpp firefox-trunk-95.0~a1~hg20211020r596404/intl/locale/Quotes.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/intl/locale/Quotes.cpp 2021-10-17 14:42:38.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/intl/locale/Quotes.cpp 2021-10-20 19:28:22.000000000 +0000 @@ -54,7 +54,7 @@ // Try parsing lang as a Locale (which will also canonicalize case of the // subtags), then see if we can match it with region or script subtags, // if present, or just the primary language tag. - Locale loc(langStr); + MozLocale loc(langStr); if (!loc.IsWellFormed()) { return nullptr; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/intl/locale/tests/gtest/TestLocaleService.cpp firefox-trunk-95.0~a1~hg20211020r596404/intl/locale/tests/gtest/TestLocaleService.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/intl/locale/tests/gtest/TestLocaleService.cpp 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/intl/locale/tests/gtest/TestLocaleService.cpp 2021-10-20 19:28:22.000000000 +0000 @@ -138,7 +138,7 @@ LocaleService::GetInstance()->GetDefaultLocale(locStr); ASSERT_FALSE(locStr.IsEmpty()); - ASSERT_TRUE(Locale(locStr).IsWellFormed()); + ASSERT_TRUE(MozLocale(locStr).IsWellFormed()); } TEST(Intl_Locale_LocaleService, IsAppLocaleRTL) diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/intl/locale/tests/gtest/TestMozLocale.cpp firefox-trunk-95.0~a1~hg20211020r596404/intl/locale/tests/gtest/TestMozLocale.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/intl/locale/tests/gtest/TestMozLocale.cpp 2021-10-17 14:42:38.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/intl/locale/tests/gtest/TestMozLocale.cpp 2021-10-20 19:28:22.000000000 +0000 @@ -8,24 +8,24 @@ using namespace mozilla::intl; -TEST(Intl_Locale_Locale, Locale) +TEST(Intl_MozLocale_MozLocale, MozLocale) { - Locale loc = Locale("en-US"); + MozLocale loc = MozLocale("en-US"); ASSERT_TRUE(loc.GetLanguage().Equals("en")); ASSERT_TRUE(loc.GetRegion().Equals("US")); } -TEST(Intl_Locale_Locale, AsString) +TEST(Intl_MozLocale_MozLocale, AsString) { - Locale loc = Locale("ja-jp-windows"); + MozLocale loc = MozLocale("ja-jp-windows"); ASSERT_TRUE(loc.AsString().Equals("ja-JP-windows")); } -TEST(Intl_Locale_Locale, GetSubTags) +TEST(Intl_MozLocale_MozLocale, GetSubTags) { - Locale loc = Locale("en-latn-us-macos"); + MozLocale loc = MozLocale("en-latn-us-macos"); ASSERT_TRUE(loc.GetLanguage().Equals("en")); ASSERT_TRUE(loc.GetScript().Equals("Latn")); @@ -37,32 +37,32 @@ ASSERT_TRUE(variants[0].Equals("macos")); } -TEST(Intl_Locale_Locale, Matches) +TEST(Intl_MozLocale_MozLocale, Matches) { - Locale loc = Locale("en-US"); + MozLocale loc = MozLocale("en-US"); - Locale loc2 = Locale("en-GB"); + MozLocale loc2 = MozLocale("en-GB"); ASSERT_FALSE(loc == loc2); - Locale loc3 = Locale("en-US"); + MozLocale loc3 = MozLocale("en-US"); ASSERT_TRUE(loc == loc3); - Locale loc4 = Locale("En_us"); + MozLocale loc4 = MozLocale("En_us"); ASSERT_TRUE(loc == loc4); } -TEST(Intl_Locale_Locale, MatchesRange) +TEST(Intl_MozLocale_MozLocale, MatchesRange) { - Locale loc = Locale("en-US"); + MozLocale loc = MozLocale("en-US"); - Locale loc2 = Locale("en-Latn-US"); + MozLocale loc2 = MozLocale("en-Latn-US"); ASSERT_FALSE(loc == loc2); ASSERT_TRUE(loc.Matches(loc2, true, false)); ASSERT_FALSE(loc.Matches(loc2, false, true)); ASSERT_FALSE(loc.Matches(loc2, false, false)); ASSERT_TRUE(loc.Matches(loc2, true, true)); - Locale loc3 = Locale("en"); + MozLocale loc3 = MozLocale("en"); ASSERT_FALSE(loc == loc3); ASSERT_TRUE(loc.Matches(loc3, false, true)); ASSERT_FALSE(loc.Matches(loc3, true, false)); @@ -70,46 +70,46 @@ ASSERT_TRUE(loc.Matches(loc3, true, true)); } -TEST(Intl_Locale_Locale, Variants) +TEST(Intl_MozLocale_MozLocale, Variants) { - Locale loc = Locale("en-US-UniFon-BasicEng"); + MozLocale loc = MozLocale("en-US-UniFon-BasicEng"); // Make sure that we canonicalize and sort variant tags ASSERT_TRUE(loc.AsString().Equals("en-US-basiceng-unifon")); } -TEST(Intl_Locale_Locale, InvalidLocale) +TEST(Intl_MozLocale_MozLocale, InvalidMozLocale) { - Locale loc = Locale("en-verylongsubtag"); + MozLocale loc = MozLocale("en-verylongsubtag"); ASSERT_FALSE(loc.IsWellFormed()); - Locale loc2 = Locale("p-te"); + MozLocale loc2 = MozLocale("p-te"); ASSERT_FALSE(loc2.IsWellFormed()); } -TEST(Intl_Locale_Locale, ClearRegion) +TEST(Intl_MozLocale_MozLocale, ClearRegion) { - Locale loc = Locale("en-US"); + MozLocale loc = MozLocale("en-US"); loc.ClearRegion(); ASSERT_TRUE(loc.AsString().Equals("en")); } -TEST(Intl_Locale_Locale, ClearVariants) +TEST(Intl_MozLocale_MozLocale, ClearVariants) { - Locale loc = Locale("en-US-windows"); + MozLocale loc = MozLocale("en-US-windows"); loc.ClearVariants(); ASSERT_TRUE(loc.AsString().Equals("en-US")); } -TEST(Intl_Locale_Locale, jaJPmac) +TEST(Intl_MozLocale_MozLocale, jaJPmac) { - Locale loc = Locale("ja-JP-mac"); + MozLocale loc = MozLocale("ja-JP-mac"); ASSERT_TRUE(loc.AsString().Equals("ja-JP-macos")); } -TEST(Intl_Locale_Locale, Maximize) +TEST(Intl_MozLocale_MozLocale, Maximize) { - Locale loc = Locale("en"); + MozLocale loc = MozLocale("en"); ASSERT_TRUE(loc.GetLanguage().Equals("en")); ASSERT_TRUE(loc.GetScript().IsEmpty()); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/intl/locale/windows/OSPreferences_win.cpp firefox-trunk-95.0~a1~hg20211020r596404/intl/locale/windows/OSPreferences_win.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/intl/locale/windows/OSPreferences_win.cpp 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/intl/locale/windows/OSPreferences_win.cpp 2021-10-20 19:28:23.000000000 +0000 @@ -64,9 +64,9 @@ // language code with no region subtag, but the // GlobalizationPreferences API may give us one (e.g. "ja"). // So if there's no hyphen in the string at this point, we use - // Locale::Maximize to get a suitable region code to + // MozLocale::Maximize to get a suitable region code to // go with it. - Locale locale(loc); + MozLocale locale(loc); if (locale.Maximize() && !locale.GetRegion().IsEmpty()) { loc.Append('-'); loc.Append(locale.GetRegion()); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/intl/lwbrk/LineBreaker.cpp firefox-trunk-95.0~a1~hg20211020r596404/intl/lwbrk/LineBreaker.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/intl/lwbrk/LineBreaker.cpp 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/intl/lwbrk/LineBreaker.cpp 2021-10-20 19:28:22.000000000 +0000 @@ -370,11 +370,12 @@ return (u == U_HYPHEN || u == 0x2010 || // HYPHEN u == 0x2012 || // FIGURE DASH u == 0x2013 || // EN DASH -#if ANDROID - /* Bug 1647377: On Android, we don't have a "platform" backend - * that supports Tibetan (nsRuleBreaker.cpp only knows about - * Thai), so instead we just treat the TSHEG like a hyphen to - * provide basic line-breaking possibilities. +#if ANDROID || XP_WIN + /* Bug 1647377: On Android and Windows, we don't have a "platform" + * backend that supports Tibetan (nsRuleBreaker.cpp only knows about + * Thai, and ScriptBreak doesn't handle Tibetan well either), so + * instead we just treat the TSHEG like a hyphen to provide basic + * line-breaking possibilities. */ u == 0x0F0B || // TIBETAN MARK INTERSYLLABIC TSHEG #endif diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/intl/lwbrk/LineBreaker.h firefox-trunk-95.0~a1~hg20211020r596404/intl/lwbrk/LineBreaker.h --- firefox-trunk-95.0~a1~hg20211017r596111/intl/lwbrk/LineBreaker.h 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/intl/lwbrk/LineBreaker.h 2021-10-20 19:28:23.000000000 +0000 @@ -76,8 +76,8 @@ static inline bool NS_NeedsPlatformNativeHandling(char16_t aChar) { return -#if ANDROID // Bug 1647377: no "platform native" support for Tibetan; - // better to just use our class-based breaker. +#if ANDROID || XP_WIN // Bug 1647377/1736393: no "platform native" support for + // Tibetan; better to just use our class-based breaker. (0x0e01 <= aChar && aChar <= 0x0eff) || // Thai, Lao #else (0x0e01 <= aChar && aChar <= 0x0fff) || // Thai, Lao, Tibetan diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/ipc/glue/BackgroundParentImpl.cpp firefox-trunk-95.0~a1~hg20211020r596404/ipc/glue/BackgroundParentImpl.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/ipc/glue/BackgroundParentImpl.cpp 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/ipc/glue/BackgroundParentImpl.cpp 2021-10-20 19:28:22.000000000 +0000 @@ -51,6 +51,7 @@ #include "mozilla/dom/WebAuthnTransactionParent.h" #include "mozilla/dom/cache/ActorUtils.h" #include "mozilla/dom/indexedDB/ActorsParent.h" +#include "mozilla/dom/locks/LockManagerParent.h" #include "mozilla/dom/localstorage/ActorsParent.h" #include "mozilla/dom/network/UDPSocketParent.h" #include "mozilla/dom/quota/ActorsParent.h" @@ -1443,6 +1444,13 @@ return true; } +already_AddRefed +BackgroundParentImpl::AllocPLockManagerParent( + const ContentPrincipalInfo& aPrincipalInfo, const nsID& aClientId) { + return MakeAndAddRef(aPrincipalInfo, + aClientId); +} + PParentToChildStreamParent* BackgroundParentImpl::SendPParentToChildStreamConstructor( PParentToChildStreamParent* aActor) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/ipc/glue/BackgroundParentImpl.h firefox-trunk-95.0~a1~hg20211020r596404/ipc/glue/BackgroundParentImpl.h --- firefox-trunk-95.0~a1~hg20211017r596111/ipc/glue/BackgroundParentImpl.h 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/ipc/glue/BackgroundParentImpl.h 2021-10-20 19:28:22.000000000 +0000 @@ -11,8 +11,7 @@ #include "mozilla/ipc/InputStreamUtils.h" #include "mozilla/ipc/PBackgroundParent.h" -namespace mozilla { -namespace ipc { +namespace mozilla::ipc { // Instances of this class should never be created directly. This class is meant // to be inherited in BackgroundImpl. @@ -420,9 +419,11 @@ AllocPWebSocketConnectionParent(const uint32_t& aListenerId) override; mozilla::ipc::IPCResult RecvPWebSocketConnectionConstructor( PWebSocketConnectionParent* actor, const uint32_t& aListenerId) override; + + already_AddRefed AllocPLockManagerParent( + const ContentPrincipalInfo& aPrincipalInfo, const nsID& aClientId) final; }; -} // namespace ipc -} // namespace mozilla +} // namespace mozilla::ipc #endif // mozilla_ipc_backgroundparentimpl_h__ diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/ipc/glue/Endpoint.cpp firefox-trunk-95.0~a1~hg20211020r596404/ipc/glue/Endpoint.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/ipc/glue/Endpoint.cpp 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/ipc/glue/Endpoint.cpp 2021-10-20 19:28:22.000000000 +0000 @@ -0,0 +1,148 @@ +/* -*- 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 "mozilla/ipc/Endpoint.h" +#include "chrome/common/ipc_message.h" +#include "mozilla/ipc/IPDLParamTraits.h" +#include "nsThreadUtils.h" +#include "mozilla/ipc/ProtocolMessageUtils.h" + +namespace mozilla::ipc { + +UntypedManagedEndpoint::UntypedManagedEndpoint(IProtocol* aActor) + : mInner(Some(Inner{ + /* mOtherSide */ aActor->GetWeakLifecycleProxy(), + /* mToplevel */ nullptr, + aActor->Id(), + aActor->GetProtocolId(), + aActor->Manager()->Id(), + aActor->Manager()->GetProtocolId(), + })) {} + +UntypedManagedEndpoint::~UntypedManagedEndpoint() { + if (!IsValid()) { + return; + } + + if (mInner->mOtherSide) { + // If this ManagedEndpoint was never sent over IPC, deliver a fake + // MANAGED_ENDPOINT_DROPPED_MESSAGE_TYPE message directly to the other side + // actor. + mInner->mOtherSide->ActorEventTarget()->Dispatch(NS_NewRunnableFunction( + "~ManagedEndpoint (Local)", + [otherSide = mInner->mOtherSide, id = mInner->mId] { + if (IProtocol* actor = otherSide->Get(); actor && actor->CanRecv()) { + MOZ_DIAGNOSTIC_ASSERT(actor->Id() == id, "Wrong Actor?"); + RefPtr strongProxy(actor->GetLifecycleProxy()); + strongProxy->Get()->OnMessageReceived( + IPC::Message(id, MANAGED_ENDPOINT_DROPPED_MESSAGE_TYPE)); + } + })); + } else if (mInner->mToplevel) { + // If it was sent over IPC, we'll need to send the message to the sending + // side. Let's send the message async. + mInner->mToplevel->ActorEventTarget()->Dispatch(NS_NewRunnableFunction( + "~ManagedEndpoint (Remote)", + [toplevel = mInner->mToplevel, id = mInner->mId] { + if (IProtocol* actor = toplevel->Get(); + actor && actor->CanSend() && actor->GetIPCChannel()) { + actor->GetIPCChannel()->Send(MakeUnique( + id, MANAGED_ENDPOINT_DROPPED_MESSAGE_TYPE)); + } + })); + } +} + +bool UntypedManagedEndpoint::BindCommon(IProtocol* aActor, + IProtocol* aManager) { + MOZ_ASSERT(aManager); + if (!mInner) { + NS_WARNING("Cannot bind to invalid endpoint"); + return false; + } + + // Perform thread assertions. + if (mInner->mToplevel) { + MOZ_DIAGNOSTIC_ASSERT( + mInner->mToplevel->ActorEventTarget()->IsOnCurrentThread()); + MOZ_DIAGNOSTIC_ASSERT(aManager->ToplevelProtocol() == + mInner->mToplevel->Get()); + } + + if (NS_WARN_IF(aManager->Id() != mInner->mManagerId) || + NS_WARN_IF(aManager->GetProtocolId() != mInner->mManagerType) || + NS_WARN_IF(aActor->GetProtocolId() != mInner->mType)) { + MOZ_ASSERT_UNREACHABLE("Actor and manager do not match Endpoint"); + return false; + } + + if (!aManager->CanSend() || !aManager->GetIPCChannel()) { + NS_WARNING("Manager cannot send"); + return false; + } + + int32_t id = mInner->mId; + mInner.reset(); + + // Our typed caller will insert the actor into the managed container. + aActor->SetManagerAndRegister(aManager, id); + + aManager->GetIPCChannel()->Send( + MakeUnique(id, MANAGED_ENDPOINT_BOUND_MESSAGE_TYPE)); + return true; +} + +/* static */ +void IPDLParamTraits::Write(IPC::Message* aMsg, + IProtocol* aActor, + paramType&& aParam) { + bool isValid = aParam.mInner.isSome(); + WriteIPDLParam(aMsg, aActor, isValid); + if (!isValid) { + return; + } + + auto inner = std::move(*aParam.mInner); + aParam.mInner.reset(); + + MOZ_RELEASE_ASSERT(inner.mOtherSide, "Has not been sent over IPC yet"); + MOZ_RELEASE_ASSERT(inner.mOtherSide->ActorEventTarget()->IsOnCurrentThread(), + "Must be being sent from the correct thread"); + MOZ_RELEASE_ASSERT( + inner.mOtherSide->Get() && inner.mOtherSide->Get()->ToplevelProtocol() == + aActor->ToplevelProtocol(), + "Must be being sent over the same toplevel protocol"); + + WriteIPDLParam(aMsg, aActor, inner.mId); + WriteIPDLParam(aMsg, aActor, inner.mType); + WriteIPDLParam(aMsg, aActor, inner.mManagerId); + WriteIPDLParam(aMsg, aActor, inner.mManagerType); +} + +/* static */ +bool IPDLParamTraits::Read(const IPC::Message* aMsg, + PickleIterator* aIter, + IProtocol* aActor, + paramType* aResult) { + *aResult = UntypedManagedEndpoint{}; + bool isValid = false; + if (!aActor || !ReadIPDLParam(aMsg, aIter, aActor, &isValid)) { + return false; + } + if (!isValid) { + return true; + } + + aResult->mInner.emplace(); + auto& inner = *aResult->mInner; + inner.mToplevel = aActor->ToplevelProtocol()->GetWeakLifecycleProxy(); + return ReadIPDLParam(aMsg, aIter, aActor, &inner.mId) && + ReadIPDLParam(aMsg, aIter, aActor, &inner.mType) && + ReadIPDLParam(aMsg, aIter, aActor, &inner.mManagerId) && + ReadIPDLParam(aMsg, aIter, aActor, &inner.mManagerType); +} + +} // namespace mozilla::ipc diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/ipc/glue/Endpoint.h firefox-trunk-95.0~a1~hg20211020r596404/ipc/glue/Endpoint.h --- firefox-trunk-95.0~a1~hg20211017r596111/ipc/glue/Endpoint.h 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/ipc/glue/Endpoint.h 2021-10-20 19:28:22.000000000 +0000 @@ -15,6 +15,7 @@ #include "mozilla/Maybe.h" #include "mozilla/UniquePtr.h" #include "mozilla/ipc/MessageLink.h" +#include "mozilla/ipc/ProtocolUtils.h" #include "mozilla/ipc/Transport.h" #include "mozilla/ipc/NodeController.h" #include "mozilla/ipc/ScopedPort.h" @@ -118,6 +119,51 @@ return NS_OK; } +class UntypedManagedEndpoint { + public: + bool IsValid() const { return mInner.isSome(); } + + UntypedManagedEndpoint(const UntypedManagedEndpoint&) = delete; + UntypedManagedEndpoint& operator=(const UntypedManagedEndpoint&) = delete; + + protected: + UntypedManagedEndpoint() = default; + explicit UntypedManagedEndpoint(IProtocol* aActor); + + UntypedManagedEndpoint(UntypedManagedEndpoint&& aOther) noexcept + : mInner(std::move(aOther.mInner)) { + aOther.mInner = Nothing(); + } + UntypedManagedEndpoint& operator=(UntypedManagedEndpoint&& aOther) noexcept { + this->~UntypedManagedEndpoint(); + new (this) UntypedManagedEndpoint(std::move(aOther)); + return *this; + } + + ~UntypedManagedEndpoint() noexcept; + + bool BindCommon(IProtocol* aActor, IProtocol* aManager); + + private: + friend struct IPDLParamTraits; + + struct Inner { + // Pointers to the toplevel actor which will manage this connection. When + // created, only `mOtherSide` will be set, and will reference the + // toplevel actor which the other side is managed by. After being sent over + // IPC, only `mToplevel` will be set, and will be the toplevel actor for the + // channel which received the IPC message. + RefPtr mOtherSide; + RefPtr mToplevel; + + int32_t mId = 0; + ProtocolId mType = LastMsgIndex; + int32_t mManagerId = 0; + ProtocolId mManagerType = LastMsgIndex; + }; + Maybe mInner; +}; + /** * A managed endpoint represents one end of a partially initialized managed * IPDL actor. It is used for situations where the usual IPDL Constructor @@ -140,39 +186,28 @@ * a browser crash. */ template -class ManagedEndpoint { +class ManagedEndpoint : public UntypedManagedEndpoint { public: - ManagedEndpoint() : mId(0) {} - - ManagedEndpoint(const PrivateIPDLInterface&, int32_t aId) : mId(aId) {} - - ManagedEndpoint(ManagedEndpoint&& aOther) : mId(aOther.mId) { - aOther.mId = 0; + ManagedEndpoint() = default; + ManagedEndpoint(ManagedEndpoint&&) noexcept = default; + ManagedEndpoint& operator=(ManagedEndpoint&&) noexcept = default; + + ManagedEndpoint(const PrivateIPDLInterface&, IProtocol* aActor) + : UntypedManagedEndpoint(aActor) {} + + bool Bind(const PrivateIPDLInterface&, PFooSide* aActor, IProtocol* aManager, + ManagedContainer& aContainer) { + if (!BindCommon(aActor, aManager)) { + return false; + } + aContainer.Insert(aActor); + return true; } - ManagedEndpoint& operator=(ManagedEndpoint&& aOther) { - mId = aOther.mId; - aOther.mId = 0; - return *this; + // Only invalid ManagedEndpoints can be equal, as valid endpoints are unique. + bool operator==(const ManagedEndpoint& _o) const { + return !IsValid() && !_o.IsValid(); } - - bool IsValid() const { return mId != 0; } - - Maybe ActorId() const { return IsValid() ? Some(mId) : Nothing(); } - - bool operator==(const ManagedEndpoint& _o) const { return mId == _o.mId; } - - private: - friend struct IPC::ParamTraits>; - - ManagedEndpoint(const ManagedEndpoint&) = delete; - ManagedEndpoint& operator=(const ManagedEndpoint&) = delete; - - // The routing ID for the to-be-created endpoint. - int32_t mId; - - // XXX(nika): Might be nice to have other info for assertions? - // e.g. mManagerId, mManagerType, etc. }; } // namespace ipc diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/ipc/glue/IPCMessageUtilsSpecializations.h firefox-trunk-95.0~a1~hg20211020r596404/ipc/glue/IPCMessageUtilsSpecializations.h --- firefox-trunk-95.0~a1~hg20211017r596111/ipc/glue/IPCMessageUtilsSpecializations.h 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/ipc/glue/IPCMessageUtilsSpecializations.h 2021-10-20 19:28:22.000000000 +0000 @@ -382,6 +382,10 @@ template struct ParamTraits> : ParamTraits> {}; +template +struct ParamTraits> : ParamTraits> { +}; + template struct ParamTraits> { typedef mozilla::Vector paramType; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/ipc/glue/moz.build firefox-trunk-95.0~a1~hg20211020r596404/ipc/glue/moz.build --- firefox-trunk-95.0~a1~hg20211017r596111/ipc/glue/moz.build 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/ipc/glue/moz.build 2021-10-20 19:28:22.000000000 +0000 @@ -161,6 +161,7 @@ "BrowserProcessSubThread.cpp", "CrashReporterClient.cpp", "CrashReporterHost.cpp", + "Endpoint.cpp", "FileDescriptor.cpp", "FileDescriptorUtils.cpp", "IdleSchedulerChild.cpp", diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/ipc/glue/PBackground.ipdl firefox-trunk-95.0~a1~hg20211020r596404/ipc/glue/PBackground.ipdl --- firefox-trunk-95.0~a1~hg20211017r596111/ipc/glue/PBackground.ipdl 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/ipc/glue/PBackground.ipdl 2021-10-20 19:28:22.000000000 +0000 @@ -36,6 +36,7 @@ include protocol PFileCreator; include protocol PMessagePort; include protocol PCameras; +include protocol PLockManager; include protocol PMIDIManager; include protocol PMIDIPort; include protocol PQuota; @@ -105,6 +106,7 @@ manages PHttpBackgroundChannel; manages PIdleScheduler; manages PRemoteLazyInputStream; + manages PLockManager; manages PMediaTransport; manages PRemoteWorker; manages PRemoteWorkerController; @@ -282,6 +284,8 @@ async PWebSocketConnection(uint32_t aListenerId); + async PLockManager(ContentPrincipalInfo aPrincipalInfo, nsID aClientId); + child: async PCache(); async PCacheStreamControl(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/ipc/glue/ProtocolMessageUtils.h firefox-trunk-95.0~a1~hg20211020r596404/ipc/glue/ProtocolMessageUtils.h --- firefox-trunk-95.0~a1~hg20211017r596111/ipc/glue/ProtocolMessageUtils.h 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/ipc/glue/ProtocolMessageUtils.h 2021-10-20 19:28:22.000000000 +0000 @@ -14,6 +14,7 @@ #include "chrome/common/ipc_message_utils.h" #include "ipc/EnumSerializer.h" #include "mozilla/Assertions.h" +#include "mozilla/ipc/Endpoint.h" #include "mozilla/ipc/ProtocolUtils.h" #include "mozilla/ipc/Transport.h" @@ -39,6 +40,11 @@ Channel::MODE_CLIENT> {}; template <> +struct ParamTraits + : ContiguousEnumSerializer {}; + +template <> struct ParamTraits { typedef mozilla::ipc::ActorHandle paramType; @@ -83,32 +89,35 @@ } }; -template -struct ParamTraits> { - typedef mozilla::ipc::ManagedEndpoint paramType; +} // namespace IPC - static void Write(Message* aMsg, const paramType& aParam) { - IPC::WriteParam(aMsg, aParam.mId); - } +namespace mozilla::ipc { - static bool Read(const Message* aMsg, PickleIterator* aIter, - paramType* aResult) { - MOZ_RELEASE_ASSERT(aResult->mId == 0); +template <> +struct IPDLParamTraits { + using paramType = UntypedManagedEndpoint; - if (!IPC::ReadParam(aMsg, aIter, &aResult->mId)) { - return false; - } - return true; + static void Write(IPC::Message* aMsg, IProtocol* aActor, paramType&& aParam); + static bool Read(const IPC::Message* aMsg, PickleIterator* aIter, + IProtocol* aActor, paramType* aResult); +}; + +template +struct IPDLParamTraits> { + using paramType = ManagedEndpoint; + + static void Write(IPC::Message* aMsg, IProtocol* aActor, paramType&& aParam) { + IPDLParamTraits::Write(aMsg, aActor, + std::move(aParam)); } - static void Log(const paramType& aParam, std::wstring* aLog) { - aLog->append(StringPrintf(L"ManagedEndpoint")); + static bool Read(const IPC::Message* aMsg, PickleIterator* aIter, + IProtocol* aActor, paramType* aResult) { + return IPDLParamTraits::Read(aMsg, aIter, aActor, + aResult); } }; -} // namespace IPC - -namespace mozilla::ipc { template <> struct IPDLParamTraits { typedef FileDescriptor paramType; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/ipc/glue/ProtocolUtils.cpp firefox-trunk-95.0~a1~hg20211020r596404/ipc/glue/ProtocolUtils.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/ipc/glue/ProtocolUtils.cpp 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/ipc/glue/ProtocolUtils.cpp 2021-10-20 19:28:23.000000000 +0000 @@ -293,6 +293,10 @@ return mProxy ? mProxy->Get() : nullptr; } +WeakActorLifecycleProxy* IProtocol::GetWeakLifecycleProxy() { + return mLifecycleProxy ? mLifecycleProxy->GetWeakProxy() : nullptr; +} + IProtocol::~IProtocol() { // If the actor still has a lifecycle proxy when it is being torn down, it // means that IPC was not given control over the lifecycle of the actor diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/ipc/glue/ProtocolUtils.h firefox-trunk-95.0~a1~hg20211020r596404/ipc/glue/ProtocolUtils.h --- firefox-trunk-95.0~a1~hg20211017r596111/ipc/glue/ProtocolUtils.h 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/ipc/glue/ProtocolUtils.h 2021-10-20 19:28:22.000000000 +0000 @@ -59,13 +59,15 @@ // starts approaching its 65,536th message. enum { // Message types used by NodeChannel - ACCEPT_INVITE_MESSAGE_TYPE = kuint16max - 14, - REQUEST_INTRODUCTION_MESSAGE_TYPE = kuint16max - 13, - INTRODUCE_MESSAGE_TYPE = kuint16max - 12, - BROADCAST_MESSAGE_TYPE = kuint16max - 11, - EVENT_MESSAGE_TYPE = kuint16max - 10, + ACCEPT_INVITE_MESSAGE_TYPE = kuint16max - 16, + REQUEST_INTRODUCTION_MESSAGE_TYPE = kuint16max - 15, + INTRODUCE_MESSAGE_TYPE = kuint16max - 14, + BROADCAST_MESSAGE_TYPE = kuint16max - 13, + EVENT_MESSAGE_TYPE = kuint16max - 12, // Message types used by MessageChannel + MANAGED_ENDPOINT_DROPPED_MESSAGE_TYPE = kuint16max - 11, + MANAGED_ENDPOINT_BOUND_MESSAGE_TYPE = kuint16max - 10, IMPENDING_SHUTDOWN_MESSAGE_TYPE = kuint16max - 9, BUILD_IDS_MATCH_MESSAGE_TYPE = kuint16max - 8, BUILD_ID_MESSAGE_TYPE = kuint16max - 7, // unused @@ -175,6 +177,7 @@ class ActorLifecycleProxy; class WeakActorLifecycleProxy; class IPDLResolverInner; +class UntypedManagedEndpoint; class IProtocol : public HasResultCodes { public: @@ -183,7 +186,8 @@ Deletion, AncestorDeletion, NormalShutdown, - AbnormalShutdown + AbnormalShutdown, + ManagedEndpointDropped }; typedef base::ProcessId ProcessId; @@ -246,6 +250,7 @@ IProtocol* Manager() const { return mManager; } ActorLifecycleProxy* GetLifecycleProxy() { return mLifecycleProxy; } + WeakActorLifecycleProxy* GetWeakLifecycleProxy(); Side GetSide() const { return mSide; } bool CanSend() const { return mLinkStatus == LinkStatus::Connected; } @@ -283,6 +288,7 @@ friend class IToplevelProtocol; friend class ActorLifecycleProxy; friend class IPDLResolverInner; + friend class UntypedManagedEndpoint; void SetId(int32_t aId); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/ipc/ipdl/ipdl/lower.py firefox-trunk-95.0~a1~hg20211020r596404/ipc/ipdl/ipdl/lower.py --- firefox-trunk-95.0~a1~hg20211017r596111/ipc/ipdl/ipdl/lower.py 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/ipc/ipdl/ipdl/lower.py 2021-10-20 19:28:22.000000000 +0000 @@ -467,6 +467,7 @@ NormalShutdown = ExprVar("NormalShutdown") AbnormalShutdown = ExprVar("AbnormalShutdown") FailedConstructor = ExprVar("FailedConstructor") + ManagedEndpointDropped = ExprVar("ManagedEndpointDropped") class _ResponseRejectReason: @@ -4004,6 +4005,40 @@ if toplevel.isInterrupt(): self.interruptSwitch = StmtSwitch(msgtype) + # Add a handler for the MANAGED_ENDPOINT_BOUND and + # MANAGED_ENDPOINT_DROPPED message types for managed actors. + if not ptype.isToplevel(): + clearawaitingmanagedendpointbind = """ + if (!mAwaitingManagedEndpointBind) { + NS_WARNING("Unexpected managed endpoint lifecycle message after actor bound!"); + return MsgNotAllowed; + } + mAwaitingManagedEndpointBind = false; + """ + self.asyncSwitch.addcase( + CaseLabel("MANAGED_ENDPOINT_BOUND_MESSAGE_TYPE"), + StmtBlock( + [ + StmtCode(clearawaitingmanagedendpointbind), + StmtReturn(_Result.Processed), + ] + ), + ) + self.asyncSwitch.addcase( + CaseLabel("MANAGED_ENDPOINT_DROPPED_MESSAGE_TYPE"), + StmtBlock( + [ + StmtCode(clearawaitingmanagedendpointbind), + *self.destroyActor( + None, + ExprVar.THIS, + why=_DestroyReason.ManagedEndpointDropped, + ), + StmtReturn(_Result.Processed), + ] + ), + ) + # implement Send*() methods and add dispatcher cases to # message switch()es for md in p.messageDecls: @@ -4199,6 +4234,17 @@ # or we're toplevel self.cls.addstmts([clearsubtree, Whitespace.NL]) + if not ptype.isToplevel(): + self.cls.addstmts( + [ + StmtDecl( + Decl(Type.BOOL, "mAwaitingManagedEndpointBind"), + init=ExprLiteral.FALSE, + ), + Whitespace.NL, + ] + ) + for managed in ptype.manages: self.cls.addstmts( [ @@ -4232,7 +4278,11 @@ openmeth.addcode( """ $*{bind} - return ${thereEp}(mozilla::ipc::PrivateIPDLInterface(), aActor->Id()); + // Mark our actor as awaiting the other side to be bound. This will + // be cleared when a `MANAGED_ENDPOINT_{DROPPED,BOUND}` message is + // received. + aActor->mAwaitingManagedEndpointBind = true; + return ${thereEp}(mozilla::ipc::PrivateIPDLInterface(), aActor); """, bind=self.bindManagedActor(actor, errfn=ExprCall(ExprVar(thereEp))), thereEp=thereEp, @@ -4251,13 +4301,9 @@ ) bindmeth.addcode( """ - MOZ_RELEASE_ASSERT(aEndpoint.ActorId(), "Invalid Endpoint!"); - $*{bind} - return true; + return aEndpoint.Bind(mozilla::ipc::PrivateIPDLInterface(), aActor, this, ${container}); """, - bind=self.bindManagedActor( - actor, errfn=ExprLiteral.FALSE, idexpr=ExprCode("*aEndpoint.ActorId()") - ), + container=self.protocol.managedVar(managed, self.side), ) self.cls.addstmts([openmeth, bindmeth, Whitespace.NL]) @@ -4749,7 +4795,7 @@ return method def destroyActor(self, md, actorexpr, why=_DestroyReason.Deletion): - if md.decl.type.isCtor(): + if md and md.decl.type.isCtor(): destroyedType = md.decl.type.constructedType() else: destroyedType = self.protocol.decl.type @@ -4757,11 +4803,11 @@ return [ StmtCode( """ - IProtocol* mgr = ${actor}->Manager(); - ${actor}->DestroySubtree(${why}); - ${actor}->ClearSubtree(); - mgr->RemoveManagee(${protoId}, ${actor}); - """, + IProtocol* mgr = ${actor}->Manager(); + ${actor}->DestroySubtree(${why}); + ${actor}->ClearSubtree(); + mgr->RemoveManagee(${protoId}, ${actor}); + """, actor=actorexpr, why=why, protoId=_protocolId(destroyedType), diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/examples/jorendb.js firefox-trunk-95.0~a1~hg20211020r596404/js/examples/jorendb.js --- firefox-trunk-95.0~a1~hg20211017r596111/js/examples/jorendb.js 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/examples/jorendb.js 2021-10-20 19:28:23.000000000 +0000 @@ -79,15 +79,17 @@ if (!style.pretty || (typeof dv !== 'object')) return [dvrepr, undefined]; + const exec = debuggeeGlobalWrapper.executeInGlobalWithBindings.bind(debuggeeGlobalWrapper); + if (dv.class == "Error") { - let errval = debuggeeGlobalWrapper.executeInGlobalWithBindings("$$.toString()", debuggeeValues); + let errval = exec("$$.toString()", debuggeeValues); return [dvrepr, errval.return]; } if (style.brief) return [dvrepr, JSON.stringify(summaryObject(dv), null, 4)]; - let str = debuggeeGlobalWrapper.executeInGlobalWithBindings("JSON.stringify(v, null, 4)", {v: dv}); + let str = exec("JSON.stringify(v, null, 4)", {v: dv}); if ('throw' in str) { if (style.noerror) return [dvrepr, undefined]; @@ -226,9 +228,11 @@ // Rerun the program (reloading it from the file) function runCommand(args) { - print("Restarting program"); + print(`Restarting program (${args})`); if (args) activeTask.scriptArgs = parseArgs(args); + else + activeTask.scriptArgs = [...actualScriptArgs]; rerun = true; for (var f = topFrame; f; f = f.older) { if (f.older) { @@ -847,7 +851,7 @@ print("jorendb: actualScriptArgs = " + actualScriptArgs); for (var task of todo) { - task['scriptArgs'] = actualScriptArgs; + task['scriptArgs'] = [...actualScriptArgs]; } // Always drop into a repl at the end. Especially if the main script throws an diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/moz.configure firefox-trunk-95.0~a1~hg20211020r596404/js/moz.configure --- firefox-trunk-95.0~a1~hg20211017r596111/js/moz.configure 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/moz.configure 2021-10-20 19:28:22.000000000 +0000 @@ -898,17 +898,44 @@ # Support for WebAssembly Memory64. # =========================== + +@depends(milestone.is_nightly, "--enable-cranelift", "--enable-simulator", target) +def default_wasm_memory64(is_nightly, cranelift, simulator, target): + if cranelift: + return + + if target.cpu == "mips32" or target.cpu == "mips64": + return + + if simulator and (simulator[0] == "mips32" or simulator[0] == "mips64"): + return + + if is_nightly: + return True + + option( "--enable-wasm-memory64", - default=False, + default=default_wasm_memory64, help="{Enable|Disable} WebAssembly Memory64", ) -@depends("--enable-wasm-memory64") -def wasm_memory64(value): - if value: - return True +@depends("--enable-wasm-memory64", "--enable-cranelift", "--enable-simulator", target) +def wasm_memory64(value, cranelift, simulator, target): + if not value: + return + + if cranelift: + die("Memory64 is incompatible with Cranelift") + + if target.cpu == "mips32" or target.cpu == "mips64": + die("Memory64 is incompatible with MIPS target") + + if simulator and (simulator[0] == "mips32" or simulator[0] == "mips64"): + die("Memory64 is incompatible with MIPS simulator") + + return True set_config("ENABLE_WASM_MEMORY64", wasm_memory64) diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/public/CompilationAndEvaluation.h firefox-trunk-95.0~a1~hg20211020r596404/js/public/CompilationAndEvaluation.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/public/CompilationAndEvaluation.h 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/public/CompilationAndEvaluation.h 2021-10-20 19:28:22.000000000 +0000 @@ -13,8 +13,8 @@ #include "jstypes.h" // JS_PUBLIC_API -#include "js/CompileOptions.h" // JS::CompileOptions, JS::ReadOnlyCompileOptions -#include "js/RootingAPI.h" // JS::Handle, JS::MutableHandle +#include "js/CompileOptions.h" // JS::CompileOptions, JS::ReadOnlyCompileOptions, JS::InstantiateOptions +#include "js/RootingAPI.h" // JS::Handle, JS::MutableHandle #include "js/Value.h" // JS::Value and specializations of JS::*Handle-related types struct JS_PUBLIC_API JSContext; @@ -247,10 +247,9 @@ * the debug metadata is provided by the UpdateDebugMetadata call. */ extern JS_PUBLIC_API bool UpdateDebugMetadata( - JSContext* cx, Handle script, - const ReadOnlyCompileOptions& options, HandleValue privateValue, - HandleString elementAttributeName, HandleScript introScript, - HandleScript scriptOrModule); + JSContext* cx, Handle script, const InstantiateOptions& options, + HandleValue privateValue, HandleString elementAttributeName, + HandleScript introScript, HandleScript scriptOrModule); // The debugger API exposes an optional "element" property on DebuggerSource // objects. The callback defined here provides that value. SpiderMonkey diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/public/CompileOptions.h firefox-trunk-95.0~a1~hg20211020r596404/js/public/CompileOptions.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/public/CompileOptions.h 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/public/CompileOptions.h 2021-10-20 19:28:22.000000000 +0000 @@ -52,6 +52,7 @@ #ifndef js_CompileOptions_h #define js_CompileOptions_h +#include "mozilla/Assertions.h" // MOZ_ASSERT #include "mozilla/MemoryReporting.h" // mozilla::MallocSizeOf #include // size_t @@ -71,6 +72,8 @@ DisabledByDebugger, }; +class JS_PUBLIC_API InstantiateOptions; + /** * The common base class for the CompileOptions hierarchy. * @@ -114,6 +117,17 @@ // See also SetFilenameValidationCallback. bool skipFilenameValidation_ = false; + bool hideScriptFromDebugger_ = false; + + // If set, this script will be hidden from the debugger. The requirement + // is that once compilation is finished, a call to UpdateDebugMetadata will + // be made, which will update the SSO with the appropiate debug metadata, + // and expose the script to the debugger (if hideScriptFromDebugger_ isn't + // set) + bool deferDebugMetadata_ = false; + + friend class JS_PUBLIC_API InstantiateOptions; + public: // POD options. bool selfHostingMode = false; @@ -123,17 +137,6 @@ bool discardSource = false; bool sourceIsLazy = false; bool allowHTMLComments = true; - bool hideScriptFromDebugger = false; - - // If set, this script will be hidden from the debugger. The requirement - // is that once compilation is finished, a call to UpdateDebugMetadata will - // be made, which will update the SSO with the appropiate debug metadata, - // and expose the script to the debugger (if hideScriptFromDebugger isn't set) - bool deferDebugMetadata = false; - - bool hideFromNewScriptInitial() const { - return deferDebugMetadata || hideScriptFromDebugger; - } bool nonSyntacticScope = false; bool privateClassFields = false; @@ -196,7 +199,6 @@ bool mutedErrors() const { return mutedErrors_; } bool forceFullParse() const { return forceFullParse_; } bool forceStrictMode() const { return forceStrictMode_; } - bool skipFilenameValidation() const { return skipFilenameValidation_; } bool sourcePragmas() const { return sourcePragmas_; } const char* filename() const { return filename_; } const char* introducerFilename() const { return introducerFilename_; } @@ -391,8 +393,13 @@ return *this; } - CompileOptions& setdeferDebugMetadata(bool v = true) { - deferDebugMetadata = v; + CompileOptions& setDeferDebugMetadata(bool v = true) { + deferDebugMetadata_ = v; + return *this; + } + + CompileOptions& setHideScriptFromDebugger(bool v = true) { + hideScriptFromDebugger_ = v; return *this; } @@ -439,6 +446,45 @@ CompileOptions& operator=(const CompileOptions& rhs) = delete; }; +/** + * Subset of CompileOptions fields used while instantiating Stencils. + */ +class JS_PUBLIC_API InstantiateOptions { + public: + bool skipFilenameValidation = false; + bool hideScriptFromDebugger = false; + bool deferDebugMetadata = false; + + InstantiateOptions() = default; + + explicit InstantiateOptions(const ReadOnlyCompileOptions& options) + : skipFilenameValidation(options.skipFilenameValidation_), + hideScriptFromDebugger(options.hideScriptFromDebugger_), + deferDebugMetadata(options.deferDebugMetadata_) {} + + void copyTo(CompileOptions& options) const { + options.skipFilenameValidation_ = skipFilenameValidation; + options.hideScriptFromDebugger_ = hideScriptFromDebugger; + options.deferDebugMetadata_ = deferDebugMetadata; + } + + bool hideFromNewScriptInitial() const { + return deferDebugMetadata || hideScriptFromDebugger; + } + +#ifdef DEBUG + // Assert that all fields have default value. + // + // This can be used when instantiation is performed as separate step than + // compile-to-stencil, and CompileOptions isn't available there. + void assertDefault() const { + MOZ_ASSERT(skipFilenameValidation == false); + MOZ_ASSERT(hideScriptFromDebugger == false); + MOZ_ASSERT(deferDebugMetadata == false); + } +#endif +}; + } // namespace JS #endif /* js_CompileOptions_h */ diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/public/experimental/JSStencil.h firefox-trunk-95.0~a1~hg20211020r596404/js/public/experimental/JSStencil.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/public/experimental/JSStencil.h 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/public/experimental/JSStencil.h 2021-10-20 19:28:22.000000000 +0000 @@ -81,7 +81,7 @@ // Instantiate the Stencil into current Realm and return the JSScript. extern JS_PUBLIC_API JSScript* InstantiateGlobalStencil( - JSContext* cx, const ReadOnlyCompileOptions& options, Stencil* stencil); + JSContext* cx, const InstantiateOptions& options, Stencil* stencil); // Return true if the stencil relies on external data as a result of XDR // decoding. @@ -93,7 +93,7 @@ // Instantiate a module Stencil and return the associated object. Inside the // engine this is a js::ModuleObject. extern JS_PUBLIC_API JSObject* InstantiateModuleStencil( - JSContext* cx, const ReadOnlyCompileOptions& options, Stencil* stencil); + JSContext* cx, const InstantiateOptions& options, Stencil* stencil); // Serialize the Stencil into the transcode buffer. extern JS_PUBLIC_API TranscodeResult diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/public/friend/ErrorNumbers.msg firefox-trunk-95.0~a1~hg20211020r596404/js/public/friend/ErrorNumbers.msg --- firefox-trunk-95.0~a1~hg20211017r596111/js/public/friend/ErrorNumbers.msg 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/public/friend/ErrorNumbers.msg 2021-10-20 19:28:22.000000000 +0000 @@ -583,7 +583,7 @@ // Intl MSG_DEF(JSMSG_DATE_NOT_FINITE, 2, JSEXN_RANGEERR, "date value is not finite in {0}.{1}()") -MSG_DEF(JSMSG_DUPLICATE_VARIANT_SUBTAG, 1, JSEXN_RANGEERR, "duplicate variant subtag: {0}") +MSG_DEF(JSMSG_DUPLICATE_VARIANT_SUBTAG, 0, JSEXN_RANGEERR, "duplicate variant subtag") MSG_DEF(JSMSG_INTERNAL_INTL_ERROR, 0, JSEXN_ERR, "internal error while computing Intl data") MSG_DEF(JSMSG_INVALID_CURRENCY_CODE, 1, JSEXN_RANGEERR, "invalid currency code in NumberFormat(): {0}") MSG_DEF(JSMSG_INVALID_UNIT_IDENTIFIER, 1, JSEXN_RANGEERR, "invalid unit identifier in NumberFormat(): {0}") diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/public/GCHashTable.h firefox-trunk-95.0~a1~hg20211020r596404/js/public/GCHashTable.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/public/GCHashTable.h 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/public/GCHashTable.h 2021-10-20 19:28:23.000000000 +0000 @@ -78,15 +78,8 @@ } } - bool needsSweep() const { return !this->empty(); } - void sweep() { - typename Base::Enum e(*this); - sweepEntries(e); - } - - void sweepEntries(typename Base::Enum& e) { - for (; !e.empty(); e.popFront()) { + for (typename Base::Enum e(*this); !e.empty(); e.popFront()) { if (MapSweepPolicy::needsSweep(&e.front().mutableKey(), &e.front().value())) { e.removeFront(); @@ -94,7 +87,13 @@ } } - void traceWeak(JSTracer* trc) { + bool traceWeak(JSTracer* trc) { + typename Base::Enum e(*this); + traceWeakEntries(trc, e); + return !this->empty(); + } + + void traceWeakEntries(JSTracer* trc, typename Base::Enum& e) { for (typename Base::Enum e(*this); !e.empty(); e.popFront()) { if (!MapSweepPolicy::traceWeak(trc, &e.front().mutableKey(), &e.front().value())) { @@ -151,7 +150,7 @@ } } - void traceWeak(JSTracer* trc) { + bool traceWeak(JSTracer* trc) { for (typename Base::Enum e(*this); !e.empty(); e.popFront()) { Key key(e.front().key()); if (!MapSweepPolicy::traceWeak(trc, &key, &e.front().value())) { @@ -160,6 +159,7 @@ e.rekeyFront(key); } } + return !this->empty(); } // GCRekeyableHashMap is movable @@ -277,23 +277,14 @@ } } - bool needsSweep() const { return !this->empty(); } - - void sweep() { + bool traceWeak(JSTracer* trc) { typename Base::Enum e(*this); - sweepEntries(e); + traceWeakEntries(trc, e); + return !this->empty(); } - void sweepEntries(typename Base::Enum& e) { + void traceWeakEntries(JSTracer* trc, typename Base::Enum& e) { for (; !e.empty(); e.popFront()) { - if (GCPolicy::needsSweep(&e.mutableFront())) { - e.removeFront(); - } - } - } - - void traceWeak(JSTracer* trc) { - for (typename Base::Enum e(*this); !e.empty(); e.popFront()) { if (!GCPolicy::traceWeak(trc, &e.mutableFront())) { e.removeFront(); } @@ -408,35 +399,31 @@ template class WeakCache> - : protected detail::WeakCacheBase { + final : protected detail::WeakCacheBase { using Map = GCHashMap; using Self = WeakCache; Map map; - bool needsBarrier; + JSTracer* barrierTracer = nullptr; public: template explicit WeakCache(Zone* zone, Args&&... args) - : WeakCacheBase(zone), - map(std::forward(args)...), - needsBarrier(false) {} + : WeakCacheBase(zone), map(std::forward(args)...) {} template explicit WeakCache(JSRuntime* rt, Args&&... args) - : WeakCacheBase(rt), - map(std::forward(args)...), - needsBarrier(false) {} - ~WeakCache() { MOZ_ASSERT(!needsBarrier); } + : WeakCacheBase(rt), map(std::forward(args)...) {} + ~WeakCache() { MOZ_ASSERT(!barrierTracer); } - bool needsSweep() override { return map.needsSweep(); } + bool empty() override { return map.empty(); } - size_t sweep(js::gc::StoreBuffer* sbToLock) override { + size_t traceWeak(JSTracer* trc, js::gc::StoreBuffer* sbToLock) override { size_t steps = map.count(); // Create an Enum and sweep the table entries. mozilla::Maybe e; e.emplace(map); - map.sweepEntries(e.ref()); + map.traceWeakEntries(trc, e.ref()); // Potentially take a lock while the Enum's destructor is called as this can // rehash/resize the table and access the store buffer. @@ -449,24 +436,26 @@ return steps; } - bool setNeedsIncrementalBarrier(bool needs) override { - MOZ_ASSERT(needsBarrier != needs); - needsBarrier = needs; + bool setIncrementalBarrierTracer(JSTracer* trc) override { + MOZ_ASSERT(bool(barrierTracer) != bool(trc)); + barrierTracer = trc; return true; } - bool needsIncrementalBarrier() const override { return needsBarrier; } + bool needsIncrementalBarrier() const override { return barrierTracer; } private: using Entry = typename Map::Entry; - static bool entryNeedsSweep(const Entry& prior) { + static bool entryNeedsSweep(JSTracer* barrierTracer, const Entry& prior) { Key key(prior.key()); Value value(prior.value()); - bool result = MapSweepPolicy::needsSweep(&key, &value); - MOZ_ASSERT(prior.key() == key); // We shouldn't update here. - MOZ_ASSERT(prior.value() == value); // We shouldn't update here. - return result; + bool needsSweep = !MapSweepPolicy::traceWeak(barrierTracer, &key, &value); + MOZ_ASSERT_IF(!needsSweep, + prior.key() == key); // We shouldn't update here. + MOZ_ASSERT_IF(!needsSweep, + prior.value() == value); // We shouldn't update here. + return needsSweep; } public: @@ -474,8 +463,11 @@ using Ptr = typename Map::Ptr; using AddPtr = typename Map::AddPtr; + // Iterator over the whole collection. struct Range { - explicit Range(const typename Map::Range& r) : range(r) { settle(); } + explicit Range(Self& self) : cache(self), range(self.map.all()) { + settle(); + } Range() = default; bool empty() const { return range.empty(); } @@ -487,11 +479,14 @@ } private: + Self& cache; typename Map::Range range; void settle() { - while (!empty() && entryNeedsSweep(front())) { - popFront(); + if (JSTracer* trc = cache.barrierTracer) { + while (!empty() && entryNeedsSweep(trc, front())) { + popFront(); + } } } }; @@ -500,13 +495,13 @@ explicit Enum(Self& cache) : Map::Enum(cache.map) { // This operation is not allowed while barriers are in place as we // may also need to enumerate the set for sweeping. - MOZ_ASSERT(!cache.needsBarrier); + MOZ_ASSERT(!cache.barrierTracer); } }; Ptr lookup(const Lookup& l) const { Ptr ptr = map.lookup(l); - if (needsBarrier && ptr && entryNeedsSweep(*ptr)) { + if (barrierTracer && ptr && entryNeedsSweep(barrierTracer, *ptr)) { const_cast(map).remove(ptr); return Ptr(); } @@ -515,20 +510,20 @@ AddPtr lookupForAdd(const Lookup& l) { AddPtr ptr = map.lookupForAdd(l); - if (needsBarrier && ptr && entryNeedsSweep(*ptr)) { + if (barrierTracer && ptr && entryNeedsSweep(barrierTracer, *ptr)) { const_cast(map).remove(ptr); return map.lookupForAdd(l); } return ptr; } - Range all() const { return Range(map.all()); } + Range all() const { return Range(*const_cast(this)); } bool empty() const { // This operation is not currently allowed while barriers are in place // as it would require iterating the map and the caller expects a // constant time operation. - MOZ_ASSERT(!needsBarrier); + MOZ_ASSERT(!barrierTracer); return map.empty(); } @@ -536,7 +531,7 @@ // This operation is not currently allowed while barriers are in place // as it would require iterating the set and the caller expects a // constant time operation. - MOZ_ASSERT(!needsBarrier); + MOZ_ASSERT(!barrierTracer); return map.count(); } @@ -554,14 +549,14 @@ void clear() { // This operation is not currently allowed while barriers are in place // since it doesn't make sense to clear a cache while it is being swept. - MOZ_ASSERT(!needsBarrier); + MOZ_ASSERT(!barrierTracer); map.clear(); } void clearAndCompact() { // This operation is not currently allowed while barriers are in place // since it doesn't make sense to clear a cache while it is being swept. - MOZ_ASSERT(!needsBarrier); + MOZ_ASSERT(!barrierTracer); map.clearAndCompact(); } @@ -604,36 +599,32 @@ // Specialize WeakCache for GCHashSet to provide a barriered set that does not // need to be swept immediately. template -class WeakCache> +class WeakCache> final : protected detail::WeakCacheBase { using Set = GCHashSet; using Self = WeakCache; Set set; - bool needsBarrier; + JSTracer* barrierTracer = nullptr; public: using Entry = typename Set::Entry; template explicit WeakCache(Zone* zone, Args&&... args) - : WeakCacheBase(zone), - set(std::forward(args)...), - needsBarrier(false) {} + : WeakCacheBase(zone), set(std::forward(args)...) {} template explicit WeakCache(JSRuntime* rt, Args&&... args) - : WeakCacheBase(rt), - set(std::forward(args)...), - needsBarrier(false) {} + : WeakCacheBase(rt), set(std::forward(args)...) {} - size_t sweep(js::gc::StoreBuffer* sbToLock) override { + size_t traceWeak(JSTracer* trc, js::gc::StoreBuffer* sbToLock) override { size_t steps = set.count(); // Create an Enum and sweep the table entries. It's not necessary to take // the store buffer lock yet. mozilla::Maybe e; e.emplace(set); - set.sweepEntries(e.ref()); + set.traceWeakEntries(trc, e.ref()); // Destroy the Enum, potentially rehashing or resizing the table. Since this // can access the store buffer, we need to take a lock for this if we're @@ -647,22 +638,22 @@ return steps; } - bool needsSweep() override { return set.needsSweep(); } + bool empty() override { return set.empty(); } - bool setNeedsIncrementalBarrier(bool needs) override { - MOZ_ASSERT(needsBarrier != needs); - needsBarrier = needs; + bool setIncrementalBarrierTracer(JSTracer* trc) override { + MOZ_ASSERT(bool(barrierTracer) != bool(trc)); + barrierTracer = trc; return true; } - bool needsIncrementalBarrier() const override { return needsBarrier; } + bool needsIncrementalBarrier() const override { return barrierTracer; } private: - static bool entryNeedsSweep(const Entry& prior) { + static bool entryNeedsSweep(JSTracer* barrierTracer, const Entry& prior) { Entry entry(prior); - bool result = GCPolicy::needsSweep(&entry); - MOZ_ASSERT(prior == entry); // We shouldn't update here. - return result; + bool needsSweep = !GCPolicy::traceWeak(barrierTracer, &entry); + MOZ_ASSERT_IF(!needsSweep, prior == entry); // We shouldn't update here. + return needsSweep; } public: @@ -670,8 +661,11 @@ using Ptr = typename Set::Ptr; using AddPtr = typename Set::AddPtr; + // Iterator over the whole collection. struct Range { - explicit Range(const typename Set::Range& r) : range(r) { settle(); } + explicit Range(Self& self) : cache(self), range(self.set.all()) { + settle(); + } Range() = default; bool empty() const { return range.empty(); } @@ -683,11 +677,14 @@ } private: + Self& cache; typename Set::Range range; void settle() { - while (!empty() && entryNeedsSweep(front())) { - popFront(); + if (JSTracer* trc = cache.barrierTracer) { + while (!empty() && entryNeedsSweep(trc, front())) { + popFront(); + } } } }; @@ -696,13 +693,13 @@ explicit Enum(Self& cache) : Set::Enum(cache.set) { // This operation is not allowed while barriers are in place as we // may also need to enumerate the set for sweeping. - MOZ_ASSERT(!cache.needsBarrier); + MOZ_ASSERT(!cache.barrierTracer); } }; Ptr lookup(const Lookup& l) const { Ptr ptr = set.lookup(l); - if (needsBarrier && ptr && entryNeedsSweep(*ptr)) { + if (barrierTracer && ptr && entryNeedsSweep(barrierTracer, *ptr)) { const_cast(set).remove(ptr); return Ptr(); } @@ -711,20 +708,20 @@ AddPtr lookupForAdd(const Lookup& l) { AddPtr ptr = set.lookupForAdd(l); - if (needsBarrier && ptr && entryNeedsSweep(*ptr)) { + if (barrierTracer && ptr && entryNeedsSweep(barrierTracer, *ptr)) { const_cast(set).remove(ptr); return set.lookupForAdd(l); } return ptr; } - Range all() const { return Range(set.all()); } + Range all() const { return Range(*const_cast(this)); } bool empty() const { // This operation is not currently allowed while barriers are in place // as it would require iterating the set and the caller expects a // constant time operation. - MOZ_ASSERT(!needsBarrier); + MOZ_ASSERT(!barrierTracer); return set.empty(); } @@ -732,7 +729,7 @@ // This operation is not currently allowed while barriers are in place // as it would require iterating the set and the caller expects a // constant time operation. - MOZ_ASSERT(!needsBarrier); + MOZ_ASSERT(!barrierTracer); return set.count(); } @@ -750,14 +747,14 @@ void clear() { // This operation is not currently allowed while barriers are in place // since it doesn't make sense to clear a cache while it is being swept. - MOZ_ASSERT(!needsBarrier); + MOZ_ASSERT(!barrierTracer); set.clear(); } void clearAndCompact() { // This operation is not currently allowed while barriers are in place // since it doesn't make sense to clear a cache while it is being swept. - MOZ_ASSERT(!needsBarrier); + MOZ_ASSERT(!barrierTracer); set.clearAndCompact(); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/public/GCVector.h firefox-trunk-95.0~a1~hg20211020r596404/js/public/GCVector.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/public/GCVector.h 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/public/GCVector.h 2021-10-20 19:28:22.000000000 +0000 @@ -182,7 +182,10 @@ return !empty(); } - bool needsSweep() const { return !this->empty(); } + bool needsSweep() { + sweep(); + return this->empty(); + } void sweep() { T* src = begin(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/public/SweepingAPI.h firefox-trunk-95.0~a1~hg20211020r596404/js/public/SweepingAPI.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/public/SweepingAPI.h 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/public/SweepingAPI.h 2021-10-20 19:28:22.000000000 +0000 @@ -60,10 +60,14 @@ WeakCacheBase(WeakCacheBase&& other) = default; virtual ~WeakCacheBase() = default; - virtual size_t sweep(js::gc::StoreBuffer* sbToLock) = 0; - virtual bool needsSweep() = 0; + virtual size_t traceWeak(JSTracer* trc, js::gc::StoreBuffer* sbToLock) = 0; - virtual bool setNeedsIncrementalBarrier(bool needs) { + // Sweeping will be skipped if the cache is empty already. + virtual bool empty() = 0; + + // Enable/disable read barrier during incremental sweeping and set the tracer + // to use. + virtual bool setIncrementalBarrierTracer(JSTracer* trc) { // Derived classes do not support incremental barriers by default. return false; } @@ -96,7 +100,7 @@ const T& get() const { return cache; } T& get() { return cache; } - size_t sweep(js::gc::StoreBuffer* sbToLock) override { + size_t traceWeak(JSTracer* trc, js::gc::StoreBuffer* sbToLock) override { // Take the store buffer lock in case sweeping triggers any generational // post barriers. This is not always required and WeakCache specializations // may delay or skip taking the lock as appropriate. @@ -105,11 +109,11 @@ lock.emplace(sbToLock); } - GCPolicy::sweep(&cache); + GCPolicy::traceWeak(trc, &cache); return 0; } - bool needsSweep() override { return cache.needsSweep(); } + bool empty() override { return cache.empty(); } } JS_HAZ_NON_GC_POINTER; } // namespace JS diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/public/WasmFeatures.h firefox-trunk-95.0~a1~hg20211020r596404/js/public/WasmFeatures.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/public/WasmFeatures.h 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/public/WasmFeatures.h 2021-10-20 19:28:22.000000000 +0000 @@ -139,7 +139,8 @@ EXPERIMENTAL(/* capitalized name */ Memory64, \ /* lower case name */ memory64, \ /* compile predicate */ WASM_MEMORY64_ENABLED, \ - /* compiler predicate */ BaselineAvailable(cx), \ + /* compiler predicate */ BaselineAvailable(cx) || \ + IonAvailable(cx), \ /* flag predicate */ !IsFuzzingIon(cx) && \ !IsFuzzingCranelift(cx), \ /* shell flag */ "memory64", \ diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/Eval.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/Eval.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/Eval.cpp 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/Eval.cpp 2021-10-20 19:28:23.000000000 +0000 @@ -301,7 +301,7 @@ options.setIsRunOnce(true) .setNoScriptRval(false) .setMutedErrors(mutedErrors) - .setdeferDebugMetadata(); + .setDeferDebugMetadata(); RootedScript introScript(cx); @@ -342,8 +342,9 @@ } RootedValue undefValue(cx); - if (!JS::UpdateDebugMetadata(cx, script, options, undefValue, nullptr, - introScript, maybeScript)) { + JS::InstantiateOptions instantiateOptions(options); + if (!JS::UpdateDebugMetadata(cx, script, instantiateOptions, undefValue, + nullptr, introScript, maybeScript)) { return false; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/FinalizationRegistryObject.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/FinalizationRegistryObject.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/FinalizationRegistryObject.cpp 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/FinalizationRegistryObject.cpp 2021-10-20 19:28:22.000000000 +0000 @@ -168,9 +168,9 @@ records()->eraseIfEqual(record); } -inline void FinalizationRegistrationsObject::sweep() { +inline bool FinalizationRegistrationsObject::traceWeak(JSTracer* trc) { MOZ_ASSERT(records()); - return records()->sweep(); + return records()->traceWeak(trc); } /////////////////////////////////////////////////////////////////////////// @@ -276,25 +276,26 @@ /* static */ void FinalizationRegistryObject::trace(JSTracer* trc, JSObject* obj) { - auto registry = &obj->as(); - // Trace the registrations weak map. At most this traces the // FinalizationRegistrationsObject values of the map; the contents of those - // objects are weakly held and are not traced. + // objects are weakly held and are not traced by this method. + + auto* registry = &obj->as(); if (ObjectWeakMap* registrations = registry->registrations()) { registrations->trace(trc); } } -void FinalizationRegistryObject::sweep() { - // Sweep the contents of the registrations weak map's values. +void FinalizationRegistryObject::traceWeak(JSTracer* trc) { + // Trace and update the contents of the registrations weak map's values, which + // are weakly held. + MOZ_ASSERT(registrations()); for (ObjectValueWeakMap::Enum e(registrations()->valueMap()); !e.empty(); e.popFront()) { - auto registrations = + auto* registrations = &e.front().value().toObject().as(); - registrations->sweep(); - if (registrations->isEmpty()) { + if (!registrations->traceWeak(trc)) { e.removeFront(); } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/FinalizationRegistryObject.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/FinalizationRegistryObject.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/FinalizationRegistryObject.h 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/FinalizationRegistryObject.h 2021-10-20 19:28:22.000000000 +0000 @@ -155,7 +155,7 @@ bool append(HandleFinalizationRecordObject record); void remove(HandleFinalizationRecordObject record); - void sweep(); + bool traceWeak(JSTracer* trc); private: static const JSClassOps classOps_; @@ -180,7 +180,7 @@ FinalizationQueueObject* queue() const; ObjectWeakMap* registrations() const; - void sweep(); + void traceWeak(JSTracer* trc); static bool unregisterRecord(FinalizationRecordObject* record); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/intl/Collator.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/intl/Collator.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/intl/Collator.cpp 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/intl/Collator.cpp 2021-10-20 19:28:23.000000000 +0000 @@ -10,10 +10,12 @@ #include "mozilla/Assertions.h" #include "mozilla/intl/Collator.h" +#include "mozilla/intl/Locale.h" #include "mozilla/Span.h" #include "builtin/Array.h" #include "builtin/intl/CommonFunctions.h" +#include "builtin/intl/FormatBuffer.h" #include "builtin/intl/LanguageTag.h" #include "builtin/intl/SharedIntlData.h" #include "gc/FreeOp.h" @@ -239,9 +241,12 @@ } if (StringEqualsLiteral(usage, "search")) { // ICU expects search as a Unicode locale extension on locale. - intl::LanguageTag tag(cx); - if (!intl::LanguageTagParser::parse( - cx, mozilla::MakeStringSpan(locale.get()), tag)) { + mozilla::intl::Locale tag; + if (mozilla::intl::LocaleParser::tryParse( + mozilla::MakeStringSpan(locale.get()), tag) + .isErr()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_INVALID_LANGUAGE_TAG, locale.get()); return nullptr; } @@ -259,7 +264,13 @@ return nullptr; } - locale = tag.toStringZ(cx); + intl::FormatBuffer buffer(cx); + if (auto result = tag.toString(buffer); result.isErr()) { + intl::ReportInternalError(cx, result.unwrapErr()); + return nullptr; + } + + locale = buffer.extractStringZ(); if (!locale) { return nullptr; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/intl/DateTimeFormat.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/intl/DateTimeFormat.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/intl/DateTimeFormat.cpp 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/intl/DateTimeFormat.cpp 2021-10-20 19:28:22.000000000 +0000 @@ -13,6 +13,7 @@ #include "mozilla/intl/Calendar.h" #include "mozilla/intl/DateTimeFormat.h" #include "mozilla/intl/DateTimePatternGenerator.h" +#include "mozilla/intl/Locale.h" #include "mozilla/intl/TimeZone.h" #include "mozilla/Range.h" #include "mozilla/Span.h" @@ -520,14 +521,14 @@ // ICU expects calendar, numberingSystem, and hourCycle as Unicode locale // extensions on locale. - intl::LanguageTag tag(cx); + mozilla::intl::Locale tag; { - JSLinearString* locale = value.toString()->ensureLinear(cx); + RootedLinearString locale(cx, value.toString()->ensureLinear(cx)); if (!locale) { return nullptr; } - if (!intl::LanguageTagParser::parse(cx, locale, tag)) { + if (!intl::ParseLocale(cx, locale, tag)) { return nullptr; } } @@ -595,7 +596,12 @@ return nullptr; } - return tag.toStringZ(cx); + FormatBuffer buffer(cx); + if (auto result = tag.toString(buffer); result.isErr()) { + intl::ReportInternalError(cx, result.unwrapErr()); + return nullptr; + } + return buffer.extractStringZ(); } static bool AssignTextComponent( diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/intl/DisplayNames.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/intl/DisplayNames.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/intl/DisplayNames.cpp 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/intl/DisplayNames.cpp 2021-10-20 19:28:22.000000000 +0000 @@ -10,6 +10,7 @@ #include "mozilla/Assertions.h" #include "mozilla/intl/DateTimePatternGenerator.h" +#include "mozilla/intl/Locale.h" #include "mozilla/Span.h" #include "mozilla/TextUtils.h" @@ -21,9 +22,11 @@ #include "jspubtd.h" #include "builtin/intl/CommonFunctions.h" +#include "builtin/intl/FormatBuffer.h" #include "builtin/intl/LanguageTag.h" #include "builtin/intl/ScopedICUObject.h" #include "builtin/intl/SharedIntlData.h" +#include "builtin/intl/StringAsciiChars.h" #include "builtin/String.h" #include "gc/AllocKind.h" #include "gc/FreeOp.h" @@ -70,6 +73,8 @@ using js::intl::CallICU; using js::intl::IcuLocale; +using mozilla::intl::LocaleParser; + const JSClassOps DisplayNamesObject::classOps_ = {nullptr, /* addProperty */ nullptr, /* delProperty */ nullptr, /* enumerate */ @@ -332,27 +337,54 @@ } } +static bool TryParseBaseName(JSContext* cx, HandleLinearString languageStr, + mozilla::intl::Locale& tag) { + if (StringIsAscii(languageStr)) { + intl::StringAsciiChars chars(languageStr); + if (!chars.init(cx)) { + return false; + } + + if (LocaleParser::tryParseBaseName(chars, tag).isOk()) { + return true; + } + } + + ReportInvalidOptionError(cx, "language", languageStr); + return false; +} + static JSString* GetLanguageDisplayName( JSContext* cx, Handle displayNames, const char* locale, DisplayNamesStyle displayStyle, DisplayNamesLanguageDisplay languageDisplay, DisplayNamesFallback fallback, HandleLinearString languageStr) { - bool ok; - intl::LanguageTag tag(cx); - JS_TRY_VAR_OR_RETURN_NULL( - cx, ok, intl::LanguageTagParser::tryParseBaseName(cx, languageStr, tag)); - if (!ok) { - ReportInvalidOptionError(cx, "language", languageStr); + mozilla::intl::Locale tag; + if (!TryParseBaseName(cx, languageStr, tag)) { return nullptr; } // ICU always canonicalizes the input locale, but since we know that ICU's // canonicalization is incomplete, we need to perform our own canonicalization // to ensure consistent result. - if (!tag.canonicalizeBaseName(cx)) { + if (auto result = tag.canonicalizeBaseName(); result.isErr()) { + if (result.unwrapErr() == + mozilla::intl::Locale::CanonicalizationError::DuplicateVariant) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_DUPLICATE_VARIANT_SUBTAG); + } else { + intl::ReportInternalError(cx); + } + + return nullptr; + } + + intl::FormatBuffer buffer(cx); + if (auto result = tag.toString(buffer); result.isErr()) { + intl::ReportInternalError(cx, result.unwrapErr()); return nullptr; } - UniqueChars languageChars = tag.toStringZ(cx); + UniqueChars languageChars = buffer.extractStringZ(); if (!languageChars) { return nullptr; } @@ -394,22 +426,24 @@ DisplayNamesStyle displayStyle, DisplayNamesFallback fallback, HandleLinearString scriptStr) { - intl::ScriptSubtag script; + mozilla::intl::ScriptSubtag script; if (!intl::ParseStandaloneScriptTag(scriptStr, script)) { ReportInvalidOptionError(cx, "script", scriptStr); return nullptr; } - intl::LanguageTag tag(cx); + mozilla::intl::Locale tag; tag.setLanguage("und"); tag.setScript(script); // ICU always canonicalizes the input locale, but since we know that ICU's // canonicalization is incomplete, we need to perform our own canonicalization // to ensure consistent result. - if (!tag.canonicalizeBaseName(cx)) { + if (tag.canonicalizeBaseName().isErr()) { + intl::ReportInternalError(cx); return nullptr; } + MOZ_ASSERT(tag.script().present()); // |uldn_scriptDisplayName| doesn't use the stand-alone form for script @@ -419,7 +453,13 @@ // ICU bug: https://unicode-org.atlassian.net/browse/ICU-9301 if (displayStyle == DisplayNamesStyle::Long) { // |uloc_getDisplayScript| expects a full locale identifier as its input. - UniqueChars scriptChars = tag.toStringZ(cx); + intl::FormatBuffer buffer(cx); + if (auto result = tag.toString(buffer); result.isErr()) { + intl::ReportInternalError(cx, result.unwrapErr()); + return nullptr; + } + + UniqueChars scriptChars = buffer.extractStringZ(); if (!scriptChars) { return nullptr; } @@ -452,9 +492,9 @@ } // Note: ICU requires the script subtag to be in canonical case. - const intl::ScriptSubtag& canonicalScript = tag.script(); + const mozilla::intl::ScriptSubtag& canonicalScript = tag.script(); - char scriptChars[intl::LanguageTagLimits::ScriptLength + 1] = {}; + char scriptChars[mozilla::intl::LanguageTagLimits::ScriptLength + 1] = {}; std::copy_n(canonicalScript.span().data(), canonicalScript.length(), scriptChars); @@ -495,28 +535,30 @@ DisplayNamesStyle displayStyle, DisplayNamesFallback fallback, HandleLinearString regionStr) { - intl::RegionSubtag region; + mozilla::intl::RegionSubtag region; if (!intl::ParseStandaloneRegionTag(regionStr, region)) { ReportInvalidOptionError(cx, "region", regionStr); return nullptr; } - intl::LanguageTag tag(cx); + mozilla::intl::Locale tag; tag.setLanguage("und"); tag.setRegion(region); // ICU always canonicalizes the input locale, but since we know that ICU's // canonicalization is incomplete, we need to perform our own canonicalization // to ensure consistent result. - if (!tag.canonicalizeBaseName(cx)) { + if (tag.canonicalizeBaseName().isErr()) { + intl::ReportInternalError(cx); return nullptr; } + MOZ_ASSERT(tag.region().present()); // Note: ICU requires the region subtag to be in canonical case. - const intl::RegionSubtag& canonicalRegion = tag.region(); + const mozilla::intl::RegionSubtag& canonicalRegion = tag.region(); - char regionChars[intl::LanguageTagLimits::RegionLength + 1] = {}; + char regionChars[mozilla::intl::LanguageTagLimits::RegionLength + 1] = {}; std::copy_n(canonicalRegion.span().data(), canonicalRegion.length(), regionChars); @@ -613,21 +655,26 @@ DisplayNamesStyle displayStyle, DisplayNamesFallback fallback, HandleLinearString calendarStr) { // Report an error if the input can't be parsed as a Unicode type nonterminal. - if (calendarStr->empty() || - !intl::LanguageTagParser::canParseUnicodeExtensionType(calendarStr)) { + if (calendarStr->empty() || !StringIsAscii(calendarStr)) { ReportInvalidOptionError(cx, "calendar", calendarStr); return nullptr; } - MOZ_ASSERT(StringIsAscii(calendarStr), "Unicode extension types are ASCII"); - UniqueChars calendar = EncodeAscii(cx, calendarStr); if (!calendar) { return nullptr; } + if (LocaleParser::canParseUnicodeExtensionType( + mozilla::Span(calendar.get(), calendarStr->length())) + .isErr()) { + ReportInvalidOptionError(cx, "calendar", calendarStr); + return nullptr; + } + // Convert into canonical case before searching for replacements. - intl::AsciiToLowerCase(calendar.get(), calendarStr->length(), calendar.get()); + mozilla::intl::AsciiToLowerCase(calendar.get(), calendarStr->length(), + calendar.get()); auto key = mozilla::MakeStringSpan("ca"); auto type = mozilla::Span(calendar.get(), calendarStr->length()); @@ -635,7 +682,7 @@ // Search if there's a replacement for the Unicode calendar keyword. const char* canonicalCalendar = calendar.get(); if (const char* replacement = - intl::LanguageTag::replaceUnicodeExtensionType(key, type)) { + mozilla::intl::Locale::replaceUnicodeExtensionType(key, type)) { canonicalCalendar = replacement; } @@ -732,9 +779,10 @@ return names; } - intl::LanguageTag tag(cx); - if (!intl::LanguageTagParser::parse(cx, mozilla::MakeStringSpan(locale), - tag)) { + mozilla::intl::Locale tag; + if (LocaleParser::tryParse(mozilla::MakeStringSpan(locale), tag).isErr()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_INVALID_LANGUAGE_TAG, locale); return nullptr; } @@ -747,7 +795,12 @@ return nullptr; } - UniqueChars localeWithCalendar = tag.toStringZ(cx); + intl::FormatBuffer buffer(cx); + if (auto result = tag.toString(buffer); result.isErr()) { + intl::ReportInternalError(cx, result.unwrapErr()); + return nullptr; + } + UniqueChars localeWithCalendar = buffer.extractStringZ(); if (!localeWithCalendar) { return nullptr; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/intl/FormatBuffer.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/intl/FormatBuffer.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/intl/FormatBuffer.h 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/intl/FormatBuffer.h 2021-10-20 19:28:22.000000000 +0000 @@ -8,7 +8,6 @@ #define builtin_intl_FormatBuffer_h #include "mozilla/Assertions.h" -#include "mozilla/Range.h" #include "mozilla/Span.h" #include @@ -16,6 +15,7 @@ #include "gc/Allocator.h" #include "js/AllocPolicy.h" +#include "js/CharacterEncoding.h" #include "js/TypeDecls.h" #include "js/UniquePtr.h" #include "js/Vector.h" @@ -89,8 +89,7 @@ std::is_same_v) { // Handle the UTF-8 encoding case. return NewStringCopyUTF8N( - cx, mozilla::Range(reinterpret_cast(buffer_.begin()), - buffer_.length())); + cx, JS::UTF8Chars(buffer_.begin(), buffer_.length())); } else { // Handle the UTF-16 encoding case. static_assert(std::is_same_v); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/intl/IntlObject.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/intl/IntlObject.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/intl/IntlObject.cpp 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/intl/IntlObject.cpp 2021-10-20 19:28:22.000000000 +0000 @@ -32,6 +32,7 @@ #include "builtin/intl/PluralRules.h" #include "builtin/intl/RelativeTimeFormat.h" #include "builtin/intl/SharedIntlData.h" +#include "builtin/intl/StringAsciiChars.h" #include "ds/Sort.h" #include "js/CharacterEncoding.h" #include "js/Class.h" @@ -260,29 +261,54 @@ #ifdef DEBUG { - intl::LanguageTag tag(cx); - bool ok; - JS_TRY_VAR_OR_RETURN_FALSE( - cx, ok, intl::LanguageTagParser::tryParse(cx, locale, tag)); - MOZ_ASSERT(ok, "locale is a structurally valid language tag"); + MOZ_ASSERT(StringIsAscii(locale), "language tags are ASCII-only"); + + // |locale| is a structurally valid language tag. + mozilla::intl::Locale tag; + + using ParserError = mozilla::intl::LocaleParser::ParserError; + mozilla::Result parse_result = Ok(); + { + intl::StringAsciiChars chars(locale); + if (!chars.init(cx)) { + return false; + } + + parse_result = mozilla::intl::LocaleParser::tryParse(chars, tag); + } + + if (parse_result.isErr()) { + MOZ_ASSERT(parse_result.unwrapErr() == ParserError::OutOfMemory, + "locale is a structurally valid language tag"); + + intl::ReportInternalError(cx); + return false; + } MOZ_ASSERT(!tag.unicodeExtension(), "locale must contain no Unicode extensions"); - if (!tag.canonicalize(cx)) { + if (auto result = tag.canonicalize(); result.isErr()) { + MOZ_ASSERT( + result.unwrapErr() != + mozilla::intl::Locale::CanonicalizationError::DuplicateVariant); + intl::ReportInternalError(cx); return false; } - JSString* tagStr = tag.toString(cx); - if (!tagStr) { + intl::FormatBuffer buffer(cx); + if (auto result = tag.toString(buffer); result.isErr()) { + intl::ReportInternalError(cx, result.unwrapErr()); return false; } - bool canonical; - if (!EqualStrings(cx, locale, tagStr, &canonical)) { + JSLinearString* tagStr = buffer.toString(cx); + if (!tagStr) { return false; } - MOZ_ASSERT(canonical, "locale is a canonicalized language tag"); + + MOZ_ASSERT(EqualStrings(locale, tagStr), + "locale is a canonicalized language tag"); } #endif @@ -318,39 +344,47 @@ return false; } - intl::LanguageTag tag(cx); - bool ok; - JS_TRY_VAR_OR_RETURN_FALSE( - cx, ok, intl::LanguageTagParser::tryParse(cx, locale, tag)); + mozilla::intl::Locale tag; + bool canParseLocale = false; + if (StringIsAscii(locale)) { + intl::StringAsciiChars chars(locale); + if (!chars.init(cx)) { + return false; + } + + // Tell the analysis the |tag.canonicalize()| method can't GC. + JS::AutoSuppressGCAnalysis nogc; + + canParseLocale = mozilla::intl::LocaleParser::tryParse(chars, tag).isOk() && + tag.canonicalize().isOk(); + } RootedLinearString candidate(cx); - if (!ok) { + if (!canParseLocale) { candidate = NewStringCopyZ(cx, intl::LastDitchLocale()); if (!candidate) { return false; } } else { - if (!tag.canonicalize(cx)) { - return false; - } - // The default locale must be in [[AvailableLocales]], and that list must // not contain any locales with Unicode extension sequences, so remove any // present in the candidate. tag.clearUnicodeExtension(); - JSString* canonical = tag.toString(cx); - if (!canonical) { + intl::FormatBuffer buffer(cx); + if (auto result = tag.toString(buffer); result.isErr()) { + intl::ReportInternalError(cx, result.unwrapErr()); return false; } - candidate = canonical->ensureLinear(cx); + candidate = buffer.toString(cx); if (!candidate) { return false; } - // Certain old-style language tags lack a script code, but in current usage - // they *would* include a script code. Map these over to modern forms. + // Certain old-style language tags lack a script code, but in current + // usage they *would* include a script code. Map these over to modern + // forms. for (const auto& mapping : js::intl::oldStyleLanguageTagMappings) { const char* oldStyle = mapping.oldStyle; const char* modernStyle = mapping.modernStyle; @@ -370,8 +404,8 @@ // - [[AvailableLocales]] is a List [...]. The list must include the value // returned by the DefaultLocale abstract operation (6.2.4), [...]. // - // That implies we must ignore any candidate which isn't supported by all Intl - // service constructors. + // That implies we must ignore any candidate which isn't supported by all + // Intl service constructors. RootedLinearString supportedCollator(cx); JS_TRY_VAR_OR_RETURN_FALSE( @@ -387,8 +421,8 @@ #ifdef DEBUG // Note: We don't test the supported locales of the remaining Intl service - // constructors, because the set of supported locales is exactly equal to the - // set of supported locales of Intl.DateTimeFormat. + // constructors, because the set of supported locales is exactly equal to + // the set of supported locales of Intl.DateTimeFormat. for (auto kind : {SupportedLocaleKind::DisplayNames, SupportedLocaleKind::ListFormat, SupportedLocaleKind::NumberFormat, SupportedLocaleKind::PluralRules, @@ -558,9 +592,10 @@ Rooted list(cx, StringList(cx)); { - // Hazard analysis complains that the mozilla::Result destructor calls a GC - // function, which is unsound when returning an unrooted value. Work around - // this issue by restricting the lifetime of |keywords| to a separate block. + // Hazard analysis complains that the mozilla::Result destructor calls a + // GC function, which is unsound when returning an unrooted value. Work + // around this issue by restricting the lifetime of |keywords| to a + // separate block. auto keywords = mozilla::intl::Calendar::GetBcp47KeywordValuesForLocale(""); if (keywords.isErr()) { intl::ReportInternalError(cx, keywords.unwrapErr()); @@ -599,9 +634,10 @@ Rooted list(cx, StringList(cx)); { - // Hazard analysis complains that the mozilla::Result destructor calls a GC - // function, which is unsound when returning an unrooted value. Work around - // this issue by restricting the lifetime of |keywords| to a separate block. + // Hazard analysis complains that the mozilla::Result destructor calls a + // GC function, which is unsound when returning an unrooted value. Work + // around this issue by restricting the lifetime of |keywords| to a + // separate block. auto keywords = mozilla::intl::Collator::GetBcp47KeywordValues(); if (keywords.isErr()) { intl::ReportInternalError(cx, keywords.unwrapErr()); @@ -617,13 +653,15 @@ // |ucol_getKeywordValues| returns the possible collations for all installed // locales. The root locale is excluded in the list of installed locales, so - // we have to explicitly request the available collations of the root locale. + // we have to explicitly request the available collations of the root + // locale. // // https://unicode-org.atlassian.net/browse/ICU-21641 { - // Hazard analysis complains that the mozilla::Result destructor calls a GC - // function, which is unsound when returning an unrooted value. Work around - // this issue by restricting the lifetime of |keywords| to a separate block. + // Hazard analysis complains that the mozilla::Result destructor calls a + // GC function, which is unsound when returning an unrooted value. Work + // around this issue by restricting the lifetime of |keywords| to a + // separate block. auto keywords = mozilla::intl::Collator::GetBcp47KeywordValuesForLocale(""); if (keywords.isErr()) { intl::ReportInternalError(cx, keywords.unwrapErr()); @@ -677,9 +715,10 @@ Rooted list(cx, StringList(cx)); { - // Hazard analysis complains that the mozilla::Result destructor calls a GC - // function, which is unsound when returning an unrooted value. Work around - // this issue by restricting the lifetime of |keywords| to a separate block. + // Hazard analysis complains that the mozilla::Result destructor calls a + // GC function, which is unsound when returning an unrooted value. Work + // around this issue by restricting the lifetime of |keywords| to a + // separate block. auto currencies = mozilla::intl::Currency::GetISOCurrencies(); if (currencies.isErr()) { intl::ReportInternalError(cx, currencies.unwrapErr()); @@ -723,7 +762,8 @@ * AvailableTimeZones ( ) */ static ArrayObject* AvailableTimeZones(JSContext* cx) { - // Unsorted list of canonical time zone names, possibly containing duplicates. + // Unsorted list of canonical time zone names, possibly containing + // duplicates. Rooted timeZones(cx, StringList(cx)); intl::SharedIntlData& sharedIntlData = cx->runtime()->sharedIntlData.ref(); @@ -740,8 +780,8 @@ // Canonicalize the time zone before adding it to the result array. - // Some time zone names are canonicalized differently by ICU -- handle those - // first. + // Some time zone names are canonicalized differently by ICU -- handle + // those first. ianaTimeZone.set(nullptr); if (!sharedIntlData.tryCanonicalizeTimeZoneConsistentWithIANA( cx, validatedTimeZone, &ianaTimeZone)) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/intl/LanguageTag.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/intl/LanguageTag.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/intl/LanguageTag.cpp 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/intl/LanguageTag.cpp 2021-10-20 19:28:22.000000000 +0000 @@ -6,1587 +6,52 @@ #include "builtin/intl/LanguageTag.h" -#include "mozilla/Assertions.h" -#include "mozilla/DebugOnly.h" -#include "mozilla/MathAlgorithms.h" +#include "mozilla/intl/Locale.h" #include "mozilla/Span.h" -#include "mozilla/TextUtils.h" -#include "mozilla/Variant.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#include "builtin/intl/CommonFunctions.h" -#include "ds/Sort.h" +#include "builtin/intl/StringAsciiChars.h" #include "gc/Tracer.h" -#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* -#include "js/Result.h" +#include "js/CharacterEncoding.h" #include "js/TracingAPI.h" -#include "js/Utility.h" -#include "js/Vector.h" -#include "unicode/uloc.h" -#include "unicode/utypes.h" -#include "util/StringBuffer.h" -#include "util/Text.h" #include "vm/JSContext.h" -#include "vm/Printer.h" -#include "vm/StringType.h" namespace js { namespace intl { -using namespace js::intl::LanguageTagLimits; - -template -bool IsStructurallyValidLanguageTag(mozilla::Span language) { - // Tell the analysis the |std::all_of| function can't GC. - JS::AutoSuppressGCAnalysis nogc; - - // unicode_language_subtag = alpha{2,3} | alpha{5,8}; - size_t length = language.size(); - const CharT* str = language.data(); - return ((2 <= length && length <= 3) || (5 <= length && length <= 8)) && - std::all_of(str, str + length, mozilla::IsAsciiAlpha); -} - -template bool IsStructurallyValidLanguageTag( - mozilla::Span language); -template bool IsStructurallyValidLanguageTag( - mozilla::Span language); -template bool IsStructurallyValidLanguageTag( - mozilla::Span language); - -template -bool IsStructurallyValidScriptTag(mozilla::Span script) { - // Tell the analysis the |std::all_of| function can't GC. - JS::AutoSuppressGCAnalysis nogc; - - // unicode_script_subtag = alpha{4} ; - size_t length = script.size(); - const CharT* str = script.data(); - return length == 4 && - std::all_of(str, str + length, mozilla::IsAsciiAlpha); -} - -template bool IsStructurallyValidScriptTag(mozilla::Span script); -template bool IsStructurallyValidScriptTag( - mozilla::Span script); -template bool IsStructurallyValidScriptTag( - mozilla::Span script); - -template -bool IsStructurallyValidRegionTag(mozilla::Span region) { - // Tell the analysis the |std::all_of| function can't GC. - JS::AutoSuppressGCAnalysis nogc; - - // unicode_region_subtag = (alpha{2} | digit{3}) ; - size_t length = region.size(); - const CharT* str = region.data(); - return (length == 2 && - std::all_of(str, str + length, mozilla::IsAsciiAlpha)) || - (length == 3 && - std::all_of(str, str + length, mozilla::IsAsciiDigit)); -} - -template bool IsStructurallyValidRegionTag(mozilla::Span region); -template bool IsStructurallyValidRegionTag( - mozilla::Span region); -template bool IsStructurallyValidRegionTag( - mozilla::Span region); - -#ifdef DEBUG -bool IsStructurallyValidVariantTag(mozilla::Span variant) { - // unicode_variant_subtag = (alphanum{5,8} | digit alphanum{3}) ; - size_t length = variant.size(); - const char* str = variant.data(); - return ((5 <= length && length <= 8) || - (length == 4 && mozilla::IsAsciiDigit(str[0]))) && - std::all_of(str, str + length, mozilla::IsAsciiAlphanumeric); -} - -bool IsStructurallyValidUnicodeExtensionTag( - mozilla::Span extension) { - return LanguageTagParser::canParseUnicodeExtension(extension); -} - -static bool IsStructurallyValidExtensionTag( - mozilla::Span extension) { - // other_extensions = sep [alphanum-[tTuUxX]] (sep alphanum{2,8})+ ; - // NB: Allow any extension, including Unicode and Transform here, because - // this function is only used for an assertion. - - size_t length = extension.size(); - const char* str = extension.data(); - const char* const end = extension.data() + length; - if (length <= 2) { - return false; - } - if (!mozilla::IsAsciiAlphanumeric(str[0]) || str[0] == 'x' || str[0] == 'X') { - return false; - } - str++; - if (*str++ != '-') { - return false; - } - while (true) { - const char* sep = - reinterpret_cast(memchr(str, '-', end - str)); - size_t len = (sep ? sep : end) - str; - if (len < 2 || len > 8 || - !std::all_of(str, str + len, mozilla::IsAsciiAlphanumeric)) { +[[nodiscard]] bool ParseLocale(JSContext* cx, HandleLinearString str, + mozilla::intl::Locale& result) { + if (StringIsAscii(str)) { + intl::StringAsciiChars chars(str); + if (!chars.init(cx)) { return false; } - if (!sep) { - return true; - } - str = sep + 1; - } -} - -bool IsStructurallyValidPrivateUseTag(mozilla::Span privateUse) { - // pu_extensions = sep [xX] (sep alphanum{1,8})+ ; - size_t length = privateUse.size(); - const char* str = privateUse.data(); - const char* const end = privateUse.data() + length; - if (length <= 2) { - return false; - } - if (str[0] != 'x' && str[0] != 'X') { - return false; - } - str++; - if (*str++ != '-') { - return false; - } - while (true) { - const char* sep = - reinterpret_cast(memchr(str, '-', end - str)); - size_t len = (sep ? sep : end) - str; - if (len == 0 || len > 8 || - !std::all_of(str, str + len, mozilla::IsAsciiAlphanumeric)) { - return false; - } - if (!sep) { + if (mozilla::intl::LocaleParser::tryParse(chars, result).isOk()) { return true; } - str = sep + 1; - } -} -#endif - -ptrdiff_t LanguageTag::unicodeExtensionIndex() const { - // The extension subtags aren't necessarily sorted, so we can't use binary - // search here. - auto p = std::find_if( - extensions().begin(), extensions().end(), - [](const auto& ext) { return ext[0] == 'u' || ext[0] == 'U'; }); - if (p != extensions().end()) { - return std::distance(extensions().begin(), p); - } - return -1; -} - -const char* LanguageTag::unicodeExtension() const { - ptrdiff_t index = unicodeExtensionIndex(); - if (index >= 0) { - return extensions()[index].get(); - } - return nullptr; -} - -bool LanguageTag::setUnicodeExtension(UniqueChars extension) { - MOZ_ASSERT(IsStructurallyValidUnicodeExtensionTag( - mozilla::MakeStringSpan(extension.get()))); - - // Replace the existing Unicode extension subtag or append a new one. - ptrdiff_t index = unicodeExtensionIndex(); - if (index >= 0) { - extensions_[index] = std::move(extension); - return true; - } - return extensions_.append(std::move(extension)); -} - -void LanguageTag::clearUnicodeExtension() { - ptrdiff_t index = unicodeExtensionIndex(); - if (index >= 0) { - extensions_.erase(extensions_.begin() + index); - } -} - -template -static bool SortAlphabetically(JSContext* cx, - Vector& subtags) { - size_t length = subtags.length(); - - // Zero or one element lists are already sorted. - if (length < 2) { - return true; - } - - // Handle two element lists inline. - if (length == 2) { - if (strcmp(subtags[0].get(), subtags[1].get()) > 0) { - subtags[0].swap(subtags[1]); - } - return true; - } - - Vector scratch(cx); - if (!scratch.resizeUninitialized(length * 2)) { - return false; - } - for (size_t i = 0; i < length; i++) { - scratch[i] = subtags[i].release(); - } - - MOZ_ALWAYS_TRUE( - MergeSort(scratch.begin(), length, scratch.begin() + length, - [](const char* a, const char* b, bool* lessOrEqualp) { - *lessOrEqualp = strcmp(a, b) <= 0; - return true; - })); - - for (size_t i = 0; i < length; i++) { - subtags[i] = UniqueChars(scratch[i]); - } - return true; -} - -bool LanguageTag::canonicalizeBaseName(JSContext* cx) { - // Per 6.2.3 CanonicalizeUnicodeLocaleId, the very first step is to - // canonicalize the syntax by normalizing the case and ordering all subtags. - // The canonical syntax form is specified in UTS 35, 3.2.1. - - // Language codes need to be in lower case. "JA" -> "ja" - language_.toLowerCase(); - MOZ_ASSERT(IsStructurallyValidLanguageTag(language().span())); - - // The first character of a script code needs to be capitalized. - // "hans" -> "Hans" - script_.toTitleCase(); - MOZ_ASSERT(script().missing() || - IsStructurallyValidScriptTag(script().span())); - - // Region codes need to be in upper case. "bu" -> "BU" - region_.toUpperCase(); - MOZ_ASSERT(region().missing() || - IsStructurallyValidRegionTag(region().span())); - - // The canonical case for variant subtags is lowercase. - for (UniqueChars& variant : variants_) { - char* variantChars = variant.get(); - size_t variantLength = strlen(variantChars); - AsciiToLowerCase(variantChars, variantLength, variantChars); - - MOZ_ASSERT(IsStructurallyValidVariantTag({variantChars, variantLength})); - } - - // Extensions and privateuse subtags are case normalized in the - // |canonicalizeExtensions| method. - - // The second step in UTS 35, 3.2.1, is to order all subtags. - - if (variants_.length() > 1) { - // 1. Any variants are in alphabetical order. - if (!SortAlphabetically(cx, variants_)) { - return false; - } - - // Reject the Locale identifier if a duplicate variant was found, e.g. - // "en-variant-Variant". - const UniqueChars* duplicate = std::adjacent_find( - variants().begin(), variants().end(), [](const auto& a, const auto& b) { - return strcmp(a.get(), b.get()) == 0; - }); - if (duplicate != variants().end()) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_DUPLICATE_VARIANT_SUBTAG, - duplicate->get()); - return false; - } - } - - // 2. Any extensions are in alphabetical order by their singleton. - // 3. All attributes are sorted in alphabetical order. - // 4. All keywords and tfields are sorted by alphabetical order of their keys, - // within their respective extensions. - // 5. Any type or tfield value "true" is removed. - // - A subsequent call to canonicalizeExtensions() will perform these steps. - - // 6.2.3 CanonicalizeUnicodeLocaleId, step 2 transforms the locale identifier - // into its canonical form per UTS 3.2.1. - - // 1. Use the bcp47 data to replace keys, types, tfields, and tvalues by their - // canonical forms. - // - A subsequent call to canonicalizeExtensions() will perform this step. - - // 2. Replace aliases in the unicode_language_id and tlang (if any). - // - tlang is handled in canonicalizeExtensions(). - - // Replace deprecated language, region, and variant subtags with their - // preferred mappings. - - if (!updateLegacyMappings(cx)) { - return false; - } - - // Replace deprecated language subtags with their preferred values. - if (!languageMapping(language_) && complexLanguageMapping(language_)) { - performComplexLanguageMappings(); - } - - // Replace deprecated script subtags with their preferred values. - if (script().present()) { - scriptMapping(script_); - } - - // Replace deprecated region subtags with their preferred values. - if (region().present()) { - if (!regionMapping(region_) && complexRegionMapping(region_)) { - performComplexRegionMappings(); - } - } - - // Replace deprecated variant subtags with their preferred values. - if (!performVariantMappings(cx)) { - return false; - } - - // No extension replacements are currently present. - // Private use sequences are left as is. - - // 3. Replace aliases in special key values. - // - A subsequent call to canonicalizeExtensions() will perform this step. - - return true; -} - -#ifdef DEBUG -template -static bool IsAsciiLowercaseAlphanumericOrDash( - mozilla::Span span) { - const CharT* ptr = span.data(); - size_t length = span.size(); - return std::all_of(ptr, ptr + length, [](auto c) { - return mozilla::IsAsciiLowercaseAlpha(c) || mozilla::IsAsciiDigit(c) || - c == '-'; - }); -} -#endif - -bool LanguageTag::canonicalizeExtensions(JSContext* cx) { - // The canonical case for all extension subtags is lowercase. - for (UniqueChars& extension : extensions_) { - char* extensionChars = extension.get(); - size_t extensionLength = strlen(extensionChars); - AsciiToLowerCase(extensionChars, extensionLength, extensionChars); - - MOZ_ASSERT( - IsStructurallyValidExtensionTag({extensionChars, extensionLength})); - } - - // Any extensions are in alphabetical order by their singleton. - // "u-ca-chinese-t-zh-latn" -> "t-zh-latn-u-ca-chinese" - if (!SortAlphabetically(cx, extensions_)) { - return false; - } - - for (UniqueChars& extension : extensions_) { - if (extension[0] == 'u') { - if (!canonicalizeUnicodeExtension(cx, extension)) { - return false; - } - } else if (extension[0] == 't') { - if (!canonicalizeTransformExtension(cx, extension)) { - return false; - } - } - - MOZ_ASSERT(IsAsciiLowercaseAlphanumericOrDash( - mozilla::MakeStringSpan(extension.get()))); - } - - // The canonical case for privateuse subtags is lowercase. - if (char* privateuse = privateuse_.get()) { - size_t privateuseLength = strlen(privateuse); - AsciiToLowerCase(privateuse, privateuseLength, privateuse); - - MOZ_ASSERT( - IsStructurallyValidPrivateUseTag({privateuse, privateuseLength})); - } - return true; -} - -/** - * CanonicalizeUnicodeExtension( attributes, keywords ) - * - * Canonical syntax per - * : - * - * - All attributes and keywords are in lowercase. - * - Note: The parser already converted keywords to lowercase. - * - All attributes are sorted in alphabetical order. - * - All keywords are sorted by alphabetical order of their keys. - * - Any type value "true" is removed. - * - * Canonical form: - * - All keys and types use the canonical form (from the name attribute; - * see Section 3.6.4 U Extension Data Files). - */ -bool LanguageTag::canonicalizeUnicodeExtension( - JSContext* cx, JS::UniqueChars& unicodeExtension) { - const char* const extension = unicodeExtension.get(); - MOZ_ASSERT(extension[0] == 'u'); - MOZ_ASSERT(extension[1] == '-'); - MOZ_ASSERT( - IsStructurallyValidExtensionTag(mozilla::MakeStringSpan(extension))); - - size_t length = strlen(extension); - - LanguageTagParser::AttributesVector attributes(cx); - LanguageTagParser::KeywordsVector keywords(cx); - - using Attribute = LanguageTagParser::AttributesVector::ElementType; - using Keyword = LanguageTagParser::KeywordsVector::ElementType; - - mozilla::DebugOnly ok; - JS_TRY_VAR_OR_RETURN_FALSE( - cx, ok, - LanguageTagParser::parseUnicodeExtension( - cx, mozilla::Span(extension, length), attributes, keywords)); - MOZ_ASSERT(ok, "unexpected invalid Unicode extension subtag"); - - auto attributesLessOrEqual = [extension](const Attribute& a, - const Attribute& b) { - const char* astr = a.begin(extension); - const char* bstr = b.begin(extension); - size_t alen = a.length(); - size_t blen = b.length(); - - if (int r = - std::char_traits::compare(astr, bstr, std::min(alen, blen))) { - return r < 0; - } - return alen <= blen; - }; - - // All attributes are sorted in alphabetical order. - size_t attributesLength = attributes.length(); - if (attributesLength > 1) { - if (!attributes.growByUninitialized(attributesLength)) { - return false; - } - - MOZ_ALWAYS_TRUE( - MergeSort(attributes.begin(), attributesLength, - attributes.begin() + attributesLength, - [&](const auto& a, const auto& b, bool* lessOrEqualp) { - *lessOrEqualp = attributesLessOrEqual(a, b); - return true; - })); - - attributes.shrinkBy(attributesLength); - } - - auto keywordsLessOrEqual = [extension](const Keyword& a, const Keyword& b) { - const char* astr = a.begin(extension); - const char* bstr = b.begin(extension); - MOZ_ASSERT(a.length() >= UnicodeKeyLength); - MOZ_ASSERT(b.length() >= UnicodeKeyLength); - - return std::char_traits::compare(astr, bstr, UnicodeKeyLength) <= 0; - }; - - // All keywords are sorted by alphabetical order of keys. - size_t keywordsLength = keywords.length(); - if (keywordsLength > 1) { - if (!keywords.growByUninitialized(keywordsLength)) { - return false; - } - - // Using merge sort, being a stable sort algorithm, guarantees that two - // keywords using the same key are never reordered. That means for example - // when we have the input "u-nu-thai-kf-false-nu-latn", we are guaranteed to - // get the result "u-kf-false-nu-thai-nu-latn", i.e. "nu-thai" still occurs - // before "nu-latn". - // This is required so that deduplication below preserves the first keyword - // for a given key and discards the rest. - MOZ_ALWAYS_TRUE(MergeSort( - keywords.begin(), keywordsLength, keywords.begin() + keywordsLength, - [&](const auto& a, const auto& b, bool* lessOrEqualp) { - *lessOrEqualp = keywordsLessOrEqual(a, b); - return true; - })); - - keywords.shrinkBy(keywordsLength); - } - - Vector sb(cx); - if (!sb.append('u')) { - return false; - } - - // Append all Unicode extension attributes. - for (size_t i = 0; i < attributes.length(); i++) { - const auto& attribute = attributes[i]; - - // Skip duplicate attributes. - if (i > 0) { - const auto& lastAttribute = attributes[i - 1]; - if (attribute.length() == lastAttribute.length() && - std::char_traits::compare(attribute.begin(extension), - lastAttribute.begin(extension), - attribute.length()) == 0) { - continue; - } - MOZ_ASSERT(!attributesLessOrEqual(attribute, lastAttribute)); - } - - if (!sb.append('-')) { - return false; - } - if (!sb.append(attribute.begin(extension), attribute.length())) { - return false; - } - } - - static constexpr size_t UnicodeKeyWithSepLength = UnicodeKeyLength + 1; - - using StringSpan = mozilla::Span; - - static auto isTrue = [](StringSpan type) { - static constexpr char True[] = "true"; - constexpr size_t TrueLength = std::char_traits::length(True); - return type.size() == TrueLength && - std::char_traits::compare(type.data(), True, TrueLength) == 0; - }; - - auto appendKey = [&sb, extension](const Keyword& keyword) { - MOZ_ASSERT(keyword.length() == UnicodeKeyLength); - return sb.append(keyword.begin(extension), UnicodeKeyLength); - }; - - auto appendKeyword = [&sb, extension](const Keyword& keyword, - StringSpan type) { - MOZ_ASSERT(keyword.length() > UnicodeKeyLength); - - // Elide the Unicode extension type "true". - if (isTrue(type)) { - return sb.append(keyword.begin(extension), UnicodeKeyLength); - } - // Otherwise append the complete Unicode extension keyword. - return sb.append(keyword.begin(extension), keyword.length()); - }; - - auto appendReplacement = [&sb, extension](const Keyword& keyword, - StringSpan replacement) { - MOZ_ASSERT(keyword.length() > UnicodeKeyLength); - - // Elide the type "true" if present in the replacement. - if (isTrue(replacement)) { - return sb.append(keyword.begin(extension), UnicodeKeyLength); - } - // Otherwise append the Unicode key (including the separator) and the - // replaced type. - return sb.append(keyword.begin(extension), UnicodeKeyWithSepLength) && - sb.append(replacement.data(), replacement.size()); - }; - - // Append all Unicode extension keywords. - for (size_t i = 0; i < keywords.length(); i++) { - const auto& keyword = keywords[i]; - - // Skip duplicate keywords. - if (i > 0) { - const auto& lastKeyword = keywords[i - 1]; - if (std::char_traits::compare(keyword.begin(extension), - lastKeyword.begin(extension), - UnicodeKeyLength) == 0) { - continue; - } - MOZ_ASSERT(!keywordsLessOrEqual(keyword, lastKeyword)); - } - - if (!sb.append('-')) { - return false; - } - - if (keyword.length() == UnicodeKeyLength) { - // Keyword without type value. - if (!appendKey(keyword)) { - return false; - } - } else { - StringSpan key(keyword.begin(extension), UnicodeKeyLength); - StringSpan type(keyword.begin(extension) + UnicodeKeyWithSepLength, - keyword.length() - UnicodeKeyWithSepLength); - - // Search if there's a replacement for the current Unicode keyword. - if (const char* replacement = replaceUnicodeExtensionType(key, type)) { - if (!appendReplacement(keyword, mozilla::MakeStringSpan(replacement))) { - return false; - } - } else { - if (!appendKeyword(keyword, type)) { - return false; - } - } - } - } - - // We can keep the previous extension when canonicalization didn't modify it. - if (sb.length() != length || - std::char_traits::compare(sb.begin(), extension, length) != 0) { - // Null-terminate the new string and replace the previous extension. - if (!sb.append('\0')) { - return false; - } - UniqueChars canonical(sb.extractOrCopyRawBuffer()); - if (!canonical) { - return false; - } - unicodeExtension = std::move(canonical); - } - - return true; -} - -template -static bool LanguageTagToString(JSContext* cx, const LanguageTag& tag, - Buffer& sb) { - auto appendSubtag = [&sb](const auto& subtag) { - auto span = subtag.span(); - MOZ_ASSERT(!span.empty()); - return sb.append(span.data(), span.size()); - }; - - auto appendSubtagZ = [&sb](const char* subtag) { - MOZ_ASSERT(strlen(subtag) > 0); - return sb.append(subtag, strlen(subtag)); - }; - - auto appendSubtagsZ = [&sb, &appendSubtagZ](const auto& subtags) { - for (const auto& subtag : subtags) { - if (!sb.append('-') || !appendSubtagZ(subtag.get())) { - return false; - } - } - return true; - }; - - // Append the language subtag. - if (!appendSubtag(tag.language())) { - return false; - } - - // Append the script subtag if present. - if (tag.script().present()) { - if (!sb.append('-') || !appendSubtag(tag.script())) { - return false; - } - } - - // Append the region subtag if present. - if (tag.region().present()) { - if (!sb.append('-') || !appendSubtag(tag.region())) { - return false; - } - } - - // Append the variant subtags if present. - if (!appendSubtagsZ(tag.variants())) { - return false; - } - - // Append the extensions subtags if present. - if (!appendSubtagsZ(tag.extensions())) { - return false; - } - - // Append the private-use subtag if present. - if (tag.privateuse()) { - if (!sb.append('-') || !appendSubtagZ(tag.privateuse())) { - return false; - } - } - - return true; -} - -/** - * CanonicalizeTransformExtension - * - * Canonical form per : - * - * - These subtags are all in lowercase (that is the canonical casing for these - * subtags), [...]. - * - * And per - * : - * - * - All keywords and tfields are sorted by alphabetical order of their keys, - * within their respective extensions. - */ -bool LanguageTag::canonicalizeTransformExtension( - JSContext* cx, JS::UniqueChars& transformExtension) { - const char* const extension = transformExtension.get(); - MOZ_ASSERT(extension[0] == 't'); - MOZ_ASSERT(extension[1] == '-'); - MOZ_ASSERT( - IsStructurallyValidExtensionTag(mozilla::MakeStringSpan(extension))); - - size_t length = strlen(extension); - - LanguageTag tag(cx); - LanguageTagParser::TFieldVector fields(cx); - - using TField = LanguageTagParser::TFieldVector::ElementType; - - mozilla::DebugOnly ok; - JS_TRY_VAR_OR_RETURN_FALSE( - cx, ok, - LanguageTagParser::parseTransformExtension( - cx, mozilla::Span(extension, length), tag, fields)); - MOZ_ASSERT(ok, "unexpected invalid transform extension subtag"); - - auto tfieldLessOrEqual = [extension](const TField& a, const TField& b) { - MOZ_ASSERT(a.length() > TransformKeyLength); - MOZ_ASSERT(b.length() > TransformKeyLength); - const char* astr = a.begin(extension); - const char* bstr = b.begin(extension); - return std::char_traits::compare(astr, bstr, TransformKeyLength) <= 0; - }; - - // All tfields are sorted by alphabetical order of their keys. - if (size_t fieldsLength = fields.length(); fieldsLength > 1) { - if (!fields.growByUninitialized(fieldsLength)) { - return false; - } - - MOZ_ALWAYS_TRUE( - MergeSort(fields.begin(), fieldsLength, fields.begin() + fieldsLength, - [&](const auto& a, const auto& b, bool* lessOrEqualp) { - *lessOrEqualp = tfieldLessOrEqual(a, b); - return true; - })); - - fields.shrinkBy(fieldsLength); } - Vector sb(cx); - if (!sb.append('t')) { - return false; - } - - // Append the language subtag if present. - // - // Replace aliases in tlang per - // . - if (tag.language().present()) { - if (!sb.append('-')) { - return false; - } - - if (!tag.canonicalizeBaseName(cx)) { - return false; - } - - // The canonical case for Transform extensions is lowercase per - // . Convert the two - // subtags which don't use lowercase for their canonical syntax. - tag.script_.toLowerCase(); - tag.region_.toLowerCase(); - - if (!LanguageTagToString(cx, tag, sb)) { - return false; - } - } - - static constexpr size_t TransformKeyWithSepLength = TransformKeyLength + 1; - - using StringSpan = mozilla::Span; - - // Append all fields. - // - // UTS 35, 3.2.1 specifies: - // - Any type or tfield value "true" is removed. - // - // But the `tvalue` subtag is mandatory in `tfield: tkey tvalue`, so ignore - // this apparently invalid part of the UTS 35 specification and simply - // append all `tfield` subtags. - for (const auto& field : fields) { - if (!sb.append('-')) { - return false; - } - - StringSpan key(field.begin(extension), TransformKeyLength); - StringSpan value(field.begin(extension) + TransformKeyWithSepLength, - field.length() - TransformKeyWithSepLength); - - // Search if there's a replacement for the current transform keyword. - if (const char* replacement = replaceTransformExtensionType(key, value)) { - if (!sb.append(field.begin(extension), TransformKeyWithSepLength)) { - return false; - } - if (!sb.append(replacement, strlen(replacement))) { - return false; - } - } else { - if (!sb.append(field.begin(extension), field.length())) { - return false; - } - } - } - - // We can keep the previous extension when canonicalization didn't modify it. - if (sb.length() != length || - std::char_traits::compare(sb.begin(), extension, length) != 0) { - // Null-terminate the new string and replace the previous extension. - if (!sb.append('\0')) { - return false; - } - UniqueChars canonical(sb.extractOrCopyRawBuffer()); - if (!canonical) { - return false; - } - transformExtension = std::move(canonical); - } - - return true; -} - -JSString* LanguageTag::toString(JSContext* cx) const { - JSStringBuilder sb(cx); - if (!LanguageTagToString(cx, *this, sb)) { - return nullptr; - } - - return sb.finishString(); -} - -UniqueChars LanguageTag::toStringZ(JSContext* cx) const { - Vector sb(cx); - if (!LanguageTagToString(cx, *this, sb)) { - return nullptr; - } - if (!sb.append('\0')) { - return nullptr; - } - - return UniqueChars(sb.extractOrCopyRawBuffer()); -} - -// Zero-terminated ICU Locale ID. -using LocaleId = - js::Vector; - -enum class LikelySubtags : bool { Add, Remove }; - -// Return true iff the language tag is already maximized resp. minimized. -static bool HasLikelySubtags(LikelySubtags likelySubtags, - const LanguageTag& tag) { - // The language tag is already maximized if the language, script, and region - // subtags are present and no placeholder subtags ("und", "Zzzz", "ZZ") are - // used. - if (likelySubtags == LikelySubtags::Add) { - return !tag.language().equalTo("und") && - (tag.script().present() && !tag.script().equalTo("Zzzz")) && - (tag.region().present() && !tag.region().equalTo("ZZ")); - } - - // The language tag is already minimized if it only contains a language - // subtag whose value is not the placeholder value "und". - return !tag.language().equalTo("und") && tag.script().missing() && - tag.region().missing(); -} - -// Create an ICU locale ID from the given language tag. -static bool CreateLocaleForLikelySubtags(const LanguageTag& tag, - LocaleId& locale) { - MOZ_ASSERT(locale.length() == 0); - - auto appendSubtag = [&locale](const auto& subtag) { - auto span = subtag.span(); - MOZ_ASSERT(!span.empty()); - return locale.append(span.data(), span.size()); - }; - - // Append the language subtag. - if (!appendSubtag(tag.language())) { - return false; - } - - // Append the script subtag if present. - if (tag.script().present()) { - if (!locale.append('_') || !appendSubtag(tag.script())) { - return false; - } - } - - // Append the region subtag if present. - if (tag.region().present()) { - if (!locale.append('_') || !appendSubtag(tag.region())) { - return false; - } - } - - // Zero-terminated for use with ICU. - return locale.append('\0'); -} - -// Assign the language, script, and region subtags from an ICU locale ID. -// -// ICU provides |uloc_getLanguage|, |uloc_getScript|, and |uloc_getCountry| to -// retrieve these subtags, but unfortunately these functions are rather slow, so -// we use our own implementation. -static bool AssignFromLocaleId(JSContext* cx, LocaleId& localeId, - LanguageTag& tag) { - MOZ_ASSERT(localeId.back() == '\0', - "Locale ID should be zero-terminated for ICU"); - - // Replace the ICU locale ID separator. - std::replace(localeId.begin(), localeId.end(), '_', '-'); - - // ICU replaces "und" with the empty string, which means "und" becomes "" and - // "und-Latn" becomes "-Latn". Handle this case separately. - if (localeId[0] == '\0' || localeId[0] == '-') { - static constexpr char und[] = "und"; - constexpr size_t length = std::char_traits::length(und); - - // Insert "und" in front of the locale ID. - if (!localeId.growBy(length)) { - return false; - } - memmove(localeId.begin() + length, localeId.begin(), localeId.length()); - memmove(localeId.begin(), und, length); - } - - mozilla::Span localeSpan(localeId.begin(), localeId.length() - 1); - - // Retrieve the language, script, and region subtags from the locale ID, but - // ignore any other subtags. - LanguageTag localeTag(cx); - if (!LanguageTagParser::parseBaseName(cx, localeSpan, localeTag)) { - return false; - } - - tag.setLanguage(localeTag.language()); - tag.setScript(localeTag.script()); - tag.setRegion(localeTag.region()); - - return true; -} - -template -static bool CallLikelySubtags(JSContext* cx, const LocaleId& localeId, - LocaleId& result) { - // Locale ID must be zero-terminated before passing it to ICU. - MOZ_ASSERT(localeId.back() == '\0'); - MOZ_ASSERT(result.length() == 0); - - // Ensure there's enough room for the result. - MOZ_ALWAYS_TRUE(result.resize(LocaleId::InlineLength)); - - int32_t length = intl::CallICU( - cx, - [&localeId](char* chars, int32_t size, UErrorCode* status) { - return likelySubtagsFn(localeId.begin(), chars, size, status); - }, - result); - if (length < 0) { - return false; - } - - MOZ_ASSERT( - size_t(length) <= LocaleId::InlineLength, - "Unexpected extra subtags were added by ICU. If this assertion ever " - "fails, simply remove it and move on like nothing ever happended."); - - // Resize the vector to the actual string length. - result.shrinkTo(length); - - // Zero-terminated for use with ICU. - return result.append('\0'); -} - -// The canonical way to compute the Unicode BCP 47 locale identifier with likely -// subtags is as follows: -// -// 1. Call uloc_forLanguageTag() to transform the locale identifer into an ICU -// locale ID. -// 2. Call uloc_addLikelySubtags() to add the likely subtags to the locale ID. -// 3. Call uloc_toLanguageTag() to transform the resulting locale ID back into -// a Unicode BCP 47 locale identifier. -// -// Since uloc_forLanguageTag() and uloc_toLanguageTag() are both kind of slow -// and we know, by construction, that the input Unicode BCP 47 locale identifier -// only contains valid language, script, and region subtags, we can avoid both -// calls if we implement them ourselves, see CreateLocaleForLikelySubtags() and -// AssignFromLocaleId(). (Where "slow" means about 50% of the execution time of -// |Intl.Locale.prototype.maximize|.) -static bool LikelySubtags(JSContext* cx, LikelySubtags likelySubtags, - LanguageTag& tag) { - // Return early if the input is already maximized/minimized. - if (HasLikelySubtags(likelySubtags, tag)) { - return true; - } - - // Create the locale ID for the input argument. - LocaleId locale(cx); - if (!CreateLocaleForLikelySubtags(tag, locale)) { - return false; - } - - // Either add or remove likely subtags to/from the locale ID. - LocaleId localeLikelySubtags(cx); - if (likelySubtags == LikelySubtags::Add) { - if (!CallLikelySubtags(cx, locale, - localeLikelySubtags)) { - return false; - } - } else { - if (!CallLikelySubtags(cx, locale, - localeLikelySubtags)) { - return false; - } - } - - // Assign the language, script, and region subtags from the locale ID. - if (!AssignFromLocaleId(cx, localeLikelySubtags, tag)) { - return false; - } - - // Update mappings in case ICU returned a non-canonical locale. - return tag.canonicalizeBaseName(cx); -} - -bool LanguageTag::addLikelySubtags(JSContext* cx) { - return LikelySubtags(cx, LikelySubtags::Add, *this); -} - -bool LanguageTag::removeLikelySubtags(JSContext* cx) { - return LikelySubtags(cx, LikelySubtags::Remove, *this); -} - -LanguageTagParser::Token LanguageTagParser::nextToken() { - MOZ_ASSERT(index_ <= length_ + 1, "called after 'None' token was read"); - - TokenKind kind = TokenKind::None; - size_t tokenLength = 0; - for (size_t i = index_; i < length_; i++) { - // UTS 35, section 3.1. - // alpha = [A-Z a-z] ; - // digit = [0-9] ; - char16_t c = charAtUnchecked(i); - if (mozilla::IsAsciiAlpha(c)) { - kind |= TokenKind::Alpha; - } else if (mozilla::IsAsciiDigit(c)) { - kind |= TokenKind::Digit; - } else if (c == '-' && i > index_ && i + 1 < length_) { - break; - } else { - return {TokenKind::Error, 0, 0}; - } - tokenLength += 1; - } - - Token token{kind, index_, tokenLength}; - index_ += tokenLength + 1; - return token; -} - -UniqueChars LanguageTagParser::chars(JSContext* cx, size_t index, - size_t length) const { - // Add +1 to null-terminate the string. - auto chars = cx->make_pod_array(length + 1); - if (chars) { - char* dest = chars.get(); - if (locale_.is()) { - std::copy_n(locale_.as() + index, length, dest); - } else { - std::copy_n(locale_.as() + index, length, dest); - } - dest[length] = '\0'; - } - return chars; -} - -// Parse the `unicode_language_id` production. -// -// unicode_language_id = unicode_language_subtag -// (sep unicode_script_subtag)? -// (sep unicode_region_subtag)? -// (sep unicode_variant_subtag)* ; -// -// sep = "-" -// -// Note: Unicode CLDR locale identifier backward compatibility extensions -// removed from `unicode_language_id`. -// -// |tok| is the current token from |ts|. -// -// All subtags will be added unaltered to |tag|, without canonicalizing their -// case or, in the case of variant subtags, detecting and rejecting duplicate -// variants. Users must subsequently |canonicalizeBaseName| to perform these -// actions. -// -// Do not use this function directly: use |parseBaseName| or -// |parseTlangFromTransformExtension| instead. -JS::Result LanguageTagParser::internalParseBaseName(JSContext* cx, - LanguageTagParser& ts, - LanguageTag& tag, - Token& tok) { - if (ts.isLanguage(tok)) { - ts.copyChars(tok, tag.language_); - - tok = ts.nextToken(); - } else { - // The language subtag is mandatory. - return false; - } - - if (ts.isScript(tok)) { - ts.copyChars(tok, tag.script_); - - tok = ts.nextToken(); - } - - if (ts.isRegion(tok)) { - ts.copyChars(tok, tag.region_); - - tok = ts.nextToken(); - } - - auto& variants = tag.variants_; - MOZ_ASSERT(variants.length() == 0); - while (ts.isVariant(tok)) { - auto variant = ts.chars(cx, tok); - if (!variant) { - return cx->alreadyReportedOOM(); - } - if (!variants.append(std::move(variant))) { - return cx->alreadyReportedOOM(); - } - - tok = ts.nextToken(); - } - - return true; -} - -static mozilla::Variant StringChars( - const char* locale) { - return mozilla::AsVariant(reinterpret_cast(locale)); -} - -static mozilla::Variant StringChars( - JSLinearString* linear, JS::AutoCheckCannotGC& nogc) { - if (linear->hasLatin1Chars()) { - return mozilla::AsVariant(linear->latin1Chars(nogc)); - } - return mozilla::AsVariant(linear->twoByteChars(nogc)); -} - -JS::Result LanguageTagParser::tryParse(JSContext* cx, - JSLinearString* locale, - LanguageTag& tag) { - JS::AutoCheckCannotGC nogc; - LocaleChars localeChars = StringChars(locale, nogc); - return tryParse(cx, localeChars, locale->length(), tag); -} - -JS::Result LanguageTagParser::tryParse(JSContext* cx, - mozilla::Span locale, - LanguageTag& tag) { - LocaleChars localeChars = StringChars(locale.data()); - return tryParse(cx, localeChars, locale.size(), tag); -} - -JS::Result LanguageTagParser::tryParse(JSContext* cx, - LocaleChars& localeChars, - size_t localeLength, - LanguageTag& tag) { - // unicode_locale_id = unicode_language_id - // extensions* - // pu_extensions? ; - - LanguageTagParser ts(localeChars, localeLength); - Token tok = ts.nextToken(); - - bool ok; - MOZ_TRY_VAR(ok, parseBaseName(cx, ts, tag, tok)); - if (!ok) { - return false; - } - - // extensions = unicode_locale_extensions - // | transformed_extensions - // | other_extensions ; - - // Bit set of seen singletons. - uint64_t seenSingletons = 0; - - auto& extensions = tag.extensions_; - while (ts.isExtensionStart(tok)) { - char singleton = ts.singletonKey(tok); - - // Reject the input if a duplicate singleton was found. - uint64_t hash = 1ULL << (mozilla::AsciiAlphanumericToNumber(singleton) + 1); - if (seenSingletons & hash) { - return false; - } - seenSingletons |= hash; - - Token start = tok; - tok = ts.nextToken(); - - // We'll check for missing non-singleton subtags after this block by - // comparing |startValue| with the then-current position. - size_t startValue = tok.index(); - - if (singleton == 'u') { - while (ts.isUnicodeExtensionPart(tok)) { - tok = ts.nextToken(); - } - } else if (singleton == 't') { - // transformed_extensions = sep [tT] - // ((sep tlang (sep tfield)*) - // | (sep tfield)+) ; - - // tlang = unicode_language_subtag - // (sep unicode_script_subtag)? - // (sep unicode_region_subtag)? - // (sep unicode_variant_subtag)* ; - if (ts.isLanguage(tok)) { - tok = ts.nextToken(); - - if (ts.isScript(tok)) { - tok = ts.nextToken(); - } - - if (ts.isRegion(tok)) { - tok = ts.nextToken(); - } - - while (ts.isVariant(tok)) { - tok = ts.nextToken(); - } - } - - // tfield = tkey tvalue; - while (ts.isTransformExtensionKey(tok)) { - tok = ts.nextToken(); - - size_t startTValue = tok.index(); - while (ts.isTransformExtensionPart(tok)) { - tok = ts.nextToken(); - } - - // `tfield` requires at least one `tvalue`. - if (tok.index() <= startTValue) { - return false; - } - } - } else { - while (ts.isOtherExtensionPart(tok)) { - tok = ts.nextToken(); - } - } - - // Singletons must be followed by a non-singleton subtag, "en-a-b" is not - // allowed. - if (tok.index() <= startValue) { - return false; - } - - UniqueChars extension = ts.extension(cx, start, tok); - if (!extension) { - return cx->alreadyReportedOOM(); - } - if (!extensions.append(std::move(extension))) { - return cx->alreadyReportedOOM(); - } - } - - // Trailing `pu_extension` component of the `unicode_locale_id` production. - if (ts.isPrivateUseStart(tok)) { - Token start = tok; - tok = ts.nextToken(); - - size_t startValue = tok.index(); - while (ts.isPrivateUsePart(tok)) { - tok = ts.nextToken(); - } - - // There must be at least one subtag after the "-x-". - if (tok.index() <= startValue) { - return false; - } - - UniqueChars privateUse = ts.extension(cx, start, tok); - if (!privateUse) { - return cx->alreadyReportedOOM(); - } - tag.privateuse_ = std::move(privateUse); - } - - // Return true if the complete input was successfully parsed. - return tok.isNone(); -} - -bool LanguageTagParser::parse(JSContext* cx, JSLinearString* locale, - LanguageTag& tag) { - bool ok; - JS_TRY_VAR_OR_RETURN_FALSE(cx, ok, tryParse(cx, locale, tag)); - if (ok) { - return true; - } - if (UniqueChars localeChars = QuoteString(cx, locale, '"')) { + if (UniqueChars localeChars = QuoteString(cx, str, '"')) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INVALID_LANGUAGE_TAG, localeChars.get()); } return false; } -bool LanguageTagParser::parse(JSContext* cx, mozilla::Span locale, - LanguageTag& tag) { - bool ok; - JS_TRY_VAR_OR_RETURN_FALSE(cx, ok, tryParse(cx, locale, tag)); - if (ok) { - return true; - } - if (UniqueChars localeChars = - DuplicateString(cx, locale.data(), locale.size())) { - JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, - JSMSG_INVALID_LANGUAGE_TAG, localeChars.get()); - } - return false; -} - -bool LanguageTagParser::parseBaseName(JSContext* cx, - mozilla::Span locale, - LanguageTag& tag) { - LocaleChars localeChars = StringChars(locale.data()); - LanguageTagParser ts(localeChars, locale.size()); - Token tok = ts.nextToken(); - - // Parse only the base-name part and ignore any trailing characters. - bool ok; - JS_TRY_VAR_OR_RETURN_FALSE(cx, ok, parseBaseName(cx, ts, tag, tok)); - if (ok) { - return true; - } - if (UniqueChars localeChars = - DuplicateString(cx, locale.data(), locale.size())) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_INVALID_LANGUAGE_TAG, localeChars.get()); - } - return false; -} - -JS::Result LanguageTagParser::tryParseBaseName(JSContext* cx, - JSLinearString* locale, - LanguageTag& tag) { - JS::AutoCheckCannotGC nogc; - LocaleChars localeChars = StringChars(locale, nogc); - LanguageTagParser ts(localeChars, locale->length()); - Token tok = ts.nextToken(); - - // Return true if the complete input was successfully parsed. - bool ok; - MOZ_TRY_VAR(ok, parseBaseName(cx, ts, tag, tok)); - return ok && tok.isNone(); -} - -// Parse |extension|, which must be a valid `transformed_extensions` subtag, and -// fill |tag| and |fields| from the `tlang` and `tfield` components. -JS::Result LanguageTagParser::parseTransformExtension( - JSContext* cx, mozilla::Span extension, LanguageTag& tag, - TFieldVector& fields) { - LocaleChars extensionChars = StringChars(extension.data()); - LanguageTagParser ts(extensionChars, extension.size()); - Token tok = ts.nextToken(); - - if (!ts.isExtensionStart(tok) || ts.singletonKey(tok) != 't') { - return false; - } - - tok = ts.nextToken(); - - if (tok.isNone()) { - return false; - } - - if (ts.isLanguage(tok)) { - // We're parsing a possible `tlang` in a known-valid transform extension, so - // use the special-purpose function that takes advantage of this to compute - // lowercased |tag| contents in an optimal manner. - MOZ_TRY(parseTlangInTransformExtension(cx, ts, tag, tok)); - - // After `tlang` we must have a `tfield` and its `tkey`, or we're at the end - // of the transform extension. - MOZ_ASSERT(ts.isTransformExtensionKey(tok) || tok.isNone()); - } else { - // If there's no `tlang` subtag, at least one `tfield` must be present. - MOZ_ASSERT(ts.isTransformExtensionKey(tok)); - } - - // Trailing `tfield` subtags. (Any other trailing subtags are an error, - // because we're guaranteed to only see a valid tranform extension here.) - while (ts.isTransformExtensionKey(tok)) { - size_t begin = tok.index(); - tok = ts.nextToken(); - - size_t startTValue = tok.index(); - while (ts.isTransformExtensionPart(tok)) { - tok = ts.nextToken(); - } - - // `tfield` requires at least one `tvalue`. - if (tok.index() <= startTValue) { - return false; - } - - size_t length = tok.index() - 1 - begin; - if (!fields.emplaceBack(begin, length)) { - return cx->alreadyReportedOOM(); - } - } - - // Return true if the complete input was successfully parsed. - return tok.isNone(); -} - -// Parse |extension|, which must be a valid `unicode_locale_extensions` subtag, -// and fill |attributes| and |keywords| from the `attribute` and `keyword` -// components. -JS::Result LanguageTagParser::parseUnicodeExtension( - JSContext* cx, mozilla::Span extension, - AttributesVector& attributes, KeywordsVector& keywords) { - LocaleChars extensionChars = StringChars(extension.data()); - LanguageTagParser ts(extensionChars, extension.size()); - Token tok = ts.nextToken(); - - // unicode_locale_extensions = sep [uU] ((sep keyword)+ | - // (sep attribute)+ (sep keyword)*) ; - - if (!ts.isExtensionStart(tok) || ts.singletonKey(tok) != 'u') { - return false; - } - - tok = ts.nextToken(); - - if (tok.isNone()) { - return false; - } - - while (ts.isUnicodeExtensionAttribute(tok)) { - if (!attributes.emplaceBack(tok.index(), tok.length())) { - return cx->alreadyReportedOOM(); - } - - tok = ts.nextToken(); - } - - // keyword = key (sep type)? ; - while (ts.isUnicodeExtensionKey(tok)) { - size_t begin = tok.index(); - tok = ts.nextToken(); - - while (ts.isUnicodeExtensionType(tok)) { - tok = ts.nextToken(); - } - - if (tok.isError()) { - return false; - } - - size_t length = tok.index() - 1 - begin; - if (!keywords.emplaceBack(begin, length)) { - return cx->alreadyReportedOOM(); - } - } - - // Return true if the complete input was successfully parsed. - return tok.isNone(); -} - -bool LanguageTagParser::canParseUnicodeExtension( - mozilla::Span extension) { - LocaleChars extensionChars = StringChars(extension.data()); - LanguageTagParser ts(extensionChars, extension.size()); - Token tok = ts.nextToken(); - - // unicode_locale_extensions = sep [uU] ((sep keyword)+ | - // (sep attribute)+ (sep keyword)*) ; - - if (!ts.isExtensionStart(tok) || ts.singletonKey(tok) != 'u') { - return false; - } - - tok = ts.nextToken(); - - if (tok.isNone()) { - return false; - } - - while (ts.isUnicodeExtensionAttribute(tok)) { - tok = ts.nextToken(); - } - - // keyword = key (sep type)? ; - while (ts.isUnicodeExtensionKey(tok)) { - tok = ts.nextToken(); - - while (ts.isUnicodeExtensionType(tok)) { - tok = ts.nextToken(); - } - - if (tok.isError()) { - return false; - } - } - - // Return true if the complete input was successfully parsed. - return tok.isNone(); -} - -bool LanguageTagParser::canParseUnicodeExtensionType( - JSLinearString* unicodeType) { - MOZ_ASSERT(unicodeType->length() > 0, "caller must exclude empty strings"); - - JS::AutoCheckCannotGC nogc; - LocaleChars unicodeTypeChars = StringChars(unicodeType, nogc); - - LanguageTagParser ts(unicodeTypeChars, unicodeType->length()); - Token tok = ts.nextToken(); - - while (ts.isUnicodeExtensionType(tok)) { - tok = ts.nextToken(); - } - - // Return true if the complete input was successfully parsed. - return tok.isNone(); -} - bool ParseStandaloneLanguageTag(HandleLinearString str, - LanguageSubtag& result) { - JS::AutoCheckCannotGC nogc; + mozilla::intl::LanguageSubtag& result) { + // Tell the analysis the |IsStructurallyValidLanguageTag| function can't GC. + JS::AutoSuppressGCAnalysis nogc; + if (str->hasLatin1Chars()) { - if (!IsStructurallyValidLanguageTag(str->latin1Range(nogc))) { + if (!mozilla::intl::IsStructurallyValidLanguageTag( + str->latin1Range(nogc))) { return false; } result.set(str->latin1Range(nogc)); } else { - if (!IsStructurallyValidLanguageTag(str->twoByteRange(nogc))) { + if (!mozilla::intl::IsStructurallyValidLanguageTag( + str->twoByteRange(nogc))) { return false; } result.set(str->twoByteRange(nogc)); @@ -1594,15 +59,20 @@ return true; } -bool ParseStandaloneScriptTag(HandleLinearString str, ScriptSubtag& result) { - JS::AutoCheckCannotGC nogc; +bool ParseStandaloneScriptTag(HandleLinearString str, + mozilla::intl::ScriptSubtag& result) { + // Tell the analysis the |IsStructurallyValidScriptTag| function can't GC. + JS::AutoSuppressGCAnalysis nogc; + if (str->hasLatin1Chars()) { - if (!IsStructurallyValidScriptTag(str->latin1Range(nogc))) { + if (!mozilla::intl::IsStructurallyValidScriptTag( + str->latin1Range(nogc))) { return false; } result.set(str->latin1Range(nogc)); } else { - if (!IsStructurallyValidScriptTag(str->twoByteRange(nogc))) { + if (!mozilla::intl::IsStructurallyValidScriptTag( + str->twoByteRange(nogc))) { return false; } result.set(str->twoByteRange(nogc)); @@ -1610,15 +80,20 @@ return true; } -bool ParseStandaloneRegionTag(HandleLinearString str, RegionSubtag& result) { - JS::AutoCheckCannotGC nogc; +bool ParseStandaloneRegionTag(HandleLinearString str, + mozilla::intl::RegionSubtag& result) { + // Tell the analysis the |IsStructurallyValidRegionTag| function can't GC. + JS::AutoSuppressGCAnalysis nogc; + if (str->hasLatin1Chars()) { - if (!IsStructurallyValidRegionTag(str->latin1Range(nogc))) { + if (!mozilla::intl::IsStructurallyValidRegionTag( + str->latin1Range(nogc))) { return false; } result.set(str->latin1Range(nogc)); } else { - if (!IsStructurallyValidRegionTag(str->twoByteRange(nogc))) { + if (!mozilla::intl::IsStructurallyValidRegionTag( + str->twoByteRange(nogc))) { return false; } result.set(str->twoByteRange(nogc)); @@ -1679,7 +154,7 @@ } } - LanguageSubtag languageTag; + mozilla::intl::LanguageSubtag languageTag; if (str->hasLatin1Chars()) { JS::AutoCheckCannotGC nogc; languageTag.set(str->latin1Range(nogc)); @@ -1695,13 +170,13 @@ // Reject the input if the canonical tag contains more than just a single // language subtag. - if (LanguageTag::complexLanguageMapping(languageTag)) { + if (mozilla::intl::Locale::complexLanguageMapping(languageTag)) { return nullptr; } // Take care to replace deprecated subtags with their preferred values. JSString* result; - if (LanguageTag::languageMapping(languageTag) || !isLowerCase) { + if (mozilla::intl::Locale::languageMapping(languageTag) || !isLowerCase) { result = NewStringCopy(cx, languageTag.span()); } else { result = str; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/intl/LanguageTagGenerated.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/intl/LanguageTagGenerated.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/intl/LanguageTagGenerated.cpp 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/intl/LanguageTagGenerated.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,1129 +0,0 @@ -// Generated by make_intl_data.py. DO NOT EDIT. -// Version: CLDR-39 -// URL: https://unicode.org/Public/cldr/39/core.zip - -#include "mozilla/Assertions.h" -#include "mozilla/Span.h" -#include "mozilla/TextUtils.h" - -#include -#include -#include -#include -#include -#include - -#include "builtin/intl/LanguageTag.h" -#include "util/Text.h" -#include "vm/JSContext.h" - -using namespace js::intl::LanguageTagLimits; - -template -static inline bool HasReplacement( - const char (&subtags)[Length][TagLength], - const js::intl::LanguageTagSubtag& subtag) { - MOZ_ASSERT(subtag.length() == TagLength - 1, - "subtag must have the same length as the list of subtags"); - - const char* ptr = subtag.span().data(); - return std::binary_search(std::begin(subtags), std::end(subtags), ptr, - [](const char* a, const char* b) { - return memcmp(a, b, TagLength - 1) < 0; - }); -} - -template -static inline const char* SearchReplacement( - const char (&subtags)[Length][TagLength], - const char* (&aliases)[Length], - const js::intl::LanguageTagSubtag& subtag) { - MOZ_ASSERT(subtag.length() == TagLength - 1, - "subtag must have the same length as the list of subtags"); - - const char* ptr = subtag.span().data(); - auto p = std::lower_bound(std::begin(subtags), std::end(subtags), ptr, - [](const char* a, const char* b) { - return memcmp(a, b, TagLength - 1) < 0; - }); - if (p != std::end(subtags) && memcmp(*p, ptr, TagLength - 1) == 0) { - return aliases[std::distance(std::begin(subtags), p)]; - } - return nullptr; -} - -#ifdef DEBUG -static bool IsAsciiLowercaseAlphanumeric(char c) { - return mozilla::IsAsciiLowercaseAlpha(c) || mozilla::IsAsciiDigit(c); -} - -static bool IsAsciiLowercaseAlphanumericOrDash(char c) { - return IsAsciiLowercaseAlphanumeric(c) || c == '-'; -} - -static bool IsCanonicallyCasedLanguageTag(mozilla::Span span) { - // Tell the analysis the |std::all_of| function can't GC. - JS::AutoSuppressGCAnalysis nogc; - - return std::all_of(span.begin(), span.end(), mozilla::IsAsciiLowercaseAlpha); -} - -static bool IsCanonicallyCasedScriptTag(mozilla::Span span) { - // Tell the analysis the |std::all_of| function can't GC. - JS::AutoSuppressGCAnalysis nogc; - - return mozilla::IsAsciiUppercaseAlpha(span[0]) && - std::all_of(span.begin() + 1, span.end(), mozilla::IsAsciiLowercaseAlpha); -} - -static bool IsCanonicallyCasedRegionTag(mozilla::Span span) { - // Tell the analysis the |std::all_of| function can't GC. - JS::AutoSuppressGCAnalysis nogc; - - return std::all_of(span.begin(), span.end(), mozilla::IsAsciiUppercaseAlpha) || - std::all_of(span.begin(), span.end(), mozilla::IsAsciiDigit); -} - -static bool IsCanonicallyCasedVariantTag(mozilla::Span span) { - // Tell the analysis the |std::all_of| function can't GC. - JS::AutoSuppressGCAnalysis nogc; - - return std::all_of(span.begin(), span.end(), IsAsciiLowercaseAlphanumeric); -} - -static bool IsCanonicallyCasedUnicodeKey(mozilla::Span key) { - return std::all_of(key.begin(), key.end(), IsAsciiLowercaseAlphanumeric); -} - -static bool IsCanonicallyCasedUnicodeType(mozilla::Span type) { - return std::all_of(type.begin(), type.end(), IsAsciiLowercaseAlphanumericOrDash); -} - -static bool IsCanonicallyCasedTransformKey(mozilla::Span key) { - return std::all_of(key.begin(), key.end(), IsAsciiLowercaseAlphanumeric); -} - -static bool IsCanonicallyCasedTransformType(mozilla::Span type) { - return std::all_of(type.begin(), type.end(), IsAsciiLowercaseAlphanumericOrDash); -} -#endif - -// Mappings from language subtags to preferred values. -// Derived from CLDR Supplemental Data, version 39. -// https://unicode.org/Public/cldr/39/core.zip -bool js::intl::LanguageTag::languageMapping(LanguageSubtag& language) { - MOZ_ASSERT(IsStructurallyValidLanguageTag(language.span())); - MOZ_ASSERT(IsCanonicallyCasedLanguageTag(language.span())); - - if (language.length() == 2) { - static const char languages[8][3] = { - "bh", "in", "iw", "ji", "jw", "mo", "tl", "tw", - }; - static const char* aliases[8] = { - "bho", "id", "he", "yi", "jv", "ro", "fil", "ak", - }; - - if (const char* replacement = SearchReplacement(languages, aliases, language)) { - language.set(mozilla::MakeStringSpan(replacement)); - return true; - } - return false; - } - - if (language.length() == 3) { - static const char languages[401][4] = { - "aam", "aar", "abk", "adp", "afr", "agp", "ais", "aju", "aka", "alb", - "als", "amh", "ara", "arb", "arg", "arm", "asd", "asm", "aue", "ava", - "ave", "aym", "ayr", "ayx", "aze", "azj", "bak", "bam", "baq", "baz", - "bcc", "bcl", "bel", "ben", "bgm", "bhk", "bih", "bis", "bjd", "bjq", - "bkb", "bod", "bos", "bre", "btb", "bul", "bur", "bxk", "bxr", "cat", - "ccq", "ces", "cha", "che", "chi", "chu", "chv", "cjr", "cka", "cld", - "cmk", "cmn", "cor", "cos", "coy", "cqu", "cre", "cwd", "cym", "cze", - "daf", "dan", "dap", "deu", "dgo", "dhd", "dik", "diq", "dit", "div", - "djl", "dkl", "drh", "drr", "dud", "duj", "dut", "dwl", "dzo", "ekk", - "ell", "elp", "emk", "eng", "epo", "esk", "est", "eus", "ewe", "fao", - "fas", "fat", "fij", "fin", "fra", "fre", "fry", "fuc", "ful", "gav", - "gaz", "gbc", "gbo", "geo", "ger", "gfx", "ggn", "ggo", "ggr", "gio", - "gla", "gle", "glg", "gli", "glv", "gno", "gre", "grn", "gti", "gug", - "guj", "guv", "gya", "hat", "hau", "hdn", "hea", "heb", "her", "him", - "hin", "hmo", "hrr", "hrv", "hun", "hye", "ibi", "ibo", "ice", "ido", - "iii", "ike", "iku", "ile", "ill", "ilw", "ina", "ind", "ipk", "isl", - "ita", "izi", "jar", "jav", "jeg", "jpn", "kal", "kan", "kas", "kat", - "kau", "kaz", "kdv", "kgc", "kgd", "kgh", "khk", "khm", "kik", "kin", - "kir", "kmr", "knc", "kng", "knn", "koj", "kom", "kon", "kor", "kpp", - "kpv", "krm", "ktr", "kua", "kur", "kvs", "kwq", "kxe", "kxl", "kzh", - "kzj", "kzt", "lao", "lat", "lav", "lbk", "leg", "lii", "lim", "lin", - "lit", "llo", "lmm", "ltz", "lub", "lug", "lvs", "mac", "mah", "mal", - "mao", "mar", "may", "meg", "mgx", "mhr", "mkd", "mlg", "mlt", "mnk", - "mnt", "mof", "mol", "mon", "mri", "msa", "mst", "mup", "mwd", "mwj", - "mya", "myd", "myt", "nad", "nau", "nav", "nbf", "nbl", "nbx", "ncp", - "nde", "ndo", "nep", "nld", "nln", "nlr", "nno", "nns", "nnx", "nob", - "noo", "nor", "npi", "nts", "nxu", "nya", "oci", "ojg", "oji", "ori", - "orm", "ory", "oss", "oun", "pan", "pbu", "pcr", "per", "pes", "pli", - "plt", "pmc", "pmu", "pnb", "pol", "por", "ppa", "ppr", "pry", "pus", - "puz", "que", "quz", "rmr", "rmy", "roh", "ron", "rum", "run", "rus", - "sag", "san", "sap", "sca", "scc", "scr", "sgl", "sin", "skk", "slk", - "slo", "slv", "sme", "smo", "sna", "snd", "som", "sot", "spa", "spy", - "sqi", "src", "srd", "srp", "ssw", "sul", "sum", "sun", "swa", "swe", - "swh", "tah", "tam", "tat", "tdu", "tel", "tgg", "tgk", "tgl", "tha", - "thc", "thw", "thx", "tib", "tid", "tie", "tir", "tkk", "tlw", "tmp", - "tne", "ton", "tsf", "tsn", "tso", "ttq", "tuk", "tur", "twi", "uig", - "ukr", "umu", "unp", "uok", "urd", "uzb", "uzn", "ven", "vie", "vol", - "wel", "wgw", "wit", "wiw", "wln", "wol", "xba", "xho", "xia", "xkh", - "xpe", "xrq", "xsj", "xsl", "ybd", "ydd", "yen", "yid", "yiy", "yma", - "ymt", "yor", "yos", "yuu", "zai", "zha", "zho", "zir", "zsm", "zul", - "zyb", - }; - static const char* aliases[401] = { - "aas", "aa", "ab", "dz", "af", "apf", "ami", "jrb", "ak", "sq", - "sq", "am", "ar", "ar", "an", "hy", "snz", "as", "ktz", "av", - "ae", "ay", "ay", "nun", "az", "az", "ba", "bm", "eu", "nvo", - "bal", "bik", "be", "bn", "bcg", "fbl", "bho", "bi", "drl", "bzc", - "ebk", "bo", "bs", "br", "beb", "bg", "my", "luy", "bua", "ca", - "rki", "cs", "ch", "ce", "zh", "cu", "cv", "mom", "cmr", "syr", - "xch", "zh", "kw", "co", "pij", "quh", "cr", "cr", "cy", "cs", - "dnj", "da", "njz", "de", "doi", "mwr", "din", "zza", "dif", "dv", - "dze", "aqd", "mn", "kzk", "uth", "dwu", "nl", "dbt", "dz", "et", - "el", "amq", "man", "en", "eo", "ik", "et", "eu", "ee", "fo", - "fa", "ak", "fj", "fi", "fr", "fr", "fy", "ff", "ff", "dev", - "om", "wny", "grb", "ka", "de", "vaj", "gvr", "esg", "gtu", "aou", - "gd", "ga", "gl", "kzk", "gv", "gon", "el", "gn", "nyc", "gn", - "gu", "duz", "gba", "ht", "ha", "hai", "hmn", "he", "hz", "srx", - "hi", "ho", "jal", "hr", "hu", "hy", "opa", "ig", "is", "io", - "ii", "iu", "iu", "ie", "ilm", "gal", "ia", "id", "ik", "is", - "it", "eza", "jgk", "jv", "oyb", "ja", "kl", "kn", "ks", "ka", - "kr", "kk", "zkd", "tdf", "ncq", "kml", "mn", "km", "ki", "rw", - "ky", "ku", "kr", "kg", "kok", "kwv", "kv", "kg", "ko", "jkm", - "kv", "bmf", "dtp", "kj", "ku", "gdj", "yam", "tvd", "kru", "dgl", - "dtp", "dtp", "lo", "la", "lv", "bnc", "enl", "raq", "li", "ln", - "lt", "ngt", "rmx", "lb", "lu", "lg", "lv", "mk", "mh", "ml", - "mi", "mr", "ms", "cir", "jbk", "chm", "mk", "mg", "mt", "man", - "wnn", "xnt", "ro", "mn", "mi", "ms", "mry", "raj", "dmw", "vaj", - "my", "aog", "mry", "xny", "na", "nv", "nru", "nr", "ekc", "kdz", - "nd", "ng", "ne", "nl", "azd", "nrk", "nn", "nbr", "ngv", "nb", - "dtd", "no", "ne", "pij", "bpp", "ny", "oc", "oj", "oj", "or", - "om", "or", "os", "vaj", "pa", "ps", "adx", "fa", "fa", "pi", - "mg", "huw", "phr", "lah", "pl", "pt", "bfy", "lcq", "prt", "ps", - "pub", "qu", "qu", "emx", "rom", "rm", "ro", "ro", "rn", "ru", - "sg", "sa", "aqt", "hle", "sr", "hr", "isk", "si", "oyb", "sk", - "sk", "sl", "se", "sm", "sn", "sd", "so", "st", "es", "kln", - "sq", "sc", "sc", "sr", "ss", "sgd", "ulw", "su", "sw", "sv", - "sw", "ty", "ta", "tt", "dtp", "te", "bjp", "tg", "fil", "th", - "tpo", "ola", "oyb", "bo", "itd", "ras", "ti", "twm", "weo", "tyj", - "kak", "to", "taj", "tn", "ts", "tmh", "tk", "tr", "ak", "ug", - "uk", "del", "wro", "ema", "ur", "uz", "uz", "ve", "vi", "vo", - "cy", "wgb", "nol", "nwo", "wa", "wo", "cax", "xh", "acn", "waw", - "kpe", "dmw", "suj", "den", "rki", "yi", "ynq", "yi", "yrm", "lrr", - "mtm", "yo", "zom", "yug", "zap", "za", "zh", "scv", "ms", "zu", - "za", - }; - - if (const char* replacement = SearchReplacement(languages, aliases, language)) { - language.set(mozilla::MakeStringSpan(replacement)); - return true; - } - return false; - } - - return false; -} - -// Language subtags with complex mappings. -// Derived from CLDR Supplemental Data, version 39. -// https://unicode.org/Public/cldr/39/core.zip -bool js::intl::LanguageTag::complexLanguageMapping(const LanguageSubtag& language) { - MOZ_ASSERT(IsStructurallyValidLanguageTag(language.span())); - MOZ_ASSERT(IsCanonicallyCasedLanguageTag(language.span())); - - if (language.length() == 2) { - return language.equalTo("sh"); - } - - if (language.length() == 3) { - static const char languages[6][4] = { - "cnr", "drw", "hbs", "prs", "swc", "tnf", - }; - - return HasReplacement(languages, language); - } - - return false; -} - -// Mappings from script subtags to preferred values. -// Derived from CLDR Supplemental Data, version 39. -// https://unicode.org/Public/cldr/39/core.zip -bool js::intl::LanguageTag::scriptMapping(ScriptSubtag& script) { - MOZ_ASSERT(IsStructurallyValidScriptTag(script.span())); - MOZ_ASSERT(IsCanonicallyCasedScriptTag(script.span())); - - { - if (script.equalTo("Qaai")) { - script.set(mozilla::MakeStringSpan("Zinh")); - return true; - } - return false; - } -} - -// Mappings from region subtags to preferred values. -// Derived from CLDR Supplemental Data, version 39. -// https://unicode.org/Public/cldr/39/core.zip -bool js::intl::LanguageTag::regionMapping(RegionSubtag& region) { - MOZ_ASSERT(IsStructurallyValidRegionTag(region.span())); - MOZ_ASSERT(IsCanonicallyCasedRegionTag(region.span())); - - if (region.length() == 2) { - static const char regions[23][3] = { - "BU", "CS", "CT", "DD", "DY", "FQ", "FX", "HV", "JT", "MI", - "NH", "NQ", "PU", "PZ", "QU", "RH", "TP", "UK", "VD", "WK", - "YD", "YU", "ZR", - }; - static const char* aliases[23] = { - "MM", "RS", "KI", "DE", "BJ", "AQ", "FR", "BF", "UM", "UM", - "VU", "AQ", "UM", "PA", "EU", "ZW", "TL", "GB", "VN", "UM", - "YE", "RS", "CD", - }; - - if (const char* replacement = SearchReplacement(regions, aliases, region)) { - region.set(mozilla::MakeStringSpan(replacement)); - return true; - } - return false; - } - - { - static const char regions[300][4] = { - "004", "008", "010", "012", "016", "020", "024", "028", "031", "032", - "036", "040", "044", "048", "050", "051", "052", "056", "060", "062", - "064", "068", "070", "072", "074", "076", "084", "086", "090", "092", - "096", "100", "104", "108", "112", "116", "120", "124", "132", "136", - "140", "144", "148", "152", "156", "158", "162", "166", "170", "174", - "175", "178", "180", "184", "188", "191", "192", "196", "203", "204", - "208", "212", "214", "218", "222", "226", "230", "231", "232", "233", - "234", "238", "239", "242", "246", "248", "249", "250", "254", "258", - "260", "262", "266", "268", "270", "275", "276", "278", "280", "288", - "292", "296", "300", "304", "308", "312", "316", "320", "324", "328", - "332", "334", "336", "340", "344", "348", "352", "356", "360", "364", - "368", "372", "376", "380", "384", "388", "392", "398", "400", "404", - "408", "410", "414", "417", "418", "422", "426", "428", "430", "434", - "438", "440", "442", "446", "450", "454", "458", "462", "466", "470", - "474", "478", "480", "484", "492", "496", "498", "499", "500", "504", - "508", "512", "516", "520", "524", "528", "531", "533", "534", "535", - "540", "548", "554", "558", "562", "566", "570", "574", "578", "580", - "581", "583", "584", "585", "586", "591", "598", "600", "604", "608", - "612", "616", "620", "624", "626", "630", "634", "638", "642", "643", - "646", "652", "654", "659", "660", "662", "663", "666", "670", "674", - "678", "682", "686", "688", "690", "694", "702", "703", "704", "705", - "706", "710", "716", "720", "724", "728", "729", "732", "736", "740", - "744", "748", "752", "756", "760", "762", "764", "768", "772", "776", - "780", "784", "788", "792", "795", "796", "798", "800", "804", "807", - "818", "826", "830", "831", "832", "833", "834", "840", "850", "854", - "858", "860", "862", "876", "882", "886", "887", "891", "894", "958", - "959", "960", "962", "963", "964", "965", "966", "967", "968", "969", - "970", "971", "972", "973", "974", "975", "976", "977", "978", "979", - "980", "981", "982", "983", "984", "985", "986", "987", "988", "989", - "990", "991", "992", "993", "994", "995", "996", "997", "998", "999", - }; - static const char* aliases[300] = { - "AF", "AL", "AQ", "DZ", "AS", "AD", "AO", "AG", "AZ", "AR", - "AU", "AT", "BS", "BH", "BD", "AM", "BB", "BE", "BM", "034", - "BT", "BO", "BA", "BW", "BV", "BR", "BZ", "IO", "SB", "VG", - "BN", "BG", "MM", "BI", "BY", "KH", "CM", "CA", "CV", "KY", - "CF", "LK", "TD", "CL", "CN", "TW", "CX", "CC", "CO", "KM", - "YT", "CG", "CD", "CK", "CR", "HR", "CU", "CY", "CZ", "BJ", - "DK", "DM", "DO", "EC", "SV", "GQ", "ET", "ET", "ER", "EE", - "FO", "FK", "GS", "FJ", "FI", "AX", "FR", "FR", "GF", "PF", - "TF", "DJ", "GA", "GE", "GM", "PS", "DE", "DE", "DE", "GH", - "GI", "KI", "GR", "GL", "GD", "GP", "GU", "GT", "GN", "GY", - "HT", "HM", "VA", "HN", "HK", "HU", "IS", "IN", "ID", "IR", - "IQ", "IE", "IL", "IT", "CI", "JM", "JP", "KZ", "JO", "KE", - "KP", "KR", "KW", "KG", "LA", "LB", "LS", "LV", "LR", "LY", - "LI", "LT", "LU", "MO", "MG", "MW", "MY", "MV", "ML", "MT", - "MQ", "MR", "MU", "MX", "MC", "MN", "MD", "ME", "MS", "MA", - "MZ", "OM", "NA", "NR", "NP", "NL", "CW", "AW", "SX", "BQ", - "NC", "VU", "NZ", "NI", "NE", "NG", "NU", "NF", "NO", "MP", - "UM", "FM", "MH", "PW", "PK", "PA", "PG", "PY", "PE", "PH", - "PN", "PL", "PT", "GW", "TL", "PR", "QA", "RE", "RO", "RU", - "RW", "BL", "SH", "KN", "AI", "LC", "MF", "PM", "VC", "SM", - "ST", "SA", "SN", "RS", "SC", "SL", "SG", "SK", "VN", "SI", - "SO", "ZA", "ZW", "YE", "ES", "SS", "SD", "EH", "SD", "SR", - "SJ", "SZ", "SE", "CH", "SY", "TJ", "TH", "TG", "TK", "TO", - "TT", "AE", "TN", "TR", "TM", "TC", "TV", "UG", "UA", "MK", - "EG", "GB", "JE", "GG", "JE", "IM", "TZ", "US", "VI", "BF", - "UY", "UZ", "VE", "WF", "WS", "YE", "YE", "RS", "ZM", "AA", - "QM", "QN", "QP", "QQ", "QR", "QS", "QT", "EU", "QV", "QW", - "QX", "QY", "QZ", "XA", "XB", "XC", "XD", "XE", "XF", "XG", - "XH", "XI", "XJ", "XK", "XL", "XM", "XN", "XO", "XP", "XQ", - "XR", "XS", "XT", "XU", "XV", "XW", "XX", "XY", "XZ", "ZZ", - }; - - if (const char* replacement = SearchReplacement(regions, aliases, region)) { - region.set(mozilla::MakeStringSpan(replacement)); - return true; - } - return false; - } -} - -// Region subtags with complex mappings. -// Derived from CLDR Supplemental Data, version 39. -// https://unicode.org/Public/cldr/39/core.zip -bool js::intl::LanguageTag::complexRegionMapping(const RegionSubtag& region) { - MOZ_ASSERT(IsStructurallyValidRegionTag(region.span())); - MOZ_ASSERT(IsCanonicallyCasedRegionTag(region.span())); - - if (region.length() == 2) { - return region.equalTo("AN") || - region.equalTo("NT") || - region.equalTo("PC") || - region.equalTo("SU"); - } - - { - static const char regions[8][4] = { - "172", "200", "530", "532", "536", "582", "810", "890", - }; - - return HasReplacement(regions, region); - } -} - -// Language subtags with complex mappings. -// Derived from CLDR Supplemental Data, version 39. -// https://unicode.org/Public/cldr/39/core.zip -void js::intl::LanguageTag::performComplexLanguageMappings() { - MOZ_ASSERT(IsStructurallyValidLanguageTag(language().span())); - MOZ_ASSERT(IsCanonicallyCasedLanguageTag(language().span())); - - if (language().equalTo("cnr")) { - setLanguage("sr"); - if (region().missing()) { - setRegion("ME"); - } - } - else if (language().equalTo("drw") || - language().equalTo("prs") || - language().equalTo("tnf")) { - setLanguage("fa"); - if (region().missing()) { - setRegion("AF"); - } - } - else if (language().equalTo("hbs") || - language().equalTo("sh")) { - setLanguage("sr"); - if (script().missing()) { - setScript("Latn"); - } - } - else if (language().equalTo("swc")) { - setLanguage("sw"); - if (region().missing()) { - setRegion("CD"); - } - } -} - -// Region subtags with complex mappings. -// Derived from CLDR Supplemental Data, version 39. -// https://unicode.org/Public/cldr/39/core.zip -void js::intl::LanguageTag::performComplexRegionMappings() { - MOZ_ASSERT(IsStructurallyValidLanguageTag(language().span())); - MOZ_ASSERT(IsCanonicallyCasedLanguageTag(language().span())); - MOZ_ASSERT(IsStructurallyValidRegionTag(region().span())); - MOZ_ASSERT(IsCanonicallyCasedRegionTag(region().span())); - - if (region().equalTo("172")) { - if (language().equalTo("hy") || - (language().equalTo("und") && script().equalTo("Armn"))) { - setRegion("AM"); - } - else if (language().equalTo("az") || - language().equalTo("tkr") || - language().equalTo("tly") || - language().equalTo("ttt")) { - setRegion("AZ"); - } - else if (language().equalTo("be")) { - setRegion("BY"); - } - else if (language().equalTo("ab") || - language().equalTo("ka") || - (language().equalTo("ku") && script().equalTo("Yezi")) || - language().equalTo("os") || - (language().equalTo("und") && script().equalTo("Geor")) || - (language().equalTo("und") && script().equalTo("Yezi")) || - language().equalTo("xmf")) { - setRegion("GE"); - } - else if (language().equalTo("ky")) { - setRegion("KG"); - } - else if (language().equalTo("kk") || - (language().equalTo("ug") && script().equalTo("Cyrl"))) { - setRegion("KZ"); - } - else if (language().equalTo("gag")) { - setRegion("MD"); - } - else if (language().equalTo("tg")) { - setRegion("TJ"); - } - else if (language().equalTo("tk")) { - setRegion("TM"); - } - else if (language().equalTo("crh") || - language().equalTo("got") || - language().equalTo("ji") || - language().equalTo("rue") || - language().equalTo("uk") || - (language().equalTo("und") && script().equalTo("Goth"))) { - setRegion("UA"); - } - else if (language().equalTo("kaa") || - language().equalTo("sog") || - (language().equalTo("und") && script().equalTo("Chrs")) || - (language().equalTo("und") && script().equalTo("Sogd")) || - (language().equalTo("und") && script().equalTo("Sogo")) || - language().equalTo("uz") || - language().equalTo("xco")) { - setRegion("UZ"); - } - else { - setRegion("RU"); - } - } - else if (region().equalTo("200")) { - if (language().equalTo("sk")) { - setRegion("SK"); - } - else { - setRegion("CZ"); - } - } - else if (region().equalTo("530") || - region().equalTo("532") || - region().equalTo("AN")) { - if (language().equalTo("vic")) { - setRegion("SX"); - } - else { - setRegion("CW"); - } - } - else if (region().equalTo("536") || - region().equalTo("NT")) { - if (language().equalTo("akk") || - language().equalTo("ckb") || - (language().equalTo("ku") && script().equalTo("Arab")) || - language().equalTo("syr") || - (language().equalTo("und") && script().equalTo("Syrc")) || - (language().equalTo("und") && script().equalTo("Xsux"))) { - setRegion("IQ"); - } - else { - setRegion("SA"); - } - } - else if (region().equalTo("582") || - region().equalTo("PC")) { - if (language().equalTo("mh")) { - setRegion("MH"); - } - else if (language().equalTo("pau")) { - setRegion("PW"); - } - else { - setRegion("FM"); - } - } - else if (region().equalTo("810") || - region().equalTo("SU")) { - if (language().equalTo("hy") || - (language().equalTo("und") && script().equalTo("Armn"))) { - setRegion("AM"); - } - else if (language().equalTo("az") || - language().equalTo("tkr") || - language().equalTo("tly") || - language().equalTo("ttt")) { - setRegion("AZ"); - } - else if (language().equalTo("be")) { - setRegion("BY"); - } - else if (language().equalTo("et") || - language().equalTo("vro")) { - setRegion("EE"); - } - else if (language().equalTo("ab") || - language().equalTo("ka") || - (language().equalTo("ku") && script().equalTo("Yezi")) || - language().equalTo("os") || - (language().equalTo("und") && script().equalTo("Geor")) || - (language().equalTo("und") && script().equalTo("Yezi")) || - language().equalTo("xmf")) { - setRegion("GE"); - } - else if (language().equalTo("ky")) { - setRegion("KG"); - } - else if (language().equalTo("kk") || - (language().equalTo("ug") && script().equalTo("Cyrl"))) { - setRegion("KZ"); - } - else if (language().equalTo("lt") || - language().equalTo("sgs")) { - setRegion("LT"); - } - else if (language().equalTo("ltg") || - language().equalTo("lv")) { - setRegion("LV"); - } - else if (language().equalTo("gag")) { - setRegion("MD"); - } - else if (language().equalTo("tg")) { - setRegion("TJ"); - } - else if (language().equalTo("tk")) { - setRegion("TM"); - } - else if (language().equalTo("crh") || - language().equalTo("got") || - language().equalTo("ji") || - language().equalTo("rue") || - language().equalTo("uk") || - (language().equalTo("und") && script().equalTo("Goth"))) { - setRegion("UA"); - } - else if (language().equalTo("kaa") || - language().equalTo("sog") || - (language().equalTo("und") && script().equalTo("Chrs")) || - (language().equalTo("und") && script().equalTo("Sogd")) || - (language().equalTo("und") && script().equalTo("Sogo")) || - language().equalTo("uz") || - language().equalTo("xco")) { - setRegion("UZ"); - } - else { - setRegion("RU"); - } - } - else if (region().equalTo("890")) { - if (language().equalTo("bs")) { - setRegion("BA"); - } - else if (language().equalTo("hr")) { - setRegion("HR"); - } - else if (language().equalTo("mk")) { - setRegion("MK"); - } - else if (language().equalTo("sl")) { - setRegion("SI"); - } - else { - setRegion("RS"); - } - } -} - -static const char* ToCharPointer(const char* str) { - return str; -} - -static const char* ToCharPointer(const js::UniqueChars& str) { - return str.get(); -} - -template -static bool IsLessThan(const T& a, const U& b) { - return strcmp(ToCharPointer(a), ToCharPointer(b)) < 0; -} - -// Mappings from variant subtags to preferred values. -// Derived from CLDR Supplemental Data, version 39. -// https://unicode.org/Public/cldr/39/core.zip -bool js::intl::LanguageTag::performVariantMappings(JSContext* cx) { - // The variant subtags need to be sorted for binary search. - MOZ_ASSERT(std::is_sorted(variants_.begin(), variants_.end(), - IsLessThan)); - - auto removeVariantAt = [&](size_t index) { - variants_.erase(variants_.begin() + index); - }; - - auto insertVariantSortedIfNotPresent = [&](const char* variant) { - auto* p = std::lower_bound(variants_.begin(), variants_.end(), variant, - IsLessThan); - - // Don't insert the replacement when already present. - if (p != variants_.end() && strcmp(p->get(), variant) == 0) { - return true; - } - - // Insert the preferred variant in sort order. - auto preferred = DuplicateString(cx, variant); - if (!preferred) { - return false; - } - return !!variants_.insert(p, std::move(preferred)); - }; - - for (size_t i = 0; i < variants_.length(); ) { - const char* variant = variants_[i].get(); - MOZ_ASSERT(IsCanonicallyCasedVariantTag(mozilla::MakeStringSpan(variant))); - - if (strcmp(variant, "arevela") == 0 || - strcmp(variant, "arevmda") == 0 || - strcmp(variant, "bokmal") == 0 || - strcmp(variant, "hakka") == 0 || - strcmp(variant, "lojban") == 0 || - strcmp(variant, "nynorsk") == 0 || - strcmp(variant, "saaho") == 0 || - strcmp(variant, "xiang") == 0) { - removeVariantAt(i); - } - else if (strcmp(variant, "aaland") == 0) { - removeVariantAt(i); - setRegion("AX"); - } - else if (strcmp(variant, "heploc") == 0) { - removeVariantAt(i); - if (!insertVariantSortedIfNotPresent("alalc97")) { - return false; - } - } - else if (strcmp(variant, "polytoni") == 0) { - removeVariantAt(i); - if (!insertVariantSortedIfNotPresent("polyton")) { - return false; - } - } - else { - i++; - } - } - return true; -} - -// Canonicalize legacy locale identifiers. -// Derived from CLDR Supplemental Data, version 39. -// https://unicode.org/Public/cldr/39/core.zip -bool js::intl::LanguageTag::updateLegacyMappings(JSContext* cx) { - // We're mapping legacy tags to non-legacy form here. - // Other tags remain unchanged. - // - // Legacy tags are either sign language tags ("sgn") or have one or multiple - // variant subtags. Therefore we can quickly exclude most tags by checking - // these two subtags. - - MOZ_ASSERT(IsCanonicallyCasedLanguageTag(language().span())); - - if (!language().equalTo("sgn") && variants().length() == 0) { - return true; - } - - for ([[maybe_unused]] const auto& variant : variants()) { - MOZ_ASSERT(IsStructurallyValidVariantTag(mozilla::MakeStringSpan(variant.get()))); - MOZ_ASSERT(IsCanonicallyCasedVariantTag(mozilla::MakeStringSpan(variant.get()))); - } - - // The variant subtags need to be sorted for binary search. - MOZ_ASSERT(std::is_sorted(variants_.begin(), variants_.end(), - IsLessThan)); - - auto findVariant = [this](const char* variant) { - auto* p = std::lower_bound(variants_.begin(), variants_.end(), variant, - IsLessThan); - - if (p != variants_.end() && strcmp(p->get(), variant) == 0) { - return p; - } - return static_cast(nullptr); - }; - - auto insertVariantSortedIfNotPresent = [&](const char* variant) { - auto* p = std::lower_bound(variants_.begin(), variants_.end(), variant, - IsLessThan); - - // Don't insert the replacement when already present. - if (p != variants_.end() && strcmp(p->get(), variant) == 0) { - return true; - } - - // Insert the preferred variant in sort order. - auto preferred = DuplicateString(cx, variant); - if (!preferred) { - return false; - } - return !!variants_.insert(p, std::move(preferred)); - }; - - auto removeVariant = [&](auto* p) { - size_t index = std::distance(variants_.begin(), p); - variants_.erase(variants_.begin() + index); - }; - - auto removeVariants = [&](auto* p, auto* q) { - size_t pIndex = std::distance(variants_.begin(), p); - size_t qIndex = std::distance(variants_.begin(), q); - MOZ_ASSERT(pIndex < qIndex, "variant subtags are sorted"); - - variants_.erase(variants_.begin() + qIndex); - variants_.erase(variants_.begin() + pIndex); - }; - - if (variants().length() >= 2) { - if (auto* hepburn = findVariant("hepburn")) { - if (auto* heploc = findVariant("heploc")) { - removeVariants(hepburn, heploc); - - if (!insertVariantSortedIfNotPresent("alalc97")) { - return false; - } - } - } - } - - if (language().equalTo("sgn")) { - if (region().present() && signLanguageMapping(language_, region())) { - region_.set(mozilla::MakeStringSpan("")); - } - } - else if (language().equalTo("aa") || - language().equalTo("aar")) { - if (auto* saaho = findVariant("saaho")) { - removeVariant(saaho); - setLanguage("ssy"); - } - } - else if (language().equalTo("arm") || - language().equalTo("hy") || - language().equalTo("hye")) { - if (auto* arevmda = findVariant("arevmda")) { - removeVariant(arevmda); - setLanguage("hyw"); - } - } - else if (language().equalTo("art")) { - if (auto* lojban = findVariant("lojban")) { - removeVariant(lojban); - setLanguage("jbo"); - } - } - else if (language().equalTo("cel")) { - if (auto* gaulish = findVariant("gaulish")) { - removeVariant(gaulish); - setLanguage("xtg"); - } - } - else if (language().equalTo("chi") || - language().equalTo("cmn") || - language().equalTo("zh") || - language().equalTo("zho")) { - if (auto* guoyu = findVariant("guoyu")) { - if (auto* hakka = findVariant("hakka")) { - removeVariants(guoyu, hakka); - setLanguage("hak"); - return true; - } - } - if (auto* guoyu = findVariant("guoyu")) { - if (auto* xiang = findVariant("xiang")) { - removeVariants(guoyu, xiang); - setLanguage("hsn"); - return true; - } - } - if (auto* guoyu = findVariant("guoyu")) { - removeVariant(guoyu); - setLanguage("zh"); - } - else if (auto* hakka = findVariant("hakka")) { - removeVariant(hakka); - setLanguage("hak"); - } - else if (auto* xiang = findVariant("xiang")) { - removeVariant(xiang); - setLanguage("hsn"); - } - } - else if (language().equalTo("no") || - language().equalTo("nor")) { - if (auto* bokmal = findVariant("bokmal")) { - removeVariant(bokmal); - setLanguage("nb"); - } - else if (auto* nynorsk = findVariant("nynorsk")) { - removeVariant(nynorsk); - setLanguage("nn"); - } - } - - return true; -} - -// Mappings from legacy sign languages. -// Derived from CLDR Supplemental Data, version 39. -// https://unicode.org/Public/cldr/39/core.zip -bool js::intl::LanguageTag::signLanguageMapping(LanguageSubtag& language, - const RegionSubtag& region) { - MOZ_ASSERT(language.equalTo("sgn")); - MOZ_ASSERT(IsStructurallyValidRegionTag(region.span())); - MOZ_ASSERT(IsCanonicallyCasedRegionTag(region.span())); - - if (region.length() == 2) { - static const char regions[22][3] = { - "BR", "CO", "DD", "DE", "DK", "ES", "FR", "FX", "GB", "GR", - "IE", "IT", "JP", "MX", "NI", "NL", "NO", "PT", "SE", "UK", - "US", "ZA", - }; - static const char* aliases[22] = { - "bzs", "csn", "gsg", "gsg", "dsl", "ssp", "fsl", "fsl", "bfi", "gss", - "isg", "ise", "jsl", "mfs", "ncs", "dse", "nsi", "psr", "swl", "bfi", - "ase", "sfs", - }; - - if (const char* replacement = SearchReplacement(regions, aliases, region)) { - language.set(mozilla::MakeStringSpan(replacement)); - return true; - } - return false; - } - - { - static const char regions[22][4] = { - "076", "170", "208", "249", "250", "276", "278", "280", "300", "372", - "380", "392", "484", "528", "558", "578", "620", "710", "724", "752", - "826", "840", - }; - static const char* aliases[22] = { - "bzs", "csn", "dsl", "fsl", "fsl", "gsg", "gsg", "gsg", "gss", "isg", - "ise", "jsl", "mfs", "dse", "ncs", "nsi", "psr", "sfs", "ssp", "swl", - "bfi", "ase", - }; - - if (const char* replacement = SearchReplacement(regions, aliases, region)) { - language.set(mozilla::MakeStringSpan(replacement)); - return true; - } - return false; - } -} - -template -static inline bool IsUnicodeKey( - mozilla::Span key, const char (&str)[Length]) { - static_assert(Length == UnicodeKeyLength + 1, - "Unicode extension key is two characters long"); - return memcmp(key.data(), str, Length - 1) == 0; -} - -template -static inline bool IsUnicodeType( - mozilla::Span type, const char (&str)[Length]) { - static_assert(Length > UnicodeKeyLength + 1, - "Unicode extension type contains more than two characters"); - return type.size() == (Length - 1) && - memcmp(type.data(), str, Length - 1) == 0; -} - -static int32_t CompareUnicodeType(const char* a, mozilla::Span b) { - MOZ_ASSERT(!std::char_traits::find(b.data(), b.size(), '\0'), - "unexpected null-character in string"); - - using UnsignedChar = unsigned char; - for (size_t i = 0; i < b.size(); i++) { - // |a| is zero-terminated and |b| doesn't contain a null-terminator. So if - // we've reached the end of |a|, the below if-statement will always be true. - // That ensures we don't read past the end of |a|. - if (int32_t r = UnsignedChar(a[i]) - UnsignedChar(b[i])) { - return r; - } - } - - // Return zero if both strings are equal or a positive number if |b| is a - // prefix of |a|. - return int32_t(UnsignedChar(a[b.size()])); -} - -template -static inline const char* SearchUnicodeReplacement( - const char* (&types)[Length], const char* (&aliases)[Length], - mozilla::Span type) { - - auto p = std::lower_bound(std::begin(types), std::end(types), type, - [](const auto& a, const auto& b) { - return CompareUnicodeType(a, b) < 0; - }); - if (p != std::end(types) && CompareUnicodeType(*p, type) == 0) { - return aliases[std::distance(std::begin(types), p)]; - } - return nullptr; -} - -/** - * Mapping from deprecated BCP 47 Unicode extension types to their preferred - * values. - * - * Spec: https://www.unicode.org/reports/tr35/#Unicode_Locale_Extension_Data_Files - * Spec: https://www.unicode.org/reports/tr35/#t_Extension - */ -const char* js::intl::LanguageTag::replaceUnicodeExtensionType( - mozilla::Span key, mozilla::Span type) { - MOZ_ASSERT(key.size() == UnicodeKeyLength); - MOZ_ASSERT(IsCanonicallyCasedUnicodeKey(key)); - - MOZ_ASSERT(type.size() > UnicodeKeyLength); - MOZ_ASSERT(IsCanonicallyCasedUnicodeType(type)); - - if (IsUnicodeKey(key, "ca")) { - if (IsUnicodeType(type, "ethiopic-amete-alem")) { - return "ethioaa"; - } - if (IsUnicodeType(type, "islamicc")) { - return "islamic-civil"; - } - } - else if (IsUnicodeKey(key, "kb") || - IsUnicodeKey(key, "kc") || - IsUnicodeKey(key, "kh") || - IsUnicodeKey(key, "kk") || - IsUnicodeKey(key, "kn")) { - if (IsUnicodeType(type, "yes")) { - return "true"; - } - } - else if (IsUnicodeKey(key, "ks")) { - if (IsUnicodeType(type, "primary")) { - return "level1"; - } - if (IsUnicodeType(type, "tertiary")) { - return "level3"; - } - } - else if (IsUnicodeKey(key, "ms")) { - if (IsUnicodeType(type, "imperial")) { - return "uksystem"; - } - } - else if (IsUnicodeKey(key, "rg") || - IsUnicodeKey(key, "sd")) { - static const char* types[144] = { - "cn11", "cn12", "cn13", "cn14", "cn15", "cn21", "cn22", - "cn23", "cn31", "cn32", "cn33", "cn34", "cn35", "cn36", - "cn37", "cn41", "cn42", "cn43", "cn44", "cn45", "cn46", - "cn50", "cn51", "cn52", "cn53", "cn54", "cn61", "cn62", - "cn63", "cn64", "cn65", "cn71", "cn91", "cn92", "cz10a", - "cz10b", "cz10c", "cz10d", "cz10e", "cz10f", "cz611", "cz612", - "cz613", "cz614", "cz615", "cz621", "cz622", "cz623", "cz624", - "cz626", "cz627", "czjc", "czjm", "czka", "czkr", "czli", - "czmo", "czol", "czpa", "czpl", "czpr", "czst", "czus", - "czvy", "czzl", "fi01", "fra", "frb", "frbl", "frc", - "frcp", "frd", "fre", "frf", "frg", "frgf", "frgp", - "frh", "fri", "frj", "frk", "frl", "frm", "frmf", - "frmq", "frn", "frnc", "fro", "frp", "frpf", "frpm", - "frq", "frr", "frre", "frs", "frt", "frtf", "fru", - "frv", "frwf", "fryt", "laxn", "lud", "lug", "lul", - "mrnkc", "nlaw", "nlcw", "nlsx", "no23", "nzn", "nzs", - "omba", "omsh", "plds", "plkp", "pllb", "plld", "pllu", - "plma", "plmz", "plop", "plpd", "plpk", "plpm", "plsk", - "plsl", "plwn", "plwp", "plzp", "shta", "tteto", "ttrcm", - "ttwto", "twkhq", "twtnq", "twtpq", "twtxq", "usas", "usgu", - "usmp", "uspr", "usum", "usvi", - }; - static const char* aliases[144] = { - "cnbj", "cntj", "cnhe", "cnsx", "cnmn", "cnln", "cnjl", - "cnhl", "cnsh", "cnjs", "cnzj", "cnah", "cnfj", "cnjx", - "cnsd", "cnha", "cnhb", "cnhn", "cngd", "cngx", "cnhi", - "cncq", "cnsc", "cngz", "cnyn", "cnxz", "cnsn", "cngs", - "cnqh", "cnnx", "cnxj", "twzzzz", "hkzzzz", "mozzzz", "cz110", - "cz111", "cz112", "cz113", "cz114", "cz115", "cz663", "cz632", - "cz633", "cz634", "cz635", "cz641", "cz642", "cz643", "cz644", - "cz646", "cz647", "cz31", "cz64", "cz41", "cz52", "cz51", - "cz80", "cz71", "cz53", "cz32", "cz10", "cz20", "cz42", - "cz63", "cz72", "axzzzz", "frges", "frnaq", "blzzzz", "frara", - "cpzzzz", "frbfc", "frbre", "frcvl", "frges", "gfzzzz", "gpzzzz", - "frcor", "frbfc", "fridf", "frocc", "frnaq", "frges", "mfzzzz", - "mqzzzz", "frocc", "nczzzz", "frhdf", "frnor", "pfzzzz", "pmzzzz", - "frnor", "frpdl", "rezzzz", "frhdf", "frnaq", "tfzzzz", "frpac", - "frara", "wfzzzz", "ytzzzz", "laxs", "lucl", "luec", "luca", - "mr13", "awzzzz", "cwzzzz", "sxzzzz", "no50", "nzauk", "nzcan", - "ombj", "omsj", "pl02", "pl04", "pl08", "pl10", "pl06", - "pl12", "pl14", "pl16", "pl20", "pl18", "pl22", "pl26", - "pl24", "pl28", "pl30", "pl32", "tazzzz", "tttob", "ttmrc", - "tttob", "twkhh", "twtnn", "twnwt", "twtxg", "aszzzz", "guzzzz", - "mpzzzz", "przzzz", "umzzzz", "vizzzz", - }; - return SearchUnicodeReplacement(types, aliases, type); - } - else if (IsUnicodeKey(key, "tz")) { - static const char* types[28] = { - "aqams", "cnckg", "cnhrb", "cnkhg", "cuba", "egypt", - "eire", "est", "gmt0", "hongkong", "hst", "iceland", - "iran", "israel", "jamaica", "japan", "libya", "mst", - "navajo", "poland", "portugal", "prc", "roc", "rok", - "turkey", "uct", "usnavajo", "zulu", - }; - static const char* aliases[28] = { - "nzakl", "cnsha", "cnsha", "cnurc", "cuhav", "egcai", - "iedub", "utcw05", "gmt", "hkhkg", "utcw10", "isrey", - "irthr", "jeruslm", "jmkin", "jptyo", "lytip", "utcw07", - "usden", "plwaw", "ptlis", "cnsha", "twtpe", "krsel", - "trist", "utc", "usden", "utc", - }; - return SearchUnicodeReplacement(types, aliases, type); - } - return nullptr; -} - -template -static inline bool IsTransformKey( - mozilla::Span key, const char (&str)[Length]) { - static_assert(Length == TransformKeyLength + 1, - "Transform extension key is two characters long"); - return memcmp(key.data(), str, Length - 1) == 0; -} - -template -static inline bool IsTransformType( - mozilla::Span type, const char (&str)[Length]) { - static_assert(Length > TransformKeyLength + 1, - "Transform extension type contains more than two characters"); - return type.size() == (Length - 1) && - memcmp(type.data(), str, Length - 1) == 0; -} - -/** - * Mapping from deprecated BCP 47 Transform extension types to their preferred - * values. - * - * Spec: https://www.unicode.org/reports/tr35/#Unicode_Locale_Extension_Data_Files - * Spec: https://www.unicode.org/reports/tr35/#t_Extension - */ -const char* js::intl::LanguageTag::replaceTransformExtensionType( - mozilla::Span key, mozilla::Span type) { - MOZ_ASSERT(key.size() == TransformKeyLength); - MOZ_ASSERT(IsCanonicallyCasedTransformKey(key)); - - MOZ_ASSERT(type.size() > TransformKeyLength); - MOZ_ASSERT(IsCanonicallyCasedTransformType(type)); - - if (IsTransformKey(key, "d0")) { - if (IsTransformType(type, "name")) { - return "charname"; - } - } - else if (IsTransformKey(key, "m0")) { - if (IsTransformType(type, "names")) { - return "prprname"; - } - } - return nullptr; -} diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/intl/LanguageTag.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/intl/LanguageTag.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/intl/LanguageTag.h 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/intl/LanguageTag.h 2021-10-20 19:28:22.000000000 +0000 @@ -9,17 +9,8 @@ #ifndef builtin_intl_LanguageTag_h #define builtin_intl_LanguageTag_h -#include "mozilla/Assertions.h" +#include "mozilla/intl/Locale.h" #include "mozilla/Span.h" -#include "mozilla/TextUtils.h" -#include "mozilla/TypedEnumBits.h" -#include "mozilla/Variant.h" - -#include -#include -#include -#include -#include #include "js/AllocPolicy.h" #include "js/GCAPI.h" @@ -38,702 +29,32 @@ namespace intl { /** - * Return true if |language| is a valid language subtag. + * Parse a string Unicode BCP 47 locale identifier. If successful, store in + * |result| and return true. Otherwise return false. */ -template -bool IsStructurallyValidLanguageTag(mozilla::Span language); - -/** - * Return true if |script| is a valid script subtag. - */ -template -bool IsStructurallyValidScriptTag(mozilla::Span script); - -/** - * Return true if |region| is a valid region subtag. - */ -template -bool IsStructurallyValidRegionTag(mozilla::Span region); - -#ifdef DEBUG -/** - * Return true if |variant| is a valid variant subtag. - */ -bool IsStructurallyValidVariantTag(mozilla::Span variant); - -/** - * Return true if |extension| is a valid Unicode extension subtag. - */ -bool IsStructurallyValidUnicodeExtensionTag( - mozilla::Span extension); - -/** - * Return true if |privateUse| is a valid private-use subtag. - */ -bool IsStructurallyValidPrivateUseTag(mozilla::Span privateUse); - -#endif - -template -char AsciiToLowerCase(CharT c) { - MOZ_ASSERT(mozilla::IsAscii(c)); - return mozilla::IsAsciiUppercaseAlpha(c) ? (c + 0x20) : c; -} - -template -char AsciiToUpperCase(CharT c) { - MOZ_ASSERT(mozilla::IsAscii(c)); - return mozilla::IsAsciiLowercaseAlpha(c) ? (c - 0x20) : c; -} - -template -void AsciiToLowerCase(CharT* chars, size_t length, char* dest) { - // Tell the analysis the |std::transform| function can't GC. - JS::AutoSuppressGCAnalysis nogc; - - char (&fn)(CharT) = AsciiToLowerCase; - std::transform(chars, chars + length, dest, fn); -} - -template -void AsciiToUpperCase(CharT* chars, size_t length, char* dest) { - // Tell the analysis the |std::transform| function can't GC. - JS::AutoSuppressGCAnalysis nogc; - - char (&fn)(CharT) = AsciiToUpperCase; - std::transform(chars, chars + length, dest, fn); -} - -template -void AsciiToTitleCase(CharT* chars, size_t length, char* dest) { - if (length > 0) { - AsciiToUpperCase(chars, 1, dest); - AsciiToLowerCase(chars + 1, length - 1, dest + 1); - } -} - -// Constants for language subtag lengths. -namespace LanguageTagLimits { - -// unicode_language_subtag = alpha{2,3} | alpha{5,8} ; -static constexpr size_t LanguageLength = 8; - -// unicode_script_subtag = alpha{4} ; -static constexpr size_t ScriptLength = 4; - -// unicode_region_subtag = (alpha{2} | digit{3}) ; -static constexpr size_t RegionLength = 3; -static constexpr size_t AlphaRegionLength = 2; -static constexpr size_t DigitRegionLength = 3; - -// key = alphanum alpha ; -static constexpr size_t UnicodeKeyLength = 2; - -// tkey = alpha digit ; -static constexpr size_t TransformKeyLength = 2; - -} // namespace LanguageTagLimits - -// Fixed size language subtag which is stored inline in LanguageTag. -template -class LanguageTagSubtag final { - uint8_t length_ = 0; - char chars_[Length] = {}; // zero initialize - - public: - LanguageTagSubtag() = default; - - LanguageTagSubtag(const LanguageTagSubtag&) = delete; - LanguageTagSubtag& operator=(const LanguageTagSubtag&) = delete; - - size_t length() const { return length_; } - bool missing() const { return length_ == 0; } - bool present() const { return length_ > 0; } - - mozilla::Span span() const { return {chars_, length_}; } - - template - void set(mozilla::Span str) { - MOZ_ASSERT(str.size() <= Length); - std::copy_n(str.data(), str.size(), chars_); - length_ = str.size(); - } - - // The toXYZCase() methods are using |Length| instead of |length()|, because - // current compilers (tested GCC and Clang) can't infer the maximum string - // length - even when using hints like |std::min| - and instead are emitting - // SIMD optimized code. Using a fixed sized length avoids emitting the SIMD - // code. (Emitting SIMD code doesn't make sense here, because the SIMD code - // only kicks in for long strings.) A fixed length will additionally ensure - // the compiler unrolls the loop in the case conversion code. - - void toLowerCase() { AsciiToLowerCase(chars_, Length, chars_); } - - void toUpperCase() { AsciiToUpperCase(chars_, Length, chars_); } - - void toTitleCase() { AsciiToTitleCase(chars_, Length, chars_); } - - template - bool equalTo(const char (&str)[N]) const { - static_assert(N - 1 <= Length, - "subtag literals must not exceed the maximum subtag length"); - - return length_ == N - 1 && memcmp(chars_, str, N - 1) == 0; - } -}; - -using LanguageSubtag = LanguageTagSubtag; -using ScriptSubtag = LanguageTagSubtag; -using RegionSubtag = LanguageTagSubtag; - -/** - * Object representing a language tag. - * - * All subtags are already in canonicalized case. - */ -class MOZ_STACK_CLASS LanguageTag final { - LanguageSubtag language_ = {}; - ScriptSubtag script_ = {}; - RegionSubtag region_ = {}; - - using VariantsVector = Vector; - using ExtensionsVector = Vector; - - VariantsVector variants_; - ExtensionsVector extensions_; - JS::UniqueChars privateuse_ = nullptr; - - friend class LanguageTagParser; - - bool canonicalizeUnicodeExtension(JSContext* cx, - JS::UniqueChars& unicodeExtension); - - bool canonicalizeTransformExtension(JSContext* cx, - JS::UniqueChars& transformExtension); - - public: - static bool languageMapping(LanguageSubtag& language); - static bool complexLanguageMapping(const LanguageSubtag& language); - - private: - static bool scriptMapping(ScriptSubtag& script); - static bool regionMapping(RegionSubtag& region); - static bool complexRegionMapping(const RegionSubtag& region); - - void performComplexLanguageMappings(); - void performComplexRegionMappings(); - [[nodiscard]] bool performVariantMappings(JSContext* cx); - - [[nodiscard]] bool updateLegacyMappings(JSContext* cx); - - static bool signLanguageMapping(LanguageSubtag& language, - const RegionSubtag& region); - - static const char* replaceTransformExtensionType( - mozilla::Span key, mozilla::Span type); - - public: - /** - * Given a Unicode key and type, return the null-terminated preferred - * replacement for that type if there is one, or null if there is none, e.g. - * in effect - * |replaceUnicodeExtensionType("ca", "islamicc") == "islamic-civil"| - * and - * |replaceUnicodeExtensionType("ca", "islamic-civil") == nullptr|. - */ - static const char* replaceUnicodeExtensionType( - mozilla::Span key, mozilla::Span type); - - public: - explicit LanguageTag(JSContext* cx) : variants_(cx), extensions_(cx) {} - - LanguageTag(const LanguageTag&) = delete; - LanguageTag& operator=(const LanguageTag&) = delete; - - const LanguageSubtag& language() const { return language_; } - const ScriptSubtag& script() const { return script_; } - const RegionSubtag& region() const { return region_; } - const auto& variants() const { return variants_; } - const auto& extensions() const { return extensions_; } - const char* privateuse() const { return privateuse_.get(); } - - /** - * Return the Unicode extension subtag or nullptr if not present. - */ - const char* unicodeExtension() const; - - private: - ptrdiff_t unicodeExtensionIndex() const; - - public: - /** - * Set the language subtag. The input must be a valid language subtag. - */ - template - void setLanguage(const char (&language)[N]) { - mozilla::Span span(language, N - 1); - MOZ_ASSERT(IsStructurallyValidLanguageTag(span)); - language_.set(span); - } - - /** - * Set the language subtag. The input must be a valid language subtag. - */ - void setLanguage(const LanguageSubtag& language) { - MOZ_ASSERT(IsStructurallyValidLanguageTag(language.span())); - language_.set(language.span()); - } - - /** - * Set the script subtag. The input must be a valid script subtag. - */ - template - void setScript(const char (&script)[N]) { - mozilla::Span span(script, N - 1); - MOZ_ASSERT(IsStructurallyValidScriptTag(span)); - script_.set(span); - } - - /** - * Set the script subtag. The input must be a valid script subtag or the empty - * string. - */ - void setScript(const ScriptSubtag& script) { - MOZ_ASSERT(script.missing() || IsStructurallyValidScriptTag(script.span())); - script_.set(script.span()); - } - - /** - * Set the region subtag. The input must be a valid region subtag. - */ - template - void setRegion(const char (®ion)[N]) { - mozilla::Span span(region, N - 1); - MOZ_ASSERT(IsStructurallyValidRegionTag(span)); - region_.set(span); - } - - /** - * Set the region subtag. The input must be a valid region subtag or the empty - * empty string. - */ - void setRegion(const RegionSubtag& region) { - MOZ_ASSERT(region.missing() || IsStructurallyValidRegionTag(region.span())); - region_.set(region.span()); - } - - /** - * Removes all variant subtags. - */ - void clearVariants() { variants_.clearAndFree(); } - - /** - * Set the Unicode extension subtag. The input must be a valid Unicode - * extension subtag. - */ - bool setUnicodeExtension(JS::UniqueChars extension); - - /** - * Remove any Unicode extension subtag if present. - */ - void clearUnicodeExtension(); - - /** - * Set the private-use subtag. The input must be a valid private-use subtag - * or nullptr. - */ - void setPrivateuse(JS::UniqueChars privateuse) { - MOZ_ASSERT(!privateuse || - IsStructurallyValidPrivateUseTag( - {privateuse.get(), strlen(privateuse.get())})); - privateuse_ = std::move(privateuse); - } - - /** Canonicalize the base-name (language, script, region, variant) subtags. */ - bool canonicalizeBaseName(JSContext* cx); - - /** - * Canonicalize all extension subtags. - */ - bool canonicalizeExtensions(JSContext* cx); - - /** - * Canonicalizes the given structurally valid Unicode BCP 47 locale - * identifier, including regularized case of subtags. For example, the - * language tag Zh-haNS-bu-variant2-Variant1-u-ca-chinese-t-Zh-laTN-x-PRIVATE, - * where - * - * Zh ; 2*3ALPHA - * -haNS ; ["-" script] - * -bu ; ["-" region] - * -variant2 ; *("-" variant) - * -Variant1 - * -u-ca-chinese ; *("-" extension) - * -t-Zh-laTN - * -x-PRIVATE ; ["-" privateuse] - * - * becomes zh-Hans-MM-variant1-variant2-t-zh-latn-u-ca-chinese-x-private - * - * Spec: ECMAScript Internationalization API Specification, 6.2.3. - */ - bool canonicalize(JSContext* cx) { - return canonicalizeBaseName(cx) && canonicalizeExtensions(cx); - } - - /** - * Return the string representation of this language tag. - */ - JSString* toString(JSContext* cx) const; - - /** - * Return the string representation of this language tag as a null-terminated - * C-string. - */ - JS::UniqueChars toStringZ(JSContext* cx) const; - - /** - * Add likely-subtags to the language tag. - * - * Spec: - */ - bool addLikelySubtags(JSContext* cx); - - /** - * Remove likely-subtags from the language tag. - * - * Spec: - */ - bool removeLikelySubtags(JSContext* cx); -}; - -/** - * Parser for Unicode BCP 47 locale identifiers. - * - * - */ -class MOZ_STACK_CLASS LanguageTagParser final { - public: - // Exposed as |public| for |MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS|. - enum class TokenKind : uint8_t { - None = 0b000, - Alpha = 0b001, - Digit = 0b010, - AlphaDigit = 0b011, - Error = 0b100 - }; - - private: - class Token final { - size_t index_; - size_t length_; - TokenKind kind_; - - public: - Token(TokenKind kind, size_t index, size_t length) - : index_(index), length_(length), kind_(kind) {} - - TokenKind kind() const { return kind_; } - size_t index() const { return index_; } - size_t length() const { return length_; } - - bool isError() const { return kind_ == TokenKind::Error; } - bool isNone() const { return kind_ == TokenKind::None; } - bool isAlpha() const { return kind_ == TokenKind::Alpha; } - bool isDigit() const { return kind_ == TokenKind::Digit; } - bool isAlphaDigit() const { return kind_ == TokenKind::AlphaDigit; } - }; - - using LocaleChars = mozilla::Variant; - - const LocaleChars& locale_; - size_t length_; - size_t index_ = 0; - - LanguageTagParser(const LocaleChars& locale, size_t length) - : locale_(locale), length_(length) {} - - char16_t charAtUnchecked(size_t index) const { - if (locale_.is()) { - return locale_.as()[index]; - } - return locale_.as()[index]; - } - - char charAt(size_t index) const { - char16_t c = charAtUnchecked(index); - MOZ_ASSERT(mozilla::IsAscii(c)); - return c; - } - - // Copy the token characters into |subtag|. - template - void copyChars(const Token& tok, LanguageTagSubtag& subtag) const { - size_t index = tok.index(); - size_t length = tok.length(); - if (locale_.is()) { - using T = const JS::Latin1Char; - subtag.set(mozilla::Span(locale_.as() + index, length)); - } else { - using T = const char16_t; - subtag.set(mozilla::Span(locale_.as() + index, length)); - } - } - - // Create a string copy of |length| characters starting at |index|. - JS::UniqueChars chars(JSContext* cx, size_t index, size_t length) const; - - // Create a string copy of the token characters. - JS::UniqueChars chars(JSContext* cx, const Token& tok) const { - return chars(cx, tok.index(), tok.length()); - } - - JS::UniqueChars extension(JSContext* cx, const Token& start, - const Token& end) const { - MOZ_ASSERT(start.index() < end.index()); - - size_t length = end.index() - 1 - start.index(); - return chars(cx, start.index(), length); - } - - Token nextToken(); - - // unicode_language_subtag = alpha{2,3} | alpha{5,8} ; - // - // Four character language subtags are not allowed in Unicode BCP 47 locale - // identifiers. Also see the comparison to Unicode CLDR locale identifiers in - // . - bool isLanguage(const Token& tok) const { - return tok.isAlpha() && ((2 <= tok.length() && tok.length() <= 3) || - (5 <= tok.length() && tok.length() <= 8)); - } - - // unicode_script_subtag = alpha{4} ; - bool isScript(const Token& tok) const { - return tok.isAlpha() && tok.length() == 4; - } - - // unicode_region_subtag = (alpha{2} | digit{3}) ; - bool isRegion(const Token& tok) const { - return (tok.isAlpha() && tok.length() == 2) || - (tok.isDigit() && tok.length() == 3); - } - - // unicode_variant_subtag = (alphanum{5,8} | digit alphanum{3}) ; - bool isVariant(const Token& tok) const { - return (5 <= tok.length() && tok.length() <= 8) || - (tok.length() == 4 && mozilla::IsAsciiDigit(charAt(tok.index()))); - } - - // Returns the code unit of the first character at the given singleton token. - // Always returns the lower case form of an alphabetical character. - char singletonKey(const Token& tok) const { - MOZ_ASSERT(tok.length() == 1); - return AsciiToLowerCase(charAt(tok.index())); - } - - // extensions = unicode_locale_extensions | - // transformed_extensions | - // other_extensions ; - // - // unicode_locale_extensions = sep [uU] ((sep keyword)+ | - // (sep attribute)+ (sep keyword)*) ; - // - // transformed_extensions = sep [tT] ((sep tlang (sep tfield)*) | - // (sep tfield)+) ; - // - // other_extensions = sep [alphanum-[tTuUxX]] (sep alphanum{2,8})+ ; - bool isExtensionStart(const Token& tok) const { - return tok.length() == 1 && singletonKey(tok) != 'x'; - } - - // other_extensions = sep [alphanum-[tTuUxX]] (sep alphanum{2,8})+ ; - bool isOtherExtensionPart(const Token& tok) const { - return 2 <= tok.length() && tok.length() <= 8; - } - - // unicode_locale_extensions = sep [uU] ((sep keyword)+ | - // (sep attribute)+ (sep keyword)*) ; - // keyword = key (sep type)? ; - bool isUnicodeExtensionPart(const Token& tok) const { - return isUnicodeExtensionKey(tok) || isUnicodeExtensionType(tok) || - isUnicodeExtensionAttribute(tok); - } - - // attribute = alphanum{3,8} ; - bool isUnicodeExtensionAttribute(const Token& tok) const { - return 3 <= tok.length() && tok.length() <= 8; - } - - // key = alphanum alpha ; - bool isUnicodeExtensionKey(const Token& tok) const { - return tok.length() == 2 && mozilla::IsAsciiAlpha(charAt(tok.index() + 1)); - } - - // type = alphanum{3,8} (sep alphanum{3,8})* ; - bool isUnicodeExtensionType(const Token& tok) const { - return 3 <= tok.length() && tok.length() <= 8; - } - - // tkey = alpha digit ; - bool isTransformExtensionKey(const Token& tok) const { - return tok.length() == 2 && mozilla::IsAsciiAlpha(charAt(tok.index())) && - mozilla::IsAsciiDigit(charAt(tok.index() + 1)); - } - - // tvalue = (sep alphanum{3,8})+ ; - bool isTransformExtensionPart(const Token& tok) const { - return 3 <= tok.length() && tok.length() <= 8; - } - - // pu_extensions = sep [xX] (sep alphanum{1,8})+ ; - bool isPrivateUseStart(const Token& tok) const { - return tok.length() == 1 && singletonKey(tok) == 'x'; - } - - // pu_extensions = sep [xX] (sep alphanum{1,8})+ ; - bool isPrivateUsePart(const Token& tok) const { - return 1 <= tok.length() && tok.length() <= 8; - } - - // Helper function for use in |parseBaseName| and - // |parseTlangInTransformExtension|. Do not use this directly! - static JS::Result internalParseBaseName(JSContext* cx, - LanguageTagParser& ts, - LanguageTag& tag, Token& tok); - - // Parse the `unicode_language_id` production, i.e. the - // language/script/region/variants portion of a language tag, into |tag|. - // |tok| must be the current token. - static JS::Result parseBaseName(JSContext* cx, LanguageTagParser& ts, - LanguageTag& tag, Token& tok) { - return internalParseBaseName(cx, ts, tag, tok); - } - - // Parse the `tlang` production within a parsed 't' transform extension. - // The precise requirements for "previously parsed" are: - // - // * the input begins from current token |tok| with a valid `tlang` - // * the `tlang` is wholly lowercase (*not* canonical case) - // * variant subtags in the `tlang` may contain duplicates and be - // unordered - // - // Return an error on internal failure. Otherwise, return a success value. If - // there was no `tlang`, then |tag.language().missing()|. But if there was a - // `tlang`, then |tag| is filled with subtags exactly as they appeared in the - // parse input. - static JS::Result parseTlangInTransformExtension( - JSContext* cx, LanguageTagParser& ts, LanguageTag& tag, Token& tok) { - MOZ_ASSERT(ts.isLanguage(tok)); - return internalParseBaseName(cx, ts, tag, tok).map([](bool parsed) { - MOZ_ASSERT(parsed); - return JS::Ok(); - }); - } - - friend class LanguageTag; - - class Range final { - size_t begin_; - size_t length_; - - public: - Range(size_t begin, size_t length) : begin_(begin), length_(length) {} - - template - T* begin(T* ptr) const { - return ptr + begin_; - } - - size_t length() const { return length_; } - }; - - using TFieldVector = js::Vector; - using AttributesVector = js::Vector; - using KeywordsVector = js::Vector; - - // Parse |extension|, which must be a validated, fully lowercase - // `transformed_extensions` subtag, and fill |tag| and |fields| from the - // `tlang` and `tfield` components. Data in |tag| is lowercase, consistent - // with |extension|. - static JS::Result parseTransformExtension( - JSContext* cx, mozilla::Span extension, LanguageTag& tag, - TFieldVector& fields); - - // Parse |extension|, which must be a validated, fully lowercase - // `unicode_locale_extensions` subtag, and fill |attributes| and |keywords| - // from the `attribute` and `keyword` components. - static JS::Result parseUnicodeExtension( - JSContext* cx, mozilla::Span extension, - AttributesVector& attributes, KeywordsVector& keywords); - - static JS::Result tryParse(JSContext* cx, LocaleChars& localeChars, - size_t localeLength, LanguageTag& tag); - - public: - // Parse the input string as a language tag. Reports an error to the context - // if the input can't be parsed completely. - static bool parse(JSContext* cx, JSLinearString* locale, LanguageTag& tag); - - // Parse the input string as a language tag. Reports an error to the context - // if the input can't be parsed completely. - static bool parse(JSContext* cx, mozilla::Span locale, - LanguageTag& tag); - - // Parse the input string as a language tag. Returns Ok(true) if the input - // could be completely parsed, Ok(false) if the input couldn't be parsed, - // or Err() in case of internal error. - static JS::Result tryParse(JSContext* cx, JSLinearString* locale, - LanguageTag& tag); - - // Parse the input string as a language tag. Returns Ok(true) if the input - // could be completely parsed, Ok(false) if the input couldn't be parsed, - // or Err() in case of internal error. - static JS::Result tryParse(JSContext* cx, - mozilla::Span locale, - LanguageTag& tag); - - // Parse the input string as the base-name parts (language, script, region, - // variants) of a language tag. Ignores any trailing characters. - static bool parseBaseName(JSContext* cx, mozilla::Span locale, - LanguageTag& tag); - - // Parse the input string as the base-name parts (language, script, region, - // variants) of a language tag. Returns Ok(true) if the input could be - // completely parsed, Ok(false) if the input couldn't be parsed, or Err() in - // case of internal error. - static JS::Result tryParseBaseName(JSContext* cx, - JSLinearString* locale, - LanguageTag& tag); - - // Return true iff |extension| can be parsed as a Unicode extension subtag. - static bool canParseUnicodeExtension(mozilla::Span extension); - - // Return true iff |unicodeType| can be parsed as a Unicode extension type. - static bool canParseUnicodeExtensionType(JSLinearString* unicodeType); -}; - -MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(LanguageTagParser::TokenKind) +[[nodiscard]] bool ParseLocale(JSContext* cx, JS::Handle str, + mozilla::intl::Locale& result); /** * Parse a string as a standalone |language| tag. If |str| is a standalone * language tag, store it in |result| and return true. Otherwise return false. */ -[[nodiscard]] bool ParseStandaloneLanguageTag(JS::Handle str, - LanguageSubtag& result); +[[nodiscard]] bool ParseStandaloneLanguageTag( + JS::Handle str, mozilla::intl::LanguageSubtag& result); /** * Parse a string as a standalone |script| tag. If |str| is a standalone script * tag, store it in |result| and return true. Otherwise return false. */ -[[nodiscard]] bool ParseStandaloneScriptTag(JS::Handle str, - ScriptSubtag& result); +[[nodiscard]] bool ParseStandaloneScriptTag( + JS::Handle str, mozilla::intl::ScriptSubtag& result); /** * Parse a string as a standalone |region| tag. If |str| is a standalone region * tag, store it in |result| and return true. Otherwise return false. */ -[[nodiscard]] bool ParseStandaloneRegionTag(JS::Handle str, - RegionSubtag& result); +[[nodiscard]] bool ParseStandaloneRegionTag( + JS::Handle str, mozilla::intl::RegionSubtag& result); /** * Parse a string as an ISO-639 language code. Return |nullptr| in the result if @@ -744,13 +65,15 @@ JSContext* cx, JS::Handle str); class UnicodeExtensionKeyword final { - char key_[LanguageTagLimits::UnicodeKeyLength]; + char key_[mozilla::intl::LanguageTagLimits::UnicodeKeyLength]; JSLinearString* type_; public: - using UnicodeKey = const char (&)[LanguageTagLimits::UnicodeKeyLength + 1]; + using UnicodeKey = + const char (&)[mozilla::intl::LanguageTagLimits::UnicodeKeyLength + 1]; using UnicodeKeySpan = - mozilla::Span; + mozilla::Span; UnicodeExtensionKeyword(UnicodeKey key, JSLinearString* type) : key_{key[0], key[1]}, type_(type) {} @@ -762,7 +85,7 @@ }; [[nodiscard]] extern bool ApplyUnicodeExtensionToTag( - JSContext* cx, LanguageTag& tag, + JSContext* cx, mozilla::intl::Locale& tag, JS::HandleVector keywords); } // namespace intl diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/intl/Locale.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/intl/Locale.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/intl/Locale.cpp 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/intl/Locale.cpp 2021-10-20 19:28:22.000000000 +0000 @@ -11,6 +11,7 @@ #include "mozilla/ArrayUtils.h" #include "mozilla/Assertions.h" #include "mozilla/Casting.h" +#include "mozilla/intl/Locale.h" #include "mozilla/Maybe.h" #include "mozilla/Span.h" #include "mozilla/TextUtils.h" @@ -23,7 +24,9 @@ #include "builtin/Boolean.h" #include "builtin/intl/CommonFunctions.h" +#include "builtin/intl/FormatBuffer.h" #include "builtin/intl/LanguageTag.h" +#include "builtin/intl/StringAsciiChars.h" #include "builtin/String.h" #include "gc/Rooting.h" #include "js/Conversions.h" @@ -42,10 +45,7 @@ #include "vm/NativeObject-inl.h" using namespace js; -using namespace js::intl::LanguageTagLimits; - -using intl::LanguageTag; -using intl::LanguageTagParser; +using namespace mozilla::intl::LanguageTagLimits; const JSClass LocaleObject::class_ = { "Intl.Locale", @@ -60,7 +60,7 @@ } // Return the length of the base-name subtags. -static size_t BaseNameLength(const LanguageTag& tag) { +static size_t BaseNameLength(const mozilla::intl::Locale& tag) { size_t baseNameLength = tag.language().length(); if (tag.script().present()) { baseNameLength += 1 + tag.script().length(); @@ -88,7 +88,7 @@ // Compute the Unicode extension's index and length in the extension subtag. static mozilla::Maybe UnicodeExtensionPosition( - const LanguageTag& tag) { + const mozilla::intl::Locale& tag) { size_t index = 0; for (const auto& extension : tag.extensions()) { MOZ_ASSERT(!mozilla::IsAsciiUppercaseAlpha(extension[0]), @@ -106,8 +106,14 @@ } static LocaleObject* CreateLocaleObject(JSContext* cx, HandleObject prototype, - const LanguageTag& tag) { - RootedString tagStr(cx, tag.toString(cx)); + const mozilla::intl::Locale& tag) { + intl::FormatBuffer buffer(cx); + if (auto result = tag.toString(buffer); result.isErr()) { + intl::ReportInternalError(cx, result.unwrapErr()); + return nullptr; + } + + RootedString tagStr(cx, buffer.toString(cx)); if (!tagStr) { return nullptr; } @@ -142,9 +148,27 @@ return locale; } -static inline bool IsValidUnicodeExtensionValue(JSLinearString* linear) { - return linear->length() > 0 && - LanguageTagParser::canParseUnicodeExtensionType(linear); +static inline bool IsValidUnicodeExtensionValue(JSContext* cx, + JSLinearString* linear, + bool* isValid) { + if (linear->length() == 0) { + *isValid = false; + return true; + } + + if (!StringIsAscii(linear)) { + *isValid = false; + return true; + } + + intl::StringAsciiChars chars(linear); + if (!chars.init(cx)) { + return false; + } + + *isValid = + mozilla::intl::LocaleParser::canParseUnicodeExtensionType(chars).isOk(); + return true; } /** Iterate through (sep keyword) in a valid, lowercased Unicode extension. */ @@ -274,7 +298,7 @@ /** * ApplyOptionsToTag ( tag, options ) */ -static bool ApplyOptionsToTag(JSContext* cx, LanguageTag& tag, +static bool ApplyOptionsToTag(JSContext* cx, mozilla::intl::Locale& tag, HandleObject options) { // Steps 1-2 (Already performed in caller). @@ -286,7 +310,7 @@ } // Step 4. - intl::LanguageSubtag language; + mozilla::intl::LanguageSubtag language; if (option && !intl::ParseStandaloneLanguageTag(option, language)) { if (UniqueChars str = QuoteString(cx, option, '"')) { JS_ReportErrorNumberASCII(cx, js::GetErrorMessage, nullptr, @@ -302,7 +326,7 @@ } // Step 6. - intl::ScriptSubtag script; + mozilla::intl::ScriptSubtag script; if (option && !intl::ParseStandaloneScriptTag(option, script)) { if (UniqueChars str = QuoteString(cx, option, '"')) { JS_ReportErrorNumberASCII(cx, js::GetErrorMessage, nullptr, @@ -318,7 +342,7 @@ } // Step 8. - intl::RegionSubtag region; + mozilla::intl::RegionSubtag region; if (option && !intl::ParseStandaloneRegionTag(option, region)) { if (UniqueChars str = QuoteString(cx, option, '"')) { JS_ReportErrorNumberASCII(cx, js::GetErrorMessage, nullptr, @@ -350,8 +374,16 @@ // Step 13. // Optimized to only canonicalize the base-name subtags. All other // canonicalization steps will happen later. - if (!tag.canonicalizeBaseName(cx)) { - return true; + auto result = tag.canonicalizeBaseName(); + if (result.isErr()) { + if (result.unwrapErr() == + mozilla::intl::Locale::CanonicalizationError::DuplicateVariant) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_DUPLICATE_VARIANT_SUBTAG); + } else { + intl::ReportInternalError(cx); + } + return false; } } @@ -362,7 +394,7 @@ * ApplyUnicodeExtensionToTag( tag, options, relevantExtensionKeys ) */ bool js::intl::ApplyUnicodeExtensionToTag( - JSContext* cx, LanguageTag& tag, + JSContext* cx, mozilla::intl::Locale& tag, JS::HandleVector keywords) { // If no Unicode extensions were present in the options object, we can skip // everything below and directly return. @@ -437,12 +469,12 @@ return false; } - // Insert the new Unicode extension string into the language tag. - UniqueChars newExtensionChars(newExtension.extractOrCopyRawBuffer()); - if (!newExtensionChars) { + if (!tag.setUnicodeExtension(newExtension.begin())) { + intl::ReportInternalError(cx); return false; } - return tag.setUnicodeExtension(std::move(newExtensionChars)); + + return true; } static JS::Result LanguageTagFromMaybeWrappedLocale(JSContext* cx, @@ -521,12 +553,19 @@ } // ApplyOptionsToTag, steps 2 and 9. - LanguageTag tag(cx); - if (!LanguageTagParser::parse(cx, tagLinearStr, tag)) { + mozilla::intl::Locale tag; + if (!intl::ParseLocale(cx, tagLinearStr, tag)) { return false; } - if (!tag.canonicalizeBaseName(cx)) { + if (auto result = tag.canonicalizeBaseName(); result.isErr()) { + if (result.unwrapErr() == + mozilla::intl::Locale::CanonicalizationError::DuplicateVariant) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_DUPLICATE_VARIANT_SUBTAG); + } else { + intl::ReportInternalError(cx); + } return false; } @@ -547,7 +586,12 @@ // Steps 15-16. if (calendar) { - if (!IsValidUnicodeExtensionValue(calendar)) { + bool isValid; + if (!IsValidUnicodeExtensionValue(cx, calendar, &isValid)) { + return false; + } + + if (!isValid) { if (UniqueChars str = QuoteString(cx, calendar, '"')) { JS_ReportErrorNumberASCII(cx, js::GetErrorMessage, nullptr, JSMSG_INVALID_OPTION_VALUE, "calendar", @@ -569,7 +613,12 @@ // Steps 18-19. if (collation) { - if (!IsValidUnicodeExtensionValue(collation)) { + bool isValid; + if (!IsValidUnicodeExtensionValue(cx, collation, &isValid)) { + return false; + } + + if (!isValid) { if (UniqueChars str = QuoteString(cx, collation, '"')) { JS_ReportErrorNumberASCII(cx, js::GetErrorMessage, nullptr, JSMSG_INVALID_OPTION_VALUE, "collation", @@ -654,7 +703,11 @@ // Steps 28-29. if (numberingSystem) { - if (!IsValidUnicodeExtensionValue(numberingSystem)) { + bool isValid; + if (!IsValidUnicodeExtensionValue(cx, numberingSystem, &isValid)) { + return false; + } + if (!isValid) { if (UniqueChars str = QuoteString(cx, numberingSystem, '"')) { JS_ReportErrorNumberASCII(cx, js::GetErrorMessage, nullptr, JSMSG_INVALID_OPTION_VALUE, @@ -676,7 +729,14 @@ // ApplyOptionsToTag, steps 9 and 13. // ApplyUnicodeExtensionToTag, step 9. - if (!tag.canonicalizeExtensions(cx)) { + if (auto result = tag.canonicalizeExtensions(); result.isErr()) { + if (result.unwrapErr() == + mozilla::intl::Locale::CanonicalizationError::DuplicateVariant) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_DUPLICATE_VARIANT_SUBTAG); + } else { + intl::ReportInternalError(cx); + } return false; } @@ -840,19 +900,25 @@ languageLength = length; } + // Tell the analysis the |IsStructurallyValid*Tag| functions can't GC. + JS::AutoSuppressGCAnalysis nogc; + IndexAndLength language{0, languageLength}; - MOZ_ASSERT(intl::IsStructurallyValidLanguageTag(language.spanOf(baseName))); + MOZ_ASSERT( + mozilla::intl::IsStructurallyValidLanguageTag(language.spanOf(baseName))); mozilla::Maybe script{}; if (scriptIndex) { script.emplace(scriptIndex, ScriptLength); - MOZ_ASSERT(intl::IsStructurallyValidScriptTag(script->spanOf(baseName))); + MOZ_ASSERT( + mozilla::intl::IsStructurallyValidScriptTag(script->spanOf(baseName))); } mozilla::Maybe region{}; if (regionIndex) { region.emplace(regionIndex, regionLength); - MOZ_ASSERT(intl::IsStructurallyValidRegionTag(region->spanOf(baseName))); + MOZ_ASSERT( + mozilla::intl::IsStructurallyValidRegionTag(region->spanOf(baseName))); } return {language, script, region}; @@ -876,12 +942,13 @@ return false; } - LanguageTag tag(cx); - if (!LanguageTagParser::parse(cx, tagStr, tag)) { + mozilla::intl::Locale tag; + if (!intl::ParseLocale(cx, tagStr, tag)) { return false; } - if (!tag.addLikelySubtags(cx)) { + if (!tag.addLikelySubtags()) { + intl::ReportInternalError(cx); return false; } @@ -912,12 +979,13 @@ return false; } - LanguageTag tag(cx); - if (!LanguageTagParser::parse(cx, tagStr, tag)) { + mozilla::intl::Locale tag; + if (!intl::ParseLocale(cx, tagStr, tag)) { return false; } - if (!tag.removeLikelySubtags(cx)) { + if (!tag.removeLikelySubtags()) { + intl::ReportInternalError(cx); return false; } @@ -1280,19 +1348,34 @@ return true; } - LanguageTag tag(cx); - if (!LanguageTagParser::parse(cx, tagLinearStr, tag)) { + mozilla::intl::Locale tag; + if (!intl::ParseLocale(cx, tagLinearStr, tag)) { + return false; + } + + auto result = tag.canonicalize(); + if (result.isErr()) { + if (result.unwrapErr() == + mozilla::intl::Locale::CanonicalizationError::DuplicateVariant) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_DUPLICATE_VARIANT_SUBTAG); + } else { + intl::ReportInternalError(cx); + } return false; } - if (!tag.canonicalize(cx)) { + intl::FormatBuffer buffer(cx); + if (auto result = tag.toString(buffer); result.isErr()) { + intl::ReportInternalError(cx, result.unwrapErr()); return false; } - JSString* resultStr = tag.toString(cx); + JSString* resultStr = buffer.toString(cx); if (!resultStr) { return false; } + args.rval().setString(resultStr); return true; } @@ -1307,22 +1390,45 @@ return false; } - LanguageTag tag(cx); - bool ok; - JS_TRY_VAR_OR_RETURN_FALSE(cx, ok, - LanguageTagParser::tryParse(cx, linear, tag)); + mozilla::intl::Locale tag; + { + if (!StringIsAscii(linear)) { + // The caller handles invalid inputs. + args.rval().setNull(); + return true; + } - // The caller handles invalid inputs. - if (!ok) { - args.rval().setNull(); - return true; + intl::StringAsciiChars chars(linear); + if (!chars.init(cx)) { + return false; + } + + if (mozilla::intl::LocaleParser::tryParse(chars, tag).isErr()) { + // The caller handles invalid inputs. + args.rval().setNull(); + return true; + } } - if (!tag.canonicalize(cx)) { + auto result = tag.canonicalize(); + if (result.isErr()) { + if (result.unwrapErr() == + mozilla::intl::Locale::CanonicalizationError::DuplicateVariant) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_DUPLICATE_VARIANT_SUBTAG); + } else { + intl::ReportInternalError(cx); + } + return false; + } + + intl::FormatBuffer buffer(cx); + if (auto result = tag.toString(buffer); result.isErr()) { + intl::ReportInternalError(cx, result.unwrapErr()); return false; } - JSString* resultStr = tag.toString(cx); + JSString* resultStr = buffer.toString(cx); if (!resultStr) { return false; } @@ -1350,7 +1456,11 @@ return false; } - if (!IsValidUnicodeExtensionValue(unicodeType)) { + bool isValid; + if (!IsValidUnicodeExtensionValue(cx, unicodeType, &isValid)) { + return false; + } + if (!isValid) { UniqueChars optionChars = EncodeAscii(cx, optionArg.toString()); if (!optionChars) { return false; @@ -1391,8 +1501,8 @@ MOZ_ASSERT(strlen(unicodeTypeChars.get()) == unicodeTypeLength); // Convert into canonical case before searching for replacements. - intl::AsciiToLowerCase(unicodeTypeChars.get(), unicodeTypeLength, - unicodeTypeChars.get()); + mozilla::intl::AsciiToLowerCase(unicodeTypeChars.get(), unicodeTypeLength, + unicodeTypeChars.get()); auto key = mozilla::Span(unicodeKey, UnicodeKeyLength); auto type = mozilla::Span(unicodeTypeChars.get(), unicodeTypeLength); @@ -1400,7 +1510,7 @@ // Search if there's a replacement for the current Unicode keyword. JSString* result; if (const char* replacement = - LanguageTag::replaceUnicodeExtensionType(key, type)) { + mozilla::intl::Locale::replaceUnicodeExtensionType(key, type)) { result = NewStringCopyZ(cx, replacement); } else { result = StringToLowerCase(cx, unicodeType); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/intl/make_intl_data.py firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/intl/make_intl_data.py --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/intl/make_intl_data.py 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/intl/make_intl_data.py 2021-10-20 19:28:22.000000000 +0000 @@ -17,7 +17,8 @@ This script extracts information about 1) mappings between deprecated and current Unicode BCP 47 locale identifiers, and 2) deprecated and current BCP 47 Unicode extension value from CLDR, and converts it to C++ mapping - code in LanguageTagGenerated.cpp. The code is used in LanguageTag.cpp. + code in intl/components/LocaleGenerated.cpp. The code is used in + intl/components/Locale.cpp. Target "tzdata": @@ -125,7 +126,7 @@ writeMappingHeader(println, description, source, url) println( """ -bool js::intl::LanguageTag::{0}({1} {2}) {{ +bool mozilla::intl::Locale::{0}({1} {2}) {{ MOZ_ASSERT({3}({2}.span())); MOZ_ASSERT({4}({2}.span())); """.format( @@ -309,7 +310,7 @@ writeMappingHeader(println, description, source, url) println( """ -void js::intl::LanguageTag::performComplexLanguageMappings() { +void mozilla::intl::Locale::performComplexLanguageMappings() { MOZ_ASSERT(IsStructurallyValidLanguageTag(language().span())); MOZ_ASSERT(IsCanonicallyCasedLanguageTag(language().span())); """.lstrip() @@ -406,7 +407,7 @@ writeMappingHeader(println, description, source, url) println( """ -void js::intl::LanguageTag::performComplexRegionMappings() { +void mozilla::intl::Locale::performComplexRegionMappings() { MOZ_ASSERT(IsStructurallyValidLanguageTag(language().span())); MOZ_ASSERT(IsCanonicallyCasedLanguageTag(language().span())); MOZ_ASSERT(IsStructurallyValidRegionTag(region().span())); @@ -524,7 +525,7 @@ return str; } -static const char* ToCharPointer(const js::UniqueChars& str) { +static const char* ToCharPointer(const mozilla::intl::UniqueChars& str) { return str.get(); } @@ -537,7 +538,7 @@ writeMappingHeader(println, description, source, url) println( """ -bool js::intl::LanguageTag::performVariantMappings(JSContext* cx) { +bool mozilla::intl::Locale::performVariantMappings() { // The variant subtags need to be sorted for binary search. MOZ_ASSERT(std::is_sorted(variants_.begin(), variants_.end(), IsLessThan)); @@ -547,9 +548,9 @@ }; auto insertVariantSortedIfNotPresent = [&](const char* variant) { - auto* p = std::lower_bound(variants_.begin(), variants_.end(), variant, - IsLessThan); + auto* p = std::lower_bound( + variants_.begin(), variants_.end(), variant, + IsLessThan); // Don't insert the replacement when already present. if (p != variants_.end() && strcmp(p->get(), variant) == 0) { @@ -557,14 +558,11 @@ } // Insert the preferred variant in sort order. - auto preferred = DuplicateString(cx, variant); - if (!preferred) { - return false; - } + auto preferred = DuplicateStringToUniqueChars(variant); return !!variants_.insert(p, std::move(preferred)); }; - for (size_t i = 0; i < variants_.length(); ) { + for (size_t i = 0; i < variants_.length();) { const char* variant = variants_[i].get(); MOZ_ASSERT(IsCanonicallyCasedVariantTag(mozilla::MakeStringSpan(variant))); """.lstrip() @@ -657,7 +655,7 @@ writeMappingHeader(println, description, source, url) println( """\ -bool js::intl::LanguageTag::updateLegacyMappings(JSContext* cx) { +bool mozilla::intl::Locale::updateLegacyMappings() { // We're mapping legacy tags to non-legacy form here. // Other tags remain unchanged. // @@ -672,8 +670,10 @@ } for ([[maybe_unused]] const auto& variant : variants()) { - MOZ_ASSERT(IsStructurallyValidVariantTag(mozilla::MakeStringSpan(variant.get()))); - MOZ_ASSERT(IsCanonicallyCasedVariantTag(mozilla::MakeStringSpan(variant.get()))); + MOZ_ASSERT( + IsStructurallyValidVariantTag(mozilla::MakeStringSpan(variant.get()))); + MOZ_ASSERT( + IsCanonicallyCasedVariantTag(mozilla::MakeStringSpan(variant.get()))); } // The variant subtags need to be sorted for binary search. @@ -702,10 +702,7 @@ } // Insert the preferred variant in sort order. - auto preferred = DuplicateString(cx, variant); - if (!preferred) { - return false; - } + auto preferred = DuplicateStringToUniqueChars(variant); return !!variants_.insert(p, std::move(preferred)); }; @@ -924,7 +921,7 @@ writeMappingHeader(println, description, source, url) println( """\ -bool js::intl::LanguageTag::signLanguageMapping(LanguageSubtag& language, +bool mozilla::intl::Locale::signLanguageMapping(LanguageSubtag& language, const RegionSubtag& region) { MOZ_ASSERT(language.equalTo("sgn")); MOZ_ASSERT(IsStructurallyValidRegionTag(region.span())); @@ -1646,39 +1643,36 @@ #include #include -#include "builtin/intl/LanguageTag.h" -#include "util/Text.h" -#include "vm/JSContext.h" +#include "mozilla/intl/Locale.h" -using namespace js::intl::LanguageTagLimits; +using namespace mozilla::intl::LanguageTagLimits; template static inline bool HasReplacement( const char (&subtags)[Length][TagLength], - const js::intl::LanguageTagSubtag& subtag) { + const mozilla::intl::LanguageTagSubtag& subtag) { MOZ_ASSERT(subtag.length() == TagLength - 1, "subtag must have the same length as the list of subtags"); const char* ptr = subtag.span().data(); return std::binary_search(std::begin(subtags), std::end(subtags), ptr, [](const char* a, const char* b) { - return memcmp(a, b, TagLength - 1) < 0; - }); + return memcmp(a, b, TagLength - 1) < 0; + }); } template static inline const char* SearchReplacement( - const char (&subtags)[Length][TagLength], - const char* (&aliases)[Length], - const js::intl::LanguageTagSubtag& subtag) { + const char (&subtags)[Length][TagLength], const char* (&aliases)[Length], + const mozilla::intl::LanguageTagSubtag& subtag) { MOZ_ASSERT(subtag.length() == TagLength - 1, "subtag must have the same length as the list of subtags"); const char* ptr = subtag.span().data(); auto p = std::lower_bound(std::begin(subtags), std::end(subtags), ptr, [](const char* a, const char* b) { - return memcmp(a, b, TagLength - 1) < 0; - }); + return memcmp(a, b, TagLength - 1) < 0; + }); if (p != std::end(subtags) && memcmp(*p, ptr, TagLength - 1) == 0) { return aliases[std::distance(std::begin(subtags), p)]; } @@ -1695,32 +1689,23 @@ } static bool IsCanonicallyCasedLanguageTag(mozilla::Span span) { - // Tell the analysis the |std::all_of| function can't GC. - JS::AutoSuppressGCAnalysis nogc; - - return std::all_of(span.begin(), span.end(), mozilla::IsAsciiLowercaseAlpha); + return std::all_of(span.begin(), span.end(), + mozilla::IsAsciiLowercaseAlpha); } static bool IsCanonicallyCasedScriptTag(mozilla::Span span) { - // Tell the analysis the |std::all_of| function can't GC. - JS::AutoSuppressGCAnalysis nogc; - return mozilla::IsAsciiUppercaseAlpha(span[0]) && - std::all_of(span.begin() + 1, span.end(), mozilla::IsAsciiLowercaseAlpha); + std::all_of(span.begin() + 1, span.end(), + mozilla::IsAsciiLowercaseAlpha); } static bool IsCanonicallyCasedRegionTag(mozilla::Span span) { - // Tell the analysis the |std::all_of| function can't GC. - JS::AutoSuppressGCAnalysis nogc; - - return std::all_of(span.begin(), span.end(), mozilla::IsAsciiUppercaseAlpha) || + return std::all_of(span.begin(), span.end(), + mozilla::IsAsciiUppercaseAlpha) || std::all_of(span.begin(), span.end(), mozilla::IsAsciiDigit); } static bool IsCanonicallyCasedVariantTag(mozilla::Span span) { - // Tell the analysis the |std::all_of| function can't GC. - JS::AutoSuppressGCAnalysis nogc; - return std::all_of(span.begin(), span.end(), IsAsciiLowercaseAlphanumeric); } @@ -1729,7 +1714,8 @@ } static bool IsCanonicallyCasedUnicodeType(mozilla::Span type) { - return std::all_of(type.begin(), type.end(), IsAsciiLowercaseAlphanumericOrDash); + return std::all_of(type.begin(), type.end(), + IsAsciiLowercaseAlphanumericOrDash); } static bool IsCanonicallyCasedTransformKey(mozilla::Span key) { @@ -1737,7 +1723,8 @@ } static bool IsCanonicallyCasedTransformType(mozilla::Span type) { - return std::all_of(type.begin(), type.end(), IsAsciiLowercaseAlphanumericOrDash); + return std::all_of(type.begin(), type.end(), + IsAsciiLowercaseAlphanumericOrDash); } #endif """.rstrip() @@ -2049,7 +2036,7 @@ def updateCLDRLangTags(args): - """ Update the LanguageTagGenerated.cpp file. """ + """ Update the LocaleGenerated.cpp file. """ version = args.version url = args.url out = args.out @@ -3204,16 +3191,14 @@ println( """ template -static inline bool Is{0}Key( - mozilla::Span key, const char (&str)[Length]) {{ +static inline bool Is{0}Key(mozilla::Span key, const char (&str)[Length]) {{ static_assert(Length == {0}KeyLength + 1, "{0} extension key is two characters long"); return memcmp(key.data(), str, Length - 1) == 0; }} template -static inline bool Is{0}Type( - mozilla::Span type, const char (&str)[Length]) {{ +static inline bool Is{0}Type(mozilla::Span type, const char (&str)[Length]) {{ static_assert(Length > {0}KeyLength + 1, "{0} extension type contains more than two characters"); return type.size() == (Length - 1) && @@ -3262,8 +3247,8 @@ auto p = std::lower_bound(std::begin(types), std::end(types), type, [](const auto& a, const auto& b) {{ - return Compare{0}Type(a, b) < 0; - }}); + return Compare{0}Type(a, b) < 0; + }}); if (p != std::end(types) && Compare{0}Type(*p, type) == 0) {{ return aliases[std::distance(std::begin(types), p)]; }} @@ -3285,7 +3270,7 @@ * Spec: https://www.unicode.org/reports/tr35/#Unicode_Locale_Extension_Data_Files * Spec: https://www.unicode.org/reports/tr35/#t_Extension */ -const char* js::intl::LanguageTag::replace{0}ExtensionType( +const char* mozilla::intl::Locale::replace{0}ExtensionType( mozilla::Span key, mozilla::Span type) {{ MOZ_ASSERT(key.size() == {0}KeyLength); MOZ_ASSERT(IsCanonicallyCased{0}Key(key)); @@ -3307,11 +3292,11 @@ for entries in grouper(subtags, max_entries): entries = ( - '"{}"'.format(tag).rjust(length + 2) + '"{}"'.format(tag).center(length + 2) for tag in entries if tag is not None ) - println(" {},".format(", ".join(entries))) + println(" {},".format(", ".join(entries))) println(" };") @@ -4054,7 +4039,9 @@ ) parser_cldr_tags.add_argument( "--out", - default="LanguageTagGenerated.cpp", + default=os.path.join( + topsrcdir, "intl", "components", "src", "LocaleGenerated.cpp" + ), help="Output file (default: %(default)s)", ) parser_cldr_tags.add_argument( diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/intl/NumberFormat.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/intl/NumberFormat.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/intl/NumberFormat.cpp 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/intl/NumberFormat.cpp 2021-10-20 19:28:22.000000000 +0000 @@ -11,6 +11,7 @@ #include "mozilla/Assertions.h" #include "mozilla/Casting.h" #include "mozilla/FloatingPoint.h" +#include "mozilla/intl/Locale.h" #include "mozilla/intl/MeasureUnit.h" #include "mozilla/intl/NumberFormat.h" #include "mozilla/intl/NumberingSystem.h" @@ -31,6 +32,7 @@ #include "builtin/Array.h" #include "builtin/intl/CommonFunctions.h" #include "builtin/intl/DecimalNumber.h" +#include "builtin/intl/FormatBuffer.h" #include "builtin/intl/LanguageTag.h" #include "builtin/intl/MeasureUnitGenerated.h" #include "builtin/intl/RelativeTimeFormat.h" @@ -288,14 +290,14 @@ // ICU expects numberingSystem as a Unicode locale extensions on locale. - intl::LanguageTag tag(cx); + mozilla::intl::Locale tag; { - JSLinearString* locale = value.toString()->ensureLinear(cx); + RootedLinearString locale(cx, value.toString()->ensureLinear(cx)); if (!locale) { return nullptr; } - if (!intl::LanguageTagParser::parse(cx, locale, tag)) { + if (!intl::ParseLocale(cx, locale, tag)) { return nullptr; } } @@ -326,7 +328,12 @@ return nullptr; } - return tag.toStringZ(cx); + intl::FormatBuffer buffer(cx); + if (auto result = tag.toString(buffer); result.isErr()) { + intl::ReportInternalError(cx, result.unwrapErr()); + return nullptr; + } + return buffer.extractStringZ(); } struct NumberFormatOptions : public mozilla::intl::NumberRangeFormatOptions { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/intl/RelativeTimeFormat.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/intl/RelativeTimeFormat.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/intl/RelativeTimeFormat.cpp 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/intl/RelativeTimeFormat.cpp 2021-10-20 19:28:22.000000000 +0000 @@ -165,14 +165,14 @@ // ICU expects numberingSystem as a Unicode locale extensions on locale. - intl::LanguageTag tag(cx); + mozilla::intl::Locale tag; { - JSLinearString* locale = value.toString()->ensureLinear(cx); + RootedLinearString locale(cx, value.toString()->ensureLinear(cx)); if (!locale) { return nullptr; } - if (!intl::LanguageTagParser::parse(cx, locale, tag)) { + if (!intl::ParseLocale(cx, locale, tag)) { return nullptr; } } @@ -203,7 +203,13 @@ return nullptr; } - UniqueChars locale = tag.toStringZ(cx); + intl::FormatBuffer buffer(cx); + if (auto result = tag.toString(buffer); result.isErr()) { + intl::ReportInternalError(cx, result.unwrapErr()); + return nullptr; + } + + UniqueChars locale = buffer.extractStringZ(); if (!locale) { return nullptr; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/intl/SharedIntlData.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/intl/SharedIntlData.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/intl/SharedIntlData.cpp 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/intl/SharedIntlData.cpp 2021-10-20 19:28:23.000000000 +0000 @@ -377,7 +377,7 @@ // + 4 * Alphanum script subtag // + 1 separator // + 2 * Alpha region subtag - using namespace intl::LanguageTagLimits; + using namespace mozilla::intl::LanguageTagLimits; static constexpr size_t MinLanguageLength = 2; static constexpr size_t MinLengthForScriptAndRegion = MinLanguageLength + 1 + ScriptLength + 1 + AlphaRegionLength; @@ -407,7 +407,8 @@ // Continue with the next locale if we didn't find a script subtag. size_t scriptLength = sep - script; - if (!IsStructurallyValidScriptTag({script, scriptLength})) { + if (!mozilla::intl::IsStructurallyValidScriptTag( + {script, scriptLength})) { continue; } @@ -419,7 +420,8 @@ // Continue with the next locale if we didn't find a region subtag. size_t regionLength = (sep ? sep : lang.end()) - region; - if (!IsStructurallyValidRegionTag({region, regionLength})) { + if (!mozilla::intl::IsStructurallyValidRegionTag( + {region, regionLength})) { continue; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/intl/StringAsciiChars.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/intl/StringAsciiChars.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/intl/StringAsciiChars.h 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/intl/StringAsciiChars.h 2021-10-20 19:28:22.000000000 +0000 @@ -0,0 +1,78 @@ +/* -*- 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/. */ + +#ifndef builtin_intl_StringAsciiChars_h +#define builtin_intl_StringAsciiChars_h + +#include "mozilla/Assertions.h" +#include "mozilla/Attributes.h" +#include "mozilla/Maybe.h" +#include "mozilla/Span.h" +#include "mozilla/TextUtils.h" + +#include + +#include "js/GCAPI.h" +#include "js/TypeDecls.h" +#include "js/Vector.h" + +#include "vm/StringType.h" + +namespace js::intl { + +/** + * String view of an ASCII-only string. + * + * This holds a reference to a JSLinearString and can produce a string view + * into that string. If the string is represented by Latin1 characters, the + * span is returned directly. If the string is represented by UTF-16 + * characters, it copies the char16_t characters into a char array, and then + * returns a span based on the copy. + * + * This allows us to avoid copying for the common use case that the ASCII + * characters are represented in Latin1. + */ +class MOZ_STACK_CLASS StringAsciiChars final { + // When copying string characters, use this many bytes of inline storage. + static const size_t InlineCapacity = 24; + + JS::AutoCheckCannotGC nogc_; + + JSLinearString* str_; + + mozilla::Maybe> ownChars_; + + public: + explicit StringAsciiChars(JSLinearString* str) : str_(str) { + MOZ_ASSERT(StringIsAscii(str)); + } + + operator mozilla::Span() const { + if (str_->hasLatin1Chars()) { + return mozilla::AsChars(str_->latin1Range(nogc_)); + } + return mozilla::AsChars(mozilla::Span(*ownChars_)); + } + + [[nodiscard]] bool init(JSContext* cx) { + if (str_->hasLatin1Chars()) { + return true; + } + + ownChars_.emplace(cx); + if (!ownChars_->resize(str_->length())) { + return false; + } + + js::CopyChars(ownChars_->begin(), *str_); + + return true; + } +}; + +} // namespace js::intl + +#endif // builtin_intl_StringAsciiChars_h diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/TestingFunctions.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/TestingFunctions.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/builtin/TestingFunctions.cpp 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/builtin/TestingFunctions.cpp 2021-10-20 19:28:22.000000000 +0000 @@ -860,6 +860,41 @@ return true; } +static bool WasmMaxMemoryPages(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + if (args.length() < 1) { + JS_ReportErrorASCII(cx, "not enough arguments"); + return false; + } + if (!args.get(0).isString()) { + JS_ReportErrorASCII(cx, "index type must be a string"); + return false; + } + RootedString s(cx, args.get(0).toString()); + RootedLinearString ls(cx, s->ensureLinear(cx)); + if (!ls) { + return false; + } + if (StringEqualsLiteral(ls, "i32")) { + args.rval().setInt32( + int32_t(wasm::MaxMemoryPages(wasm::IndexType::I32).value())); + return true; + } + if (StringEqualsLiteral(ls, "i64")) { +#ifdef ENABLE_WASM_MEMORY64 + if (wasm::Memory64Available(cx)) { + args.rval().setInt32( + int32_t(wasm::MaxMemoryPages(wasm::IndexType::I64).value())); + return true; + } +#endif + JS_ReportErrorASCII(cx, "memory64 not enabled"); + return false; + } + JS_ReportErrorASCII(cx, "bad index type"); + return false; +} + static bool WasmThreadsEnabled(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); args.rval().setBoolean(wasm::ThreadsAvailable(cx)); @@ -4672,9 +4707,9 @@ // ShapeSnapshot holds information about an object's properties. This is used // for checking object and shape changes between two points in time. class ShapeSnapshot { - GCPtr object_; - GCPtr shape_; - GCPtr baseShape_; + HeapPtr object_; + HeapPtr shape_; + HeapPtr baseShape_; ObjectFlags objectFlags_; GCVector, 8> slots_; @@ -7954,6 +7989,15 @@ " Returns a boolean indicating whether WebAssembly supports using a large" " virtual memory reservation in order to elide bounds checks on this platform."), + JS_FN_HELP("wasmMaxMemoryPages", WasmMaxMemoryPages, 1, 0, +"wasmMaxMemoryPages(indexType)", +" Returns an int with the maximum number of pages that can be allocated to a memory." +" This is an implementation artifact that does depend on the index type, the hardware," +" the operating system, the build configuration, and flags. The result is constant for" +" a given combination of those; there is no guarantee that that size allocation will" +" always succeed, only that it can succeed in principle. The indexType is a string," +" 'i32' or 'i64'."), + #define WASM_FEATURE(NAME, ...) \ JS_FN_HELP("wasm" #NAME "Enabled", Wasm##NAME##Enabled, 0, 0, \ "wasm" #NAME "Enabled()", \ diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/debugger/Source.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/debugger/Source.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/debugger/Source.cpp 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/debugger/Source.cpp 2021-10-20 19:28:23.000000000 +0000 @@ -617,7 +617,7 @@ ScriptSource* ss = sso->source(); JS::CompileOptions options(cx); - options.hideScriptFromDebugger = true; + options.setHideScriptFromDebugger(true); options.setFileAndLine(ss->filename(), ss->startLine()); UncompressedSourceCache::AutoHoldEntry holder; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/devtools/rootAnalysis/analyze.py firefox-trunk-95.0~a1~hg20211020r596404/js/src/devtools/rootAnalysis/analyze.py --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/devtools/rootAnalysis/analyze.py 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/devtools/rootAnalysis/analyze.py 2021-10-20 19:28:23.000000000 +0000 @@ -14,23 +14,25 @@ import os import subprocess import sys -import re try: from shlex import quote except ImportError: from pipes import quote -# Python 2/3 version independence polyfills -anystring_t = str if sys.version_info[0] > 2 else basestring +def execfile(thefile, globals): + exec(compile(open(thefile).read(), filename=thefile, mode="exec"), globals) -try: - execfile -except Exception: - def execfile(thefile, globals): - exec(compile(open(thefile).read(), filename=thefile, mode="exec"), globals) +# Label a string as an output. +class Output(str): + pass + + +# Label a string as a pattern for multiple inputs. +class MultiInput(str): + pass def env(config): @@ -45,19 +47,28 @@ def fill(command, config): - try: - return tuple(s % config for s in command) - except Exception: - print("Substitution failed:") - problems = [] - for fragment in command: - try: - fragment % config - except Exception: - problems.append(fragment) - raise Exception( - "\n".join(["Substitution failed:"] + [" %s" % s for s in problems]) - ) + filled = [] + for s in command: + try: + rep = s.format(**config) + except KeyError: + print("Substitution failed: %s" % s) + filled = None + break + + if isinstance(s, Output): + filled.append(Output(rep)) + elif isinstance(s, MultiInput): + N = int(config["jobs"]) + for i in range(1, N + 1): + filled.append(rep.format(i=i, n=N)) + else: + filled.append(rep) + + if filled is None: + raise Exception("substitution failure") + + return tuple(filled) def print_command(command, outfile=None, env=None): @@ -86,172 +97,214 @@ print(output) -def generate_hazards(config, outfilename): - jobs = [] - for i in range(int(config["jobs"])): - command = fill( - ( - "%(js)s", - "%(analysis_scriptdir)s/analyzeRoots.js", - "%(gcFunctions_list)s", - "%(gcEdges)s", - "%(limitedFunctions_list)s", - "%(gcTypes)s", - "%(typeInfo)s", - str(i + 1), - "%(jobs)s", - "tmp.%s" % (i + 1,), - ), - config, - ) - outfile = "rootingHazards.%s" % (i + 1,) - output = open(outfile, "w") - if config["verbose"]: - print_command(command, outfile=outfile, env=env(config)) - jobs.append((command, Popen(command, stdout=output, env=env(config)))) - - final_status = 0 - while jobs: - pid, status = os.wait() - jobs = [job for job in jobs if job[1].pid != pid] - final_status = final_status or status - - if final_status: - raise subprocess.CalledProcessError(final_status, "analyzeRoots.js") - - with open(outfilename, "w") as output: - command = ["cat"] + [ - "rootingHazards.%s" % (i + 1,) for i in range(int(config["jobs"])) - ] - if config["verbose"]: - print_command(command, outfile=outfilename) - subprocess.call(command, stdout=output) - - JOBS = { - "dbs": ( - ( - "%(analysis_scriptdir)s/run_complete", + "dbs": { + "command": [ + "{analysis_scriptdir}/run_complete", "--foreground", "--no-logs", - "--build-root=%(objdir)s", - "--wrap-dir=%(sixgill)s/scripts/wrap_gcc", + "--build-root={objdir}", + "--wrap-dir={sixgill}/scripts/wrap_gcc", "--work-dir=work", "-b", - "%(sixgill_bin)s", - "--buildcommand=%(buildcommand)s", + "{sixgill_bin}", + "--buildcommand={buildcommand}", ".", - ), - (), - ), - "list-dbs": (("ls", "-l"), ()), - "callgraph": ( - ( - "%(js)s", - "%(analysis_scriptdir)s/computeCallgraph.js", - "%(typeInfo)s", - "[callgraph]", - ), - ("callgraph.txt",), - ), - "gcFunctions": ( - ( - "%(js)s", - "%(analysis_scriptdir)s/computeGCFunctions.js", - "%(callgraph)s", - "[gcFunctions]", - "[gcFunctions_list]", - "[gcEdges]", - "[limitedFunctions_list]", - ), - ("gcFunctions.txt", "gcFunctions.lst", "gcEdges.txt", "limitedFunctions.lst"), - ), - "gcTypes": ( - ( - "%(js)s", - "%(analysis_scriptdir)s/computeGCTypes.js", - "[gcTypes]", - "[typeInfo]", - ), - ("gcTypes.txt", "typeInfo.txt"), - ), - "allFunctions": ( - ( - "%(sixgill_bin)s/xdbkeys", - "src_body.xdb", - ), - "allFunctions.txt", - ), - "hazards": (generate_hazards, "rootingHazards.txt"), - "explain": ( - ( + ], + "outputs": [], + }, + "list-dbs": {"command": ["ls", "-l"]}, + "rawcalls": { + "command": [ + "{js}", + "{analysis_scriptdir}/computeCallgraph.js", + "{typeInfo}", + Output("rawcalls"), + "{i}", + "{n}", + ], + "multi-output": True, + "outputs": ["rawcalls.{i}.of.{n}"], + }, + "gcFunctions": { + "command": [ + "{js}", + "{analysis_scriptdir}/computeGCFunctions.js", + MultiInput("{rawcalls}"), + "--outputs", + Output("callgraph"), + Output("gcFunctions"), + Output("gcFunctions_list"), + Output("gcEdges"), + Output("limitedFunctions_list"), + ], + "outputs": [ + "callgraph.txt", + "gcFunctions.txt", + "gcFunctions.lst", + "gcEdges.txt", + "limitedFunctions.lst", + ], + }, + "gcTypes": { + "command": [ + "{js}", + "{analysis_scriptdir}/computeGCTypes.js", + Output("gcTypes"), + Output("typeInfo"), + ], + "outputs": ["gcTypes.txt", "typeInfo.txt"], + }, + "allFunctions": { + "command": ["{sixgill_bin}/xdbkeys", "src_body.xdb"], + "redirect-output": "allFunctions.txt", + }, + "hazards": { + "command": [ + "{js}", + "{analysis_scriptdir}/analyzeRoots.js", + "{gcFunctions_list}", + "{gcEdges}", + "{limitedFunctions_list}", + "{gcTypes}", + "{typeInfo}", + "{i}", + "{n}", + "tmp.{i}.of.{n}", + ], + "multi-output": True, + "redirect-output": "rootingHazards.{i}.of.{n}", + }, + "gather-hazards": { + "command": ["cat", MultiInput("{hazards}")], + "redirect-output": "rootingHazards.txt", + }, + "explain": { + "command": [ sys.executable, - "%(analysis_scriptdir)s/explain.py", - "%(hazards)s", - "%(gcFunctions)s", - "[explained_hazards]", - "[unnecessary]", - "[refs]", - ), - ("hazards.txt", "unnecessary.txt", "refs.txt"), - ), - "heapwrites": ( - ("%(js)s", "%(analysis_scriptdir)s/analyzeHeapWrites.js"), - "heapWriteHazards.txt", - ), + "{analysis_scriptdir}/explain.py", + "{gather-hazards}", + "{gcFunctions}", + Output("explained_hazards"), + Output("unnecessary"), + Output("refs"), + ], + "outputs": ["hazards.txt", "unnecessary.txt", "refs.txt"], + }, + "heapwrites": { + "command": ["{js}", "{analysis_scriptdir}/analyzeHeapWrites.js"], + "redirect-output": "heapWriteHazards.txt", + }, } +# Generator of (i, j, item) tuples: +# - i is just the index of the yielded tuple (a la enumerate()) +# - j is the index of the item in the command list +# - item is command[j] def out_indexes(command): - for i in range(len(command)): - m = re.match(r"^\[(.*)\]$", command[i]) - if m: - yield (i, m.group(1)) + i = 0 + for (j, fragment) in enumerate(command): + if isinstance(fragment, Output): + yield (i, j, fragment) + i += 1 def run_job(name, config): - cmdspec, outfiles = JOBS[name] - print("Running " + name + " to generate " + str(outfiles)) - if hasattr(cmdspec, "__call__"): - cmdspec(config, outfiles) - else: - temp_map = {} - cmdspec = fill(cmdspec, config) - if isinstance(outfiles, anystring_t): - stdout_filename = "%s.tmp" % name - temp_map[stdout_filename] = outfiles - if config["verbose"]: - print_command(cmdspec, outfile=outfiles, env=env(config)) - else: - stdout_filename = None - pc = list(cmdspec) - outfile = 0 - for (i, name) in out_indexes(cmdspec): - pc[i] = outfiles[outfile] - outfile += 1 - if config["verbose"]: - print_command(pc, env=env(config)) + job = JOBS[name] + outs = job.get("outputs") or job.get("redirect-output") + print("Running " + name + " to generate " + str(outs)) + if "function" in job: + job["function"](config, job["redirect-output"]) + return + + N = int(config["jobs"]) if job.get("multi-output") else 1 + config["n"] = N + jobs = {} + for i in range(1, N + 1): + config["i"] = i + cmd = fill(job["command"], config) + info = spawn_command(cmd, job, name, config) + jobs[info["proc"].pid] = info - command = list(cmdspec) - outfile = 0 - for (i, name) in out_indexes(cmdspec): - command[i] = "%s.tmp" % name - temp_map[command[i]] = outfiles[outfile] - outfile += 1 - - sys.stdout.flush() - if stdout_filename is None: - subprocess.check_call(command, env=env(config)) - else: - with open(stdout_filename, "w") as output: - subprocess.check_call(command, stdout=output, env=env(config)) - for (temp, final) in temp_map.items(): + final_status = 0 + while jobs: + pid, status = os.wait() + final_status = final_status or status + info = jobs[pid] + del jobs[pid] + if "redirect" in info: + info["redirect"].close() + + # Rename the temporary files to their final names. + for (temp, final) in info["rename_map"].items(): try: + if config["verbose"]: + print("Renaming %s -> %s" % (temp, final)) os.rename(temp, final) except OSError: print("Error renaming %s -> %s" % (temp, final)) raise + if final_status != 0: + raise Exception("job {} returned status {}".format(name, final_status)) + + +def spawn_command(cmdspec, job, name, config): + rename_map = {} + + if "redirect-output" in job: + stdout_filename = "{}.tmp{}".format(name, config.get("i", "")) + final_outfile = job["redirect-output"].format(**config) + rename_map[stdout_filename] = final_outfile + command = cmdspec + if config["verbose"]: + print_command(cmdspec, outfile=final_outfile, env=env(config)) + else: + outfiles = job["outputs"] + outfiles = fill(outfiles, config) + stdout_filename = None + + # To print the supposedly-executed command, replace the Outputs in the + # command with final output file names. (The actual command will be + # using temporary files that get renamed at the end.) + if config["verbose"]: + pc = list(cmdspec) + for (i, j, name) in out_indexes(cmdspec): + pc[j] = outfiles[i] + print_command(pc, env=env(config)) + + # Replace the Outputs with temporary filenames, and record a mapping + # from those temp names to their actual final names that will be used + # if the command succeeds. + command = list(cmdspec) + for (i, j, name) in out_indexes(cmdspec): + command[j] = "{}.tmp{}".format(name, config.get("i", "")) + rename_map[command[j]] = outfiles[i] + + sys.stdout.flush() + info = {"rename_map": rename_map} + if stdout_filename: + info["redirect"] = open(stdout_filename, "w") + info["proc"] = Popen(command, stdout=info["redirect"], env=env(config)) + else: + info["proc"] = Popen(command, env=env(config)) + + if config["verbose"]: + print("Spawned process {}".format(info["proc"].pid)) + + return info + + +# Default to conservatively assuming 4GB/job. +def max_parallel_jobs(job_size=4 * 2 ** 30): + """Return the max number of parallel jobs we can run without overfilling + memory, assuming heavyweight jobs.""" + from_cores = int(subprocess.check_output(["nproc", "--ignore=1"]).strip()) + mem_bytes = os.sysconf("SC_PAGE_SIZE") * os.sysconf("SC_PHYS_PAGES") + from_mem = round(mem_bytes / job_size) + return min(from_cores, from_mem) + config = {"analysis_scriptdir": os.path.dirname(__file__)} @@ -264,7 +317,7 @@ description="Statically analyze build tree for rooting hazards." ) parser.add_argument( - "step", metavar="STEP", type=str, nargs="?", help="run starting from this step" + "step", metavar="STEP", type=str, nargs="?", help="run only step STEP" ) parser.add_argument( "--source", metavar="SOURCE", type=str, nargs="?", help="source code to analyze" @@ -284,7 +337,14 @@ help="full path to ctypes-capable JS shell", ) parser.add_argument( - "--upto", metavar="UPTO", type=str, nargs="?", help="last step to execute" + "--first", + metavar="STEP", + type=str, + nargs="?", + help="execute all jobs starting with STEP", +) +parser.add_argument( + "--last", metavar="STEP", type=str, nargs="?", help="stop at step STEP" ) parser.add_argument( "--jobs", @@ -350,14 +410,14 @@ if args.jobs is not None: data["jobs"] = args.jobs if not data.get("jobs"): - data["jobs"] = int(subprocess.check_output(["nproc", "--ignore=1"]).strip()) + data["jobs"] = max_parallel_jobs() if args.buildcommand: data["buildcommand"] = args.buildcommand elif "BUILD" in os.environ: data["buildcommand"] = os.environ["BUILD"] else: - data["buildcommand"] = "make -j4 -s" + data["buildcommand"] = "make -j{} -s".format(data["jobs"]) if "ANALYZED_OBJDIR" in os.environ: data["objdir"] = os.environ["ANALYZED_OBJDIR"] @@ -370,45 +430,56 @@ steps = [ "dbs", "gcTypes", - "callgraph", + "rawcalls", "gcFunctions", "allFunctions", "hazards", + "gather-hazards", "explain", "heapwrites", ] if args.list: for step in steps: - command, outfilename = JOBS[step] - if outfilename: - print("%s -> %s" % (step, outfilename)) + job = JOBS[step] + outfiles = job.get("outputs") or job.get("redirect-output") + if outfiles: + print( + "%s\n ->%s %s" + % (step, "*" if job.get("multi-output") else "", outfiles) + ) else: print(step) sys.exit(0) for step in steps: - command, outfiles = JOBS[step] - if isinstance(outfiles, anystring_t): - data[step] = outfiles - else: - outfile = 0 - for (i, name) in out_indexes(command): - data[name] = outfiles[outfile] - outfile += 1 + job = JOBS[step] + if "redirect-output" in job: + data[step] = job["redirect-output"] + elif "outputs" in job and "command" in job: + outfiles = job["outputs"] + for (i, j, name) in out_indexes(job["command"]): + data[name] = outfiles[i] + num_outputs = len(list(out_indexes(job["command"]))) assert ( - len(outfiles) == outfile - ), "step '%s': mismatched number of output files (%d) and params (%d)" % ( + len(outfiles) == num_outputs + ), 'step "%s": mismatched number of output files (%d) and params (%d)' % ( step, - outfile, + num_outputs, len(outfiles), ) # NOQA: E501 if args.step: - steps = steps[steps.index(args.step) :] - -if args.upto: - steps = steps[: steps.index(args.upto) + 1] + if args.first or args.last: + raise Exception( + "--first and --last cannot be used when a step argument is given" + ) + steps = [args.step] +else: + if args.first: + steps = steps[steps.index(args.first) :] + if args.last: + steps = steps[: steps.index(args.last) + 1] for step in steps: run_job(step, data) diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/devtools/rootAnalysis/analyzeRoots.js firefox-trunk-95.0~a1~hg20211020r596404/js/src/devtools/rootAnalysis/analyzeRoots.js --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/devtools/rootAnalysis/analyzeRoots.js 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/devtools/rootAnalysis/analyzeRoots.js 2021-10-20 19:28:22.000000000 +0000 @@ -692,7 +692,7 @@ var src_gcInfo = gcInfo; var src_preGCLive = preGCLive; - if (!gcInfo && !(body.limits[source] & LIMIT_CANNOT_GC) && !(suppressed & LIMIT_CANNOT_GC)) { + if (!gcInfo && !(body.attrs[source] & ATTR_GC_SUPPRESSED) && !(suppressed & ATTR_GC_SUPPRESSED)) { var gcName = edgeCanGC(edge, body); if (gcName) src_gcInfo = {name:gcName, body:body, ppoint:source}; @@ -830,7 +830,7 @@ continue; for (var edge of body.PEdge) { if (edgeTakesVariableAddress(edge, variable, body)) { - if (edge.Kind == "Assign" || (!(suppressed & LIMIT_CANNOT_GC) && edgeCanGC(edge))) + if (edge.Kind == "Assign" || (!(suppressed & ATTR_GC_SUPPRESSED) && edgeCanGC(edge))) return {body:body, ppoint:edge.Index[0]}; } } @@ -974,12 +974,12 @@ } } -function processBodies(functionName, wholeBodyLimits) +function processBodies(functionName, wholeBodyAttrs) { if (!("DefineVariable" in functionBodies[0])) return; - const funcInfo = limitedFunctions[mangled(functionName)] || {}; - const suppressed = funcInfo.limits | wholeBodyLimits; + const funcInfo = limitedFunctions[mangled(functionName)] || { attributes: 0 }; + const suppressed = funcInfo.attributes | wholeBodyAttrs; // Look for the JS_EXPECT_HAZARDS annotation, and output a different // message in that case that won't be counted as a hazard. @@ -1114,10 +1114,8 @@ var minStream = xdb.min_data_stream()|0; var maxStream = xdb.max_data_stream()|0; -var N = (maxStream - minStream) + 1; -var start = Math.floor((batch - 1) / numBatches * N) + minStream; -var start_next = Math.floor(batch / numBatches * N) + minStream; -var end = start_next - 1; +var start = batchStart(batch, numBatches, minStream, maxStream); +var end = batchLast(batch, numBatches, minStream, maxStream); function process(name, json) { functionName = name; @@ -1125,16 +1123,16 @@ // Annotate body with a table of all points within the body that may be in // a limited scope (eg within the scope of a GC suppression RAII class.) - // body.limits is a plain object indexed by point, with the value being a - // bit set stored in an integer of the limit bits. + // body.attrs is a plain object indexed by point, with the value being a + // bit set stored in an integer. for (var body of functionBodies) - body.limits = []; + body.attrs = []; for (var body of functionBodies) { - for (var [pbody, id, limits] of allRAIIGuardedCallPoints(typeInfo, functionBodies, body, isLimitConstructor)) + for (var [pbody, id, attrs] of allRAIIGuardedCallPoints(typeInfo, functionBodies, body, isLimitConstructor)) { - if (limits) - pbody.limits[id] = limits; + if (attrs) + pbody.attrs[id] = attrs; } } @@ -1142,12 +1140,12 @@ // ref count to zero. Or rather, it just calls operator=() in a context // where the refcount will never drop to zero. Limit all calls within the // body with LIMIT_CANNOT_GC. - let wholeBodyLimits = 0; + let wholeBodyAttrs = 0; if (functionName.includes("std::swap") || functionName.includes("mozilla::Swap")) { - wholeBodyLimits = LIMIT_CANNOT_GC; + wholeBodyAttrs = ATTR_GC_SUPPRESSED; } - processBodies(functionName, wholeBodyLimits); + processBodies(functionName, wholeBodyAttrs); } if (theFunctionNameToFind) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/devtools/rootAnalysis/annotations.js firefox-trunk-95.0~a1~hg20211020r596404/js/src/devtools/rootAnalysis/annotations.js --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/devtools/rootAnalysis/annotations.js 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/devtools/rootAnalysis/annotations.js 2021-10-20 19:28:22.000000000 +0000 @@ -40,8 +40,8 @@ // This is usually a simple variable name, but sometimes a full name gets // passed through. And sometimes that name is truncated. Examples: - // _ZL13gAbortHandler|mozalloc_oom.cpp:void (* gAbortHandler)(size_t) - // _ZL14pMutexUnlockFn|umutex.cpp:void (* pMutexUnlockFn)(const void* + // _ZL13gAbortHandler$mozalloc_oom.cpp:void (* gAbortHandler)(size_t) + // _ZL14pMutexUnlockFn$umutex.cpp:void (* pMutexUnlockFn)(const void* var name = readable(fullVariable); if (name in ignoreIndirectCalls) @@ -296,7 +296,7 @@ "void mozilla::dom::JSStreamConsumer::~JSStreamConsumer() [[base_dtor]]": true, }; -function extraGCFunctions() { +function extraGCFunctions(readableNames) { return ["ffi_call"].filter(f => f in readableNames); } @@ -323,7 +323,7 @@ name.match(/u(prv_malloc|prv_realloc|prv_free|case_toFullLower)_\d+/) } -function ignoreGCFunction(mangled) +function ignoreGCFunction(mangled, readableNames) { // Field calls will not be in readableNames if (!(mangled in readableNames)) @@ -408,7 +408,7 @@ return typeName.startsWith('UniquePtr<'); } -// If edgeType is a constructor type, return whatever limits it implies for its +// If edgeType is a constructor type, return whatever bits it implies for its // scope (or zero if not matching). function isLimitConstructor(typeInfo, edgeType, varName) { @@ -422,9 +422,9 @@ // Check whether the type is a known suppression type. var type = edgeType.TypeFunctionCSU.Type.Name; - let limit = 0; + let attrs = 0; if (type in typeInfo.GCSuppressors) - limit = limit | LIMIT_CANNOT_GC; + attrs = attrs | ATTR_GC_SUPPRESSED; // And now make sure this is the constructor, not some other method on a // suppression type. varName[0] contains the qualified name. @@ -438,7 +438,7 @@ if (m[1] != type_stem) return 0; - return limit; + return attrs; } // nsISupports subclasses' methods may be scriptable (or overridden diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/devtools/rootAnalysis/callgraph.js firefox-trunk-95.0~a1~hg20211020r596404/js/src/devtools/rootAnalysis/callgraph.js --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/devtools/rootAnalysis/callgraph.js 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/devtools/rootAnalysis/callgraph.js 2021-10-20 19:28:23.000000000 +0000 @@ -37,6 +37,13 @@ var virtualResolutionsSeen = new Set(); +var ID = { + jscode: 1, + anyfunc: 2, + nogcfunc: 3, + gc: 4, +}; + // map is a map from names to sets of entries. function addToNamedSet(map, name, entry) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/devtools/rootAnalysis/CFG.js firefox-trunk-95.0~a1~hg20211020r596404/js/src/devtools/rootAnalysis/CFG.js --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/devtools/rootAnalysis/CFG.js 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/devtools/rootAnalysis/CFG.js 2021-10-20 19:28:22.000000000 +0000 @@ -11,7 +11,7 @@ // Find all points (positions within the code) of the body given by the list of // bodies and the blockId to match (which will specify an outer function or a // loop within it), recursing into loops if needed. -function findAllPoints(bodies, blockId, limits) +function findAllPoints(bodies, blockId, bits) { var points = []; var body; @@ -27,9 +27,9 @@ if (!("PEdge" in body)) return; for (var edge of body.PEdge) { - points.push([body, edge.Index[0], limits]); + points.push([body, edge.Index[0], bits]); if (edge.Kind == "Loop") - points.push(...findAllPoints(bodies, edge.BlockId, limits)); + points.push(...findAllPoints(bodies, edge.BlockId, bits)); } return points; @@ -87,15 +87,15 @@ continue; var variable = callee.Variable; assert(variable.Kind == "Func"); - const limits = isConstructor(typeInfo, edge.Type, variable.Name); - if (!limits) + const bits = isConstructor(typeInfo, edge.Type, variable.Name); + if (!bits) continue; if (!("PEdgeCallInstance" in edge)) continue; if (edge.PEdgeCallInstance.Exp.Kind != "Var") continue; - points.push(...pointsInRAIIScope(bodies, body, edge, limits)); + points.push(...pointsInRAIIScope(bodies, body, edge, bits)); } return points; @@ -153,7 +153,7 @@ return undefined; } -function pointsInRAIIScope(bodies, body, constructorEdge, limits) { +function pointsInRAIIScope(bodies, body, constructorEdge, bits) { var seen = {}; var worklist = [constructorEdge.Index[1]]; var points = []; @@ -162,7 +162,7 @@ if (point in seen) continue; seen[point] = true; - points.push([body, point, limits]); + points.push([body, point, bits]); var successors = getSuccessors(body); if (!(point in successors)) continue; @@ -170,7 +170,7 @@ if (isMatchingDestructor(constructorEdge, nedge)) continue; if (nedge.Kind == "Loop") - points.push(...findAllPoints(bodies, nedge.BlockId, limits)); + points.push(...findAllPoints(bodies, nedge.BlockId, bits)); worklist.push(nedge.Index[1]); } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/devtools/rootAnalysis/computeCallgraph.js firefox-trunk-95.0~a1~hg20211020r596404/js/src/devtools/rootAnalysis/computeCallgraph.js --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/devtools/rootAnalysis/computeCallgraph.js 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/devtools/rootAnalysis/computeCallgraph.js 2021-10-20 19:28:23.000000000 +0000 @@ -15,15 +15,14 @@ } var typeInfo_filename = scriptArgs[0] || "typeInfo.txt"; -var callgraphOut_filename = scriptArgs[1] || "callgraph.txt"; +var callgraphOut_filename = scriptArgs[1] || "rawcalls.txt"; +var batch = (scriptArgs[2]|0) || 1; +var numBatches = (scriptArgs[3]|0) || 1; var origOut = os.file.redirect(callgraphOut_filename); var memoized = new Map(); -var JSNativeCaller = Object.create(null); -var JSNatives = []; - var unmangled2id = new Set(); // Insert a string into the name table and return the ID. Do not use for @@ -111,11 +110,11 @@ if (!('PEdge' in body)) return; - for (var tag of getAnnotations(functionName, body).values()) { - print("T " + functionId(functionName) + " " + tag); + const id = functionId(functionName); + print(`T ${id} ${tag}`); if (tag == "Calls JSNatives") - JSNativeCaller[functionName] = true; + printOnce(`D ${id} ${functionId("(js-code)")}`); } // Set of all callees that have been output so far, in order to suppress @@ -133,23 +132,23 @@ if (edge.Kind != "Call") continue; - // The limits (eg LIMIT_CANNOT_GC) are determined by whatever RAII + // The attrs (eg ATTR_GC_SUPPRESSED) are determined by whatever RAII // scopes might be active, which have been computed previously for all // points in the body. - var edgeLimited = body.limits[edge.Index[0]] | 0; + var edgeAttrs = body.attrs[edge.Index[0]] | 0; for (var callee of getCallees(edge)) { - // Individual callees may have additional limits. The only such - // limit currently is that nsISupports.{AddRef,Release} are assumed + // Individual callees may have additional attrs. The only such + // bit currently is that nsISupports.{AddRef,Release} are assumed // to never GC. - const limits = edgeLimited | callee.limits; - let prologue = limits ? `/${limits} ` : ""; + const attrs = edgeAttrs | callee.attrs; + let prologue = attrs ? `/${attrs} ` : ""; prologue += functionId(functionName) + " "; if (callee.kind == 'direct') { - const prev_limits = seen.has(callee.name) ? seen.get(callee.name) : LIMIT_UNVISITED; - if (prev_limits & ~limits) { + const prev_attrs = seen.has(callee.name) ? seen.get(callee.name) : ATTRS_UNVISITED; + if (prev_attrs & ~attrs) { // Only output an edge if it loosens a limit. - seen.set(callee.name, prev_limits & limits); + seen.set(callee.name, prev_attrs & attrs); printOnce("D " + prologue + functionId(callee.name)); } } else if (callee.kind == 'field') { @@ -159,9 +158,9 @@ printOnce(`${tag} ${prologue}${getId(fullfield)} CLASS ${csu} FIELD ${field}`); } else if (callee.kind == 'resolved-field') { // Fully-resolved field (virtual method) call. Record the - // callgraph edges. Do not consider limits, since they are - // local to this callsite and we are writing out a global - // record here. + // callgraph edges. Do not consider attrs, since they are local + // to this callsite and we are writing out a global record + // here. // // Any field call that does *not* have an R entry must be // assumed to call anything. @@ -184,6 +183,21 @@ } } +// Reserve IDs for special function names. + +// represents anything that can run JS +assert(ID.jscode == functionId("(js-code)")); + +// function pointers will get an edge to this in loadCallgraph.js; only the ID +// reservation is present in callgraph.txt +assert(ID.anyfunc == functionId("(any-function)")); + +// same as above, but for fields annotated to never GC +assert(ID.nogcfunc == functionId("(nogc-function)")); + +// garbage collection +assert(ID.gc == functionId("(GC)")); + var typeInfo = loadTypeInfo(typeInfo_filename); loadTypes("src_comp.xdb"); @@ -223,7 +237,7 @@ for (const {field, dtor} of methods) { const caller = getId(fieldKey(csu, field)); if (isOverridable(csu, field.Name[0])) - printOnce(`D ${caller} ${functionId("(unknown-definition)")}`); + printOnce(`D ${caller} ${functionId("(js-code)")}`); if (dtor) printOnce(`D ${caller} ${functionId(dtor)}`); if (!subclasses.has(csu)) @@ -254,17 +268,23 @@ function process(functionName, functionBodies) { for (var body of functionBodies) - body.limits = []; + body.attrs = []; for (var body of functionBodies) { - for (var [pbody, id, limits] of allRAIIGuardedCallPoints(typeInfo, functionBodies, body, isLimitConstructor)) { - pbody.limits[id] = limits; + for (var [pbody, id, attrs] of allRAIIGuardedCallPoints(typeInfo, functionBodies, body, isLimitConstructor)) { + pbody.attrs[id] = attrs; } } for (var body of functionBodies) processBody(functionName, body); + // Not strictly necessary, but add an edge from the synthetic "(js-code)" + // to RunScript to allow better stacks than just randomly selecting a + // JSNative to blame things on. + if (functionName.includes("js::RunScript")) + print(`D ${functionId("(js-code)")} ${functionId(functionName)}`); + // GCC generates multiple constructors and destructors ("in-charge" and // "not-in-charge") to handle virtual base classes. They are normally // identical, and it appears that GCC does some magic to alias them to the @@ -367,18 +387,13 @@ } if (isJSNative(mangled)) - JSNatives.push(functionName); + printOnce(`D ${functionId("(js-code)")} ${functionId(functionName)}`); } -function postprocess_callgraph() { - for (const caller of Object.keys(JSNativeCaller)) { - const caller_id = functionId(caller); - for (const callee of JSNatives) - printOnce(`D ${caller_id} ${functionId(callee)}`); - } -} +var start = batchStart(batch, numBatches, minStream, maxStream); +var end = batchLast(batch, numBatches, minStream, maxStream); -for (var nameIndex = minStream; nameIndex <= maxStream; nameIndex++) { +for (var nameIndex = start; nameIndex <= end; nameIndex++) { var name = xdb.read_key(nameIndex); var data = xdb.read_entry(name); process(name.readString(), JSON.parse(data.readString())); @@ -386,6 +401,4 @@ xdb.free_string(data); } -postprocess_callgraph(); - os.file.close(os.file.redirect(origOut)); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/devtools/rootAnalysis/computeGCFunctions.js firefox-trunk-95.0~a1~hg20211020r596404/js/src/devtools/rootAnalysis/computeGCFunctions.js --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/devtools/rootAnalysis/computeGCFunctions.js 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/devtools/rootAnalysis/computeGCFunctions.js 2021-10-20 19:28:23.000000000 +0000 @@ -9,32 +9,53 @@ loadRelativeToScript('annotations.js'); loadRelativeToScript('loadCallgraph.js'); +function usage() { + throw "Usage: computeGCFunctions.js ... --outputs "; +} + if (typeof scriptArgs[0] != 'string') - throw "Usage: computeGCFunctions.js "; + usage(); var start = "Time: " + new Date; -var callgraph_filename = scriptArgs[0]; -var gcFunctions_filename = scriptArgs[1] || "gcFunctions.txt"; -var gcFunctionsList_filename = scriptArgs[2] || "gcFunctions.lst"; -var gcEdges_filename = scriptArgs[3] || "gcEdges.txt"; +var rawcalls_filenames = []; +while (scriptArgs.length) { + const arg = scriptArgs.shift(); + if (arg == '--outputs') + break; + rawcalls_filenames.push(arg); +} +if (scriptArgs.length == 0) + usage(); + +var callgraph_filename = scriptArgs[0] || "callgraph.txt"; +var gcFunctions_filename = scriptArgs[1] || "gcFunctions.txt"; +var gcFunctionsList_filename = scriptArgs[2] || "gcFunctions.lst"; +var gcEdges_filename = scriptArgs[3] || "gcEdges.txt"; var limitedFunctionsList_filename = scriptArgs[4] || "limitedFunctions.lst"; -loadCallgraph(callgraph_filename); +var { + gcFunctions, + functions, + calleesOf, + limitedFunctions +} = loadCallgraph(rawcalls_filenames); printErr("Writing " + gcFunctions_filename); redirect(gcFunctions_filename); for (var name in gcFunctions) { - for (const readable of (readableNames[name] || [name])) { + for (let readable of (functions.readableName[name] || [name])) { print(""); const fullname = (name == readable) ? name : name + "$" + readable; print("GC Function: " + fullname); let current = name; do { current = gcFunctions[current]; - if (current in readableNames) - print(" " + readableNames[current][0]); + if (current === 'internal') + ; // Hit the end + else if (current in functions.readableName) + print(" " + functions.readableName[current][0]); else print(" " + current); } while (current in gcFunctions); @@ -44,8 +65,8 @@ printErr("Writing " + gcFunctionsList_filename); redirect(gcFunctionsList_filename); for (var name in gcFunctions) { - if (name in readableNames) { - for (var readable of readableNames[name]) + if (name in functions.readableName) { + for (var readable of functions.readableName[name]) print(name + "$" + readable); } else { print(name); @@ -73,3 +94,7 @@ printErr("Writing " + limitedFunctionsList_filename); redirect(limitedFunctionsList_filename); print(JSON.stringify(limitedFunctions, null, 4)); + +printErr("Writing " + callgraph_filename); +redirect(callgraph_filename); +saveCallgraph(functions, calleesOf); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/devtools/rootAnalysis/loadCallgraph.js firefox-trunk-95.0~a1~hg20211020r596404/js/src/devtools/rootAnalysis/loadCallgraph.js --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/devtools/rootAnalysis/loadCallgraph.js 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/devtools/rootAnalysis/loadCallgraph.js 2021-10-20 19:28:23.000000000 +0000 @@ -7,6 +7,7 @@ "use strict"; loadRelativeToScript('utility.js'); +loadRelativeToScript('callgraph.js'); // Functions come out of sixgill in the form "mangled$readable". The mangled // name is Truth. One mangled name might correspond to multiple readable names, @@ -32,26 +33,16 @@ // consider the mangled name. And some of the names encoded in callgraph.txt // are FieldCalls, not just function names. -var readableNames = {}; // map from mangled name => list of readable names -var calleesOf = {}; // map from mangled => list of tuples of {'callee':mangled, 'limits':intset} -var callersOf; // map from mangled => list of tuples of {'caller':mangled, 'limits':intset} -var gcFunctions = {}; // map from mangled callee => reason -var limitedFunctions = {}; // set of mangled names (map from mangled name => limit intset) var gcEdges = {}; -// "Map" from identifier to mangled name, or sometimes to a Class.Field name. -var functionNames = [""]; - -var mangledToId = {}; - // Returns whether the function was added. (It will be refused if it was -// already there, or if limits or annotations say it shouldn't be added.) -function addGCFunction(caller, reason, functionLimits) +// already there, or if attrs or annotations say it shouldn't be added.) +function addGCFunction(caller, reason, gcFunctions, functionAttrs, functions) { - if (functionLimits[caller] & LIMIT_CANNOT_GC) + if (functionAttrs[caller] && functionAttrs[caller][1] & ATTR_GC_SUPPRESSED) return false; - if (ignoreGCFunction(functionNames[caller])) + if (ignoreGCFunction(functions.name[caller], functions.readableName)) return false; if (!(caller in gcFunctions)) { @@ -62,61 +53,83 @@ return false; } -// Every caller->callee callsite is associated with a limit saying what is +// Every caller->callee callsite is associated with attrs saying what is // allowed at that callsite (eg if it's in a GC suppression zone, it would have -// LIMIT_CANNOT_GC set.) A given caller might call the same callee multiple -// times, with different limits, so we want to associate the -// edge with the intersection ('AND') of all of the callsites' limits. -// -// Scan through all call edges and intersect the limits for all matching -// edges (so that the result is the least limiting of all -// matching edges.) Preserve the original order. +// ATTR_GC_SUPPRESSED set.) A given caller might call the same callee multiple +// times, with different attributes. Associate the edge with +// the intersection (AND) and disjunction (OR) of all of the callsites' attrs. +// The AND ('all') says what attributes are present for all callers; the OR +// ('any') says what attributes are present on any caller. Preserve the +// original order. // // During the same scan, build callersOf from calleesOf. -function merge_repeated_calls(calleesOf) { - const callersOf = Object.create(null); +function generate_callgraph(rawCallees) { + const callersOf = new Map(); + const calleesOf = new Map(); - for (const [caller_prop, callee_limits] of Object.entries(calleesOf)) { - const caller = caller_prop|0; + for (const [caller, callee_attrs] of rawCallees) { const ordered_callees = []; - // callee_limits is a list of {callee,limit} objects. - const callee2limit = new Map(); - for (const {callee, limits} of callee_limits) { - const prev_limits = callee2limit.get(callee); - if (prev_limits === undefined) { - callee2limit.set(callee, limits); + // callee_attrs is a list of {callee,any,all} objects. + const callee2any = new Map(); + const callee2all = new Map(); + for (const {callee, any, all} of callee_attrs) { + const prev_any = callee2any.get(callee); + if (prev_any === undefined) { + assert(!callee2all.has(callee)); + callee2any.set(callee, any); + callee2all.set(callee, all); ordered_callees.push(callee); } else { - callee2limit.set(callee, prev_limits & limits); + const prev_all = callee2all.get(callee); + callee2any.set(callee, prev_any | any); + callee2all.set(callee, prev_all & all); } } - // Update the contents of callee_limits to contain a single entry for - // each callee, with its limits set to the AND of the limits observed - // at all callsites within this caller function. - callee_limits.length = 0; + // Update the contents of callee_attrs to contain a single entry for + // each callee, with its attrs set to the AND of the attrs observed at + // all callsites within this caller function. + callee_attrs.length = 0; for (const callee of ordered_callees) { - const limits = callee2limit.get(callee); - callee_limits.push({callee, limits}); - if (!(callee in callersOf)) - callersOf[callee] = []; - callersOf[callee].push({caller, limits}); + const any = callee2any.get(callee); + const all = callee2all.get(callee); + if (!calleesOf.has(caller)) + calleesOf.set(caller, new Map()); + calleesOf.get(caller).set(callee, {any, all}); + if (!callersOf.has(callee)) + callersOf.set(callee, new Map()); + callersOf.get(callee).set(caller, {any, all}); } } - return callersOf; + return {callersOf, calleesOf}; } -function loadCallgraph(file) +// Returns object mapping mangled => reason for GCing +function loadRawCallgraphFile(file) { - const fieldCallLimits = {}; + const functions = { + // "Map" from identifier to mangled name, or sometimes to a Class.Field name. + name: [""], + + // map from mangled name => list of readable names + readableName: {}, + + mangledToId: {} + }; + + const fieldCallAttrs = {}; const fieldCallCSU = new Map(); // map from full field name id => csu name - // set of mangled names (map from mangled name => limit intset) - var functionLimits = {}; + // set of mangled names (map from mangled name => [any,all]) + var functionAttrs = {}; + + const gcCalls = []; + const indirectCalls = []; - let numGCCalls = 0; + // map from mangled => list of tuples of {'callee':mangled, 'any':intset, 'all':intset} + const rawCallees = new Map(); for (let line of readFileLines_gen(file)) { line = line.replace(/\n/, ""); @@ -124,107 +137,219 @@ let match; if (match = line.charAt(0) == "#" && /^\#(\d+) (.*)/.exec(line)) { const [ _, id, mangled ] = match; - assert(functionNames.length == id); - functionNames.push(mangled); - mangledToId[mangled] = id; + assert(functions.name.length == id); + functions.name.push(mangled); + functions.mangledToId[mangled] = id|0; continue; } if (match = line.charAt(0) == "=" && /^= (\d+) (.*)/.exec(line)) { const [ _, id, readable ] = match; - const mangled = functionNames[id]; - if (mangled in readableNames) - readableNames[mangled].push(readable); + const mangled = functions.name[id]; + if (mangled in functions.readableName) + functions.readableName[mangled].push(readable); else - readableNames[mangled] = [ readable ]; + functions.readableName[mangled] = [ readable ]; continue; } - let limits = 0; + let attrs = 0; // Example line: D /17 6 7 // // This means a direct call from 6 -> 7, but within a scope that - // applies limits 0x1 and 0x10 to the callee. + // applies attrs 0x1 and 0x10 to the callee. // - // Look for a limit and remove it from the line if found. + // Look for a bit specifier and remove it from the line if found. if (line.indexOf("/") != -1) { match = /^(..)\/(\d+) (.*)/.exec(line); line = match[1] + match[3]; - limits = match[2]|0; + attrs = match[2]|0; } const tag = line.charAt(0); if (match = tag == 'I' && /^I (\d+) VARIABLE ([^\,]*)/.exec(line)) { const caller = match[1]|0; const name = match[2]; - if (!indirectCallCannotGC(functionNames[caller], name) && - !(limits & LIMIT_CANNOT_GC)) - { - addGCFunction(caller, "IndirectCall: " + name, functionLimits); - } + if (indirectCallCannotGC(functions.name[caller], name)) + attrs |= ATTR_GC_SUPPRESSED; + indirectCalls.push([caller, "IndirectCall: " + name, attrs]); } else if (match = tag == 'F' && /^F (\d+) (\d+) CLASS (.*?) FIELD (.*)/.exec(line)) { const caller = match[1]|0; const fullfield = match[2]|0; const csu = match[3]; const fullfield_str = csu + "." + match[4]; - assert(functionNames[fullfield] == fullfield_str); - if (limits) - fieldCallLimits[fullfield] = limits; - addToKeyedList(calleesOf, caller, {callee:fullfield, limits}); + assert(functions.name[fullfield] == fullfield_str); + if (attrs) + fieldCallAttrs[fullfield] = attrs; + addToMappedList(rawCallees, caller, {callee:fullfield, any:attrs, all:attrs}); fieldCallCSU.set(fullfield, csu); + + if (fieldCallCannotGC(csu, fullfield_str)) + addToMappedList(rawCallees, fullfield, {callee:ID.nogcfunc, any:0, all:0}); + else + addToMappedList(rawCallees, fullfield, {callee:ID.anyfunc, any:0, all:0}); } else if (match = tag == 'V' && /^V (\d+) (\d+) CLASS (.*?) FIELD (.*)/.exec(line)) { // V tag is no longer used, but we are still emitting it becasue it // can be helpful to understand what's going on. } else if (match = tag == 'D' && /^D (\d+) (\d+)/.exec(line)) { const caller = match[1]|0; const callee = match[2]|0; - addToKeyedList(calleesOf, caller, {callee:callee, limits:limits}); + addToMappedList(rawCallees, caller, {callee, any:attrs, all:attrs}); } else if (match = tag == 'R' && /^R (\d+) (\d+)/.exec(line)) { assert(false, "R tag is no longer used"); } else if (match = tag == 'T' && /^T (\d+) (.*)/.exec(line)) { const id = match[1]|0; let tag = match[2]; - if (tag == 'GC Call') { - addGCFunction(id, "GC", functionLimits); - numGCCalls++; - } + if (tag == 'GC Call') + gcCalls.push(id); } else { assert(false, "Invalid format in callgraph line: " + line); } } - // Callers have a list of callees, with duplicates (if the same function is - // called more than once.) Merge the repeated calls, only keeping limits - // that are in force for *every* callsite of that callee. Also, generate - // the callersOf table at the same time. - callersOf = merge_repeated_calls(calleesOf); + printErr("Loaded " + file); + + return { + fieldCallAttrs, + fieldCallCSU, + gcCalls, + indirectCalls, + rawCallees, + functions + }; +} + +// Take a set of rawcalls filenames (as in, the raw callgraph data output by +// computeCallgraph.js) and combine them into a global callgraph, renumbering +// the IDs as needed. +function mergeRawCallgraphs(filenames) { + let d; + for (const filename of filenames) { + const raw = loadRawCallgraphFile(filename); + if (!d) { + d = raw; + continue; + } + + const { + fieldCallAttrs, + fieldCallCSU, + gcCalls, + indirectCalls, + rawCallees, + functions + } = raw; + + // Compute the ID mapping. Incoming functions that already have an ID + // will be mapped to that ID; new ones will allocate a fresh ID. + const remap = new Array(functions.name.length); + for (let i = 1; i < functions.name.length; i++) { + const mangled = functions.name[i]; + const old_id = d.functions.mangledToId[mangled] + if (old_id) { + remap[i] = old_id; + } else { + const newid = d.functions.name.length; + d.functions.mangledToId[mangled] = newid; + d.functions.name.push(mangled); + remap[i] = newid; + assert(!(mangled in d.functions.readableName), mangled + " readable name is already found"); + const readables = functions.readableName[mangled]; + if (readables !== undefined) + d.functions.readableName[mangled] = readables; + } + } + + for (const [fullfield, attrs] of Object.entries(fieldCallAttrs)) + d.fieldCallAttrs[remap[fullfield]] = attrs; + for (const [fullfield, csu] of fieldCallCSU.entries()) + d.fieldCallCSU.set(remap[fullfield], csu); + for (const call of gcCalls) + d.gcCalls.push(remap[call]); + for (const [caller, name, attrs] of indirectCalls) + d.indirectCalls.push([remap[caller], name, attrs]); + for (const [caller, callees] of rawCallees) { + for (const {callee, any, all} of callees) { + addToMappedList(d.rawCallees, remap[caller]|0, {callee:remap[callee], any, all}); + } + } + } + + return d; +} + +function loadCallgraph(files) +{ + const { + fieldCallAttrs, + fieldCallCSU, + gcCalls, + indirectCalls, + rawCallees, + functions + } = mergeRawCallgraphs(files); + + assert(ID.jscode == functions.mangledToId["(js-code)"]); + assert(ID.anyfunc == functions.mangledToId["(any-function)"]); + assert(ID.nogcfunc == functions.mangledToId["(nogc-function)"]); + assert(ID.gc == functions.mangledToId["(GC)"]); + + addToMappedList(rawCallees, functions.mangledToId["(any-function)"], {callee:ID.gc, any:0, all:0}); + + // Compute functionAttrs: it should contain the set of functions that + // are *always* called within some sort of limited context (eg GC + // suppression). + + // set of mangled names (map from mangled name => [any,all]) + const functionAttrs = {}; + + // Initialize to field calls with attrs set. + for (var [name, attrs] of Object.entries(fieldCallAttrs)) + functionAttrs[name] = [attrs, attrs]; + + // map from ID => reason + const gcFunctions = { [ID.gc]: 'internal' }; // Add in any extra functions at the end. (If we did this early, it would // mess up the id <-> name correspondence. Also, we need to know if the // functions even exist in the first place.) - for (var func of extraGCFunctions()) { - addGCFunction(mangledToId[func], "annotation", functionLimits); + for (var func of extraGCFunctions(functions.readableName)) { + addGCFunction(functions.mangledToId[func], "annotation", gcFunctions, functionAttrs, functions); } - const unknown = mangledToId['(unknown-definition)']; - if (unknown) { - addGCFunction(unknown, "internal", functionLimits); + + for (const func of gcCalls) + addToMappedList(rawCallees, func, {callee:ID.gc, any:0, all:0}); + + for (const [caller, indirect, attrs] of indirectCalls) { + const id = functions.name.length; + functions.name.push(indirect); + functions.mangledToId[indirect] = id; + addToMappedList(rawCallees, caller, {callee:id, any:attrs, all:attrs}); + addToMappedList(rawCallees, id, {callee:ID.anyfunc, any:0, all:0}); } - // Compute functionLimits: it should contain the set of functions that + // Callers have a list of callees, with duplicates (if the same function is + // called more than once.) Merge the repeated calls, only keeping attrs + // that are in force for *every* callsite of that callee. Also, generate + // the callersOf table at the same time. + // + // calleesOf : map from mangled => {mangled callee => {'any':intset, 'all':intset}} + // callersOf : map from mangled => {mangled caller => {'any':intset, 'all':intset}} + const {callersOf, calleesOf} = generate_callgraph(rawCallees); + + // Compute functionAttrs: it should contain the set of functions that // are *always* called within some sort of limited context (eg GC // suppression). - // Initialize to limited field calls. - for (var [name, limits] of Object.entries(fieldCallLimits)) { - if (limits) - functionLimits[name] = limits; - } + // Initialize to field calls with attrs set. + for (var [name, attrs] of Object.entries(fieldCallAttrs)) + functionAttrs[name] = [attrs, attrs]; - // Initialize functionLimits to the set of all functions, where each one is - // maximally limited, and return a worklist containing all simple roots + // Initialize functionAttrs to the set of all functions, where each one is + // maximally attributed, and return a worklist containing all simple roots // (nodes with no callers). - var roots = gather_simple_roots(functionLimits, callersOf); + const simple_roots = gather_simple_roots(functionAttrs, calleesOf, callersOf); - // Traverse the graph, spreading the limits down from the roots. - propagate_limits(roots, functionLimits, calleesOf); + // Traverse the graph, spreading the attrs down from the roots. + propagate_attrs(simple_roots, functionAttrs, calleesOf); // There are a surprising number of "recursive roots", where there is a // cycle of functions calling each other but not called by anything else, @@ -232,34 +357,41 @@ // has eliminated everything reachable from simple roots, traverse the // remaining graph to gather up a representative function from each root // cycle. - const recursive_roots = gather_recursive_roots(functionLimits, calleesOf, callersOf); + // + // Simple example: in the JS shell build, moz_xstrdup calls itself, but + // there are no calls to it from within js/src. + const recursive_roots = gather_recursive_roots(functionAttrs, calleesOf, callersOf, functions); // And do a final traversal starting with the recursive roots. - propagate_limits(recursive_roots, functionLimits, calleesOf); + propagate_attrs(recursive_roots, functionAttrs, calleesOf); + + for (const [f, [any, all]] of Object.entries(functionAttrs)) { + // Throw out all functions with no attrs set, to reduce the size of the + // output. From now on, "not in functionAttrs" means [any=0, all=0]. + if (any == 0 && all == 0) + delete functionAttrs[f]; - // Eliminate GC-limited functions from the set of functions known to GC. - for (var name in gcFunctions) { - if (functionLimits[name] & LIMIT_CANNOT_GC) + // Remove GC-suppressed functions from the set of functions known to GC. + if (all & ATTR_GC_SUPPRESSED) delete gcFunctions[name]; } - // functionLimits should now contain all functions that are always called - // in a limited context. + // functionAttrs now contains all functions that are ever called in an + // attributed context, based on the known callgraph (i.e., calls through + // function pointers are not taken into consideration.) // Sanity check to make sure the callgraph has some functions annotated as // GC Calls. This is mostly a check to be sure the earlier processing // succeeded (as opposed to, say, running on empty xdb files because you // didn't actually compile anything interesting.) - assert(numGCCalls > 0, "No GC functions found!"); + assert(gcCalls.length > 0, "No GC functions found!"); // Initialize the worklist to all known gcFunctions. - var worklist = []; - for (const name in gcFunctions) - worklist.push(name); + const worklist = [ID.gc]; - // Add all field calls (but not virtual method calls) to gcFunctions. + // Include all field calls (but not virtual method calls). for (const [name, csuName] of fieldCallCSU) { - const fullFieldName = functionNames[name]; + const fullFieldName = functions.name[name]; if (!fieldCallCannotGC(csuName, fullFieldName)) { gcFunctions[name] = 'arbitrary function pointer ' + fullFieldName; worklist.push(name); @@ -271,80 +403,117 @@ while (worklist.length) { name = worklist.shift(); assert(name in gcFunctions, "gcFunctions does not contain " + name); - if (!(name in callersOf)) + if (!callersOf.has(name)) continue; - for (const {caller, limits} of callersOf[name]) { - if (!(limits & LIMIT_CANNOT_GC)) { - if (addGCFunction(caller, name, functionLimits)) + for (const [caller, {any, all}] of callersOf.get(name)) { + if (!(all & ATTR_GC_SUPPRESSED)) { + if (addGCFunction(caller, name, gcFunctions, functionAttrs, functions)) worklist.push(caller); } } } - // Convert functionLimits to limitedFunctions (using mangled names instead + // Convert functionAttrs to limitedFunctions (using mangled names instead // of ids.) - for (const [id, limits] of Object.entries(functionLimits)) - limitedFunctions[functionNames[id]] = { limits }; + // set of mangled names (map from mangled name => {any,all,recursive_root:bool} + var limitedFunctions = {}; + + for (const [id, [any, all]] of Object.entries(functionAttrs)) + limitedFunctions[functions.name[id]] = { attributes: all }; for (const [id, limits, label] of recursive_roots) { - const name = functionNames[id]; + const name = functions.name[id]; const s = limitedFunctions[name] || (limitedFunctions[name] = {}); s.recursive_root = true; } - // The above code uses integer ids for efficiency. External code uses - // mangled names. Rewrite the various data structures to convert ids to - // mangled names. - remap_ids_to_mangled_names(); + // Remap ids to mangled names. + const namedGCFunctions = {}; + for (const [caller, reason] of Object.entries(gcFunctions)) { + namedGCFunctions[functions.name[caller]] = functions.name[reason] || reason; + } + + return { + gcFunctions: namedGCFunctions, + functions, + calleesOf, + callersOf, + limitedFunctions + }; +} + +function saveCallgraph(functions, calleesOf) { + // Write out all the ids and their readable names. + let id = -1; + for (const name of functions.name) { + id += 1; + if (id == 0) continue; + print(`#${id} ${name}`); + for (const readable of (functions.readableName[name] || [])) { + if (readable != name) + print(`= ${id} ${readable}`); + } + } + + // Omit field calls for now; let them appear as if they were functions. + + const attrstring = range => range.any || range.all ? `${range.all}:${range.any} ` : ''; + for (const [caller, callees] of calleesOf) { + for (const [callee, attrs] of callees) { + print(`D ${attrstring(attrs)}${caller} ${callee}`); + } + } + + // Omit tags for now. This really should preserve all tags. The "GC Call" + // tag will already be represented in the graph by having an edge to the + // "(GC)" node. } // Return a worklist of functions with no callers, and also initialize -// functionLimits to the set of all functions, each mapped to LIMIT_UNVISTED. -function gather_simple_roots(functionLimits, callersOf) { +// functionAttrs to the set of all functions, each mapped to +// [ATTRS_NONE, ATTRS_UNVISITED]. +function gather_simple_roots(functionAttrs, calleesOf, callersOf) { const roots = []; - for (let callee in callersOf) - functionLimits[callee] = LIMIT_UNVISITED; - for (let caller in calleesOf) { - if (!(caller in callersOf)) { - functionLimits[caller] = LIMIT_UNVISITED; - roots.push([caller, LIMIT_NONE, 'root']); - } + for (const callee of callersOf.keys()) + functionAttrs[callee] = [ATTRS_NONE, ATTRS_UNVISITED]; + for (const caller of calleesOf.keys()) { + functionAttrs[caller] = [ATTRS_NONE, ATTRS_UNVISITED]; + if (!callersOf.has(caller)) + roots.push([caller, ATTRS_NONE, 'root']); } return roots; } // Recursively traverse the callgraph from the roots. Recurse through every -// edge that weakens the limits. (Limits that entirely disappear, aka go to a -// zero intset, will be removed from functionLimits.) -function propagate_limits(roots, functionLimits, calleesOf) { +// edge that weakens the attrs. (Attrs that entirely disappear, ie go to a zero +// intset, will be removed from functionAttrs.) +function propagate_attrs(roots, functionAttrs, calleesOf) { const worklist = Array.from(roots); let top = worklist.length; while (top > 0) { // Consider caller where (graph) -> caller -> (0 or more callees) // 'callercaller' is for debugging. - const [caller, edge_limits, callercaller] = worklist[--top]; - const prev_limits = functionLimits[caller]; - if (prev_limits & ~edge_limits) { - // Turning off a limit (or unvisited marker). Must recurse to the - // children. But first, update this caller's limits: we just found - // out it is reachable by an unlimited path, so it must be treated - // as unlimited (with respect to that bit). - const new_limits = prev_limits & edge_limits; - if (new_limits) - functionLimits[caller] = new_limits; - else - delete functionLimits[caller]; - for (const {callee, limits} of (calleesOf[caller] || [])) - worklist[top++] = [callee, limits | edge_limits, caller]; + const [caller, edge_attrs, callercaller] = worklist[--top]; + assert(caller in functionAttrs); + const [prev_any, prev_all] = functionAttrs[caller]; + assert(prev_any !== undefined); + assert(prev_all !== undefined); + const [new_any, new_all] = [prev_any | edge_attrs, prev_all & edge_attrs]; + if (prev_any != new_any || prev_all != new_all) { + // Update function attrs, then recurse to the children if anything + // was updated. + functionAttrs[caller] = [new_any, new_all]; + for (const [callee, {any, all}] of (calleesOf.get(caller) || new Map)) + worklist[top++] = [callee, all | edge_attrs, caller]; } } } // Mutually-recursive roots and their descendants will not have been visited, -// and will still be set to LIMIT_UNVISITED. Scan through and gather them. -function gather_recursive_roots(functionLimits, calleesOf, callersOf) { +// and will still be set to [0, ATTRS_UNVISITED]. Scan through and gather them. +function gather_recursive_roots(functionAttrs, calleesOf, callersOf, functions) { const roots = []; // Pick any node. Mark everything reachable by adding to a 'seen' set. At @@ -356,14 +525,15 @@ // // Repeat with remaining unmarked nodes until all nodes are marked. const seen = new Set(); - for (let func in functionLimits) { + for (let [func, [any, all]] of Object.entries(functionAttrs)) { func = func|0; - if (functionLimits[func] != LIMIT_UNVISITED) + if (all != ATTRS_UNVISITED) continue; // We should only be looking at nodes with callers, since otherwise // they would have been handled in the previous pass! - assert(callersOf[func].length > 0); + assert(callersOf.has(func)); + assert(callersOf.get(func).size > 0); if (seen.has(func)) continue; @@ -371,10 +541,11 @@ const work = [func]; while (work.length > 0) { const f = work.pop(); - for (const callee of (calleesOf[f] || []).map(o => o.callee)) { + if (!calleesOf.has(f)) continue; + for (const callee of calleesOf.get(f).keys()) { if (!seen.has(callee) && callee != func && - functionLimits[callee] == LIMIT_UNVISITED) + functionAttrs[callee][1] == ATTRS_UNVISITED) { work.push(callee); seen.add(callee); @@ -384,19 +555,29 @@ assert(!seen.has(func)); seen.add(func); - if (callersOf[func].findIndex(o => !seen.has(o.caller)) == -1) { + if ([...callersOf.get(func).keys()].findIndex(f => !seen.has(f)) == -1) { // No unmarked incoming edges, including self-edges, so this is a // (recursive) root. - roots.push([func, LIMIT_NONE, 'recursive-root']); + roots.push([func, ATTRS_NONE, 'recursive-root']); } } return roots; -} -function remap_ids_to_mangled_names() { - var tmp = gcFunctions; - gcFunctions = {}; - for (const [caller, reason] of Object.entries(tmp)) - gcFunctions[functionNames[caller]] = functionNames[reason] || reason; + tmp = calleesOf; + calleesOf = {}; + for (const [callerId, callees] of Object.entries(calleesOf)) { + const caller = functionNames[callerId]; + for (const {calleeId, limits} of callees) + calleesOf[caller][functionNames[calleeId]] = limits; + } + + tmp = callersOf; + callersOf = {}; + for (const [calleeId, callers] of Object.entries(callersOf)) { + const callee = functionNames[calleeId]; + callersOf[callee] = {}; + for (const {callerId, limits} of callers) + callersOf[callee][functionNames[caller]] = limits; + } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/devtools/rootAnalysis/mach_commands.py firefox-trunk-95.0~a1~hg20211020r596404/js/src/devtools/rootAnalysis/mach_commands.py --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/devtools/rootAnalysis/mach_commands.py 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/devtools/rootAnalysis/mach_commands.py 2021-10-20 19:28:22.000000000 +0000 @@ -267,8 +267,6 @@ sys.executable, os.path.join(script_dir(command_context), "analyze.py"), "dbs", - "--upto", - "dbs", "-v", "--buildcommand=" + buildscript, ] @@ -378,6 +376,7 @@ args += extra else: args += [ + "--first", "gcTypes", "-v", ] diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/devtools/rootAnalysis/t/graph/test.py firefox-trunk-95.0~a1~hg20211020r596404/js/src/devtools/rootAnalysis/t/graph/test.py --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/devtools/rootAnalysis/t/graph/test.py 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/devtools/rootAnalysis/t/graph/test.py 2021-10-20 19:28:22.000000000 +0000 @@ -51,4 +51,4 @@ ) assert len(set([node[1], node[2]]) & rroots) == 1 assert len(set([node[4], node[5]]) & rroots) == 1 -assert len(rroots) == 4 # n1, n4, f, self_recursive +assert len(rroots) == 4, "rroots = {}".format(rroots) # n1, n4, f, self_recursive diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/devtools/rootAnalysis/t/testlib.py firefox-trunk-95.0~a1~hg20211020r596404/js/src/devtools/rootAnalysis/t/testlib.py --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/devtools/rootAnalysis/t/testlib.py 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/devtools/rootAnalysis/t/testlib.py 2021-10-20 19:28:22.000000000 +0000 @@ -85,7 +85,7 @@ ) return json.loads(output) - def run_analysis_script(self, phase, upto=None): + def run_analysis_script(self, startPhase, upto=None): open("defaults.py", "w").write( """\ analysis_scriptdir = '{scriptdir}' @@ -98,10 +98,10 @@ sys.executable, os.path.join(scriptdir, "analyze.py"), "-v" if self.verbose else "-q", - phase, ] + cmd += ["--first", startPhase] if upto: - cmd += ["--upto", upto] + cmd += ["--last", upto] cmd.append("--source=%s" % self.indir) cmd.append("--objdir=%s" % self.outdir) cmd.append("--js=%s" % self.cfg.js) diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/devtools/rootAnalysis/utility.js firefox-trunk-95.0~a1~hg20211020r596404/js/src/devtools/rootAnalysis/utility.js --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/devtools/rootAnalysis/utility.js 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/devtools/rootAnalysis/utility.js 2021-10-20 19:28:23.000000000 +0000 @@ -8,19 +8,22 @@ loadRelativeToScript('dumpCFG.js'); -// Limit inset bits - each call edge may carry a set of 'limit' bits, saying eg +// Attribute bits - each call edge may carry a set of 'attrs' bits, saying eg // that the edge takes place within a scope where GC is suppressed, for // example. -var LIMIT_NONE = 0; -var LIMIT_CANNOT_GC = 1; -var LIMIT_ALL = 1; +var ATTR_GC_SUPPRESSED = 1; +var ATTR_CANSCRIPT_BOUNDED = 2; // Unimplemented +var ATTR_DOM_ITERATING = 4; // Unimplemented + +var ATTRS_NONE = 0; +var ATTRS_ALL = 7; // All possible bits set // The traversal algorithms we run will recurse into children if you change any -// limit bit to zero. Use all bits set to maximally limited, including +// attrs bit to zero. Use all bits set to maximally attributed, including // additional bits that all just mean "unvisited", so that the first time we -// see a node with this limit, we're guaranteed to turn at least one bit off +// see a node with this attrs, we're guaranteed to turn at least one bit off // and thereby keep going. -var LIMIT_UNVISITED = 0xffff; +var ATTRS_UNVISITED = 0xffff; // gcc appends this to mangled function names for "not in charge" // constructors/destructors. @@ -286,7 +289,56 @@ return collection[key]; } +function addToMappedList(map, key, entry) +{ + if (!map.has(key)) + map.set(key, []); + map.get(key).push(entry); + return map.get(key); +} + function loadTypeInfo(filename) { return JSON.parse(os.file.readFile(filename)); } + +// Given the range `first` .. `last`, break it down into `count` batches and +// return the start of the (1-based) `num` batch. +function batchStart(num, count, first, last) { + const N = (last - first) + 1; + return Math.floor((num - 1) / count * N) + first; +} + +// As above, but return the last value in the (1-based) `num` batch. +function batchLast(num, count, first, last) { + const N = (last - first) + 1; + return Math.floor(num / count * N) + first - 1; +} + +// Debugging tool. See usage below. +function PropertyTracer(traced_prop, check) { + return { + matches(prop, value) { + if (prop != traced_prop) + return false; + if ('value' in check) + return value == check.value; + return true; + }, + + // Also called when defining a property. + set(obj, prop, value) { + if (this.matches(prop, value)) + debugger; + return Reflect.set(...arguments); + }, + }; +} + +// Usage: var myobj = traced({}, 'name', {value: 'Bob'}) +// +// This will execute a `debugger;` statement when myobj['name'] is defined or +// set to 'Bob'. +function traced(obj, traced_prop, check) { + return new Proxy(obj, PropertyTracer(traced_prop, check)); +} diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/frontend/BytecodeCompilation.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/frontend/BytecodeCompilation.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/frontend/BytecodeCompilation.h 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/frontend/BytecodeCompilation.h 2021-10-20 19:28:23.000000000 +0000 @@ -9,11 +9,12 @@ #include "mozilla/Utf8.h" // mozilla::Utf8Unit -#include "js/CompileOptions.h" // JS::ReadOnlyCompileOptions -#include "js/SourceText.h" // JS::SourceText -#include "js/TypeDecls.h" // JS::Handle (fwd) -#include "js/UniquePtr.h" // js::UniquePtr -#include "vm/ScopeKind.h" // js::ScopeKind +#include "js/CompileOptions.h" // JS::ReadOnlyCompileOptions, JS::InstantiateOptions +#include "js/GCVector.h" // JS::GCVector +#include "js/SourceText.h" // JS::SourceText +#include "js/TypeDecls.h" // JS::Handle (fwd) +#include "js/UniquePtr.h" // js::UniquePtr +#include "vm/ScopeKind.h" // js::ScopeKind namespace js { @@ -83,6 +84,12 @@ !options.forceFullParse(); } +void FireOnNewScript(JSContext* cx, const JS::InstantiateOptions& options, + JS::Handle script); + +void FireOnNewScripts(JSContext* cx, const JS::InstantiateOptions& options, + JS::Handle> scripts); + } // namespace frontend } // namespace js diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/frontend/BytecodeCompiler.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/frontend/BytecodeCompiler.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/frontend/BytecodeCompiler.cpp 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/frontend/BytecodeCompiler.cpp 2021-10-20 19:28:23.000000000 +0000 @@ -396,9 +396,8 @@ } Rooted script(cx, gcOutput.script); - if (!input.options.hideFromNewScriptInitial()) { - DebugAPI::onNewScript(cx, script); - } + const JS::InstantiateOptions instantiateOptions(input.options); + FireOnNewScript(cx, instantiateOptions, script); } return true; @@ -410,7 +409,7 @@ AutoGeckoProfilerEntry pseudoFrame(cx, "stencil instantiate", JS::ProfilingCategoryPair::JS_Parsing); - return CompilationStencil::prepareForInstantiate(cx, input, stencil, + return CompilationStencil::prepareForInstantiate(cx, input.atomCache, stencil, gcOutput); } @@ -1220,10 +1219,9 @@ MOZ_ASSERT(!cx->isHelperThreadContext()); + const JS::InstantiateOptions instantiateOptions(options); Rooted script(cx, gcOutput.get().script); - if (!options.hideFromNewScriptInitial()) { - DebugAPI::onNewScript(cx, script); - } + FireOnNewScript(cx, instantiateOptions, script); } assertException.reset(); @@ -1277,3 +1275,25 @@ FunctionAsyncKind::SyncFunction, enclosingScope); } + +void frontend::FireOnNewScript(JSContext* cx, + const JS::InstantiateOptions& options, + JS::Handle script) { + if (!options.hideFromNewScriptInitial()) { + DebugAPI::onNewScript(cx, script); + } +} + +void frontend::FireOnNewScripts(JSContext* cx, + const JS::InstantiateOptions& options, + JS::Handle> scripts) { + if (!options.hideFromNewScriptInitial()) { + JS::Rooted rootedScript(cx); + for (auto& script : scripts) { + MOZ_ASSERT(script->isGlobalCode()); + + rootedScript = script; + DebugAPI::onNewScript(cx, rootedScript); + } + } +} diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/frontend/CompilationStencil.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/frontend/CompilationStencil.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/frontend/CompilationStencil.h 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/frontend/CompilationStencil.h 2021-10-20 19:28:23.000000000 +0000 @@ -26,6 +26,7 @@ #include "frontend/Stencil.h" #include "frontend/TaggedParserAtomIndexHasher.h" // TaggedParserAtomIndexHasher #include "frontend/UsedNameTracker.h" +#include "js/CompileOptions.h" // JS::ReadOnlyCompileOptions #include "js/GCVector.h" #include "js/HashTable.h" #include "js/RealmOptions.h" @@ -748,8 +749,8 @@ CompilationGCOutput& gcOutput); [[nodiscard]] static bool prepareForInstantiate( - JSContext* cx, CompilationInput& input, const CompilationStencil& stencil, - CompilationGCOutput& gcOutput); + JSContext* cx, CompilationAtomCache& atomCache, + const CompilationStencil& stencil, CompilationGCOutput& gcOutput); [[nodiscard]] static bool instantiateStencils( JSContext* cx, CompilationInput& input, const CompilationStencil& stencil, diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/frontend/Stencil.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/frontend/Stencil.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/frontend/Stencil.cpp 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/frontend/Stencil.cpp 2021-10-20 19:28:23.000000000 +0000 @@ -1107,7 +1107,7 @@ } static bool InstantiateScriptSourceObject(JSContext* cx, - CompilationInput& input, + const JS::InstantiateOptions& options, const CompilationStencil& stencil, CompilationGCOutput& gcOutput) { MOZ_ASSERT(stencil.source); @@ -1128,7 +1128,7 @@ // until after we've merged compartments. if (!cx->isHelperThreadContext()) { Rooted sourceObject(cx, gcOutput.sourceObject); - if (!ScriptSourceObject::initFromOptions(cx, sourceObject, input.options)) { + if (!ScriptSourceObject::initFromOptions(cx, sourceObject, options)) { return false; } } @@ -1537,7 +1537,7 @@ CompilationInput& input, const CompilationStencil& stencil, CompilationGCOutput& gcOutput) { - if (!prepareForInstantiate(cx, input, stencil, gcOutput)) { + if (!prepareForInstantiate(cx, input.atomCache, stencil, gcOutput)) { return false; } @@ -1553,14 +1553,17 @@ bool isInitialParse = stencil.isInitialStencil(); MOZ_ASSERT(stencil.isInitialStencil() == input.isInitialStencil()); + CompilationAtomCache& atomCache = input.atomCache; + const JS::InstantiateOptions options(input.options); + // Phase 1: Instantiate JSAtom/JSStrings. - if (!InstantiateAtoms(cx, input.atomCache, stencil)) { + if (!InstantiateAtoms(cx, atomCache, stencil)) { return false; } // Phase 2: Instantiate ScriptSourceObject, ModuleObject, JSFunctions. if (isInitialParse) { - if (!InstantiateScriptSourceObject(cx, input, stencil, gcOutput)) { + if (!InstantiateScriptSourceObject(cx, options, stencil, gcOutput)) { return false; } @@ -1572,12 +1575,12 @@ MOZ_ASSERT(input.enclosingScope->environmentChainLength() == ModuleScope::EnclosingEnvironmentChainLength); - if (!InstantiateModuleObject(cx, input.atomCache, stencil, gcOutput)) { + if (!InstantiateModuleObject(cx, atomCache, stencil, gcOutput)) { return false; } } - if (!InstantiateFunctions(cx, input.atomCache, stencil, gcOutput)) { + if (!InstantiateFunctions(cx, atomCache, stencil, gcOutput)) { return false; } } else { @@ -1605,7 +1608,7 @@ // Phase 4: Instantiate (inner) BaseScripts. if (isInitialParse) { - if (!InstantiateScriptStencils(cx, input.atomCache, stencil, gcOutput)) { + if (!InstantiateScriptStencils(cx, atomCache, stencil, gcOutput)) { return false; } } @@ -1619,7 +1622,7 @@ // Phase 6: Update lazy scripts. if (stencil.canLazilyParse) { - UpdateEmittedInnerFunctions(cx, input.atomCache, stencil, gcOutput); + UpdateEmittedInnerFunctions(cx, atomCache, stencil, gcOutput); if (isInitialParse) { LinkEnclosingLazyScript(stencil, gcOutput); @@ -1825,15 +1828,15 @@ /* static */ bool CompilationStencil::prepareForInstantiate( - JSContext* cx, CompilationInput& input, const CompilationStencil& stencil, - CompilationGCOutput& gcOutput) { + JSContext* cx, CompilationAtomCache& atomCache, + const CompilationStencil& stencil, CompilationGCOutput& gcOutput) { // Reserve the `gcOutput` vectors. if (!gcOutput.ensureReserved(cx, stencil.scriptData.size(), stencil.scopeData.size())) { return false; } - return input.atomCache.allocate(cx, stencil.parserAtomData.size()); + return atomCache.allocate(cx, stencil.parserAtomData.size()); } bool CompilationStencil::serializeStencils(JSContext* cx, @@ -4050,16 +4053,12 @@ return CompileModuleScriptToStencilImpl(cx, options, srcBuf); } -JSScript* JS::InstantiateGlobalStencil( - JSContext* cx, const JS::ReadOnlyCompileOptions& options, - JS::Stencil* stencil) { - if (stencil->canLazilyParse != CanLazilyParse(options)) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_STENCIL_OPTIONS_MISMATCH); - return nullptr; - } - - Rooted input(cx, CompilationInput(options)); +JSScript* JS::InstantiateGlobalStencil(JSContext* cx, + const JS::InstantiateOptions& options, + JS::Stencil* stencil) { + CompileOptions compileOptions(cx); + options.copyTo(compileOptions); + Rooted input(cx, CompilationInput(compileOptions)); Rooted gcOutput(cx); if (!InstantiateStencils(cx, input.get(), *stencil, gcOutput.get())) { return nullptr; @@ -4076,19 +4075,14 @@ return stencil->canLazilyParse; } -JSObject* JS::InstantiateModuleStencil( - JSContext* cx, const JS::ReadOnlyCompileOptions& optionsInput, - JS::Stencil* stencil) { - JS::CompileOptions options(cx, optionsInput); - options.setModule(); +JSObject* JS::InstantiateModuleStencil(JSContext* cx, + const JS::InstantiateOptions& options, + JS::Stencil* stencil) { + CompileOptions compileOptions(cx); + options.copyTo(compileOptions); + compileOptions.setModule(); - if (stencil->canLazilyParse != CanLazilyParse(options)) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_STENCIL_OPTIONS_MISMATCH); - return nullptr; - } - - Rooted input(cx, CompilationInput(options)); + Rooted input(cx, CompilationInput(compileOptions)); Rooted gcOutput(cx); if (!InstantiateStencils(cx, input.get(), *stencil, gcOutput.get())) { return nullptr; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/gc/Compacting.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/gc/Compacting.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/gc/Compacting.cpp 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/gc/Compacting.cpp 2021-10-20 19:28:23.000000000 +0000 @@ -459,11 +459,10 @@ traceWeakFinalizationRegistryEdges(trc, zone); zone->weakRefMap().sweep(&storeBuffer()); - { - zone->sweepWeakMaps(); - for (auto* cache : zone->weakCaches()) { - cache->sweep(nullptr); - } + zone->traceWeakMaps(trc); + + for (auto* cache : zone->weakCaches()) { + cache->traceWeak(trc, nullptr); } if (jit::JitZone* jitZone = zone->jitZone()) { @@ -656,7 +655,7 @@ // need to be updated. Do not update any non-reserved slots, since they might // point back to unprocessed descriptor objects. - zone->rttValueObjects().sweep(nullptr); + zone->rttValueObjects().traceWeak(trc, nullptr); for (auto r = zone->rttValueObjects().all(); !r.empty(); r.popFront()) { RttValue* obj = &MaybeForwardedObjectAs(r.front()); @@ -797,13 +796,6 @@ // as much as possible. updateAllCellPointers(&trc, zone); - // Mark roots to update them. - { - gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::MARK_ROOTS); - - WeakMapBase::traceZone(zone, &trc); - } - // Sweep everything to fix up weak pointers. sweepZoneAfterCompacting(&trc, zone); @@ -845,7 +837,7 @@ // Sweep everything to fix up weak pointers. jit::JitRuntime::TraceWeakJitcodeGlobalTable(rt, &trc); for (JS::detail::WeakCacheBase* cache : rt->weakCaches()) { - cache->sweep(nullptr); + cache->traceWeak(&trc, nullptr); } // Type inference may put more blocks here to free. diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/gc/FinalizationRegistry.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/gc/FinalizationRegistry.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/gc/FinalizationRegistry.cpp 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/gc/FinalizationRegistry.cpp 2021-10-20 19:28:23.000000000 +0000 @@ -88,7 +88,7 @@ registry->queue()->setHasRegistry(false); e.removeFront(); } else { - result.finalTarget()->as().sweep(); + result.finalTarget()->as().traceWeak(trc); } } @@ -97,7 +97,7 @@ FinalizationRecordVector& records = e.front().value(); // Update any pointers moved by the GC. - records.sweep(); + records.traceWeak(trc); // Sweep finalization records and remove records for: records.eraseIf([](JSObject* obj) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/gc/GC.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/gc/GC.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/gc/GC.cpp 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/gc/GC.cpp 2021-10-20 19:28:23.000000000 +0000 @@ -370,6 +370,7 @@ stats_(this), marker(rt), barrierTracer(rt), + sweepingTracer(rt), heapSize(nullptr), helperThreadRatio(TuningDefaults::HelperThreadRatio), maxHelperThreads(TuningDefaults::MaxHelperThreads), diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/gc/GCInternals.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/gc/GCInternals.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/gc/GCInternals.h 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/gc/GCInternals.h 2021-10-20 19:28:23.000000000 +0000 @@ -279,15 +279,6 @@ friend class GenericTracerImpl; }; -struct SweepingTracer final : public GenericTracerImpl { - explicit SweepingTracer(JSRuntime* rt); - - private: - template - T* onEdge(T* thingp); - friend class GenericTracerImpl; -}; - struct MinorSweepingTracer final : public GenericTracerImpl { explicit MinorSweepingTracer(JSRuntime* rt); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/gc/GCRuntime.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/gc/GCRuntime.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/gc/GCRuntime.h 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/gc/GCRuntime.h 2021-10-20 19:28:23.000000000 +0000 @@ -271,6 +271,15 @@ GCMarker& marker; }; +struct SweepingTracer final : public GenericTracerImpl { + explicit SweepingTracer(JSRuntime* rt); + + private: + template + T* onEdge(T* thingp); + friend class GenericTracerImpl; +}; + class GCRuntime { friend GCMarker::MarkQueueProgress GCMarker::processMarkQueue(); @@ -912,6 +921,7 @@ GCMarker marker; BarrierTracer barrierTracer; + SweepingTracer sweepingTracer; Vector unmarkGrayStack; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/gc/Marking.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/gc/Marking.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/gc/Marking.cpp 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/gc/Marking.cpp 2021-10-20 19:28:23.000000000 +0000 @@ -2875,12 +2875,12 @@ return thing; } - // TODO: We should assert the zone of the tenured cell is in Sweeping state, - // however we need to fix atoms and JitcodeGlobalTable first. - // Bug 1501334 : IsAboutToBeFinalized doesn't work for atoms - // Bug 1071218 : Refactor Debugger::sweepAll and - // JitRuntime::SweepJitcodeGlobalTable to work per sweep group - if (!thing->isMarkedAny()) { + // It would be nice if we could assert that the zone of the tenured cell is in + // the Sweeping state, but that isn't always true for: + // - atoms + // - the jitcode map + // - the mark queue + if (thing->zoneFromAnyThread()->isGCSweeping() && !thing->isMarkedAny()) { return nullptr; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/gc/NurseryAwareHashMap.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/gc/NurseryAwareHashMap.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/gc/NurseryAwareHashMap.h 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/gc/NurseryAwareHashMap.h 2021-10-20 19:28:23.000000000 +0000 @@ -134,8 +134,8 @@ } // Drop the entry if the value is not marked. - if (JS::GCPolicy::needsSweep(&p->value())) { - map.remove(key); + if (!JS::GCPolicy::traceWeak(trc, &p->value())) { + map.remove(p); continue; } @@ -148,7 +148,7 @@ // as a cache to avoid re-copying the string, and currently that entire // cache is flushed on major GC. MapKey copy(key); - if (JS::GCPolicy::needsSweep(©)) { + if (!JS::GCPolicy::traceWeak(trc, ©)) { map.remove(p); continue; } @@ -176,7 +176,7 @@ nurseryEntries.clear(); } - void sweep() { map.sweep(); } + void traceWeak(JSTracer* trc) { map.traceWeak(trc); } void clear() { map.clear(); @@ -196,6 +196,10 @@ const char* name) { js::TraceEdge(trc, thingp, name); } + static bool traceWeak(JSTracer* trc, + js::detail::UnsafeBareWeakHeapPtr* thingp) { + return js::TraceWeakEdge(trc, thingp, "UnsafeBareWeakHeapPtr"); + } static bool needsSweep(js::detail::UnsafeBareWeakHeapPtr* thingp) { return js::gc::IsAboutToBeFinalized(thingp); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/gc/Scheduling.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/gc/Scheduling.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/gc/Scheduling.h 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/gc/Scheduling.h 2021-10-20 19:28:22.000000000 +0000 @@ -10,6 +10,9 @@ * GC Scheduling Overview * ====================== * + * See also GC scheduling from Firefox's perspective here: + * https://searchfox.org/mozilla-central/source/dom/base/CCGCScheduler.cpp + * * Scheduling GC's in SpiderMonkey/Firefox is tremendously complicated because * of the large number of subtle, cross-cutting, and widely dispersed factors * that must be taken into account. A summary of some of the more important diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/gc/Sweeping.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/gc/Sweeping.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/gc/Sweeping.cpp 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/gc/Sweeping.cpp 2021-10-20 19:28:22.000000000 +0000 @@ -1177,7 +1177,8 @@ void run(AutoLockHelperThreadState& lock) override { AutoUnlockHelperThreadState unlock(lock); AutoSetThreadIsSweeping threadIsSweeping(zone); - cache.sweep(&gc->storeBuffer()); + SweepingTracer trc(gc->rt); + cache.traceWeak(&trc, &gc->storeBuffer()); } }; @@ -1202,9 +1203,10 @@ } void GCRuntime::sweepCCWrappers() { + SweepingTracer trc(rt); AutoSetThreadIsSweeping threadIsSweeping; // This can touch all zones. for (SweepGroupZonesIter zone(this); !zone.done(); zone.next()) { - zone->sweepAllCrossCompartmentWrappers(); + zone->traceWeakCCWEdges(&trc); } } @@ -1236,6 +1238,7 @@ } void GCRuntime::sweepWeakMaps() { + SweepingTracer trc(rt); AutoSetThreadIsSweeping threadIsSweeping; // This may touch any zone. for (SweepGroupZonesIter zone(this); !zone.done(); zone.next()) { /* No need to look up any more weakmap keys from this sweep group. */ @@ -1247,7 +1250,7 @@ // Lock the storebuffer since this may access it when rehashing or resizing // the tables. AutoLockStoreBuffer lock(&storeBuffer()); - zone->sweepWeakMaps(); + zone->sweepWeakMaps(&trc); } } @@ -1405,18 +1408,19 @@ MOZ_ASSERT(immediateTasks->empty()); + GCRuntime* gc = &rt->gc; bool ok = IterateWeakCaches(rt, [&](JS::detail::WeakCacheBase* cache, Zone* zone) { - if (!cache->needsSweep()) { + if (cache->empty()) { return true; } // Caches that support incremental sweeping will be swept later. - if (zone && cache->setNeedsIncrementalBarrier(true)) { + if (zone && cache->setIncrementalBarrierTracer(&gc->sweepingTracer)) { return true; } - return immediateTasks->emplaceBack(&rt->gc, zone, *cache); + return immediateTasks->emplaceBack(gc, zone, *cache); }); if (!ok) { @@ -1429,11 +1433,12 @@ static void SweepAllWeakCachesOnMainThread(JSRuntime* rt) { // If we ran out of memory, do all the work on the main thread. gcstats::AutoPhase ap(rt->gc.stats(), gcstats::PhaseKind::SWEEP_WEAK_CACHES); + SweepingTracer trc(rt); IterateWeakCaches(rt, [&](JS::detail::WeakCacheBase* cache, Zone* zone) { if (cache->needsIncrementalBarrier()) { - cache->setNeedsIncrementalBarrier(false); + cache->setIncrementalBarrierTracer(nullptr); } - cache->sweep(&rt->gc.storeBuffer()); + cache->traceWeak(&trc, &rt->gc.storeBuffer()); return true; }); } @@ -1842,8 +1847,10 @@ JS::detail::WeakCacheBase* cache = item.cache; MOZ_ASSERT(cache->needsIncrementalBarrier()); - size_t steps = cache->sweep(&gc->storeBuffer()); - cache->setNeedsIncrementalBarrier(false); + + SweepingTracer trc(gc->rt); + size_t steps = cache->traceWeak(&trc, &gc->storeBuffer()); + cache->setIncrementalBarrierTracer(nullptr); return steps; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/gc/WeakMap.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/gc/WeakMap.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/gc/WeakMap.cpp 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/gc/WeakMap.cpp 2021-10-20 19:28:23.000000000 +0000 @@ -41,11 +41,11 @@ } } -void WeakMapBase::traceZone(JS::Zone* zone, JSTracer* tracer) { - MOZ_ASSERT(tracer->weakMapAction() != JS::WeakMapTraceAction::Skip); - for (WeakMapBase* m : zone->gcWeakMapList()) { - m->trace(tracer); - TraceNullableEdge(tracer, &m->memberOf, "memberOf"); +void Zone::traceWeakMaps(JSTracer* trc) { + MOZ_ASSERT(trc->weakMapAction() != JS::WeakMapTraceAction::Skip); + for (WeakMapBase* m : gcWeakMapList()) { + m->trace(trc); + TraceNullableEdge(trc, &m->memberOf, "memberOf"); } } @@ -84,20 +84,20 @@ return true; } -void WeakMapBase::sweepZone(JS::Zone* zone) { - for (WeakMapBase* m = zone->gcWeakMapList().getFirst(); m;) { +void Zone::sweepWeakMaps(JSTracer* trc) { + for (WeakMapBase* m = gcWeakMapList().getFirst(); m;) { WeakMapBase* next = m->getNext(); if (m->mapColor) { - m->sweep(); + m->traceWeakEdges(trc); } else { m->clearAndCompact(); - m->removeFrom(zone->gcWeakMapList()); + m->removeFrom(gcWeakMapList()); } m = next; } #ifdef DEBUG - for (WeakMapBase* m : zone->gcWeakMapList()) { + for (WeakMapBase* m : gcWeakMapList()) { MOZ_ASSERT(m->isInList() && m->mapColor); } #endif diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/gc/WeakMap.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/gc/WeakMap.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/gc/WeakMap.h 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/gc/WeakMap.h 2021-10-20 19:28:22.000000000 +0000 @@ -107,9 +107,6 @@ // Unmark all weak maps in a zone. static void unmarkZone(JS::Zone* zone); - // Trace all the weakmaps in a zone. - static void traceZone(JS::Zone* zone, JSTracer* tracer); - // Check all weak maps in a zone that have been marked as live in this garbage // collection, and mark the values of all entries that have become strong // references to them. Return true if we marked any new values, indicating @@ -120,10 +117,6 @@ // Add zone edges for weakmaps with key delegates in a different zone. [[nodiscard]] static bool findSweepGroupEdgesForZone(JS::Zone* zone); - // Sweep the weak maps in a zone, removing dead weak maps and removing - // entries of live weak maps whose keys are dead. - static void sweepZone(JS::Zone* zone); - // Sweep the marked weak maps in a zone, updating moved keys. static void sweepZoneAfterMinorGC(JS::Zone* zone); @@ -146,7 +139,7 @@ // override these with definitions appropriate for their Key and Value types. virtual void trace(JSTracer* tracer) = 0; virtual bool findSweepGroupEdges() = 0; - virtual void sweep() = 0; + virtual void traceWeakEdges(JSTracer* trc) = 0; virtual void traceMappings(WeakMapTracer* tracer) = 0; virtual void clearAndCompact() = 0; @@ -316,7 +309,7 @@ JS::ExposeObjectToActiveJS(obj); } - void sweep() override; + void traceWeakEdges(JSTracer* trc) override; void clearAndCompact() override { Base::clear(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/gc/WeakMap-inl.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/gc/WeakMap-inl.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/gc/WeakMap-inl.h 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/gc/WeakMap-inl.h 2021-10-20 19:28:23.000000000 +0000 @@ -336,10 +336,10 @@ } template -void WeakMap::sweep() { - /* Remove all entries whose keys remain unmarked. */ +void WeakMap::traceWeakEdges(JSTracer* trc) { + // Remove all entries whose keys remain unmarked. for (Enum e(*this); !e.empty(); e.popFront()) { - if (gc::IsAboutToBeFinalized(&e.front().mutableKey())) { + if (!TraceWeakEdge(trc, &e.front().mutableKey(), "WeakMap key")) { e.removeFront(); } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/gc/Zone.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/gc/Zone.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/gc/Zone.cpp 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/gc/Zone.cpp 2021-10-20 19:28:23.000000000 +0000 @@ -341,10 +341,10 @@ } } -void Zone::sweepAllCrossCompartmentWrappers() { - crossZoneStringWrappers().sweep(); +void Zone::traceWeakCCWEdges(JSTracer* trc) { + crossZoneStringWrappers().traceWeak(trc); for (CompartmentsInZoneIter comp(this); !comp.done(); comp.next()) { - comp->sweepCrossCompartmentObjectWrappers(); + comp->traceCrossCompartmentObjectWrapperEdges(trc); } } @@ -353,9 +353,9 @@ MOZ_ASSERT(trc->runtime()->gc.isHeapCompacting()); for (ZonesIter zone(trc->runtime(), WithAtoms); !zone.done(); zone.next()) { - // Sweep the wrapper map to update keys (wrapped values) in other + // Trace the wrapper map to update keys (wrapped values) in other // compartments that may have been moved. - zone->crossZoneStringWrappers().sweep(); + zone->crossZoneStringWrappers().traceWeak(trc); for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) { comp->fixupCrossCompartmentObjectWrappersAfterMovingGC(trc); @@ -392,11 +392,6 @@ } #endif -void Zone::sweepWeakMaps() { - /* Finalize unreachable (key,value) pairs in all weak maps. */ - WeakMapBase::sweepZone(this); -} - void Zone::discardJitCode(JSFreeOp* fop, const DiscardOptions& options) { if (!jitZone()) { return; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/gc/Zone.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/gc/Zone.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/gc/Zone.h 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/gc/Zone.h 2021-10-20 19:28:23.000000000 +0000 @@ -480,9 +480,15 @@ void sweepAfterMinorGC(JSTracer* trc); void sweepUniqueIds(); - void sweepWeakMaps(); void sweepCompartments(JSFreeOp* fop, bool keepAtleastOne, bool lastGC); + // Remove dead weak maps from gcWeakMapList_ and remove entries from the + // remaining weak maps whose keys are dead. + void sweepWeakMaps(JSTracer* trc); + + // Trace all weak maps in this zone. Used to update edges after a moving GC. + void traceWeakMaps(JSTracer* trc); + js::gc::UniqueIdMap& uniqueIds() { return uniqueIds_.ref(); } void notifyObservingDebuggers(); @@ -512,7 +518,7 @@ void dropStringWrappersOnGC(); - void sweepAllCrossCompartmentWrappers(); + void traceWeakCCWEdges(JSTracer* trc); static void fixupAllCrossCompartmentWrappersAfterMovingGC(JSTracer* trc); mozilla::LinkedList& weakCaches() { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/arm/CodeGenerator-arm.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/arm/CodeGenerator-arm.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/arm/CodeGenerator-arm.cpp 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/arm/CodeGenerator-arm.cpp 2021-10-20 19:28:23.000000000 +0000 @@ -2044,6 +2044,23 @@ masm.bind(&ok); } +void CodeGenerator::visitWasmAddOffset64(LWasmAddOffset64* lir) { + MWasmAddOffset* mir = lir->mir(); + Register64 base = ToRegister64(lir->base()); + Register64 out = ToOutRegister64(lir); + MOZ_ASSERT(base.low != out.high && base.high != out.low); + + ScratchRegisterScope scratch(masm); + masm.ma_add(base.low, Imm32(mir->offset()), out.low, scratch, SetCC); + masm.xor32(scratch, scratch); + masm.ma_adc(base.high, scratch, out.high, SetCC); + + Label ok; + masm.ma_b(&ok, Assembler::CarryClear); + masm.wasmTrap(wasm::Trap::OutOfBounds, mir->bytecodeOffset()); + masm.bind(&ok); +} + template void CodeGeneratorARM::emitWasmStore(T* lir) { const MWasmStore* mir = lir->mir(); @@ -2670,8 +2687,10 @@ MOZ_CRASH("64-bit only"); } -void CodeGenerator::visitWasmWrapU32Index(LWasmWrapU32Index*) { - MOZ_CRASH("64-bit only"); +void CodeGenerator::visitWasmWrapU32Index(LWasmWrapU32Index* lir) { + // Generates no code on this platform because we just return the low part of + // the input register pair. + MOZ_ASSERT(ToRegister(lir->input()) == ToRegister(lir->output())); } void CodeGenerator::visitDivOrModI64(LDivOrModI64* lir) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/arm/MacroAssembler-arm.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/arm/MacroAssembler-arm.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/arm/MacroAssembler-arm.cpp 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/arm/MacroAssembler-arm.cpp 2021-10-20 19:28:24.000000000 +0000 @@ -4726,27 +4726,44 @@ } void MacroAssembler::wasmBoundsCheck32(Condition cond, Register index, - Register boundsCheckLimit, - Label* label) { + Register boundsCheckLimit, Label* ok) { as_cmp(index, O2Reg(boundsCheckLimit)); - as_b(label, cond); + as_b(ok, cond); if (JitOptions.spectreIndexMasking) { ma_mov(boundsCheckLimit, index, LeaveCC, cond); } } void MacroAssembler::wasmBoundsCheck32(Condition cond, Register index, - Address boundsCheckLimit, Label* label) { + Address boundsCheckLimit, Label* ok) { ScratchRegisterScope scratch(*this); ma_ldr(DTRAddr(boundsCheckLimit.base, DtrOffImm(boundsCheckLimit.offset)), scratch); as_cmp(index, O2Reg(scratch)); - as_b(label, cond); + as_b(ok, cond); if (JitOptions.spectreIndexMasking) { ma_mov(scratch, index, LeaveCC, cond); } } +void MacroAssembler::wasmBoundsCheck64(Condition cond, Register64 index, + Register64 boundsCheckLimit, Label* ok) { + Label notOk; + cmp32(index.high, Imm32(0)); + j(Assembler::NonZero, ¬Ok); + wasmBoundsCheck32(cond, index.low, boundsCheckLimit.low, ok); + bind(¬Ok); +} + +void MacroAssembler::wasmBoundsCheck64(Condition cond, Register64 index, + Address boundsCheckLimit, Label* ok) { + Label notOk; + cmp32(index.high, Imm32(0)); + j(Assembler::NonZero, ¬Ok); + wasmBoundsCheck32(cond, index.low, boundsCheckLimit, ok); + bind(¬Ok); +} + void MacroAssembler::wasmTruncateDoubleToUInt32(FloatRegister input, Register output, bool isSaturating, diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/arm/MacroAssembler-arm-inl.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/arm/MacroAssembler-arm-inl.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/arm/MacroAssembler-arm-inl.h 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/arm/MacroAssembler-arm-inl.h 2021-10-20 19:28:23.000000000 +0000 @@ -1740,6 +1740,14 @@ j(cond, label); } +void MacroAssembler::branchAdd64(Condition cond, Imm32 imm, Register64 dest, + Label* label) { + ScratchRegisterScope scratch(*this); + ma_add(imm, dest.low, scratch, SetCC); + as_adc(dest.high, dest.high, Imm8(0), SetCC); + j(cond, label); +} + template void MacroAssembler::branchAddPtr(Condition cond, T src, Register dest, Label* label) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/arm/Simulator-arm.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/arm/Simulator-arm.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/arm/Simulator-arm.cpp 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/arm/Simulator-arm.cpp 2021-10-20 19:28:24.000000000 +0000 @@ -2393,11 +2393,25 @@ int32_t); typedef int32_t (*Prototype_Int32_GeneralGeneralInt32Int32)(int32_t, int32_t, int32_t, int32_t); +typedef int32_t (*Prototype_Int32_GeneralInt64Int32Int32Int32)(int32_t, int64_t, + int32_t, int32_t, + int32_t); +typedef int32_t (*Prototype_Int32_GeneralInt64Int32)(int32_t, int64_t, int32_t); +typedef int32_t (*Prototype_Int32_GeneralInt64Int32Int64)(int32_t, int64_t, + int32_t, int64_t); +typedef int32_t (*Prototype_Int32_GeneralInt64Int32Int64General)( + int32_t, int64_t, int32_t, int64_t, int32_t); +typedef int32_t (*Prototype_Int32_GeneralInt64Int64Int64)(int32_t, int64_t, + int64_t, int64_t); +typedef int32_t (*Prototype_Int32_GeneralInt64Int64Int64General)( + int32_t, int64_t, int64_t, int64_t, int32_t); typedef int32_t (*Prototype_General_GeneralInt32)(int32_t, int32_t); typedef int32_t (*Prototype_General_GeneralInt32Int32)(int32_t, int32_t, int32_t); typedef int32_t (*Prototype_General_GeneralInt32General)(int32_t, int32_t, int32_t); +typedef int64_t (*Prototype_Int64_General)(int32_t); +typedef int64_t (*Prototype_Int64_GeneralInt64)(int32_t, int64_t); // Fill the volatile registers with scratch values. // @@ -2446,6 +2460,9 @@ int32_t* stack_pointer = reinterpret_cast(get_register(sp)); int32_t arg4 = stack_pointer[0]; int32_t arg5 = stack_pointer[1]; + int32_t arg6 = stack_pointer[2]; + int32_t arg7 = stack_pointer[3]; + int32_t arg8 = stack_pointer[4]; int32_t saved_lr = get_register(lr); intptr_t external = @@ -2930,6 +2947,65 @@ setCallResult(result); break; } + case Args_Int32_GeneralInt64Int32Int32Int32: { + Prototype_Int32_GeneralInt64Int32Int32Int32 target = + reinterpret_cast( + external); + int64_t result = + target(arg0, MakeInt64(arg2, arg3), arg4, arg5, arg6); + scratchVolatileRegisters(/* scratchFloat = true */); + setCallResult(result); + break; + } + case Args_Int32_GeneralInt64Int32: { + Prototype_Int32_GeneralInt64Int32 target = + reinterpret_cast(external); + int64_t result = target(arg0, MakeInt64(arg2, arg3), arg4); + scratchVolatileRegisters(/* scratchFloat = true */); + setCallResult(result); + break; + } + case Args_Int32_GeneralInt64Int32Int64: { + Prototype_Int32_GeneralInt64Int32Int64 target = + reinterpret_cast( + external); + int64_t result = + target(arg0, MakeInt64(arg2, arg3), arg4, MakeInt64(arg6, arg7)); + scratchVolatileRegisters(/* scratchFloat = true */); + setCallResult(result); + break; + } + case Args_Int32_GeneralInt64Int32Int64General: { + Prototype_Int32_GeneralInt64Int32Int64General target = + reinterpret_cast( + external); + int64_t result = target(arg0, MakeInt64(arg2, arg3), arg4, + MakeInt64(arg6, arg7), arg8); + scratchVolatileRegisters(/* scratchFloat = true */); + setCallResult(result); + break; + } + case Args_Int32_GeneralInt64Int64Int64: { + Prototype_Int32_GeneralInt64Int64Int64 target = + reinterpret_cast( + external); + int64_t result = target(arg0, MakeInt64(arg2, arg3), + MakeInt64(arg4, arg5), MakeInt64(arg6, arg7)); + scratchVolatileRegisters(/* scratchFloat = true */); + setCallResult(result); + break; + } + case Args_Int32_GeneralInt64Int64Int64General: { + Prototype_Int32_GeneralInt64Int64Int64General target = + reinterpret_cast( + external); + int64_t result = + target(arg0, MakeInt64(arg2, arg3), MakeInt64(arg4, arg5), + MakeInt64(arg6, arg7), arg8); + scratchVolatileRegisters(/* scratchFloat = true */); + setCallResult(result); + break; + } case Args_General_GeneralInt32: { Prototype_General_GeneralInt32 target = reinterpret_cast(external); @@ -2953,6 +3029,22 @@ scratchVolatileRegisters(/* scratchFloat = true */); setCallResult(result); break; + } + case Args_Int64_General: { + Prototype_Int64_General target = + reinterpret_cast(external); + int64_t result = target(arg0); + scratchVolatileRegisters(/* scratchFloat = true */); + setCallResult(result); + break; + } + case Args_Int64_GeneralInt64: { + Prototype_Int64_GeneralInt64 target = + reinterpret_cast(external); + int64_t result = target(arg0, MakeInt64(arg2, arg3)); + scratchVolatileRegisters(/* scratchFloat = true */); + setCallResult(result); + break; } default: diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/arm64/CodeGenerator-arm64.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/arm64/CodeGenerator-arm64.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/arm64/CodeGenerator-arm64.cpp 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/arm64/CodeGenerator-arm64.cpp 2021-10-20 19:28:24.000000000 +0000 @@ -2295,8 +2295,11 @@ static Maybe IsAbsoluteAddress(const LAllocation* ptr, const wasm::MemoryAccessDesc& access) { - if (ptr->isConstant()) { - uint64_t base_address = uint32_t(ToInt32(ptr)); + if (ptr->isConstantValue()) { + const MConstant* c = ptr->toConstant(); + uint64_t base_address = c->type() == MIRType::Int32 + ? uint64_t(uint32_t(c->toInt32())) + : uint64_t(c->toInt64()); uint64_t offset = access.offset(); return Some(base_address + offset); } @@ -2312,6 +2315,8 @@ return; } + // ptr is a GPR and is either a 32-bit value zero-extended to 64-bit, or a + // true 64-bit value. masm.wasmLoad(mir->access(), HeapReg, ToRegister(lir->ptr()), ToAnyRegister(lir->output())); } @@ -2553,6 +2558,20 @@ Operand(mir->offset())); Label ok; + masm.j(Assembler::CarryClear, &ok); + masm.wasmTrap(wasm::Trap::OutOfBounds, mir->bytecodeOffset()); + masm.bind(&ok); +} + +void CodeGenerator::visitWasmAddOffset64(LWasmAddOffset64* lir) { + MWasmAddOffset* mir = lir->mir(); + Register64 base = ToRegister64(lir->base()); + Register64 out = ToOutRegister64(lir); + + masm.Adds(ARMRegister(out.reg, 64), ARMRegister(base.reg, 64), + Operand(mir->offset())); + + Label ok; masm.j(Assembler::CarryClear, &ok); masm.wasmTrap(wasm::Trap::OutOfBounds, mir->bytecodeOffset()); masm.bind(&ok); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/arm64/Lowering-arm64.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/arm64/Lowering-arm64.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/arm64/Lowering-arm64.cpp 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/arm64/Lowering-arm64.cpp 2021-10-20 19:28:23.000000000 +0000 @@ -661,7 +661,8 @@ void LIRGenerator::visitWasmCompareExchangeHeap(MWasmCompareExchangeHeap* ins) { MDefinition* base = ins->base(); - MOZ_ASSERT(base->type() == MIRType::Int32); + // See comment in visitWasmLoad re the type of 'base'. + MOZ_ASSERT(base->type() == MIRType::Int32 || base->type() == MIRType::Int64); // Note, the access type may be Int64 here. @@ -674,7 +675,8 @@ void LIRGenerator::visitWasmAtomicExchangeHeap(MWasmAtomicExchangeHeap* ins) { MDefinition* base = ins->base(); - MOZ_ASSERT(base->type() == MIRType::Int32); + // See comment in visitWasmLoad re the type of 'base'. + MOZ_ASSERT(base->type() == MIRType::Int32 || base->type() == MIRType::Int64); // Note, the access type may be Int64 here. @@ -685,7 +687,8 @@ void LIRGenerator::visitWasmAtomicBinopHeap(MWasmAtomicBinopHeap* ins) { MDefinition* base = ins->base(); - MOZ_ASSERT(base->type() == MIRType::Int32); + // See comment in visitWasmLoad re the type of 'base'. + MOZ_ASSERT(base->type() == MIRType::Int32 || base->type() == MIRType::Int64); // Note, the access type may be Int64 here. @@ -906,7 +909,9 @@ void LIRGenerator::visitWasmLoad(MWasmLoad* ins) { MDefinition* base = ins->base(); - MOZ_ASSERT(base->type() == MIRType::Int32); + // 'base' is a GPR but may be of either type. If it is 32-bit it is + // zero-extended and can act as 64-bit. + MOZ_ASSERT(base->type() == MIRType::Int32 || base->type() == MIRType::Int64); LAllocation ptr = useRegisterOrConstantAtStart(base); @@ -921,7 +926,8 @@ void LIRGenerator::visitWasmStore(MWasmStore* ins) { MDefinition* base = ins->base(); - MOZ_ASSERT(base->type() == MIRType::Int32); + // See comment in visitWasmLoad re the type of 'base'. + MOZ_ASSERT(base->type() == MIRType::Int32 || base->type() == MIRType::Int64); MDefinition* value = ins->value(); @@ -1368,6 +1374,9 @@ void LIRGenerator::visitWasmLoadLaneSimd128(MWasmLoadLaneSimd128* ins) { #ifdef ENABLE_WASM_SIMD + // On 64-bit systems, the base pointer can be 32 bits or 64 bits. Either way, + // it fits in a GPR so we can ignore the Register/Register64 distinction here. + // Optimal allocation here reuses the value input for the output register // because codegen otherwise has to copy the input to the output; this is // because load-lane is implemented as load + replace-lane. Bug 1706106 may @@ -1385,6 +1394,8 @@ void LIRGenerator::visitWasmStoreLaneSimd128(MWasmStoreLaneSimd128* ins) { #ifdef ENABLE_WASM_SIMD + // See comment above about the base pointer. + LUse base = useRegisterAtStart(ins->base()); LUse input = useRegisterAtStart(ins->value()); MOZ_ASSERT(!ins->hasMemoryBase()); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/arm64/MacroAssembler-arm64.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/arm64/MacroAssembler-arm64.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/arm64/MacroAssembler-arm64.cpp 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/arm64/MacroAssembler-arm64.cpp 2021-10-20 19:28:24.000000000 +0000 @@ -1767,26 +1767,24 @@ } void MacroAssembler::wasmBoundsCheck32(Condition cond, Register index, - Register boundsCheckLimit, - Label* label) { - branch32(cond, index, boundsCheckLimit, label); + Register boundsCheckLimit, Label* ok) { + branch32(cond, index, boundsCheckLimit, ok); if (JitOptions.spectreIndexMasking) { csel(ARMRegister(index, 32), vixl::wzr, ARMRegister(index, 32), cond); } } void MacroAssembler::wasmBoundsCheck32(Condition cond, Register index, - Address boundsCheckLimit, Label* label) { - branch32(cond, index, boundsCheckLimit, label); + Address boundsCheckLimit, Label* ok) { + branch32(cond, index, boundsCheckLimit, ok); if (JitOptions.spectreIndexMasking) { csel(ARMRegister(index, 32), vixl::wzr, ARMRegister(index, 32), cond); } } void MacroAssembler::wasmBoundsCheck64(Condition cond, Register64 index, - Register64 boundsCheckLimit, - Label* label) { - branchPtr(cond, index.reg, boundsCheckLimit.reg, label); + Register64 boundsCheckLimit, Label* ok) { + branchPtr(cond, index.reg, boundsCheckLimit.reg, ok); if (JitOptions.spectreIndexMasking) { csel(ARMRegister(index.reg, 64), vixl::xzr, ARMRegister(index.reg, 64), cond); @@ -1794,8 +1792,8 @@ } void MacroAssembler::wasmBoundsCheck64(Condition cond, Register64 index, - Address boundsCheckLimit, Label* label) { - branchPtr(InvertCondition(cond), boundsCheckLimit, index.reg, label); + Address boundsCheckLimit, Label* ok) { + branchPtr(InvertCondition(cond), boundsCheckLimit, index.reg, ok); if (JitOptions.spectreIndexMasking) { csel(ARMRegister(index.reg, 64), vixl::xzr, ARMRegister(index.reg, 64), cond); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/arm64/vixl/MozSimulator-vixl.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/arm64/vixl/MozSimulator-vixl.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/arm64/vixl/MozSimulator-vixl.cpp 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/arm64/vixl/MozSimulator-vixl.cpp 2021-10-20 19:28:24.000000000 +0000 @@ -581,6 +581,21 @@ int64_t, int32_t, int32_t); +typedef int32_t (*Prototype_Int32_GeneralInt64Int32Int32Int32)(int64_t, int64_t, + int32_t, int32_t, + int32_t); +typedef int32_t (*Prototype_Int32_GeneralInt64Int32)(int64_t, int64_t, + int32_t); +typedef int32_t (*Prototype_Int32_GeneralInt64Int32Int64)(int64_t, int64_t, + int32_t, int64_t); +typedef int32_t (*Prototype_Int32_GeneralInt64Int32Int64General)(int64_t, int64_t, + int32_t, int64_t, + int64_t); +typedef int32_t (*Prototype_Int32_GeneralInt64Int64Int64)(int64_t, int64_t, + int64_t, int64_t); +typedef int32_t (*Prototype_Int32_GeneralInt64Int64Int64General)(int64_t, int64_t, + int64_t, int64_t, + int64_t); typedef int64_t (*Prototype_General_GeneralInt32)(int64_t, int32_t); typedef int64_t (*Prototype_General_GeneralInt32Int32)(int64_t, int32_t, @@ -588,6 +603,8 @@ typedef int64_t (*Prototype_General_GeneralInt32General)(int64_t, int32_t, int64_t); +typedef int64_t (*Prototype_Int64_General)(int64_t); +typedef int64_t (*Prototype_Int64_GeneralInt64)(int64_t, int64_t); // Simulator support for callWithABI(). void @@ -883,6 +900,42 @@ setGPR32Result(ret); break; } + case js::jit::Args_Int32_GeneralInt64Int32Int32Int32: { + int32_t ret = reinterpret_cast( + nativeFn)(x0, x1, x2, x3, x4); + setGPR32Result(ret); + break; + } + case js::jit::Args_Int32_GeneralInt64Int32: { + int32_t ret = reinterpret_cast( + nativeFn)(x0, x1, x2); + setGPR32Result(ret); + break; + } + case js::jit::Args_Int32_GeneralInt64Int32Int64: { + int32_t ret = reinterpret_cast( + nativeFn)(x0, x1, x2, x3); + setGPR32Result(ret); + break; + } + case js::jit::Args_Int32_GeneralInt64Int32Int64General: { + int32_t ret = reinterpret_cast( + nativeFn)(x0, x1, x2, x3, x4); + setGPR32Result(ret); + break; + } + case js::jit::Args_Int32_GeneralInt64Int64Int64: { + int32_t ret = reinterpret_cast( + nativeFn)(x0, x1, x2, x3); + setGPR32Result(ret); + break; + } + case js::jit::Args_Int32_GeneralInt64Int64Int64General: { + int32_t ret = reinterpret_cast( + nativeFn)(x0, x1, x2, x3, x4); + setGPR32Result(ret); + break; + } case js::jit::Args_General_GeneralInt32: { int64_t ret = reinterpret_cast(nativeFn)(x0, x1); @@ -902,6 +955,20 @@ setGPR64Result(ret); break; } + case js::jit::Args_Int64_General: { + int64_t ret = + reinterpret_cast( + nativeFn)(x0); + setGPR64Result(ret); + break; + } + case js::jit::Args_Int64_GeneralInt64: { + int64_t ret = + reinterpret_cast( + nativeFn)(x0, x1); + setGPR64Result(ret); + break; + } default: MOZ_CRASH("Unknown function type."); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/CacheIRCompiler.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/CacheIRCompiler.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/CacheIRCompiler.cpp 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/CacheIRCompiler.cpp 2021-10-20 19:28:24.000000000 +0000 @@ -6741,9 +6741,6 @@ masm.jump(failure->label()); masm.bind(&ok); - if (JitOptions.spectreJitToCxxCalls) { - masm.speculationBarrier(); - } masm.setFramePushed(framePushed); masm.loadTypedOrValue(Address(masm.getStackPointer(), 0), output); masm.adjustStack(sizeof(Value)); @@ -7031,9 +7028,6 @@ masm.adjustStack(sizeof(Value)); masm.branchIfFalseBool(scratch2, failure->label()); - if (JitOptions.spectreJitToCxxCalls) { - masm.speculationBarrier(); - } return true; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/CodeGenerator.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/CodeGenerator.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/CodeGenerator.cpp 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/CodeGenerator.cpp 2021-10-20 19:28:24.000000000 +0000 @@ -4218,10 +4218,6 @@ masm.branchIfFalseBool(ReturnReg, &bail); - if (JitOptions.spectreJitToCxxCalls) { - masm.speculationBarrier(); - } - bailoutFrom(&bail, lir->snapshot()); } @@ -4259,9 +4255,6 @@ masm.jump(&bail); masm.bind(&ok); - if (JitOptions.spectreJitToCxxCalls) { - masm.speculationBarrier(); - } masm.popValue(output); bailoutFrom(&bail, lir->snapshot()); @@ -13088,19 +13081,22 @@ masm.loadValue(BaseObjectElementIndex(elements, index), out); - // If a hole check is needed, and the value wasn't a hole, we're done. - // Otherwise, we'll load undefined. - if (lir->mir()->needsHoleCheck()) { - masm.branchTestMagic(Assembler::NotEqual, out, &done); - masm.moveValue(UndefinedValue(), out); - } - masm.jump(&done); + // If the value wasn't a hole, we're done. Otherwise, we'll load undefined. + masm.branchTestMagic(Assembler::NotEqual, out, &done); - masm.bind(&outOfBounds); if (mir->needsNegativeIntCheck()) { + Label loadUndefined; + masm.jump(&loadUndefined); + + masm.bind(&outOfBounds); + Label negative; masm.branch32(Assembler::LessThan, index, Imm32(0), &negative); bailoutFrom(&negative, lir->snapshot()); + + masm.bind(&loadUndefined); + } else { + masm.bind(&outOfBounds); } masm.moveValue(UndefinedValue(), out); @@ -14851,14 +14847,9 @@ void CodeGenerator::visitWasmBoundsCheck64(LWasmBoundsCheck64* ins) { const MWasmBoundsCheck* mir = ins->mir(); Label ok; -#ifdef JS_64BIT Register64 ptr = ToRegister64(ins->ptr()); Register64 boundsCheckLimit = ToRegister64(ins->boundsCheckLimit()); masm.wasmBoundsCheck64(Assembler::Below, ptr, boundsCheckLimit, &ok); -#else - // 64-bit bounds checks are used only on 64-bit systems. - MOZ_CRASH("Should not happen"); -#endif masm.wasmTrap(wasm::Trap::OutOfBounds, mir->bytecodeOffset()); masm.bind(&ok); } @@ -14871,6 +14862,20 @@ masm.wasmTrap(wasm::Trap::UnalignedAccess, mir->bytecodeOffset()); masm.bind(&ok); } + +void CodeGenerator::visitWasmAlignmentCheck64(LWasmAlignmentCheck64* ins) { + const MWasmAlignmentCheck* mir = ins->mir(); + Register64 ptr = ToRegister64(ins->ptr()); +#ifdef JS_64BIT + Register r = ptr.reg; +#else + Register r = ptr.low; +#endif + Label ok; + masm.branchTestPtr(Assembler::Zero, r, Imm32(mir->byteSize() - 1), &ok); + masm.wasmTrap(wasm::Trap::UnalignedAccess, mir->bytecodeOffset()); + masm.bind(&ok); +} void CodeGenerator::visitWasmLoadTls(LWasmLoadTls* ins) { switch (ins->mir()->type()) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/CodeGenerator.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/CodeGenerator.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/CodeGenerator.h 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/CodeGenerator.h 2021-10-20 19:28:23.000000000 +0000 @@ -234,15 +234,9 @@ const ConstantOrRegister& id, const ConstantOrRegister& value, bool strict); - [[nodiscard]] bool generateBranchV(const ValueOperand& value, Label* ifTrue, - Label* ifFalse, FloatRegister fr); - void emitLambdaInit(Register resultReg, Register envChainReg, const LambdaFunctionInfo& info); - void emitFilterArgumentsOrEval(LInstruction* lir, Register string, - Register temp1, Register temp2); - template void emitGetNextEntryForIterator(LGetNextEntryForIterator* lir); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/EffectiveAddressAnalysis.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/EffectiveAddressAnalysis.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/EffectiveAddressAnalysis.cpp 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/EffectiveAddressAnalysis.cpp 2021-10-20 19:28:24.000000000 +0000 @@ -199,8 +199,9 @@ MDefinition* base = ins->base(); if (base->isConstant()) { - // If the index is within the minimum heap length, we can optimize - // away the bounds check. + // If the index is within the minimum heap length, we can optimize away the + // bounds check. Asm.js accesses always have an int32 base, the memory is + // always a memory32. int32_t imm = base->toConstant()->toInt32(); if (imm >= 0) { int32_t end = (uint32_t)imm + ins->byteSize(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/IonTypes.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/IonTypes.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/IonTypes.h 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/IonTypes.h 2021-10-20 19:28:23.000000000 +0000 @@ -912,6 +912,31 @@ ArgType_General, {ArgType_General, ArgType_Int32, ArgType_Int32}), Args_General_GeneralInt32General = detail::MakeABIFunctionType( ArgType_General, {ArgType_General, ArgType_Int32, ArgType_General}), + Args_Int32_GeneralInt64Int32Int32Int32 = detail::MakeABIFunctionType( + ArgType_Int32, {ArgType_General, ArgType_Int64, ArgType_Int32, + ArgType_Int32, ArgType_Int32}), + Args_Int32_GeneralInt64Int32 = detail::MakeABIFunctionType( + ArgType_Int32, {ArgType_General, ArgType_Int64, ArgType_Int32}), + Args_Int32_GeneralInt64Int32Int64 = detail::MakeABIFunctionType( + ArgType_Int32, + {ArgType_General, ArgType_Int64, ArgType_Int32, ArgType_Int64}), + Args_Int32_GeneralInt64Int32Int64General = detail::MakeABIFunctionType( + ArgType_Int32, {ArgType_General, ArgType_Int64, ArgType_Int32, + ArgType_Int64, ArgType_General}), + Args_Int32_GeneralInt64Int64Int64 = detail::MakeABIFunctionType( + ArgType_Int32, + {ArgType_General, ArgType_Int64, ArgType_Int64, ArgType_Int64}), + Args_Int32_GeneralInt64Int64Int64General = detail::MakeABIFunctionType( + ArgType_Int32, {ArgType_General, ArgType_Int64, ArgType_Int64, + ArgType_Int64, ArgType_General}), + + // Functions that return Int64 are tricky because SpiderMonkey's ReturnRegI64 + // does not match the ABI int64 return register on x86. Wasm only! + Args_Int64_General = + detail::MakeABIFunctionType(ArgType_Int64, {ArgType_General}), + Args_Int64_GeneralInt64 = detail::MakeABIFunctionType( + ArgType_Int64, {ArgType_General, ArgType_Int64}), + }; static constexpr ABIFunctionType MakeABIFunctionType( diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/LIROps.yaml firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/LIROps.yaml --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/LIROps.yaml 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/LIROps.yaml 2021-10-20 19:28:23.000000000 +0000 @@ -2550,6 +2550,12 @@ base: WordSized mir_op: true +- name: WasmAddOffset64 + result_type: Int64 + operands: + base: Int64 + mir_op: WasmAddOffset + - name: WasmBoundsCheck result_type: WordSized operands: @@ -2577,6 +2583,11 @@ ptr: WordSized mir_op: true +- name: WasmAlignmentCheck64 + operands: + ptr: Int64 + mir_op: WasmAlignmentCheck + - name: WasmLoadTls result_type: WordSized operands: diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/Lowering.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/Lowering.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/Lowering.cpp 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/Lowering.cpp 2021-10-20 19:28:23.000000000 +0000 @@ -2499,6 +2499,9 @@ void LIRGenerator::visitWasmExtendU32Index(MWasmExtendU32Index* ins) { #ifdef JS_64BIT + // Technically this produces an Int64 register and I guess we could clean that + // up, but it's a 64-bit only operation, so it doesn't actually matter. + MDefinition* input = ins->input(); MOZ_ASSERT(input->type() == MIRType::Int32); MOZ_ASSERT(ins->type() == MIRType::Int64); @@ -2513,18 +2516,22 @@ } void LIRGenerator::visitWasmWrapU32Index(MWasmWrapU32Index* ins) { -#ifdef JS_64BIT MDefinition* input = ins->input(); MOZ_ASSERT(input->type() == MIRType::Int64); MOZ_ASSERT(ins->type() == MIRType::Int32); - // Input reuse is OK even on ARM64 because this node *must* reuse its input in - // order not to generate any code at all, as is the intent. + // Tricky: On 64-bit, this just returns its input (except on MIPS64 there may + // be a sign/zero extension). On 32-bit, it returns the low register of the + // input, and should generate no code. + + // If this assertion does not hold then using "input" unadorned as an alias + // for the low register will not work. +#if defined(JS_NUNBOX32) + static_assert(INT64LOW_INDEX == 0); +#endif + auto* lir = new (alloc()) LWasmWrapU32Index(useRegisterAtStart(input)); defineReuseInput(lir, ins, 0); -#else - MOZ_CRASH("64-bit only"); -#endif } void LIRGenerator::visitIntPtrToDouble(MIntPtrToDouble* ins) { @@ -4797,10 +4804,23 @@ } void LIRGenerator::visitWasmAddOffset(MWasmAddOffset* ins) { - MOZ_ASSERT(ins->base()->type() == MIRType::Int32); - MOZ_ASSERT(ins->type() == MIRType::Int32); MOZ_ASSERT(ins->offset()); - define(new (alloc()) LWasmAddOffset(useRegisterAtStart(ins->base())), ins); + if (ins->base()->type() == MIRType::Int32) { + MOZ_ASSERT(ins->type() == MIRType::Int32); + define(new (alloc()) LWasmAddOffset(useRegisterAtStart(ins->base())), ins); + } else { + MOZ_ASSERT(ins->type() == MIRType::Int64); +#ifdef JS_64BIT + defineInt64(new (alloc()) + LWasmAddOffset64(useInt64RegisterAtStart(ins->base())), + ins); +#else + // Avoid situation where the input is (a,b) and the output is (b,a). + defineInt64ReuseInput( + new (alloc()) LWasmAddOffset64(useInt64RegisterAtStart(ins->base())), + ins, 0); +#endif + } } void LIRGenerator::visitWasmLoadTls(MWasmLoadTls* ins) { @@ -4856,10 +4876,14 @@ void LIRGenerator::visitWasmAlignmentCheck(MWasmAlignmentCheck* ins) { MDefinition* index = ins->index(); - MOZ_ASSERT(index->type() == MIRType::Int32); - - auto* lir = new (alloc()) LWasmAlignmentCheck(useRegisterAtStart(index)); - add(lir, ins); + if (index->type() == MIRType::Int64) { + auto* lir = + new (alloc()) LWasmAlignmentCheck64(useInt64RegisterAtStart(index)); + add(lir, ins); + } else { + auto* lir = new (alloc()) LWasmAlignmentCheck(useRegisterAtStart(index)); + add(lir, ins); + } } void LIRGenerator::visitWasmLoadGlobalVar(MWasmLoadGlobalVar* ins) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/MacroAssembler.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/MacroAssembler.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/MacroAssembler.cpp 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/MacroAssembler.cpp 2021-10-20 19:28:23.000000000 +0000 @@ -4743,7 +4743,7 @@ #ifdef JS_PUNBOX64 auto r64 = Register64(temp); move64(value.toRegister64(), r64); - rshift64(Imm32(32), r64); + rshift64Arithmetic(Imm32(32), r64); #else // TODO: This seems like a bug in mozilla::detail::AddUintptrToHash(). // The uint64_t input is first converted to uintptr_t and then back to @@ -4837,7 +4837,7 @@ // |BigInt::hash()|. // Inline implementation of |mozilla::AddU32ToHash()|. - auto addU32ToHash = [&](Register toAdd) { + auto addU32ToHash = [&](auto toAdd) { rotateLeft(Imm32(5), result, result); xor32(toAdd, result); mul32(Imm32(mozilla::kGoldenRatioU32), result); @@ -4854,19 +4854,26 @@ jump(&start); bind(&loop); - loadPtr(Address(temp2, 0), temp3); { // Compute |AddToHash(AddToHash(hash, data), sizeof(Digit))|. +#if defined(JS_CODEGEN_MIPS64) + // Hash the lower 32-bits. + addU32ToHash(Address(temp2, 0)); + + // Hash the upper 32-bits. + addU32ToHash(Address(temp2, sizeof(int32_t))); +#elif JS_PUNBOX64 + // Use a single 64-bit load on non-MIPS64 platforms. + loadPtr(Address(temp2, 0), temp3); -#if JS_PUNBOX64 // Hash the lower 32-bits. addU32ToHash(temp3); // Hash the upper 32-bits. - rshift64(Imm32(32), Register64(temp3)); + rshiftPtr(Imm32(32), temp3); addU32ToHash(temp3); #else - addU32ToHash(temp3); + addU32ToHash(Address(temp2, 0)); #endif } addPtr(Imm32(sizeof(BigInt::Digit)), temp2); @@ -4911,7 +4918,7 @@ // Hash numbers are 32-bit values, so only hash the lower double-word. static_assert(sizeof(mozilla::HashNumber) == 4); - move64To32(value.toRegister64(), result); + move32To64ZeroExtend(value.valueReg(), Register64(result)); // Inline implementation of |SipHasher::sipHash()|. auto m = Register64(result); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/MacroAssembler.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/MacroAssembler.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/MacroAssembler.h 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/MacroAssembler.h 2021-10-20 19:28:23.000000000 +0000 @@ -1549,6 +1549,9 @@ inline void branchNeg32(Condition cond, Register reg, Label* label) PER_SHARED_ARCH; + inline void branchAdd64(Condition cond, Imm32 imm, Register64 dest, + Label* label) DEFINED_ON(x86, arm); + template inline void branchAddPtr(Condition cond, T src, Register dest, Label* label) PER_SHARED_ARCH; @@ -3641,7 +3644,7 @@ std::pair wasmReserveStackChecked( uint32_t amount, wasm::BytecodeOffset trapOffset); - // Emit a bounds check against the wasm heap limit, jumping to 'label' if + // Emit a bounds check against the wasm heap limit, jumping to 'ok' if // 'cond' holds. If JitOptions.spectreMaskIndex is true, in speculative // executions 'index' is saturated in-place to 'boundsCheckLimit'. // @@ -3651,20 +3654,20 @@ // limited to something much larger. void wasmBoundsCheck32(Condition cond, Register index, - Register boundsCheckLimit, Label* label) + Register boundsCheckLimit, Label* ok) DEFINED_ON(arm, arm64, mips32, mips64, x86_shared); void wasmBoundsCheck32(Condition cond, Register index, - Address boundsCheckLimit, Label* label) + Address boundsCheckLimit, Label* ok) DEFINED_ON(arm, arm64, mips32, mips64, x86_shared); void wasmBoundsCheck64(Condition cond, Register64 index, - Register64 boundsCheckLimit, Label* label) - DEFINED_ON(arm64, mips64, x64); + Register64 boundsCheckLimit, Label* ok) + DEFINED_ON(arm64, mips64, x64, x86, arm); void wasmBoundsCheck64(Condition cond, Register64 index, - Address boundsCheckLimit, Label* label) - DEFINED_ON(arm64, mips64, x64); + Address boundsCheckLimit, Label* ok) + DEFINED_ON(arm64, mips64, x64, x86, arm); // Each wasm load/store instruction appends its own wasm::Trap::OutOfBounds. void wasmLoad(const wasm::MemoryAccessDesc& access, Operand srcAddr, diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/mips32/MacroAssembler-mips32.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/mips32/MacroAssembler-mips32.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/mips32/MacroAssembler-mips32.cpp 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/mips32/MacroAssembler-mips32.cpp 2021-10-20 19:28:24.000000000 +0000 @@ -2302,16 +2302,15 @@ void MacroAssembler::PushBoxed(FloatRegister reg) { Push(reg); } void MacroAssembler::wasmBoundsCheck32(Condition cond, Register index, - Register boundsCheckLimit, - Label* label) { - ma_b(index, boundsCheckLimit, label, cond); + Register boundsCheckLimit, Label* ok) { + ma_b(index, boundsCheckLimit, ok, cond); } void MacroAssembler::wasmBoundsCheck32(Condition cond, Register index, - Address boundsCheckLimit, Label* label) { + Address boundsCheckLimit, Label* ok) { SecondScratchRegisterScope scratch2(*this); load32(boundsCheckLimit, SecondScratchReg); - ma_b(index, SecondScratchReg, label, cond); + ma_b(index, SecondScratchReg, ok, cond); } void MacroAssembler::wasmTruncateDoubleToUInt32(FloatRegister input, diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/mips64/MacroAssembler-mips64.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/mips64/MacroAssembler-mips64.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/mips64/MacroAssembler-mips64.cpp 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/mips64/MacroAssembler-mips64.cpp 2021-10-20 19:28:23.000000000 +0000 @@ -2267,26 +2267,24 @@ } void MacroAssembler::wasmBoundsCheck32(Condition cond, Register index, - Register boundsCheckLimit, - Label* label) { - ma_b(index, boundsCheckLimit, label, cond); + Register boundsCheckLimit, Label* ok) { + ma_b(index, boundsCheckLimit, ok, cond); } void MacroAssembler::wasmBoundsCheck32(Condition cond, Register index, - Address boundsCheckLimit, Label* label) { + Address boundsCheckLimit, Label* ok) { SecondScratchRegisterScope scratch2(*this); load32(boundsCheckLimit, SecondScratchReg); - ma_b(index, SecondScratchReg, label, cond); + ma_b(index, SecondScratchReg, ok, cond); } void MacroAssembler::wasmBoundsCheck64(Condition cond, Register64 index, - Register64 boundsCheckLimit, - Label* label) { + Register64 boundsCheckLimit, Label* ok) { MOZ_CRASH("IMPLEMENTME"); } void MacroAssembler::wasmBoundsCheck64(Condition cond, Register64 index, - Address boundsCheckLimit, Label* label) { + Address boundsCheckLimit, Label* ok) { MOZ_CRASH("IMPLEMENTME"); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/mips64/Simulator-mips64.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/mips64/Simulator-mips64.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/mips64/Simulator-mips64.cpp 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/mips64/Simulator-mips64.cpp 2021-10-20 19:28:24.000000000 +0000 @@ -1981,11 +1981,25 @@ int64_t); typedef int32_t (*Prototype_Int32_GeneralGeneralInt32Int32)(int64_t, int64_t, int32_t, int32_t); +typedef int32_t (*Prototype_Int32_GeneralInt64Int32Int32Int32)(int64_t, int64_t, + int32_t, int32_t, + int32_t); +typedef int32_t (*Prototype_Int32_GeneralInt64Int32)(int64_t, int64_t, int32_t); +typedef int32_t (*Prototype_Int32_GeneralInt64Int32Int64)(int64_t, int64_t, + int32_t, int64_t); +typedef int32_t (*Prototype_Int32_GeneralInt64Int32Int64General)( + int64_t, int64_t, int32_t, int64_t, int64_t); +typedef int32_t (*Prototype_Int32_GeneralInt64Int64Int64)(int64_t, int64_t, + int64_t, int64_t); +typedef int32_t (*Prototype_Int32_GeneralInt64Int64Int64General)( + int64_t, int64_t, int64_t, int64_t, int64_t); typedef int64_t (*Prototype_General_GeneralInt32)(int64_t, int32_t); typedef int64_t (*Prototype_General_GeneralInt32Int32)(int64_t, int32_t, int32_t); typedef int64_t (*Prototype_General_GeneralInt32General)(int64_t, int32_t, int64_t); +typedef int64_t (*Prototype_Int64_General)(int64_t); +typedef int64_t (*Prototype_Int64_GeneralInt64)(int64_t, int64_t); // Software interrupt instructions are used by the simulator to call into C++. void Simulator::softwareInterrupt(SimInstruction* instr) { @@ -2051,7 +2065,7 @@ Prototype_General3 target = reinterpret_cast(external); int64_t result = target(arg0, arg1, arg2); - if (external == intptr_t(&js::wasm::Instance::wake)) { + if (external == intptr_t(&js::wasm::Instance::wake_m32)) { result = int32_t(result); } setCallResult(result); @@ -2117,7 +2131,7 @@ Prototype_GeneralGeneralGeneralInt64 target = reinterpret_cast(external); int64_t result = target(arg0, arg1, arg2, arg3); - if (external == intptr_t(&js::wasm::Instance::wait_i32)) { + if (external == intptr_t(&js::wasm::Instance::wait_i32_m32)) { result = int32_t(result); } setRegister(v0, result); @@ -2127,7 +2141,7 @@ Prototype_GeneralGeneralInt64Int64 target = reinterpret_cast(external); int64_t result = target(arg0, arg1, arg2, arg3); - if (external == intptr_t(&js::wasm::Instance::wait_i64)) { + if (external == intptr_t(&js::wasm::Instance::wait_i64_m32)) { result = int32_t(result); } setRegister(v0, result); @@ -2347,6 +2361,45 @@ setRegister(v0, I64(ret)); break; } + case js::jit::Args_Int32_GeneralInt64Int32Int32Int32: { + int32_t ret = + reinterpret_cast( + nativeFn)(arg0, arg1, I32(arg2), I32(arg3), I32(arg4)); + setRegister(v0, I64(ret)); + break; + } + case js::jit::Args_Int32_GeneralInt64Int32: { + int32_t ret = reinterpret_cast( + nativeFn)(arg0, arg1, I32(arg2)); + setRegister(v0, I64(ret)); + break; + } + case js::jit::Args_Int32_GeneralInt64Int32Int64: { + int32_t ret = reinterpret_cast( + nativeFn)(arg0, arg1, I32(arg2), arg3); + setRegister(v0, I64(ret)); + break; + } + case js::jit::Args_Int32_GeneralInt64Int32Int64General: { + int32_t ret = + reinterpret_cast( + nativeFn)(arg0, arg1, I32(arg2), arg3, arg4); + setRegister(v0, I64(ret)); + break; + } + case js::jit::Args_Int32_GeneralInt64Int64Int64: { + int32_t ret = reinterpret_cast( + nativeFn)(arg0, arg1, arg2, arg3); + setRegister(v0, I64(ret)); + break; + } + case js::jit::Args_Int32_GeneralInt64Int64Int64General: { + int32_t ret = + reinterpret_cast( + nativeFn)(arg0, arg1, arg2, arg3, arg4); + setRegister(v0, I64(ret)); + break; + } case Args_General_GeneralInt32: { int64_t ret = reinterpret_cast( nativeFn)(arg0, I32(arg1)); @@ -2365,7 +2418,17 @@ setRegister(v0, ret); break; } - + case js::jit::Args_Int64_General: { + int64_t ret = reinterpret_cast(nativeFn)(arg0); + setRegister(v0, ret); + break; + } + case js::jit::Args_Int64_GeneralInt64: { + int64_t ret = reinterpret_cast(nativeFn)( + arg0, arg1); + setRegister(v0, ret); + break; + } default: MOZ_CRASH("Unknown function type."); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp 2021-10-20 19:28:24.000000000 +0000 @@ -2071,6 +2071,10 @@ masm.bind(&ok); } +void CodeGenerator::visitWasmAddOffset64(LWasmAddOffset64* lir) { + MOZ_CRASH("NYI"); +} + void CodeGenerator::visitAtomicTypedArrayElementBinop( LAtomicTypedArrayElementBinop* lir) { MOZ_ASSERT(!lir->mir()->isForEffect()); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/MIR.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/MIR.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/MIR.cpp 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/MIR.cpp 2021-10-20 19:28:23.000000000 +0000 @@ -1385,6 +1385,8 @@ } #endif +AliasSet MRandom::getAliasSet() const { return AliasSet::Store(AliasSet::RNG); } + MDefinition* MSign::foldsTo(TempAllocator& alloc) { MDefinition* input = getOperand(0); if (!input->isConstant() || @@ -4452,15 +4454,14 @@ return true; } -bool MObjectState::initFromTemplateObject(TempAllocator& alloc, +void MObjectState::initFromTemplateObject(TempAllocator& alloc, MDefinition* undefinedVal) { if (object()->isNewPlainObject()) { MOZ_ASSERT(object()->toNewPlainObject()->shape()->slotSpan() == numSlots()); for (size_t i = 0; i < numSlots(); i++) { initSlot(i, undefinedVal); } - - return true; + return; } JSObject* templateObject = templateObjectOf(object()); @@ -4484,8 +4485,6 @@ } initSlot(i, def); } - - return true; } MObjectState* MObjectState::New(TempAllocator& alloc, MDefinition* obj) { @@ -4539,12 +4538,11 @@ return true; } -bool MArrayState::initFromTemplateObject(TempAllocator& alloc, +void MArrayState::initFromTemplateObject(TempAllocator& alloc, MDefinition* undefinedVal) { for (size_t i = 0; i < numElements(); i++) { initElement(i, undefinedVal); } - return true; } MArrayState* MArrayState::New(TempAllocator& alloc, MDefinition* arr, @@ -4650,16 +4648,22 @@ return this; } - MOZ_ASSERT(baseArg->type() == MIRType::Int32); - CheckedInt ptr = baseArg->toConstant()->toInt32(); + if (baseArg->type() == MIRType::Int32) { + CheckedInt ptr = baseArg->toConstant()->toInt32(); + ptr += offset(); + if (!ptr.isValid()) { + return this; + } + return MConstant::New(alloc, Int32Value(ptr.value())); + } + MOZ_ASSERT(baseArg->type() == MIRType::Int64); + CheckedInt ptr = baseArg->toConstant()->toInt64(); ptr += offset(); - if (!ptr.isValid()) { return this; } - - return MConstant::New(alloc, Int32Value(ptr.value())); + return MConstant::NewInt64(alloc, ptr.value()); } bool MWasmAlignmentCheck::congruentTo(const MDefinition* ins) const { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/MIR.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/MIR.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/MIR.h 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/MIR.h 2021-10-20 19:28:23.000000000 +0000 @@ -349,10 +349,13 @@ // Hash table of a Map or Set object. MapOrSetHashTable = 1 << 15, - Last = MapOrSetHashTable, + // Internal state of the random number generator + RNG = 1 << 16, + + Last = RNG, Any = Last | (Last - 1), - NumCategories = 16, + NumCategories = 17, // Indicates load or store. Store_ = 1 << 31 @@ -2027,8 +2030,7 @@ // As we might do read of uninitialized properties, we have to copy the // initial values from the template object. - [[nodiscard]] bool initFromTemplateObject(TempAllocator& alloc, - MDefinition* undefinedVal); + void initFromTemplateObject(TempAllocator& alloc, MDefinition* undefinedVal); size_t numFixedSlots() const { return numFixedSlots_; } size_t numSlots() const { return numSlots_; } @@ -2089,8 +2091,7 @@ MDefinition* initLength); static MArrayState* Copy(TempAllocator& alloc, MArrayState* state); - [[nodiscard]] bool initFromTemplateObject(TempAllocator& alloc, - MDefinition* undefinedVal); + void initFromTemplateObject(TempAllocator& alloc, MDefinition* undefinedVal); void setInitializedLength(MDefinition* def) { replaceOperand(1, def); } @@ -6537,14 +6538,11 @@ // Load a value from the elements vector of a native object. If the index is // out-of-bounds, or the indexed slot has a hole, undefined is returned instead. class MLoadElementHole : public MTernaryInstruction, public NoTypePolicy::Data { - bool needsNegativeIntCheck_; - bool needsHoleCheck_; + bool needsNegativeIntCheck_ = true; MLoadElementHole(MDefinition* elements, MDefinition* index, - MDefinition* initLength, bool needsHoleCheck) - : MTernaryInstruction(classOpcode, elements, index, initLength), - needsNegativeIntCheck_(true), - needsHoleCheck_(needsHoleCheck) { + MDefinition* initLength) + : MTernaryInstruction(classOpcode, elements, index, initLength) { setResultType(MIRType::Value); setMovable(); @@ -6564,15 +6562,11 @@ NAMED_OPERANDS((0, elements), (1, index), (2, initLength)) bool needsNegativeIntCheck() const { return needsNegativeIntCheck_; } - bool needsHoleCheck() const { return needsHoleCheck_; } bool congruentTo(const MDefinition* ins) const override { if (!ins->isLoadElementHole()) { return false; } const MLoadElementHole* other = ins->toLoadElementHole(); - if (needsHoleCheck() != other->needsHoleCheck()) { - return false; - } if (needsNegativeIntCheck() != other->needsNegativeIntCheck()) { return false; } @@ -9006,13 +9000,14 @@ AliasSet getAliasSet() const override { return aliases_; } }; -// Bounds check nodes are of type Int32 on 32-bit systems for both wasm and -// asm.js code, as well as on 64-bit systems for asm.js code and for wasm code -// that is known to have a bounds check limit that fits into 32 bits. They are -// of type Int64 only on 64-bit systems for wasm code with 4GB (or larger) -// heaps. There is no way for nodes of both types to be present in the same -// function. Should this change, then BCE must be updated to take type into -// account. +// For memory32, bounds check nodes are of type Int32 on 32-bit systems for both +// wasm and asm.js code, as well as on 64-bit systems for asm.js code and for +// wasm code that is known to have a bounds check limit that fits into 32 bits. +// They are of type Int64 only on 64-bit systems for wasm code with 4GB heaps. +// There is no way for nodes of both types to be present in the same function. +// Should this change, then BCE must be updated to take type into account. +// +// For memory64, bounds check nodes are always of type Int64. class MWasmBoundsCheck : public MBinaryInstruction, public NoTypePolicy::Data { wasm::BytecodeOffset bytecodeOffset_; @@ -9021,6 +9016,8 @@ wasm::BytecodeOffset bytecodeOffset) : MBinaryInstruction(classOpcode, index, boundsCheckLimit), bytecodeOffset_(bytecodeOffset) { + MOZ_ASSERT(index->type() == boundsCheckLimit->type()); + // Bounds check is effectful: it throws for OOB. setGuard(); @@ -9053,7 +9050,9 @@ offset_(offset), bytecodeOffset_(bytecodeOffset) { setGuard(); - setResultType(MIRType::Int32); + MOZ_ASSERT(base->type() == MIRType::Int32 || + base->type() == MIRType::Int64); + setResultType(base->type()); } public: diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/MIROps.yaml firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/MIROps.yaml --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/MIROps.yaml 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/MIROps.yaml 2021-10-20 19:28:24.000000000 +0000 @@ -669,7 +669,7 @@ - name: Random result_type: Double - alias_set: none + alias_set: custom possibly_calls: true compute_range: custom can_recover: custom diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/ScalarReplacement.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/ScalarReplacement.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/ScalarReplacement.cpp 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/ScalarReplacement.cpp 2021-10-20 19:28:23.000000000 +0000 @@ -328,9 +328,6 @@ void visitLambda(MLambda* ins); void visitLambdaArrow(MLambdaArrow* ins); void visitFunctionWithProto(MFunctionWithProto* ins); - - private: - void visitObjectGuard(MInstruction* ins, MDefinition* operand); }; /* static */ const char ObjectMemoryView::phaseName[] = @@ -369,9 +366,7 @@ startBlock_->insertAfter(obj_, state); // Initialize the properties of the object state. - if (!state->initFromTemplateObject(alloc_, undefinedVal_)) { - return false; - } + state->initFromTemplateObject(alloc_, undefinedVal_); // Hold out of resume point until it is visited. state->setInWorklist(); @@ -622,14 +617,9 @@ ins->block()->discard(ins); } -void ObjectMemoryView::visitObjectGuard(MInstruction* ins, - MDefinition* operand) { - MOZ_ASSERT(ins->numOperands() == 1); - MOZ_ASSERT(ins->getOperand(0) == operand); - MOZ_ASSERT(ins->type() == MIRType::Object); - +void ObjectMemoryView::visitGuardShape(MGuardShape* ins) { // Skip guards on other objects. - if (operand != obj_) { + if (ins->object() != obj_) { return; } @@ -640,10 +630,6 @@ ins->block()->discard(ins); } -void ObjectMemoryView::visitGuardShape(MGuardShape* ins) { - visitObjectGuard(ins, ins->object()); -} - void ObjectMemoryView::visitCheckIsObj(MCheckIsObj* ins) { // Skip checks on other objects. if (ins->input() != obj_) { @@ -1052,9 +1038,7 @@ startBlock_->insertAfter(arr_, state); // Initialize the elements of the array state. - if (!state->initFromTemplateObject(alloc_, undefinedVal_)) { - return false; - } + state->initFromTemplateObject(alloc_, undefinedVal_); // Hold out of resume point until it is visited. state->setInWorklist(); @@ -1512,20 +1496,17 @@ // Iterates over phis and instructions. // We do not have to visit resume points. Any resume points that capture // the argument object will be handled by the Sink pass. - for (MNodeIterator iter(*block); iter;) { + for (MDefinitionIterator iter(*block); iter;) { // Increment the iterator before visiting the instruction, as the // visit function might discard itself from the basic block. - MNode* ins = *iter++; - if (ins->isDefinition()) { - MDefinition* def = ins->toDefinition(); - switch (def->op()) { + MDefinition* def = *iter++; + switch (def->op()) { #define MIR_OP(op) \ case MDefinition::Opcode::op: \ visit##op(def->to##op()); \ break; - MIR_OPCODE_LIST(MIR_OP) + MIR_OPCODE_LIST(MIR_OP) #undef MIR_OP - } } } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h 2021-10-20 19:28:24.000000000 +0000 @@ -1083,6 +1083,9 @@ JitSpew(JitSpew_Pools, "[%d] No-Pool instruction(%zu) caused a spill.", id, sizeExcludingCurrentPool()); finishPool(maxInst * InstSize); + if (this->oom()) { + return; + } MOZ_ASSERT(hasSpaceForInsts(maxInst, 0)); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/shared/LIR-shared.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/shared/LIR-shared.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/shared/LIR-shared.h 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/shared/LIR-shared.h 2021-10-20 19:28:24.000000000 +0000 @@ -1868,10 +1868,6 @@ setOperand(2, initLength); } - const char* extraName() const { - return mir()->needsHoleCheck() ? "HoleCheck" : nullptr; - } - const MLoadElementHole* mir() const { return mir_->toLoadElementHole(); } const LAllocation* elements() { return getOperand(0); } const LAllocation* index() { return getOperand(1); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/WarpCacheIRTranspiler.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/WarpCacheIRTranspiler.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/WarpCacheIRTranspiler.cpp 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/WarpCacheIRTranspiler.cpp 2021-10-20 19:28:23.000000000 +0000 @@ -1741,9 +1741,7 @@ auto* length = MInitializedLength::New(alloc(), elements); add(length); - bool needsHoleCheck = true; - auto* load = - MLoadElementHole::New(alloc(), elements, index, length, needsHoleCheck); + auto* load = MLoadElementHole::New(alloc(), elements, index, length); add(load); pushResult(load); @@ -2821,10 +2819,10 @@ #endif auto* ins = MRandom::New(alloc()); - add(ins); + addEffectful(ins); pushResult(ins); - return true; + return resumeAfter(ins); } bool WarpCacheIRTranspiler::emitInt32MinMax(bool isMax, Int32OperandId firstId, diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/x64/CodeGenerator-x64.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/x64/CodeGenerator-x64.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/x64/CodeGenerator-x64.cpp 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/x64/CodeGenerator-x64.cpp 2021-10-20 19:28:24.000000000 +0000 @@ -564,6 +564,8 @@ uint32_t offset = mir->access().offset(); MOZ_ASSERT(offset < masm.wasmMaxOffsetGuardLimit()); + // ptr is a GPR and is either a 32-bit value zero-extended to 64-bit, or a + // true 64-bit value. const LAllocation* ptr = ins->ptr(); Operand srcAddr = ptr->isBogus() ? Operand(HeapReg, offset) diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/x64/Lowering-x64.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/x64/Lowering-x64.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/x64/Lowering-x64.cpp 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/x64/Lowering-x64.cpp 2021-10-20 19:28:23.000000000 +0000 @@ -262,7 +262,9 @@ void LIRGenerator::visitWasmLoad(MWasmLoad* ins) { MDefinition* base = ins->base(); - MOZ_ASSERT(base->type() == MIRType::Int32); + // 'base' is a GPR but may be of either type. If it is 32-bit it is + // zero-extended and can act as 64-bit. + MOZ_ASSERT(base->type() == MIRType::Int32 || base->type() == MIRType::Int64); if (ins->type() != MIRType::Int64) { auto* lir = new (alloc()) LWasmLoad(useRegisterOrZeroAtStart(base)); @@ -276,7 +278,8 @@ void LIRGenerator::visitWasmStore(MWasmStore* ins) { MDefinition* base = ins->base(); - MOZ_ASSERT(base->type() == MIRType::Int32); + // See comment in visitWasmLoad re the type of 'base'. + MOZ_ASSERT(base->type() == MIRType::Int32 || base->type() == MIRType::Int64); MDefinition* value = ins->value(); LAllocation valueAlloc; @@ -322,7 +325,8 @@ void LIRGenerator::visitWasmCompareExchangeHeap(MWasmCompareExchangeHeap* ins) { MDefinition* base = ins->base(); - MOZ_ASSERT(base->type() == MIRType::Int32); + // See comment in visitWasmLoad re the type of 'base'. + MOZ_ASSERT(base->type() == MIRType::Int32 || base->type() == MIRType::Int64); // The output may not be used but will be clobbered regardless, so // pin the output to eax. @@ -339,7 +343,9 @@ } void LIRGenerator::visitWasmAtomicExchangeHeap(MWasmAtomicExchangeHeap* ins) { - MOZ_ASSERT(ins->base()->type() == MIRType::Int32); + // See comment in visitWasmLoad re the type of 'base'. + MOZ_ASSERT(ins->base()->type() == MIRType::Int32 || + ins->base()->type() == MIRType::Int64); const LAllocation base = useRegister(ins->base()); const LAllocation value = useRegister(ins->value()); @@ -355,7 +361,8 @@ void LIRGenerator::visitWasmAtomicBinopHeap(MWasmAtomicBinopHeap* ins) { MDefinition* base = ins->base(); - MOZ_ASSERT(base->type() == MIRType::Int32); + // See comment in visitWasmLoad re the type of 'base'. + MOZ_ASSERT(base->type() == MIRType::Int32 || base->type() == MIRType::Int64); // No support for 64-bit operations with constants at the masm level. diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/x64/MacroAssembler-x64.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/x64/MacroAssembler-x64.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/x64/MacroAssembler-x64.cpp 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/x64/MacroAssembler-x64.cpp 2021-10-20 19:28:24.000000000 +0000 @@ -1494,19 +1494,18 @@ } void MacroAssembler::wasmBoundsCheck64(Condition cond, Register64 index, - Register64 boundsCheckLimit, - Label* label) { + Register64 boundsCheckLimit, Label* ok) { cmpPtr(index.reg, boundsCheckLimit.reg); - j(cond, label); + j(cond, ok); if (JitOptions.spectreIndexMasking) { cmovCCq(cond, Operand(boundsCheckLimit.reg), index.reg); } } void MacroAssembler::wasmBoundsCheck64(Condition cond, Register64 index, - Address boundsCheckLimit, Label* label) { + Address boundsCheckLimit, Label* ok) { cmpPtr(index.reg, Operand(boundsCheckLimit)); - j(cond, label); + j(cond, ok); if (JitOptions.spectreIndexMasking) { cmovCCq(cond, Operand(boundsCheckLimit), index.reg); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/x86/CodeGenerator-x86.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/x86/CodeGenerator-x86.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/x86/CodeGenerator-x86.cpp 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/x86/CodeGenerator-x86.cpp 2021-10-20 19:28:24.000000000 +0000 @@ -1361,8 +1361,10 @@ MOZ_CRASH("64-bit only"); } -void CodeGenerator::visitWasmWrapU32Index(LWasmWrapU32Index*) { - MOZ_CRASH("64-bit only"); +void CodeGenerator::visitWasmWrapU32Index(LWasmWrapU32Index* lir) { + // Generates no code on this platform because we just return the low part of + // the input register pair. + MOZ_ASSERT(ToRegister(lir->input()) == ToRegister(lir->output())); } void CodeGenerator::visitClzI64(LClzI64* lir) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/x86/MacroAssembler-x86.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/x86/MacroAssembler-x86.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/x86/MacroAssembler-x86.cpp 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/x86/MacroAssembler-x86.cpp 2021-10-20 19:28:23.000000000 +0000 @@ -1729,4 +1729,22 @@ PatchDataWithValueCheck(loc, ImmPtr(target.raw()), ImmPtr(nullptr)); } +void MacroAssembler::wasmBoundsCheck64(Condition cond, Register64 index, + Register64 boundsCheckLimit, Label* ok) { + Label notOk; + cmp32(index.high, Imm32(0)); + j(Assembler::NonZero, ¬Ok); + wasmBoundsCheck32(cond, index.low, boundsCheckLimit.low, ok); + bind(¬Ok); +} + +void MacroAssembler::wasmBoundsCheck64(Condition cond, Register64 index, + Address boundsCheckLimit, Label* ok) { + Label notOk; + cmp32(index.high, Imm32(0)); + j(Assembler::NonZero, ¬Ok); + wasmBoundsCheck32(cond, index.low, boundsCheckLimit, ok); + bind(¬Ok); +} + //}}} check_macroassembler_style diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/x86/MacroAssembler-x86-inl.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/x86/MacroAssembler-x86-inl.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/x86/MacroAssembler-x86-inl.h 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/x86/MacroAssembler-x86-inl.h 2021-10-20 19:28:23.000000000 +0000 @@ -962,6 +962,12 @@ j(Assembler::Overflow, fail); } +void MacroAssembler::branchAdd64(Condition cond, Imm32 imm, Register64 dest, + Label* label) { + add64(imm, dest); + j(cond, label); +} + void MacroAssembler::branchTest32(Condition cond, const AbsoluteAddress& lhs, Imm32 rhs, Label* label) { test32(Operand(lhs), rhs); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp 2021-10-20 19:28:23.000000000 +0000 @@ -481,6 +481,22 @@ masm.bind(&ok); } +void CodeGenerator::visitWasmAddOffset64(LWasmAddOffset64* lir) { + MWasmAddOffset* mir = lir->mir(); + Register64 base = ToRegister64(lir->base()); + Register64 out = ToOutRegister64(lir); + + if (base != out) { + masm.move64(base, out); + } + masm.add64(Imm64(mir->offset()), out); + + Label ok; + masm.j(Assembler::CarryClear, &ok); + masm.wasmTrap(wasm::Trap::OutOfBounds, mir->bytecodeOffset()); + masm.bind(&ok); +} + void CodeGenerator::visitWasmTruncateToInt32(LWasmTruncateToInt32* lir) { FloatRegister input = ToFloatRegister(lir->input()); Register output = ToRegister(lir->output()); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/x86-shared/Lowering-x86-shared.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/x86-shared/Lowering-x86-shared.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/x86-shared/Lowering-x86-shared.cpp 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/x86-shared/Lowering-x86-shared.cpp 2021-10-20 19:28:24.000000000 +0000 @@ -1476,6 +1476,13 @@ void LIRGenerator::visitWasmLoadLaneSimd128(MWasmLoadLaneSimd128* ins) { #ifdef ENABLE_WASM_SIMD + // A trick: On 32-bit systems, the base pointer is 32 bits (it was bounds + // checked and then chopped). On 64-bit systems, it can be 32 bits or 64 + // bits. Either way, it fits in a GPR so we can ignore the + // Register/Register64 distinction here. +# ifndef JS_64BIT + MOZ_ASSERT(ins->base()->type() == MIRType::Int32); +# endif LUse base = useRegisterAtStart(ins->base()); LUse inputUse = useRegisterAtStart(ins->value()); LAllocation memoryBase = ins->hasMemoryBase() @@ -1491,6 +1498,10 @@ void LIRGenerator::visitWasmStoreLaneSimd128(MWasmStoreLaneSimd128* ins) { #ifdef ENABLE_WASM_SIMD + // See comment above. +# ifndef JS_64BIT + MOZ_ASSERT(ins->base()->type() == MIRType::Int32); +# endif LUse base = useRegisterAtStart(ins->base()); LUse input = useRegisterAtStart(ins->value()); LAllocation memoryBase = ins->hasMemoryBase() diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp 2021-10-20 19:28:23.000000000 +0000 @@ -705,19 +705,18 @@ CodeOffset MacroAssembler::wasmTrapInstruction() { return ud2(); } void MacroAssembler::wasmBoundsCheck32(Condition cond, Register index, - Register boundsCheckLimit, - Label* label) { + Register boundsCheckLimit, Label* ok) { cmp32(index, boundsCheckLimit); - j(cond, label); + j(cond, ok); if (JitOptions.spectreIndexMasking) { cmovCCl(cond, Operand(boundsCheckLimit), index); } } void MacroAssembler::wasmBoundsCheck32(Condition cond, Register index, - Address boundsCheckLimit, Label* label) { + Address boundsCheckLimit, Label* ok) { cmp32(index, Operand(boundsCheckLimit)); - j(cond, label); + j(cond, ok); if (JitOptions.spectreIndexMasking) { cmovCCl(cond, Operand(boundsCheckLimit), index); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/x86-shared/MacroAssembler-x86-shared-SIMD.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/x86-shared/MacroAssembler-x86-shared-SIMD.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit/x86-shared/MacroAssembler-x86-shared-SIMD.cpp 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit/x86-shared/MacroAssembler-x86-shared-SIMD.cpp 2021-10-20 19:28:23.000000000 +0000 @@ -1213,8 +1213,9 @@ // Convert lanes below 80000000h into unsigned int without issues. vcvttps2dq(dest, dest); // Knowing IEEE-754 number representation, to convert lanes above - // 7FFFFFFFh, just shift left by 7 bits. - vpslld(Imm32(7), scratch, scratch); + // 7FFFFFFFh, mutiply by 2 (to add 1 in exponent) and shift left by 8 bits. + vaddps(Operand(scratch), scratch, scratch); + vpslld(Imm32(8), scratch, scratch); // Combine the results. vpaddd(Operand(scratch), dest, dest); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit-test/tests/gc/bug-1736310.js firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit-test/tests/gc/bug-1736310.js --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit-test/tests/gc/bug-1736310.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit-test/tests/gc/bug-1736310.js 2021-10-20 19:28:23.000000000 +0000 @@ -0,0 +1,16 @@ +gczeal(9, 10); +function a() { + var b = new Int32Array(buffer); + function c(d) { + b[5] = d; + } + return c; +} +b = new Int32Array(6); +var buffer = b.buffer; +a()({ + valueOf() { + detachArrayBuffer(buffer); + } +}) + diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit-test/tests/warp/bug1735157.js firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit-test/tests/warp/bug1735157.js --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit-test/tests/warp/bug1735157.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit-test/tests/warp/bug1735157.js 2021-10-20 19:28:24.000000000 +0000 @@ -0,0 +1,5 @@ +const options = ["", {}]; +let value = ""; +for (let i = 0; i < 10000; i++) { + value += options[(Math.random() < 0.01) | 0]; +} diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit-test/tests/wasm/bce-x64-ion-codegen.js firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit-test/tests/wasm/bce-x64-ion-codegen.js --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit-test/tests/wasm/bce-x64-ion-codegen.js 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit-test/tests/wasm/bce-x64-ion-codegen.js 2021-10-20 19:28:23.000000000 +0000 @@ -1,4 +1,4 @@ -// |jit-test| --wasm-compiler=ion; --disable-wasm-huge-memory; --spectre-mitigations=off; skip-if: !hasDisassembler() || wasmCompileMode() != "ion" || !getBuildConfiguration().x64 || getBuildConfiguration().simulator || getJitCompilerOptions()["ion.check-range-analysis"]; include:codegen-x64-test.js +// |jit-test| --wasm-compiler=optimizing; --disable-wasm-huge-memory; --spectre-mitigations=off; skip-if: !hasDisassembler() || wasmCompileMode() != "ion" || !getBuildConfiguration().x64 || getBuildConfiguration().simulator || getJitCompilerOptions()["ion.check-range-analysis"]; include:codegen-x64-test.js // Spectre mitigation is disabled above to make the generated code simpler to // match; ion.check-range-analysis makes a hash of the code and makes testing diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit-test/tests/wasm/bce-x86-ion-codegen.js firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit-test/tests/wasm/bce-x86-ion-codegen.js --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit-test/tests/wasm/bce-x86-ion-codegen.js 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit-test/tests/wasm/bce-x86-ion-codegen.js 2021-10-20 19:28:23.000000000 +0000 @@ -1,4 +1,4 @@ -// |jit-test| --wasm-compiler=ion; --spectre-mitigations=off; skip-if: !hasDisassembler() || wasmCompileMode() != "ion" || !getBuildConfiguration().x86 || getBuildConfiguration().simulator || getJitCompilerOptions()["ion.check-range-analysis"]; include:codegen-x86-test.js +// |jit-test| --wasm-compiler=optimizing; --spectre-mitigations=off; skip-if: !hasDisassembler() || wasmCompileMode() != "ion" || !getBuildConfiguration().x86 || getBuildConfiguration().simulator || getJitCompilerOptions()["ion.check-range-analysis"]; include:codegen-x86-test.js // Spectre mitigation is disabled above to make the generated code simpler to // match; ion.check-range-analysis makes a hash of the code and makes testing diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit-test/tests/wasm/memory64/basic.js firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit-test/tests/wasm/memory64/basic.js --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit-test/tests/wasm/memory64/basic.js 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit-test/tests/wasm/memory64/basic.js 2021-10-20 19:28:23.000000000 +0000 @@ -235,3 +235,1244 @@ (memory i64 1) (func (param $p i64) (drop (i32.load offset=0x100000000 (local.get $p)))))`)), false); + + +/////////////////////////////////////////////////////////////////////////////// +// +// EXECUTION + +// Smoketest: Can we actually allocate a memory larger than 4GB? + +if (getBuildConfiguration()["pointer-byte-size"] == 8) { + // TODO: "index" is not yet part of the spec + // https://github.com/WebAssembly/memory64/issues/24 + + new WebAssembly.Memory({index:"i64", initial:65536 * 1.5, maximum:65536 * 2}); +} + +// JS-API + +if (WebAssembly.Function) { + // TODO: "index" is not yet part of the spec + // https://github.com/WebAssembly/memory64/issues/24 + + let m64 = new WebAssembly.Memory({index:"i64", initial:1}); + assertEq(m64.type().index, "i64"); + + let m32 = new WebAssembly.Memory({initial:1}); + assertEq(m32.type().index, "i32"); +} + +const SMALL = 64; // < offsetguard everywhere +const BIG = 131072; // > offsetguard on 32-bit +const HUGE = 2147483656; // > offsetguard on 64-bit + +function makeTest(LOC, INITIAL, MAXIMUM, SHARED) { + const v128Prefix = +` (func $stash (param v128) + (v128.store (i64.const 0) (local.get 0))) + + (func $unstash (result v128) + (v128.load (i64.const 0))) +`; + + const readV128Code = +` (func (export "readv128@0") (param $p i64) + (call $stash (v128.load (local.get $p)))) + + (func (export "readv128@small") (param $p i64) + (call $stash (v128.load offset=${SMALL} (local.get $p)))) + + (func (export "readv128@big") (param $p i64) + (call $stash (v128.load offset=${BIG} (local.get $p)))) + + (func (export "readv128@huge") (param $p i64) + (call $stash (v128.load offset=${HUGE} (local.get $p)))) + + (func (export "readv128/const@0") + (call $stash (v128.load (i64.const ${LOC})))) + + (func (export "readv128/const@small") + (call $stash (v128.load offset=${SMALL} (i64.const ${LOC})))) + + (func (export "readv128/const@big") + (call $stash (v128.load offset=${BIG} (i64.const ${LOC})))) + + (func (export "v128.load_splat@small") (param $p i64) + (call $stash (v128.load32_splat offset=${SMALL} (local.get $p)))) + + (func (export "v128.load_zero@small") (param $p i64) + (call $stash (v128.load32_zero offset=${SMALL} (local.get $p)))) + + (func (export "v128.load_extend@small") (param $p i64) + (call $stash (v128.load32x2_u offset=${SMALL} (local.get $p)))) + + (func (export "v128.load_lane@small") (param $p i64) + (call $stash (v128.load32_lane offset=${SMALL} 2 (local.get $p) (call $unstash)))) +`; + + const writeV128Code = +` (func (export "writev128@0") (param $p i64) + (v128.store (local.get $p) (call $unstash))) + + (func (export "writev128@small") (param $p i64) + (v128.store offset=${SMALL} (local.get $p) (call $unstash))) + + (func (export "writev128@big") (param $p i64) + (v128.store offset=${BIG} (local.get $p) (call $unstash))) + + (func (export "writev128@huge") (param $p i64) + (v128.store offset=${HUGE} (local.get $p) (call $unstash))) + + (func (export "writev128/const@0") + (v128.store (i64.const ${LOC}) (call $unstash))) + + (func (export "writev128/const@small") + (v128.store offset=${SMALL} (i64.const ${LOC}) (call $unstash))) + + (func (export "writev128/const@big") + (v128.store offset=${BIG} (i64.const ${LOC}) (call $unstash))) + + (func (export "v128.store_lane@small") (param $p i64) + (v128.store32_lane offset=${SMALL} 2 (local.get $p) (call $unstash))) +`; + + const ins = wasmEvalText(` +(module + (memory (export "mem") i64 ${INITIAL} ${MAXIMUM} ${SHARED}) + + ;; About the test cases: there are various optimizations in the engine + ;; for different shapes of a pointer+offset. Constant pointers are + ;; resolved early; large offsets are folded early using explicit code + ;; with an overflow check (but "large" depends on 32-bit vs 64-bit); + ;; wait/notify fold offsets early regardless; zero offsets lead to + ;; tighter code with variable pointers; and don't get me started on + ;; alignment checks. These test cases are not exhaustive but aim + ;; to test at least some things. + + ;; TODO: more sizes for all operations, though this is not critical + ;; TODO: sign extending loads, again not critical + + ${wasmSimdEnabled() ? v128Prefix : ""} + + ;; Read i32 + (func (export "readi32@0") (param $p i64) (result i32) + (i32.load (local.get $p))) + + (func (export "readi32@small") (param $p i64) (result i32) + (i32.load offset=${SMALL} (local.get $p))) + + (func (export "readi32@big") (param $p i64) (result i32) + (i32.load offset=${BIG} (local.get $p))) + + (func (export "readi32@huge") (param $p i64) (result i32) + (i32.load offset=${HUGE} (local.get $p))) + + (func (export "readi32/const@0") (result i32) + (i32.load (i64.const ${LOC}))) + + (func (export "readi32/const@small") (result i32) + (i32.load offset=${SMALL} (i64.const ${LOC}))) + + (func (export "readi32/const@big") (result i32) + (i32.load offset=${BIG} (i64.const ${LOC}))) + + ;; Read i64 + (func (export "readi64@0") (param $p i64) (result i64) + (i64.load (local.get $p))) + + (func (export "readi64@small") (param $p i64) (result i64) + (i64.load offset=${SMALL} (local.get $p))) + + (func (export "readi64@big") (param $p i64) (result i64) + (i64.load offset=${BIG} (local.get $p))) + + (func (export "readi64@huge") (param $p i64) (result i64) + (i64.load offset=${HUGE} (local.get $p))) + + (func (export "readi64/const@0") (result i64) + (i64.load (i64.const ${LOC}))) + + (func (export "readi64/const@small") (result i64) + (i64.load offset=${SMALL} (i64.const ${LOC}))) + + (func (export "readi64/const@big") (result i64) + (i64.load offset=${BIG} (i64.const ${LOC}))) + + ;; Read v128 + ${wasmSimdEnabled() ? readV128Code : ""} + + ;; write i32 + (func (export "writei32@0") (param $p i64) (param $v i32) + (i32.store (local.get $p) (local.get $v))) + + (func (export "writei32@small") (param $p i64) (param $v i32) + (i32.store offset=${SMALL} (local.get $p) (local.get $v))) + + (func (export "writei32@big") (param $p i64) (param $v i32) + (i32.store offset=${BIG} (local.get $p) (local.get $v))) + + (func (export "writei32@huge") (param $p i64) (param $v i32) + (i32.store offset=${HUGE} (local.get $p) (local.get $v))) + + (func (export "writei32/const@0") (param $v i32) + (i32.store (i64.const ${LOC}) (local.get $v))) + + (func (export "writei32/const@small") (param $v i32) + (i32.store offset=${SMALL} (i64.const ${LOC}) (local.get $v))) + + (func (export "writei32/const@big") (param $v i32) + (i32.store offset=${BIG} (i64.const ${LOC}) (local.get $v))) + + ;; write i64 + (func (export "writei64@0") (param $p i64) (param $v i64) + (i64.store (local.get $p) (local.get $v))) + + (func (export "writei64@small") (param $p i64) (param $v i64) + (i64.store offset=${SMALL} (local.get $p) (local.get $v))) + + (func (export "writei64@big") (param $p i64) (param $v i64) + (i64.store offset=${BIG} (local.get $p) (local.get $v))) + + (func (export "writei64@huge") (param $p i64) (param $v i64) + (i64.store offset=${HUGE} (local.get $p) (local.get $v))) + + (func (export "writei64/const@0") (param $v i64) + (i64.store (i64.const ${LOC}) (local.get $v))) + + (func (export "writei64/const@small") (param $v i64) + (i64.store offset=${SMALL} (i64.const ${LOC}) (local.get $v))) + + (func (export "writei64/const@big") (param $v i64) + (i64.store offset=${BIG} (i64.const ${LOC}) (local.get $v))) + + ;; Read v128 + ${wasmSimdEnabled() ? writeV128Code : ""} + + ;; Atomic read i32 + + (func (export "areadi32@0") (param $p i64) (result i32) + (i32.atomic.load (local.get $p))) + + (func (export "areadi32@small") (param $p i64) (result i32) + (i32.atomic.load offset=${SMALL} (local.get $p))) + + (func (export "areadi32@big") (param $p i64) (result i32) + (i32.atomic.load offset=${BIG} (local.get $p))) + + (func (export "areadi32@huge") (param $p i64) (result i32) + (i32.atomic.load offset=${HUGE} (local.get $p))) + + (func (export "areadi32/const@0") (result i32) + (i32.atomic.load (i64.const ${LOC}))) + + (func (export "areadi32/const@small") (result i32) + (i32.atomic.load offset=${SMALL} (i64.const ${LOC}))) + + (func (export "areadi32/const@big") (result i32) + (i32.atomic.load offset=${BIG} (i64.const ${LOC}))) + + ;; Atomic read i64 + + (func (export "areadi64@0") (param $p i64) (result i64) + (i64.atomic.load (local.get $p))) + + (func (export "areadi64@small") (param $p i64) (result i64) + (i64.atomic.load offset=${SMALL} (local.get $p))) + + (func (export "areadi64@big") (param $p i64) (result i64) + (i64.atomic.load offset=${BIG} (local.get $p))) + + (func (export "areadi64@huge") (param $p i64) (result i64) + (i64.atomic.load offset=${HUGE} (local.get $p))) + + (func (export "areadi64/const@0") (result i64) + (i64.atomic.load (i64.const ${LOC}))) + + (func (export "areadi64/const@small") (result i64) + (i64.atomic.load offset=${SMALL} (i64.const ${LOC}))) + + (func (export "areadi64/const@big") (result i64) + (i64.atomic.load offset=${BIG} (i64.const ${LOC}))) + + ;; Atomic write i32 + + (func (export "awritei32@0") (param $p i64) (param $v i32) + (i32.atomic.store (local.get $p) (local.get $v))) + + (func (export "awritei32@small") (param $p i64) (param $v i32) + (i32.atomic.store offset=${SMALL} (local.get $p) (local.get $v))) + + (func (export "awritei32@big") (param $p i64) (param $v i32) + (i32.atomic.store offset=${BIG} (local.get $p) (local.get $v))) + + (func (export "awritei32@huge") (param $p i64) (param $v i32) + (i32.atomic.store offset=${HUGE} (local.get $p) (local.get $v))) + + (func (export "awritei32/const@0") (param $v i32) + (i32.atomic.store (i64.const ${LOC}) (local.get $v))) + + (func (export "awritei32/const@small") (param $v i32) + (i32.atomic.store offset=${SMALL} (i64.const ${LOC}) (local.get $v))) + + (func (export "awritei32/const@big") (param $v i32) + (i32.atomic.store offset=${BIG} (i64.const ${LOC}) (local.get $v))) + + ;; Atomic write i64 + + (func (export "awritei64@0") (param $p i64) (param $v i64) + (i64.atomic.store (local.get $p) (local.get $v))) + + (func (export "awritei64@small") (param $p i64) (param $v i64) + (i64.atomic.store offset=${SMALL} (local.get $p) (local.get $v))) + + (func (export "awritei64@big") (param $p i64) (param $v i64) + (i64.atomic.store offset=${BIG} (local.get $p) (local.get $v))) + + (func (export "awritei64@huge") (param $p i64) (param $v i64) + (i64.atomic.store offset=${HUGE} (local.get $p) (local.get $v))) + + (func (export "awritei64/const@0") (param $v i64) + (i64.atomic.store (i64.const ${LOC}) (local.get $v))) + + (func (export "awritei64/const@small") (param $v i64) + (i64.atomic.store offset=${SMALL} (i64.const ${LOC}) (local.get $v))) + + (func (export "awritei64/const@big") (param $v i64) + (i64.atomic.store offset=${BIG} (i64.const ${LOC}) (local.get $v))) + + ;; xchg i32 + + (func (export "xchgi32@0") (param $p i64) (param $v i32) (result i32) + (i32.atomic.rmw.xchg (local.get $p) (local.get $v))) + + (func (export "xchgi32@small") (param $p i64) (param $v i32) (result i32) + (i32.atomic.rmw.xchg offset=${SMALL} (local.get $p) (local.get $v))) + + (func (export "xchgi32@big") (param $p i64) (param $v i32) (result i32) + (i32.atomic.rmw.xchg offset=${BIG} (local.get $p) (local.get $v))) + + (func (export "xchgi32@huge") (param $p i64) (param $v i32) (result i32) + (i32.atomic.rmw.xchg offset=${HUGE} (local.get $p) (local.get $v))) + + (func (export "xchgi32/const@0") (param $v i32) (result i32) + (i32.atomic.rmw.xchg (i64.const ${LOC}) (local.get $v))) + + (func (export "xchgi32/const@small") (param $v i32) (result i32) + (i32.atomic.rmw.xchg offset=${SMALL} (i64.const ${LOC}) (local.get $v))) + + (func (export "xchgi32/const@big") (param $v i32) (result i32) + (i32.atomic.rmw.xchg offset=${BIG} (i64.const ${LOC}) (local.get $v))) + + ;; xchg i64 + + (func (export "xchgi64@0") (param $p i64) (param $v i64) (result i64) + (i64.atomic.rmw.xchg (local.get $p) (local.get $v))) + + (func (export "xchgi64@small") (param $p i64) (param $v i64) (result i64) + (i64.atomic.rmw.xchg offset=${SMALL} (local.get $p) (local.get $v))) + + (func (export "xchgi64@big") (param $p i64) (param $v i64) (result i64) + (i64.atomic.rmw.xchg offset=${BIG} (local.get $p) (local.get $v))) + + (func (export "xchgi64@huge") (param $p i64) (param $v i64) (result i64) + (i64.atomic.rmw.xchg offset=${HUGE} (local.get $p) (local.get $v))) + + (func (export "xchgi64/const@0") (param $v i64) (result i64) + (i64.atomic.rmw.xchg (i64.const ${LOC}) (local.get $v))) + + (func (export "xchgi64/const@small") (param $v i64) (result i64) + (i64.atomic.rmw.xchg offset=${SMALL} (i64.const ${LOC}) (local.get $v))) + + (func (export "xchgi64/const@big") (param $v i64) (result i64) + (i64.atomic.rmw.xchg offset=${BIG} (i64.const ${LOC}) (local.get $v))) + + ;; add i32 + + (func (export "addi32@0") (param $p i64) (param $v i32) (result i32) + (i32.atomic.rmw.add (local.get $p) (local.get $v))) + + (func (export "addi32@small") (param $p i64) (param $v i32) (result i32) + (i32.atomic.rmw.add offset=${SMALL} (local.get $p) (local.get $v))) + + (func (export "addi32@big") (param $p i64) (param $v i32) (result i32) + (i32.atomic.rmw.add offset=${BIG} (local.get $p) (local.get $v))) + + (func (export "addi32@huge") (param $p i64) (param $v i32) (result i32) + (i32.atomic.rmw.add offset=${HUGE} (local.get $p) (local.get $v))) + + (func (export "addi32/const@0") (param $v i32) (result i32) + (i32.atomic.rmw.add (i64.const ${LOC}) (local.get $v))) + + (func (export "addi32/const@small") (param $v i32) (result i32) + (i32.atomic.rmw.add offset=${SMALL} (i64.const ${LOC}) (local.get $v))) + + (func (export "addi32/const@big") (param $v i32) (result i32) + (i32.atomic.rmw.add offset=${BIG} (i64.const ${LOC}) (local.get $v))) + + ;; add i64 + + (func (export "addi64@0") (param $p i64) (param $v i64) (result i64) + (i64.atomic.rmw.add (local.get $p) (local.get $v))) + + (func (export "addi64@small") (param $p i64) (param $v i64) (result i64) + (i64.atomic.rmw.add offset=${SMALL} (local.get $p) (local.get $v))) + + (func (export "addi64@big") (param $p i64) (param $v i64) (result i64) + (i64.atomic.rmw.add offset=${BIG} (local.get $p) (local.get $v))) + + (func (export "addi64@huge") (param $p i64) (param $v i64) (result i64) + (i64.atomic.rmw.add offset=${HUGE} (local.get $p) (local.get $v))) + + (func (export "addi64/const@0") (param $v i64) (result i64) + (i64.atomic.rmw.add (i64.const ${LOC}) (local.get $v))) + + (func (export "addi64/const@small") (param $v i64) (result i64) + (i64.atomic.rmw.add offset=${SMALL} (i64.const ${LOC}) (local.get $v))) + + (func (export "addi64/const@big") (param $v i64) (result i64) + (i64.atomic.rmw.add offset=${BIG} (i64.const ${LOC}) (local.get $v))) + + ;; sub i32 + + (func (export "subi32@0") (param $p i64) (param $v i32) (result i32) + (i32.atomic.rmw.sub (local.get $p) (local.get $v))) + + (func (export "subi32@small") (param $p i64) (param $v i32) (result i32) + (i32.atomic.rmw.sub offset=${SMALL} (local.get $p) (local.get $v))) + + (func (export "subi32@big") (param $p i64) (param $v i32) (result i32) + (i32.atomic.rmw.sub offset=${BIG} (local.get $p) (local.get $v))) + + (func (export "subi32@huge") (param $p i64) (param $v i32) (result i32) + (i32.atomic.rmw.sub offset=${HUGE} (local.get $p) (local.get $v))) + + (func (export "subi32/const@0") (param $v i32) (result i32) + (i32.atomic.rmw.sub (i64.const ${LOC}) (local.get $v))) + + (func (export "subi32/const@small") (param $v i32) (result i32) + (i32.atomic.rmw.sub offset=${SMALL} (i64.const ${LOC}) (local.get $v))) + + (func (export "subi32/const@big") (param $v i32) (result i32) + (i32.atomic.rmw.sub offset=${BIG} (i64.const ${LOC}) (local.get $v))) + + ;; sub i64 + + (func (export "subi64@0") (param $p i64) (param $v i64) (result i64) + (i64.atomic.rmw.sub (local.get $p) (local.get $v))) + + (func (export "subi64@small") (param $p i64) (param $v i64) (result i64) + (i64.atomic.rmw.sub offset=${SMALL} (local.get $p) (local.get $v))) + + (func (export "subi64@big") (param $p i64) (param $v i64) (result i64) + (i64.atomic.rmw.sub offset=${BIG} (local.get $p) (local.get $v))) + + (func (export "subi64@huge") (param $p i64) (param $v i64) (result i64) + (i64.atomic.rmw.sub offset=${HUGE} (local.get $p) (local.get $v))) + + (func (export "subi64/const@0") (param $v i64) (result i64) + (i64.atomic.rmw.sub (i64.const ${LOC}) (local.get $v))) + + (func (export "subi64/const@small") (param $v i64) (result i64) + (i64.atomic.rmw.sub offset=${SMALL} (i64.const ${LOC}) (local.get $v))) + + (func (export "subi64/const@big") (param $v i64) (result i64) + (i64.atomic.rmw.sub offset=${BIG} (i64.const ${LOC}) (local.get $v))) + + ;; and i32 + + (func (export "andi32@0") (param $p i64) (param $v i32) (result i32) + (i32.atomic.rmw.and (local.get $p) (local.get $v))) + + (func (export "andi32@small") (param $p i64) (param $v i32) (result i32) + (i32.atomic.rmw.and offset=${SMALL} (local.get $p) (local.get $v))) + + (func (export "andi32@big") (param $p i64) (param $v i32) (result i32) + (i32.atomic.rmw.and offset=${BIG} (local.get $p) (local.get $v))) + + (func (export "andi32@huge") (param $p i64) (param $v i32) (result i32) + (i32.atomic.rmw.and offset=${HUGE} (local.get $p) (local.get $v))) + + (func (export "andi32/const@0") (param $v i32) (result i32) + (i32.atomic.rmw.and (i64.const ${LOC}) (local.get $v))) + + (func (export "andi32/const@small") (param $v i32) (result i32) + (i32.atomic.rmw.and offset=${SMALL} (i64.const ${LOC}) (local.get $v))) + + (func (export "andi32/const@big") (param $v i32) (result i32) + (i32.atomic.rmw.and offset=${BIG} (i64.const ${LOC}) (local.get $v))) + + ;; and i64 + + (func (export "andi64@0") (param $p i64) (param $v i64) (result i64) + (i64.atomic.rmw.and (local.get $p) (local.get $v))) + + (func (export "andi64@small") (param $p i64) (param $v i64) (result i64) + (i64.atomic.rmw.and offset=${SMALL} (local.get $p) (local.get $v))) + + (func (export "andi64@big") (param $p i64) (param $v i64) (result i64) + (i64.atomic.rmw.and offset=${BIG} (local.get $p) (local.get $v))) + + (func (export "andi64@huge") (param $p i64) (param $v i64) (result i64) + (i64.atomic.rmw.and offset=${HUGE} (local.get $p) (local.get $v))) + + (func (export "andi64/const@0") (param $v i64) (result i64) + (i64.atomic.rmw.and (i64.const ${LOC}) (local.get $v))) + + (func (export "andi64/const@small") (param $v i64) (result i64) + (i64.atomic.rmw.and offset=${SMALL} (i64.const ${LOC}) (local.get $v))) + + (func (export "andi64/const@big") (param $v i64) (result i64) + (i64.atomic.rmw.and offset=${BIG} (i64.const ${LOC}) (local.get $v))) + + ;; or i32 + + (func (export "ori32@0") (param $p i64) (param $v i32) (result i32) + (i32.atomic.rmw.or (local.get $p) (local.get $v))) + + (func (export "ori32@small") (param $p i64) (param $v i32) (result i32) + (i32.atomic.rmw.or offset=${SMALL} (local.get $p) (local.get $v))) + + (func (export "ori32@big") (param $p i64) (param $v i32) (result i32) + (i32.atomic.rmw.or offset=${BIG} (local.get $p) (local.get $v))) + + (func (export "ori32@huge") (param $p i64) (param $v i32) (result i32) + (i32.atomic.rmw.or offset=${HUGE} (local.get $p) (local.get $v))) + + (func (export "ori32/const@0") (param $v i32) (result i32) + (i32.atomic.rmw.or (i64.const ${LOC}) (local.get $v))) + + (func (export "ori32/const@small") (param $v i32) (result i32) + (i32.atomic.rmw.or offset=${SMALL} (i64.const ${LOC}) (local.get $v))) + + (func (export "ori32/const@big") (param $v i32) (result i32) + (i32.atomic.rmw.or offset=${BIG} (i64.const ${LOC}) (local.get $v))) + + ;; or i64 + + (func (export "ori64@0") (param $p i64) (param $v i64) (result i64) + (i64.atomic.rmw.or (local.get $p) (local.get $v))) + + (func (export "ori64@small") (param $p i64) (param $v i64) (result i64) + (i64.atomic.rmw.or offset=${SMALL} (local.get $p) (local.get $v))) + + (func (export "ori64@big") (param $p i64) (param $v i64) (result i64) + (i64.atomic.rmw.or offset=${BIG} (local.get $p) (local.get $v))) + + (func (export "ori64@huge") (param $p i64) (param $v i64) (result i64) + (i64.atomic.rmw.or offset=${HUGE} (local.get $p) (local.get $v))) + + (func (export "ori64/const@0") (param $v i64) (result i64) + (i64.atomic.rmw.or (i64.const ${LOC}) (local.get $v))) + + (func (export "ori64/const@small") (param $v i64) (result i64) + (i64.atomic.rmw.or offset=${SMALL} (i64.const ${LOC}) (local.get $v))) + + (func (export "ori64/const@big") (param $v i64) (result i64) + (i64.atomic.rmw.or offset=${BIG} (i64.const ${LOC}) (local.get $v))) + + ;; xor i32 + + (func (export "xori32@0") (param $p i64) (param $v i32) (result i32) + (i32.atomic.rmw.xor (local.get $p) (local.get $v))) + + (func (export "xori32@small") (param $p i64) (param $v i32) (result i32) + (i32.atomic.rmw.xor offset=${SMALL} (local.get $p) (local.get $v))) + + (func (export "xori32@big") (param $p i64) (param $v i32) (result i32) + (i32.atomic.rmw.xor offset=${BIG} (local.get $p) (local.get $v))) + + (func (export "xori32@huge") (param $p i64) (param $v i32) (result i32) + (i32.atomic.rmw.xor offset=${HUGE} (local.get $p) (local.get $v))) + + (func (export "xori32/const@0") (param $v i32) (result i32) + (i32.atomic.rmw.xor (i64.const ${LOC}) (local.get $v))) + + (func (export "xori32/const@small") (param $v i32) (result i32) + (i32.atomic.rmw.xor offset=${SMALL} (i64.const ${LOC}) (local.get $v))) + + (func (export "xori32/const@big") (param $v i32) (result i32) + (i32.atomic.rmw.xor offset=${BIG} (i64.const ${LOC}) (local.get $v))) + + ;; xor i64 + + (func (export "xori64@0") (param $p i64) (param $v i64) (result i64) + (i64.atomic.rmw.xor (local.get $p) (local.get $v))) + + (func (export "xori64@small") (param $p i64) (param $v i64) (result i64) + (i64.atomic.rmw.xor offset=${SMALL} (local.get $p) (local.get $v))) + + (func (export "xori64@big") (param $p i64) (param $v i64) (result i64) + (i64.atomic.rmw.xor offset=${BIG} (local.get $p) (local.get $v))) + + (func (export "xori64@huge") (param $p i64) (param $v i64) (result i64) + (i64.atomic.rmw.xor offset=${HUGE} (local.get $p) (local.get $v))) + + (func (export "xori64/const@0") (param $v i64) (result i64) + (i64.atomic.rmw.xor (i64.const ${LOC}) (local.get $v))) + + (func (export "xori64/const@small") (param $v i64) (result i64) + (i64.atomic.rmw.xor offset=${SMALL} (i64.const ${LOC}) (local.get $v))) + + (func (export "xori64/const@big") (param $v i64) (result i64) + (i64.atomic.rmw.xor offset=${BIG} (i64.const ${LOC}) (local.get $v))) + + ;; cmpxchg i32 + + (func (export "cmpxchgi32@0") (param $p i64) (param $expect i32) (param $new i32) (result i32) + (i32.atomic.rmw.cmpxchg (local.get $p) (local.get $expect) (local.get $new))) + + (func (export "cmpxchgi32@small") (param $p i64) (param $expect i32) (param $new i32) (result i32) + (i32.atomic.rmw.cmpxchg offset=${SMALL} (local.get $p) (local.get $expect) (local.get $new))) + + (func (export "cmpxchgi32@big") (param $p i64) (param $expect i32) (param $new i32) (result i32) + (i32.atomic.rmw.cmpxchg offset=${BIG} (local.get $p) (local.get $expect) (local.get $new))) + + (func (export "cmpxchgi32@huge") (param $p i64) (param $expect i32) (param $new i32) (result i32) + (i32.atomic.rmw.cmpxchg offset=${HUGE} (local.get $p) (local.get $expect) (local.get $new))) + + (func (export "cmpxchgi32/const@0") (param $expect i32) (param $new i32) (result i32) + (i32.atomic.rmw.cmpxchg (i64.const ${LOC}) (local.get $expect) (local.get $new))) + + (func (export "cmpxchgi32/const@small") (param $expect i32) (param $new i32) (result i32) + (i32.atomic.rmw.cmpxchg offset=${SMALL} (i64.const ${LOC}) (local.get $expect) (local.get $new))) + + (func (export "cmpxchgi32/const@big") (param $expect i32) (param $new i32) (result i32) + (i32.atomic.rmw.cmpxchg offset=${BIG} (i64.const ${LOC}) (local.get $expect) (local.get $new))) + + ;; cmpxchg i64 + + (func (export "cmpxchgi64@0") (param $p i64) (param $expect i64) (param $new i64) (result i64) + (i64.atomic.rmw.cmpxchg (local.get $p) (local.get $expect) (local.get $new))) + + (func (export "cmpxchgi64@small") (param $p i64) (param $expect i64) (param $new i64) (result i64) + (i64.atomic.rmw.cmpxchg offset=${SMALL} (local.get $p) (local.get $expect) (local.get $new))) + + (func (export "cmpxchgi64@big") (param $p i64) (param $expect i64) (param $new i64) (result i64) + (i64.atomic.rmw.cmpxchg offset=${BIG} (local.get $p) (local.get $expect) (local.get $new))) + + (func (export "cmpxchgi64@huge") (param $p i64) (param $expect i64) (param $new i64) (result i64) + (i64.atomic.rmw.cmpxchg offset=${HUGE} (local.get $p) (local.get $expect) (local.get $new))) + + (func (export "cmpxchgi64/const@0") (param $expect i64) (param $new i64) (result i64) + (i64.atomic.rmw.cmpxchg (i64.const ${LOC}) (local.get $expect) (local.get $new))) + + (func (export "cmpxchgi64/const@small") (param $expect i64) (param $new i64) (result i64) + (i64.atomic.rmw.cmpxchg offset=${SMALL} (i64.const ${LOC}) (local.get $expect) (local.get $new))) + + (func (export "cmpxchgi64/const@big") (param $expect i64) (param $new i64) (result i64) + (i64.atomic.rmw.cmpxchg offset=${BIG} (i64.const ${LOC}) (local.get $expect) (local.get $new))) + + ;; wait + + (func (export "waiti32@small") (param $p i64) (result i32) + (memory.atomic.wait32 offset=${SMALL} (local.get $p) (i32.const 1) (i64.const 0))) + + (func (export "waiti32@huge") (param $p i64) (result i32) + (memory.atomic.wait32 offset=${HUGE} (local.get $p) (i32.const 1) (i64.const 0))) + + (func (export "waiti64@small") (param $p i64) (result i32) + (memory.atomic.wait64 offset=${SMALL} (local.get $p) (i64.const 1) (i64.const 0))) + + (func (export "waiti64@huge") (param $p i64) (result i32) + (memory.atomic.wait64 offset=${HUGE} (local.get $p) (i64.const 1) (i64.const 0))) + + ;; wake + + (func (export "wake@0") (param $p i64) (result i32) + (memory.atomic.notify (local.get $p) (i32.const 1))) + + (func (export "wake@small") (param $p i64) (result i32) + (memory.atomic.notify offset=${SMALL} (local.get $p) (i32.const 1))) + + (func (export "wake@big") (param $p i64) (result i32) + (memory.atomic.notify offset=${BIG} (local.get $p) (i32.const 1))) + + (func (export "wake@huge") (param $p i64) (result i32) + (memory.atomic.notify offset=${HUGE} (local.get $p) (i32.const 1))) +) +`); + return ins; +} + +function i32Random() { + // Limit this to small positive numbers to keep life simple. + for (;;) { + let r = (Math.random() * 0x3FFF_FFFF) | 0; + if (r != 0) + return r; + } +} + +function i64Random() { + return (BigInt(i32Random()) << 32n) | BigInt(i32Random()); +} + +function Random(sz) { + if (sz == 4) + return i32Random(); + return i64Random(); +} + +function Random2(sz) { + return [Random(sz), Random(sz)]; +} + +function Random4(sz) { + return [Random(sz), Random(sz), Random(sz), Random(sz)]; +} + +function Zero(sz) { + if (sz == 4) + return 0; + return 0n; +} + +function testRead(ins, mem, LOC, prefix) { + let r = 0; + let SZ = mem.BYTES_PER_ELEMENT; + let len = mem.length * SZ; + let NM = prefix + "readi" + (SZ * 8); + + // Read in-bounds + + r = Random(SZ); + mem[LOC / SZ] = r; + assertEq(ins.exports[NM + "@0"](BigInt(LOC)), r); + assertEq(ins.exports[NM + "/const@0"](), r); + + mem[(len / SZ) - 1] = Zero(SZ); + assertEq(ins.exports[NM + "@0"](BigInt(len - SZ)), Zero(SZ)); // Just barely in-bounds + + r = Random(SZ); + mem[(LOC + SMALL) / SZ] = r; + assertEq(ins.exports[NM + "@small"](BigInt(LOC)), r); + assertEq(ins.exports[NM + "/const@small"](), r); + + if (len >= LOC + BIG + SZ) { + r = Random(SZ); + mem[(LOC + BIG) / SZ] = r; + assertEq(ins.exports[NM + "@big"](BigInt(LOC)), r); + assertEq(ins.exports[NM + "/const@big"](), r); + } + + // Read out-of-bounds + + assertErrorMessage(() => ins.exports[NM + "@0"](BigInt(len)), + WebAssembly.RuntimeError, + /out of bounds/); + + assertErrorMessage(() => ins.exports[NM + "@0"](BigInt(len-(SZ-1))), + WebAssembly.RuntimeError, + prefix == "" ? /out of bounds/ : /unaligned memory access/); + + // This is OOB if we consider the whole pointer as we must, but if we + // mistakenly only look at the low bits then it's in-bounds. + if (len < 0x1_0000_0000) { + assertErrorMessage(() => ins.exports[NM + "@0"](0x1_0000_0000n), + WebAssembly.RuntimeError, + /out of bounds/); + } + + if (len < HUGE) { + assertErrorMessage(() => ins.exports[NM + "@huge"](0n), + WebAssembly.RuntimeError, + /out of bounds/); + } +} + +function testReadV128(ins, mem, LOC) { + let r = 0; + let SZ = mem.BYTES_PER_ELEMENT; + let len = mem.length * SZ; + let NM = "readv128"; + + assertEq(SZ, 4); + + // Read in-bounds + + r = Random4(4); + mem.set(r, LOC / SZ); + ins.exports[NM + "@0"](BigInt(LOC)) + assertSame(mem.slice(0, 4), r); + ins.exports[NM + "/const@0"](); + assertSame(mem.slice(0, 4), r); + + r = new Int32Array([0,0,0,0]); + mem.set(r, (len / SZ) - 4); + ins.exports[NM + "@0"](BigInt(len - (SZ * 4))); // Just barely in-bounds + assertSame(mem.slice(0, 4), r); + + r = Random4(SZ); + mem.set(r, (LOC + SMALL) / SZ); + ins.exports[NM + "@small"](BigInt(LOC)) + assertSame(mem.slice(0, 4), r); + ins.exports[NM + "/const@small"](); + assertSame(mem.slice(0, 4), r); + + if (len >= LOC + BIG + SZ * 4) { + r = Random4(SZ); + mem.set(r, (LOC + BIG) / SZ); + ins.exports[NM + "@big"](BigInt(LOC)); + assertSame(mem.slice(0, 4), r); + ins.exports[NM + "/const@big"](); + assertSame(mem.slice(0, 4), r); + } + + // Read out-of-bounds + + assertErrorMessage(() => ins.exports[NM + "@0"](BigInt(len)), + WebAssembly.RuntimeError, + /out of bounds/); + + assertErrorMessage(() => ins.exports[NM + "@0"](BigInt(len-((SZ*4)-1))), + WebAssembly.RuntimeError, + /out of bounds/); + + // This is OOB if we consider the whole pointer as we must, but if we + // mistakenly only look at the low bits then it's in-bounds. + if (len < 0x1_0000_0000) { + assertErrorMessage(() => ins.exports[NM + "@0"](0x1_0000_0000n), + WebAssembly.RuntimeError, + /out of bounds/); + } + + if (len < HUGE) { + assertErrorMessage(() => ins.exports[NM + "@huge"](0n), + WebAssembly.RuntimeError, + /out of bounds/); + } + + // Superficial testing of other load operations + + r = i32Random() + mem[(LOC + SMALL) / SZ] = r; + ins.exports["v128.load_splat@small"](BigInt(LOC)); + assertSame(mem.slice(0, 4), [r, r, r, r]); + + r = i32Random() + mem[(LOC + SMALL) / SZ] = r; + ins.exports["v128.load_zero@small"](BigInt(LOC)); + assertSame(mem.slice(0, 4), [r, 0, 0, 0]); + + r = Random2(SZ) + mem.set(r, (LOC + SMALL) / SZ); + ins.exports["v128.load_extend@small"](BigInt(LOC)); + assertSame(mem.slice(0, 4), [r[0], 0, r[1], 0]); + + r = Random4(SZ) + mem.set(r, 0); + let s = i32Random() + mem[(LOC + SMALL) / SZ] = s; + ins.exports["v128.load_lane@small"](BigInt(LOC)); + assertSame(mem.slice(0, 4), [r[0], r[1], s, r[3]]); +} + +function testWrite(ins, mem, LOC, prefix) { + let r = 0; + let SZ = mem.BYTES_PER_ELEMENT; + let len = mem.length * SZ; + let WNM = prefix + "writei" + (SZ * 8); + let RNM = prefix + "readi" + (SZ * 8); + + // Write in-bounds + + r = Random(SZ); + ins.exports[WNM + "@0"](BigInt(LOC), r); + assertEq(ins.exports[RNM + "@0"](BigInt(LOC)), r); + + r = Random(SZ); + ins.exports[WNM + "@small"](BigInt(LOC), r); + assertEq(ins.exports[RNM + "@small"](BigInt(LOC)), r); + + if (len >= LOC + BIG + SZ) { + r = Random(SZ); + ins.exports[WNM + "@big"](BigInt(LOC), r); + assertEq(ins.exports[RNM + "@big"](BigInt(LOC)), r); + } + + r = Random(SZ); + ins.exports[WNM + "@0"](BigInt(len - SZ), r); // Just barely in-bounds + assertEq(ins.exports[RNM + "@0"](BigInt(len - SZ)), r); + + // Write out-of-bounds + + assertErrorMessage(() => ins.exports[WNM + "@0"](BigInt(len), Random(SZ)), + WebAssembly.RuntimeError, + /out of bounds/); + + assertErrorMessage(() => ins.exports[WNM + "@0"](BigInt(len - (SZ - 1)), Random(SZ)), + WebAssembly.RuntimeError, + prefix == "" ? /out of bounds/ : /unaligned memory access/); + + if (len < 0x1_0000_0000) { + let xs = ins.exports[RNM + "@0"](0n); + assertErrorMessage(() => ins.exports[WNM + "@0"](0x1_0000_0000n, Random(SZ)), + WebAssembly.RuntimeError, + /out of bounds/); + // Check for scribbling + assertEq(ins.exports[RNM + "@0"](0n), xs); + } + + if (len < HUGE) { + assertErrorMessage(() => ins.exports[WNM + "@huge"](0n, Random(SZ)), + WebAssembly.RuntimeError, + /out of bounds/); + } +} + +function testWriteV128(ins, mem, LOC) { + let r = 0; + let p = 0; + let SZ = mem.BYTES_PER_ELEMENT; + let len = mem.length * SZ; + let WNM = "writev128"; + let RNM = "readv128"; + + assertEq(SZ, 4); + + // Write in-bounds + + r = Random4(SZ); + mem.set(r, 0); + p = LOC / SZ; + ins.exports[WNM + "@0"](BigInt(LOC)); + assertSame(mem.slice(p, p + 4), r); + + r = Random4(SZ); + mem.set(r, 0); + p = (LOC + SMALL) / SZ; + ins.exports[WNM + "@small"](BigInt(LOC)); + assertSame(mem.slice(p, p + 4), r); + + if (len >= LOC + BIG + SZ) { + r = Random4(SZ); + mem.set(r, 0); + p = (LOC + BIG) / SZ; + ins.exports[WNM + "@big"](BigInt(LOC)); + assertSame(mem.slice(p, p + 4), r); + } + + r = Random4(SZ); + mem.set(r, 0); + p = (len - (SZ * 4)) / SZ; + ins.exports[WNM + "@0"](BigInt(len - (SZ * 4))); // Just barely in-bounds + assertSame(mem.slice(p, p + 4), r); + + // Write out-of-bounds + + assertErrorMessage(() => ins.exports[WNM + "@0"](BigInt(len)), + WebAssembly.RuntimeError, + /out of bounds/); + + assertErrorMessage(() => ins.exports[WNM + "@0"](BigInt(len - ((SZ * 4) - 1))), + WebAssembly.RuntimeError, + /out of bounds/); + + if (len < HUGE) { + assertErrorMessage(() => ins.exports[WNM + "@huge"](0n), + WebAssembly.RuntimeError, + /out of bounds/); + } + + // Superficial testing of other store operations + + r = Random4(SZ); + mem.set(r, 0); + ins.exports["v128.store_lane@small"](BigInt(LOC)); + assertEq(mem[(LOC + SMALL) / SZ], r[2]); +} + +function testAtomicRMW(ins, mem, LOC, op, fn) { + let r = 0, s = 0; + let SZ = mem.BYTES_PER_ELEMENT; + let len = mem.length * SZ; + let NM = op + "i" + (SZ * 8); + + [r,s] = Random2(SZ); + mem[LOC / SZ] = r; + assertEq(ins.exports[NM + "@0"](BigInt(LOC), s), r); + assertEq(mem[LOC / SZ], fn(r, s)); + + [r,s] = Random2(SZ); + mem[(LOC + SMALL) / SZ] = r; + assertEq(ins.exports[NM + "@small"](BigInt(LOC), s), r); + assertEq(mem[(LOC + SMALL) / SZ], fn(r, s)); + + if (len >= LOC + BIG + SZ) { + [r,s] = Random2(SZ); + mem[(LOC + BIG) / SZ] = r; + assertEq(ins.exports[NM + "@big"](BigInt(LOC), s), r); + assertEq(mem[(LOC + BIG) / SZ], fn(r, s)); + } + + + assertErrorMessage(() => ins.exports[NM + "@0"](BigInt(len), Zero(SZ)), + WebAssembly.RuntimeError, + /out of bounds/); + + assertErrorMessage(() => ins.exports[NM + "@0"](BigInt(len - (SZ - 1)), Zero(SZ)), + WebAssembly.RuntimeError, + /unaligned memory access/); + + if (len < HUGE) { + assertErrorMessage(() => ins.exports[NM + "@huge"](0n, Zero(SZ)), + WebAssembly.RuntimeError, + /out of bounds/); + } +} + +function testAtomicCmpxchg(ins, mem, LOC) { + let r = 0, s = 0; + let SZ = mem.BYTES_PER_ELEMENT; + let len = mem.length * SZ; + let NM = "cmpxchgi" + (SZ * 8); + + [r,s] = Random2(SZ); + mem[LOC / SZ] = r; + assertEq(ins.exports[NM + "@0"](BigInt(LOC), Zero(SZ), s), r); + assertEq(ins.exports[NM + "@0"](BigInt(LOC), r, s), r); + assertEq(mem[LOC / SZ], s); + + [r,s] = Random2(SZ); + mem[(LOC + SMALL) / SZ] = r; + assertEq(ins.exports[NM + "@0"](BigInt(LOC + SMALL), Zero(SZ), s), r); + assertEq(ins.exports[NM + "@0"](BigInt(LOC + SMALL), r, s), r); + assertEq(mem[(LOC + SMALL) / SZ], s); + + if (len >= LOC + BIG + SZ) { + [r,s] = Random2(SZ); + mem[(LOC + BIG) / SZ] = r; + assertEq(ins.exports[NM + "@0"](BigInt(LOC + BIG), Zero(SZ), s), r); + assertEq(ins.exports[NM + "@0"](BigInt(LOC + BIG), r, s), r); + assertEq(mem[(LOC + BIG) / SZ], s); + } + + assertErrorMessage(() => ins.exports[NM + "@0"](BigInt(len), Zero(SZ), Zero(SZ)), + WebAssembly.RuntimeError, + /out of bounds/); + + assertErrorMessage(() => ins.exports[NM + "@0"](BigInt(len - (SZ - 1)), Zero(SZ), Zero(SZ)), + WebAssembly.RuntimeError, + /unaligned memory access/); + + if (len < HUGE) { + assertErrorMessage(() => ins.exports[NM + "@huge"](0n, Zero(SZ), Zero(SZ)), + WebAssembly.RuntimeError, + /out of bounds/); + } +} + +function testAtomicWake(ins, mem, LOC) { + let SZ = mem.BYTES_PER_ELEMENT; + let len = mem.length * SZ; + + assertEq(ins.exports["wake@0"](BigInt(LOC)), 0); + assertEq(ins.exports["wake@small"](BigInt(LOC)), 0); + if (len >= LOC + BIG + SZ) { + assertEq(ins.exports["wake@big"](BigInt(LOC)), 0); + } + + assertErrorMessage(() => ins.exports["wake@0"](BigInt(len)), + WebAssembly.RuntimeError, + /out of bounds/); + + assertErrorMessage(() => ins.exports["wake@0"](BigInt(len - (SZ - 1))), + WebAssembly.RuntimeError, + /unaligned memory access/); + + if (len < HUGE) { + assertErrorMessage(() => ins.exports["wake@huge"](BigInt(LOC)), + WebAssembly.RuntimeError, + /out of bounds/); + } +} + +// Sometimes we start memory at zero to disable certain bounds checking +// optimizations. Other times we start memory at something beyond most of +// our references to enable those optimizations. +let configs = [[40, 0, 3, ''], [40, 3, '', '']]; + +// On 64-bit systems, test beyond 2GB and also beyond 4GB +if (getBuildConfiguration()["pointer-byte-size"] == 8) { + configs.push([Math.pow(2, 31) + 40, 32771, '', '']); + configs.push([Math.pow(2, 32) + 40, 65539, '', '']); +} + +for (let [LOC, start, max, shared] of configs) { + const ins = makeTest(LOC, start, max, shared); + if (start == 0) { + assertEq(ins.exports.mem.grow(max), 0); + } + + const mem32 = new Int32Array(ins.exports.mem.buffer); + const mem64 = new BigInt64Array(ins.exports.mem.buffer); + + for ( let m of [mem32, mem64] ) { + testRead(ins, m, LOC, ""); + testWrite(ins, m, LOC, ""); + testRead(ins, m, LOC, "a"); + testWrite(ins, m, LOC, "a"); + testAtomicRMW(ins, m, LOC, "add", (r,s) => r+s); + testAtomicRMW(ins, m, LOC, "sub", (r,s) => r-s); + testAtomicRMW(ins, m, LOC, "and", (r,s) => r&s); + testAtomicRMW(ins, m, LOC, "or", (r,s) => r|s); + testAtomicRMW(ins, m, LOC, "xor", (r,s) => r^s); + testAtomicRMW(ins, m, LOC, "xchg", (r,s) => s); + testAtomicCmpxchg(ins, m, LOC); + testAtomicWake(ins, m, LOC); + } + + if (wasmSimdEnabled()) { + testReadV128(ins, mem32, LOC); + testWriteV128(ins, mem32, LOC); + } +} + +// Bulk memory operations + +function makeModule(initial, maximum, shared) { + return ` +(module + (memory (export "mem") i64 ${initial} ${maximum} ${shared}) + + (data $seg passive "0123456789") + + (func (export "size") (result i64) + memory.size) + + (func (export "grow") (param $delta i64) (result i64) + (memory.grow (local.get $delta))) + + (func (export "copy") (param $to i64) (param $from i64) (param $len i64) + (memory.copy (local.get $to) (local.get $from) (local.get $len))) + + (func (export "fill") (param $to i64) (param $val i32) (param $len i64) + (memory.fill (local.get $to) (local.get $val) (local.get $len))) + + (func (export "init") (param $to i64) (param $src i32) (param $count i32) + (memory.init $seg (local.get $to) (local.get $src) (local.get $count))) +)`; +} + +for ( let shared of ['','shared'] ) { + let ins = wasmEvalText(makeModule(1, 3, shared)); + + assertEq(ins.exports.size(), 1n); + + assertEq(ins.exports.grow(2n), 1n); + assertEq(ins.exports.size(), 3n); + + assertEq(ins.exports.grow(1n), -1n); + assertEq(ins.exports.size(), 3n); + + assertEq(ins.exports.grow(100000n), -1n); + assertEq(ins.exports.size(), 3n); + + assertEq(ins.exports.grow(0x1_0000_0000_0000n), -1n); // More than 2^48 pages + assertEq(ins.exports.size(), 3n); + + assertEq(ins.exports.grow(-1n), -1n); // More than 2^48 pages - interpreted as unsigned + assertEq(ins.exports.size(), 3n); + + var mem = new Uint8Array(ins.exports.mem.buffer); + var val = [1,2,3,4,5]; + mem.set(val, 20); + ins.exports.copy(40n, 20n, 5n); + assertSame(val, mem.slice(40, 45)); + + ins.exports.fill(39n, 37, 8n); + assertSame(iota(8).map(_ => 37), mem.slice(39, 47)); + + ins.exports.init(128n, 1, 5); + assertSame(iota(5).map(x => x+49), mem.slice(128, 133)); +} + +if (getBuildConfiguration()["pointer-byte-size"] == 8) { + for ( let shared of ['','shared'] ) { + let limit = wasmMaxMemoryPages('i64'); + let initial = 65537; + let maximum = limit + 1; + let pagesize = 65536n; + + let ins = wasmEvalText(makeModule(initial, maximum, shared)); + + assertEq(ins.exports.size(), BigInt(initial)); + + assertEq(ins.exports.grow(2n), BigInt(initial)); + assertEq(ins.exports.size(), BigInt(initial + 2)); + + assertEq(ins.exports.grow(BigInt(limit - (initial + 2) + 1)), -1n); + assertEq(ins.exports.size(), BigInt(initial + 2)); + + assertEq(ins.exports.grow(BigInt(limit - (initial + 2))), BigInt(initial + 2)); + assertEq(ins.exports.size(), BigInt(limit)); + + let mem = new Uint8Array(ins.exports.mem.buffer); + + let copyval = [1,2,3,4,5]; + let source = 20n; + let target = BigInt(initial) * pagesize + 40n; + let oobTarget = BigInt(limit) * pagesize - 1n; + + // Copy from memory below 4GB to memory beyond 4GB + mem.set(copyval, Number(source)); + ins.exports.copy(target, source, BigInt(copyval.length)); + assertSame(copyval, mem.slice(Number(target), Number(target) + copyval.length)) + + // Try to copy out of bounds + // src and target are both in bounds but len brings it oob + assertErrorMessage(() => ins.exports.copy(oobTarget, source, BigInt(copyval.length)), + WebAssembly.RuntimeError, + /out of bounds/); + assertEq(mem[Number(oobTarget-1n)], 0); + assertErrorMessage(() => ins.exports.copy(-1n, source, BigInt(copyval.length)), + WebAssembly.RuntimeError, + /out of bounds/); + assertEq(mem[Number(oobTarget-1n)], 0); + + // Fill above 4GB + ins.exports.fill(target, 37, 30n); + assertSame(iota(30).map(_ => 37), mem.slice(Number(target), Number(target) + 30)); + + // Try to fill out of bounds + assertErrorMessage(() => ins.exports.fill(oobTarget, 37, 2n), + WebAssembly.RuntimeError, + /out of bounds/); + assertEq(mem[Number(oobTarget-1n)], 0); + + // Init above 4GB + ins.exports.init(target, 1, 5); + assertSame(iota(5).map(x => x+49), mem.slice(Number(target), Number(target)+5)); + + // Try to init out of bounds + assertErrorMessage(() => ins.exports.init(oobTarget, 1, 5), + WebAssembly.RuntimeError, + /out of bounds/); + assertEq(mem[Number(oobTarget-1n)], 0); + } +} + +////////////////////////////////////////////////////////////////////////////// + +function assertSame(got, expected) { + assertEq(got.length, expected.length); + for ( let i=0; i < got.length; i++ ) { + let g = got[i]; + let e = expected[i]; + if (typeof g != typeof e) { + if (typeof g == "bigint") + e = BigInt(e); + else if (typeof e == "bigint") + g = BigInt(g); + } + assertEq(g, e); + } +} + +function iota(len) { + let xs = []; + for ( let i=0 ; i < len ; i++ ) + xs.push(i); + return xs; +} diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit-test/tests/wasm/memory-cloning.js firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit-test/tests/wasm/memory-cloning.js --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit-test/tests/wasm/memory-cloning.js 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit-test/tests/wasm/memory-cloning.js 2021-10-20 19:28:23.000000000 +0000 @@ -1,12 +1,25 @@ -// |jit-test| skip-if: !wasmThreadsEnabled() +// |jit-test| --wasm-memory64; skip-if: !wasmThreadsEnabled() // Basic structured cloning tests (specific to SpiderMonkey shell) +var memtypes = wasmMemory64Enabled() ? ['i32', 'i64'] : ['']; + +function makeMemoryDesc(memtype, d) { + if (memtype != '') { + d.index = memtype; + } + return d; +} + +function Zero(memtype) { + return memtype == 'i64' ? 0n : 0; +} + // Should *not* be possible to serialize and deserialize memories that are not // shared, whether we transfer them or not. -{ - let mem1 = new WebAssembly.Memory({initial: 2, maximum: 4}); +for ( let memtype of memtypes ) { + let mem1 = new WebAssembly.Memory(makeMemoryDesc(memtype, {initial: 2, maximum: 4})); assertErrorMessage(() => serialize(mem1), TypeError, /unsupported type for structured data/); @@ -18,8 +31,9 @@ // Should be possible to serialize and deserialize memories that are shared, and // observe shared effects. -{ - let mem1 = new WebAssembly.Memory({initial: 2, maximum: 4, shared: true}); +for ( let memtype of memtypes ) { + let ptrtype = memtype == 'i64' ? memtype : 'i32'; + let mem1 = new WebAssembly.Memory(makeMemoryDesc(memtype, {initial: 2, maximum: 4, shared: true})); let buf1 = mem1.buffer; // Serialization and deserialization of shared memories work: @@ -31,6 +45,9 @@ assertEq(buf1 !== buf2, true); assertEq(buf1.byteLength, buf2.byteLength); + if (memtype != '' && mem2.type) { + assertEq(mem2.type().index, memtype); + } // Effects to one buffer must be reflected in the other: @@ -44,9 +61,9 @@ let index = 2*65536 + 200; let access = wasmEvalText(`(module - (memory (import "" "memory") 2 4 shared) - (func (export "l") (result i32) - (i32.load (i32.const ${index}))))`, + (memory (import "" "memory") ${memtype} 2 4 shared) + (func (export "l") (result ${ptrtype}) + (${ptrtype}.load (${ptrtype}.const ${index}))))`, {"": {memory: mem2}}).exports.l; // initially mem2 cannot be accessed at index @@ -54,18 +71,18 @@ // then we grow mem1 wasmEvalText(`(module - (memory (import "" "memory") 2 4 shared) - (func (export "g") (drop (memory.grow (i32.const 1)))))`, + (memory (import "" "memory") ${memtype} 2 4 shared) + (func (export "g") (drop (memory.grow (${ptrtype}.const 1)))))`, {"": {memory: mem1}}).exports.g(); // after growing mem1, mem2 can be accessed at index - assertEq(access(), 0); + assertEq(access(), Zero(memtype)); } // Should not be possible to transfer a shared memory -{ - let mem1 = new WebAssembly.Memory({initial: 2, maximum: 4, shared: true}); +for ( let memtype of memtypes ) { + let mem1 = new WebAssembly.Memory(makeMemoryDesc(memtype, {initial: 2, maximum: 4, shared: true})); assertErrorMessage(() => serialize(mem1, [mem1]), TypeError, /Shared memory objects must not be in the transfer list/); @@ -76,8 +93,8 @@ // of the SAB should not change even if the memory was grown after serialization // and before deserialization. -{ - let mem = new WebAssembly.Memory({initial: 2, maximum: 4, shared: true}); +for ( let memtype of memtypes ) { + let mem = new WebAssembly.Memory(makeMemoryDesc(memtype, {initial: 2, maximum: 4, shared: true})); let buf = mem.buffer; let clonedbuf = serialize(buf, [], {SharedArrayBuffer: 'allow'}); mem.grow(1); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit-test/tests/wasm/simd/experimental.js firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit-test/tests/wasm/simd/experimental.js --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jit-test/tests/wasm/simd/experimental.js 2021-10-17 14:42:39.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jit-test/tests/wasm/simd/experimental.js 2021-10-20 19:28:24.000000000 +0000 @@ -254,10 +254,15 @@ ins.exports.from32s(); var result = get(new Int32Array(mem), 0, 4); assertSame(result, [0, 2, -3, 100000]); + set(new Float32Array(mem), 4, [0, 3.3, 0x80000000, 200000]); ins.exports.from32u(); var result = get(new Uint32Array(mem), 0, 4); assertSame(result, [0, 3, 0x80000000, 200000]); +set(new Float32Array(mem), 4, [0, 0x80000100, 0x80000101, 0xFFFFFF00]); +ins.exports.from32u(); +var result = get(new Uint32Array(mem), 0, 4); +assertSame(result, [0, 0x80000100, 0x80000100, 0xFFFFFF00]); set(new Float64Array(mem), 2, [200000.3, -3.4]); ins.exports.from64s(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jsapi.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/jsapi.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jsapi.cpp 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jsapi.cpp 2021-10-20 19:28:24.000000000 +0000 @@ -2306,8 +2306,8 @@ introductionLineno = rhs.introductionLineno; introductionOffset = rhs.introductionOffset; hasIntroductionInfo = rhs.hasIntroductionInfo; - hideScriptFromDebugger = rhs.hideScriptFromDebugger; - deferDebugMetadata = rhs.deferDebugMetadata; + hideScriptFromDebugger_ = rhs.hideScriptFromDebugger_; + deferDebugMetadata_ = rhs.deferDebugMetadata_; nonSyntacticScope = rhs.nonSyntacticScope; privateClassFields = rhs.privateClassFields; privateClassMethods = rhs.privateClassMethods; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jsapi-tests/testGCWeakCache.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/jsapi-tests/testGCWeakCache.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jsapi-tests/testGCWeakCache.cpp 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jsapi-tests/testGCWeakCache.cpp 2021-10-20 19:28:23.000000000 +0000 @@ -136,8 +136,9 @@ HeapPtr obj; explicit ObjectEntry(JSObject* o) : obj(o) {} bool operator==(const ObjectEntry& other) const { return obj == other.obj; } - bool needsSweep() { - return JS::GCPolicy>::needsSweep(&obj); + bool needsSweep() { return IsAboutToBeFinalized(&obj); } + bool traceWeak(JSTracer* trc) { + return TraceWeakEdge(trc, &obj, "ObjectEntry::obj"); } }; @@ -173,8 +174,9 @@ bool operator==(const NumberAndObjectEntry& other) const { return number == other.number && obj == other.obj; } - bool needsSweep() { - return JS::GCPolicy>::needsSweep(&obj); + bool needsSweep() { return IsAboutToBeFinalized(&obj); } + bool traceWeak(JSTracer* trc) { + return TraceWeakEdge(trc, &obj, "NumberAndObjectEntry::obj"); } }; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/jsapi-tests/testStencil.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/jsapi-tests/testStencil.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/jsapi-tests/testStencil.cpp 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/jsapi-tests/testStencil.cpp 2021-10-20 19:28:24.000000000 +0000 @@ -48,8 +48,9 @@ JS::CompileGlobalScriptToStencil(cx, options, srcBuf); CHECK(stencil); - JS::RootedScript script(cx, - JS::InstantiateGlobalStencil(cx, options, stencil)); + JS::InstantiateOptions instantiateOptions(options); + JS::RootedScript script( + cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil)); CHECK(script); JS::RootedValue rval(cx); @@ -88,8 +89,9 @@ JS::CompileModuleScriptToStencil(cx, options, srcBuf); CHECK(stencil); + JS::InstantiateOptions instantiateOptions(options); JS::RootedObject moduleObject( - cx, JS::InstantiateModuleStencil(cx, options, stencil)); + cx, JS::InstantiateModuleStencil(cx, instantiateOptions, stencil)); CHECK(moduleObject); // Link and evaluate the module graph. The link step used to be call @@ -122,8 +124,9 @@ JS::CompileGlobalScriptToStencil(cx, options, srcBuf); CHECK(stencil); - JS::RootedScript script(cx, - JS::InstantiateGlobalStencil(cx, options, stencil)); + JS::InstantiateOptions instantiateOptions(options); + JS::RootedScript script( + cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil)); CHECK(script); JS::RootedObject obj(cx, JS_NewPlainObject(cx)); @@ -178,9 +181,9 @@ JSAutoRealm ar(cx, otherGlobal); - JS::CompileOptions options(cx); - JS::RootedScript script(cx, - JS::InstantiateGlobalStencil(cx, options, stencil)); + JS::InstantiateOptions instantiateOptions; + JS::RootedScript script( + cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil)); CHECK(script); JS::RootedValue rval(cx); @@ -215,8 +218,9 @@ CHECK(!buffer.empty()); // Instantiate and Run - JS::RootedScript script(cx, - JS::InstantiateGlobalStencil(cx, options, stencil)); + JS::InstantiateOptions instantiateOptions(options); + JS::RootedScript script( + cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil)); JS::RootedValue rval(cx); CHECK(script); CHECK(JS_ExecuteScript(cx, script, &rval)); @@ -250,8 +254,9 @@ buffer.clear(); // Instantiate and Run - JS::RootedScript script(cx, - JS::InstantiateGlobalStencil(cx, options, stencil)); + JS::InstantiateOptions instantiateOptions(options); + JS::RootedScript script( + cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil)); stencil = nullptr; JS::RootedValue rval(cx); CHECK(script); @@ -301,7 +306,8 @@ JS::DecodeStencil(cx, options, range, getter_AddRefs(stencil)); CHECK(res == JS::TranscodeResult::Ok); - script = JS::InstantiateGlobalStencil(cx, options, stencil); + JS::InstantiateOptions instantiateOptions(options); + script = JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil); CHECK(script); } @@ -350,8 +356,9 @@ RefPtr stencil = JS::FinishOffThreadStencil(cx, token); CHECK(stencil); - JS::RootedScript script(cx, - JS::InstantiateGlobalStencil(cx, options, stencil)); + JS::InstantiateOptions instantiateOptions(options); + JS::RootedScript script( + cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil)); CHECK(script); JS::RootedValue rval(cx); @@ -399,8 +406,9 @@ RefPtr stencil = JS::FinishOffThreadStencil(cx, token); CHECK(stencil); + JS::InstantiateOptions instantiateOptions(options); JS::RootedObject moduleObject( - cx, JS::InstantiateModuleStencil(cx, options, stencil)); + cx, JS::InstantiateModuleStencil(cx, instantiateOptions, stencil)); CHECK(moduleObject); JS::RootedValue rval(cx); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/moz.build firefox-trunk-95.0~a1~hg20211020r596404/js/src/moz.build --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/moz.build 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/moz.build 2021-10-20 19:28:24.000000000 +0000 @@ -478,7 +478,6 @@ "builtin/intl/DisplayNames.cpp", "builtin/intl/IntlObject.cpp", "builtin/intl/LanguageTag.cpp", - "builtin/intl/LanguageTagGenerated.cpp", "builtin/intl/ListFormat.cpp", "builtin/intl/Locale.cpp", "builtin/intl/NumberFormat.cpp", diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/shell/js.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/shell/js.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/shell/js.cpp 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/shell/js.cpp 2021-10-20 19:28:24.000000000 +0000 @@ -2298,7 +2298,7 @@ options.setIntroductionType("js shell evaluate") .setFileAndLine("@evaluate", 1) - .setdeferDebugMetadata(); + .setDeferDebugMetadata(); options.borrowBuffer = true; @@ -2497,7 +2497,8 @@ return false; } - script = JS::InstantiateGlobalStencil(cx, options, stencil); + JS::InstantiateOptions instantiateOptions(options); + script = JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil); if (!script) { return false; } @@ -2548,7 +2549,8 @@ } } - if (!JS::UpdateDebugMetadata(cx, script, options, privateValue, + JS::InstantiateOptions instantiateOptions(options); + if (!JS::UpdateDebugMetadata(cx, script, instantiateOptions, privateValue, elementAttributeName, nullptr, nullptr)) { return false; } @@ -5334,7 +5336,7 @@ .setFileAndLine("", 1) .setIsRunOnce(true) .setNoScriptRval(true) - .setdeferDebugMetadata(); + .setDeferDebugMetadata(); RootedValue privateValue(cx); RootedString elementAttributeName(cx); @@ -5365,7 +5367,8 @@ return false; } - if (!JS::UpdateDebugMetadata(cx, script, options, privateValue, + JS::InstantiateOptions instantiateOptions(options); + if (!JS::UpdateDebugMetadata(cx, script, instantiateOptions, privateValue, elementAttributeName, nullptr, nullptr)) { return false; } @@ -6227,7 +6230,7 @@ CompileOptions options(cx); options.setIntroductionType("js shell offThreadCompileScript") .setFileAndLine("", 1) - .setdeferDebugMetadata(); + .setDeferDebugMetadata(); if (args.length() >= 2) { if (args[1].isPrimitive()) { @@ -6340,8 +6343,10 @@ } CompileOptions dummyOptions(cx); - if (!JS::UpdateDebugMetadata(cx, script, dummyOptions, privateValue, - elementAttributeName, nullptr, nullptr)) { + JS::InstantiateOptions dummyInstantiateOptions(dummyOptions); + if (!JS::UpdateDebugMetadata(cx, script, dummyInstantiateOptions, + privateValue, elementAttributeName, nullptr, + nullptr)) { return false; } @@ -6370,7 +6375,7 @@ CompileOptions options(cx); options.setIntroductionType("js shell offThreadCompileToStencil") .setFileAndLine("", 1) - .setdeferDebugMetadata(); + .setDeferDebugMetadata(); if (args.length() >= 2) { if (args[1].isPrimitive()) { @@ -7827,6 +7832,7 @@ struct { SharedArrayRawBuffer* buffer; size_t length; + bool isHugeMemory; // For a WasmMemory tag, otherwise false } sarb; JS::WasmModule* module; double number; @@ -7929,7 +7935,8 @@ } RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmMemory)); - newObj = WasmMemoryObject::create(cx, maybesab, proto); + newObj = WasmMemoryObject::create(cx, maybesab, + mbx->val.sarb.isHugeMemory, proto); MOZ_ASSERT_IF(newObj, newObj->as().isShared()); if (!newObj) { return false; @@ -7983,6 +7990,7 @@ tag = MailboxTag::SharedArrayBuffer; value.sarb.buffer = sab->rawBufferObject(); value.sarb.length = sab->byteLength(); + value.sarb.isHugeMemory = false; if (!value.sarb.buffer->addReference()) { JS_ReportErrorASCII(cx, "Reference count overflow on SharedArrayBuffer"); @@ -7999,6 +8007,7 @@ tag = MailboxTag::WasmMemory; value.sarb.buffer = sab->rawBufferObject(); value.sarb.length = sab->byteLength(); + value.sarb.isHugeMemory = obj->as().isHuge(); if (!value.sarb.buffer->addReference()) { JS_ReportErrorASCII(cx, "Reference count overflow on SharedArrayBuffer"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/shell/jsshell.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/shell/jsshell.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/shell/jsshell.h 2021-10-17 14:42:40.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/shell/jsshell.h 2021-10-20 19:28:24.000000000 +0000 @@ -175,12 +175,11 @@ class NonshrinkingGCObjectVector : public GCVector { public: - void sweep() { + bool traceWeak(JSTracer* trc) { for (HeapPtrObject& obj : *this) { - if (JS::GCPolicy::needsSweep(&obj)) { - obj = nullptr; - } + TraceWeakEdge(trc, &obj, "NonshrinkingGCObjectVector element"); } + return true; } }; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/vm/ArrayBufferObject.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/vm/ArrayBufferObject.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/vm/ArrayBufferObject.cpp 2021-10-17 14:42:46.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/vm/ArrayBufferObject.cpp 2021-10-20 19:28:29.000000000 +0000 @@ -151,13 +151,16 @@ return true; } -void* js::MapBufferMemory(size_t mappedSize, size_t initialCommittedSize) { +void* js::MapBufferMemory(wasm::IndexType t, size_t mappedSize, + size_t initialCommittedSize) { MOZ_ASSERT(mappedSize % gc::SystemPageSize() == 0); MOZ_ASSERT(initialCommittedSize % gc::SystemPageSize() == 0); MOZ_ASSERT(initialCommittedSize <= mappedSize); auto decrement = mozilla::MakeScopeExit([&] { liveBufferCount--; }); - if (wasm::IsHugeMemoryEnabled()) { + // Testing just IsHugeMemoryEnabled() here will overestimate the number of + // live buffers, as asm.js buffers are not huge. This is OK in practice. + if (wasm::IsHugeMemoryEnabled(t)) { liveBufferCount++; } else { decrement.release(); @@ -271,7 +274,7 @@ #endif } -void js::UnmapBufferMemory(void* base, size_t mappedSize) { +void js::UnmapBufferMemory(wasm::IndexType t, void* base, size_t mappedSize) { MOZ_ASSERT(mappedSize % gc::SystemPageSize() == 0); #ifdef XP_WIN @@ -288,9 +291,12 @@ mappedSize); #endif - if (wasm::IsHugeMemoryEnabled()) { + if (wasm::IsHugeMemoryEnabled(t)) { // Decrement the buffer counter at the end -- otherwise, a race condition // could enable the creation of unlimited buffers. + // + // Also see comment in MapBufferMemory about overestimation due to the + // imprecision of the above guard. --liveBufferCount; } } @@ -695,8 +701,8 @@ uint64_t mappedSizeWithHeader = mappedSize + gc::SystemPageSize(); uint64_t numBytesWithHeader = numBytes + gc::SystemPageSize(); - void* data = - MapBufferMemory((size_t)mappedSizeWithHeader, (size_t)numBytesWithHeader); + void* data = MapBufferMemory(indexType, (size_t)mappedSizeWithHeader, + (size_t)numBytesWithHeader); if (!data) { return nullptr; } @@ -717,7 +723,8 @@ MOZ_RELEASE_ASSERT(header->mappedSize() <= SIZE_MAX - gc::SystemPageSize()); size_t mappedSizeWithHeader = header->mappedSize() + gc::SystemPageSize(); - UnmapBufferMemory(header->basePointer(), mappedSizeWithHeader); + UnmapBufferMemory(header->indexType(), header->basePointer(), + mappedSizeWithHeader); } WasmArrayRawBuffer* ArrayBufferObject::BufferContents::wasmBuffer() const { @@ -726,15 +733,14 @@ } template -static bool CreateSpecificWasmBuffer32( +static bool CreateSpecificWasmBuffer( JSContext* cx, const wasm::MemoryDesc& memory, MutableHandleArrayBufferObjectMaybeShared maybeSharedObject) { - bool useHugeMemory = - memory.indexType() == IndexType::I32 && wasm::IsHugeMemoryEnabled(); + bool useHugeMemory = wasm::IsHugeMemoryEnabled(memory.indexType()); Pages initialPages = memory.initialPages(); Maybe sourceMaxPages = memory.maximumPages(); - Pages clampedMaxPages = - wasm::ClampedMaxPages(initialPages, sourceMaxPages, useHugeMemory); + Pages clampedMaxPages = wasm::ClampedMaxPages( + memory.indexType(), initialPages, sourceMaxPages, useHugeMemory); Maybe mappedSize; #ifdef WASM_SUPPORTS_HUGE_MEMORY @@ -839,9 +845,10 @@ return true; } -bool js::CreateWasmBuffer32(JSContext* cx, const wasm::MemoryDesc& memory, - MutableHandleArrayBufferObjectMaybeShared buffer) { - MOZ_RELEASE_ASSERT(memory.initialPages() <= wasm::MaxMemoryPages()); +bool js::CreateWasmBuffer(JSContext* cx, const wasm::MemoryDesc& memory, + MutableHandleArrayBufferObjectMaybeShared buffer) { + MOZ_RELEASE_ASSERT(memory.initialPages() <= + wasm::MaxMemoryPages(memory.indexType())); MOZ_RELEASE_ASSERT(cx->wasm().haveSignalHandlers); if (memory.isShared()) { @@ -850,10 +857,10 @@ JSMSG_WASM_NO_SHMEM_LINK); return false; } - return CreateSpecificWasmBuffer32(cx, memory, buffer); + return CreateSpecificWasmBuffer(cx, memory, buffer); } - return CreateSpecificWasmBuffer32( + return CreateSpecificWasmBuffer( cx, memory, buffer); } @@ -1083,7 +1090,7 @@ /* static */ bool ArrayBufferObject::wasmGrowToPagesInPlace( - Pages newPages, HandleArrayBufferObject oldBuf, + wasm::IndexType t, Pages newPages, HandleArrayBufferObject oldBuf, MutableHandleArrayBufferObject newBuf, JSContext* cx) { CheckStealPreconditions(oldBuf, cx); @@ -1095,7 +1102,7 @@ if (newPages > oldBuf->wasmClampedMaxPages()) { return false; } - MOZ_ASSERT(newPages <= wasm::MaxMemoryPages() && + MOZ_ASSERT(newPages <= wasm::MaxMemoryPages(t) && newPages.byteLength() < ArrayBufferObject::maxBufferByteLength()); // We have checked against the clamped maximum and so we know we can convert @@ -1139,7 +1146,7 @@ /* static */ bool ArrayBufferObject::wasmMovingGrowToPages( - Pages newPages, HandleArrayBufferObject oldBuf, + IndexType t, Pages newPages, HandleArrayBufferObject oldBuf, MutableHandleArrayBufferObject newBuf, JSContext* cx) { // On failure, do not throw and ensure that the original buffer is // unmodified and valid. @@ -1150,7 +1157,7 @@ if (newPages > oldBuf->wasmClampedMaxPages()) { return false; } - MOZ_ASSERT(newPages <= wasm::MaxMemoryPages() && + MOZ_ASSERT(newPages <= wasm::MaxMemoryPages(t) && newPages.byteLength() < ArrayBufferObject::maxBufferByteLength()); // We have checked against the clamped maximum and so we know we can convert @@ -1159,7 +1166,7 @@ if (wasm::ComputeMappedSize(newPages) <= oldBuf->wasmMappedSize() || oldBuf->contents().wasmBuffer()->extendMappedSize(newPages)) { - return wasmGrowToPagesInPlace(newPages, oldBuf, newBuf, cx); + return wasmGrowToPagesInPlace(t, newPages, oldBuf, newBuf, cx); } newBuf.set(ArrayBufferObject::createEmpty(cx)); @@ -1169,7 +1176,7 @@ } Pages clampedMaxPages = - wasm::ClampedMaxPages(newPages, Nothing(), /* hugeMemory */ false); + wasm::ClampedMaxPages(t, newPages, Nothing(), /* hugeMemory */ false); WasmArrayRawBuffer* newRawBuf = WasmArrayRawBuffer::AllocateWasm( oldBuf->wasmIndexType(), newPages, clampedMaxPages, Nothing(), Nothing()); if (!newRawBuf) { @@ -1724,27 +1731,27 @@ map.remove(p); } -void InnerViewTable::sweep() { map.sweep(); } +bool InnerViewTable::traceWeak(JSTracer* trc) { return map.traceWeak(trc); } -void InnerViewTable::sweepAfterMinorGC() { +void InnerViewTable::sweepAfterMinorGC(JSTracer* trc) { MOZ_ASSERT(needsSweepAfterMinorGC()); if (nurseryKeysValid) { for (size_t i = 0; i < nurseryKeys.length(); i++) { JSObject* buffer = MaybeForwarded(nurseryKeys[i]); Map::Ptr p = map.lookup(buffer); - if (p && Map::SweepPolicy::needsSweep(&p->mutableKey(), &p->value())) { + if (p && + !Map::SweepPolicy::traceWeak(trc, &p->mutableKey(), &p->value())) { map.remove(p); } } - nurseryKeys.clear(); } else { // Do the required sweeping by looking at every map entry. - nurseryKeys.clear(); - sweep(); - - nurseryKeysValid = true; + map.traceWeak(trc); } + + nurseryKeys.clear(); + nurseryKeysValid = true; } size_t InnerViewTable::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/vm/ArrayBufferObject.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/vm/ArrayBufferObject.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/vm/ArrayBufferObject.h 2021-10-17 14:42:45.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/vm/ArrayBufferObject.h 2021-10-20 19:28:30.000000000 +0000 @@ -34,7 +34,8 @@ // of size `initialCommittedSize`. Both arguments denote bytes and must be // multiples of the page size, with `initialCommittedSize` <= `mappedSize`. // Returns nullptr on failure. -void* MapBufferMemory(size_t mappedSize, size_t initialCommittedSize); +void* MapBufferMemory(wasm::IndexType, size_t mappedSize, + size_t initialCommittedSize); // Commit additional memory in an existing mapping. `dataEnd` must be the // correct value for the end of the existing committed area, and `delta` must be @@ -52,7 +53,7 @@ // Remove an existing mapping. `dataStart` must be the pointer to the start of // the mapping, and `mappedSize` the size of that mapping. -void UnmapBufferMemory(void* dataStart, size_t mappedSize); +void UnmapBufferMemory(wasm::IndexType t, void* dataStart, size_t mappedSize); // Return the number of currently live mapped buffers. int32_t LiveMappedBufferCount(); @@ -466,10 +467,12 @@ mozilla::Maybe wasmSourceMaxPages() const; [[nodiscard]] static bool wasmGrowToPagesInPlace( - wasm::Pages newPages, Handle oldBuf, + wasm::IndexType t, wasm::Pages newPages, + Handle oldBuf, MutableHandle newBuf, JSContext* cx); [[nodiscard]] static bool wasmMovingGrowToPages( - wasm::Pages newPages, Handle oldBuf, + wasm::IndexType t, wasm::Pages newPages, + Handle oldBuf, MutableHandle newBuf, JSContext* cx); static void finalize(JSFreeOp* fop, JSObject* obj); @@ -513,9 +516,10 @@ using HandleArrayBufferObject = Handle; using MutableHandleArrayBufferObject = MutableHandle; -// Create a buffer for a 32-bit wasm memory. -bool CreateWasmBuffer32(JSContext* cx, const wasm::MemoryDesc& memory, - MutableHandleArrayBufferObjectMaybeShared buffer); +// Create a buffer for a wasm memory, whose type is determined by +// memory.indexType(). +bool CreateWasmBuffer(JSContext* cx, const wasm::MemoryDesc& memory, + MutableHandleArrayBufferObjectMaybeShared buffer); // Per-compartment table that manages the relationship between array buffers // and the views that use their storage. @@ -560,10 +564,10 @@ // Remove references to dead objects in the table and update table entries // to reflect moved objects. - void sweep(); - void sweepAfterMinorGC(); + bool traceWeak(JSTracer* trc); + void sweepAfterMinorGC(JSTracer* trc); - bool needsSweep() const { return map.needsSweep(); } + bool empty() const { return map.empty(); } bool needsSweepAfterMinorGC() const { return !nurseryKeys.empty() || !nurseryKeysValid; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/vm/Compartment.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/vm/Compartment.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/vm/Compartment.cpp 2021-10-17 14:42:46.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/vm/Compartment.cpp 2021-10-20 19:28:30.000000000 +0000 @@ -504,13 +504,13 @@ crossCompartmentObjectWrappers.sweepAfterMinorGC(trc); for (RealmsInCompartmentIter r(this); !r.done(); r.next()) { - r->sweepAfterMinorGC(); + r->sweepAfterMinorGC(trc); } } // Remove dead wrappers from the table or update pointers to moved objects. -void Compartment::sweepCrossCompartmentObjectWrappers() { - crossCompartmentObjectWrappers.sweep(); +void Compartment::traceCrossCompartmentObjectWrapperEdges(JSTracer* trc) { + crossCompartmentObjectWrappers.traceWeak(trc); } void Compartment::fixupCrossCompartmentObjectWrappersAfterMovingGC( @@ -519,7 +519,7 @@ // Sweep the wrapper map to update keys (wrapped values) in other // compartments that may have been moved. - sweepCrossCompartmentObjectWrappers(); + traceCrossCompartmentObjectWrapperEdges(trc); // Trace the wrappers in the map to update their cross-compartment edges // to wrapped values in other compartments that may have been moved. @@ -535,7 +535,7 @@ // Sweep the wrapper map to update values (wrapper objects) in this // compartment that may have been moved. - sweepCrossCompartmentObjectWrappers(); + traceCrossCompartmentObjectWrapperEdges(trc); } void Compartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/vm/Compartment.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/vm/Compartment.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/vm/Compartment.h 2021-10-17 14:42:46.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/vm/Compartment.h 2021-10-20 19:28:29.000000000 +0000 @@ -233,10 +233,10 @@ } } - void sweep() { + void traceWeak(JSTracer* trc) { for (OuterMap::Enum e(map); !e.empty(); e.popFront()) { InnerMap& m = e.front().value(); - m.sweep(); + m.traceWeak(trc); if (m.empty()) { e.removeFront(); } @@ -422,7 +422,7 @@ void sweepRealms(JSFreeOp* fop, bool keepAtleastOne, bool destroyingRuntime); void sweepAfterMinorGC(JSTracer* trc); - void sweepCrossCompartmentObjectWrappers(); + void traceCrossCompartmentObjectWrapperEdges(JSTracer* trc); void fixupCrossCompartmentObjectWrappersAfterMovingGC(JSTracer* trc); void fixupAfterMovingGC(JSTracer* trc); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/vm/CompilationAndEvaluation.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/vm/CompilationAndEvaluation.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/vm/CompilationAndEvaluation.cpp 2021-10-17 14:42:45.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/vm/CompilationAndEvaluation.cpp 2021-10-20 19:28:30.000000000 +0000 @@ -422,10 +422,9 @@ } JS_PUBLIC_API bool JS::UpdateDebugMetadata( - JSContext* cx, Handle script, - const ReadOnlyCompileOptions& options, HandleValue privateValue, - HandleString elementAttributeName, HandleScript introScript, - HandleScript scriptOrModule) { + JSContext* cx, Handle script, const InstantiateOptions& options, + HandleValue privateValue, HandleString elementAttributeName, + HandleScript introScript, HandleScript scriptOrModule) { RootedScriptSourceObject sso(cx, script->sourceObject()); if (!ScriptSourceObject::initElementProperties(cx, sso, diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/vm/HelperThreads.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/vm/HelperThreads.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/vm/HelperThreads.cpp 2021-10-17 14:42:45.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/vm/HelperThreads.cpp 2021-10-20 19:28:30.000000000 +0000 @@ -13,8 +13,8 @@ #include -#include "frontend/BytecodeCompilation.h" -#include "frontend/BytecodeCompiler.h" +#include "frontend/BytecodeCompilation.h" // frontend::{CompileGlobalScriptToExtensibleStencil, FireOnNewScript, FireOnNewScripts} +#include "frontend/BytecodeCompiler.h" // frontend::ParseModuleToExtensibleStencil #include "frontend/CompilationStencil.h" // frontend::{CompilationStencil, ExtensibleCompilationStencil, CompilationInput, CompilationGCOutput, BorrowingCompilationStencil} #include "frontend/ParserAtom.h" // frontend::ParserAtomsTable #include "gc/GC.h" // gc::MergeRealms @@ -2066,7 +2066,8 @@ for (auto& sourceObject : parseTask->sourceObjects) { RootedScriptSourceObject sso(cx, sourceObject); - if (!ScriptSourceObject::initFromOptions(cx, sso, parseTask->options)) { + const JS::InstantiateOptions instantiateOptions(parseTask->options); + if (!ScriptSourceObject::initFromOptions(cx, sso, instantiateOptions)) { return nullptr; } @@ -2191,9 +2192,8 @@ // The Debugger only needs to be told about the topmost script that was // compiled. - if (!parseTask->options.hideFromNewScriptInitial()) { - DebugAPI::onNewScript(cx, script); - } + const JS::InstantiateOptions instantiateOptions(parseTask->options); + frontend::FireOnNewScript(cx, instantiateOptions, script); } else { MOZ_ASSERT(parseTask->stencil_.get() || parseTask->extensibleStencil_.get()); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/vm/JSFunction.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/vm/JSFunction.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/vm/JSFunction.cpp 2021-10-17 14:42:46.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/vm/JSFunction.cpp 2021-10-20 19:28:30.000000000 +0000 @@ -1526,7 +1526,7 @@ .setNoScriptRval(false) .setIntroductionInfo(introducerFilename, introductionType, lineno, pcOffset) - .setdeferDebugMetadata(); + .setDeferDebugMetadata(); JSStringBuilder sb(cx); @@ -1663,8 +1663,10 @@ RootedValue undefValue(cx); RootedScript funScript(cx, JS_GetFunctionScript(cx, fun)); - if (funScript && !UpdateDebugMetadata(cx, funScript, options, undefValue, - nullptr, maybeScript, maybeScript)) { + JS::InstantiateOptions instantiateOptions(options); + if (funScript && + !UpdateDebugMetadata(cx, funScript, instantiateOptions, undefValue, + nullptr, maybeScript, maybeScript)) { return false; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/vm/JSScript.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/vm/JSScript.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/vm/JSScript.cpp 2021-10-17 14:42:46.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/vm/JSScript.cpp 2021-10-20 19:28:29.000000000 +0000 @@ -31,6 +31,7 @@ #include "jsapi.h" #include "jstypes.h" +#include "frontend/BytecodeCompilation.h" // frontend::FireOnNewScript #include "frontend/BytecodeCompiler.h" #include "frontend/BytecodeEmitter.h" #include "frontend/CompilationStencil.h" // frontend::CompilationStencil @@ -770,7 +771,7 @@ [[nodiscard]] static bool MaybeValidateFilename( JSContext* cx, HandleScriptSourceObject sso, - const ReadOnlyCompileOptions& options) { + const JS::InstantiateOptions& options) { // When parsing off-thread we want to do filename validation on the main // thread. This makes off-thread parsing more pure and is simpler because we // can't easily throw exceptions off-thread. @@ -781,7 +782,7 @@ } const char* filename = sso->source()->filename(); - if (!filename || options.skipFilenameValidation()) { + if (!filename || options.skipFilenameValidation) { return true; } @@ -803,7 +804,7 @@ /* static */ bool ScriptSourceObject::initFromOptions( JSContext* cx, HandleScriptSourceObject source, - const ReadOnlyCompileOptions& options) { + const JS::InstantiateOptions& options) { cx->releaseCheck(source); MOZ_ASSERT( source->getReservedSlot(ELEMENT_PROPERTY_SLOT).isMagic(JS_GENERIC_MAGIC)); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/vm/JSScript.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/vm/JSScript.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/vm/JSScript.h 2021-10-17 14:42:45.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/vm/JSScript.h 2021-10-20 19:28:30.000000000 +0000 @@ -1084,7 +1084,7 @@ // Initialize those properties of this ScriptSourceObject whose values // are provided by |options|, re-wrapping as necessary. static bool initFromOptions(JSContext* cx, HandleScriptSourceObject source, - const JS::ReadOnlyCompileOptions& options); + const JS::InstantiateOptions& options); static bool initElementProperties(JSContext* cx, HandleScriptSourceObject source, diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/vm/Realm.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/vm/Realm.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/vm/Realm.cpp 2021-10-17 14:42:45.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/vm/Realm.cpp 2021-10-20 19:28:30.000000000 +0000 @@ -326,17 +326,17 @@ objects_.finishRoots(); } -void ObjectRealm::sweepAfterMinorGC() { +void ObjectRealm::sweepAfterMinorGC(JSTracer* trc) { InnerViewTable& table = innerViews.get(); if (table.needsSweepAfterMinorGC()) { - table.sweepAfterMinorGC(); + table.sweepAfterMinorGC(trc); } } -void Realm::sweepAfterMinorGC() { +void Realm::sweepAfterMinorGC(JSTracer* trc) { globalWriteBarriered = 0; dtoaCache.purge(); - objects_.sweepAfterMinorGC(); + objects_.sweepAfterMinorGC(trc); } void Realm::traceWeakSavedStacks(JSTracer* trc) { savedStacks_.traceWeak(trc); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/vm/Realm.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/vm/Realm.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/vm/Realm.h 2021-10-17 14:42:46.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/vm/Realm.h 2021-10-20 19:28:31.000000000 +0000 @@ -265,7 +265,7 @@ void finishRoots(); void trace(JSTracer* trc); - void sweepAfterMinorGC(); + void sweepAfterMinorGC(JSTracer* trc); void traceWeakNativeIterators(JSTracer* trc); void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, @@ -518,7 +518,7 @@ */ void finishRoots(); - void sweepAfterMinorGC(); + void sweepAfterMinorGC(JSTracer* trc); void traceWeakDebugEnvironmentEdges(JSTracer* trc); void traceWeakObjectRealm(JSTracer* trc); void traceWeakRegExps(JSTracer* trc); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/vm/SelfHosting.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/vm/SelfHosting.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/vm/SelfHosting.cpp 2021-10-17 14:42:45.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/vm/SelfHosting.cpp 2021-10-20 19:28:30.000000000 +0000 @@ -2823,7 +2823,9 @@ return nullptr; } - if (!ScriptSourceObject::initFromOptions(cx, sourceObject, options)) { + JS::InstantiateOptions instantiateOptions(options); + if (!ScriptSourceObject::initFromOptions(cx, sourceObject, + instantiateOptions)) { return nullptr; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/vm/SharedArrayObject.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/vm/SharedArrayObject.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/vm/SharedArrayObject.cpp 2021-10-17 14:42:45.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/vm/SharedArrayObject.cpp 2021-10-20 19:28:31.000000000 +0000 @@ -74,7 +74,8 @@ uint64_t mappedSizeWithHeader = computedMappedSize + gc::SystemPageSize(); uint64_t accessibleSizeWithHeader = accessibleSize + gc::SystemPageSize(); - void* p = MapBufferMemory(mappedSizeWithHeader, accessibleSizeWithHeader); + void* p = MapBufferMemory(wasmIndexType, mappedSizeWithHeader, + accessibleSizeWithHeader); if (!p) { return nullptr; } @@ -99,7 +100,7 @@ const mozilla::Maybe& sourceMaxPages, const mozilla::Maybe& mappedSize) { // Prior code has asserted that initial pages is within our implementation - // limits (wasm::MaxMemory32Pages) and we can assume it is a valid size_t. + // limits (wasm::MaxMemoryPages()) and we can assume it is a valid size_t. MOZ_ASSERT(initialPages.hasByteLength()); size_t length = initialPages.byteLength(); return SharedArrayRawBuffer::AllocateInternal( @@ -129,6 +130,7 @@ } bool SharedArrayRawBuffer::wasmGrowToPagesInPlace(const Lock&, + wasm::IndexType t, wasm::Pages newPages) { // Check that the new pages is within our allowable range. This will // simultaneously check against the maximum specified in source and our @@ -136,7 +138,7 @@ if (newPages > wasmClampedMaxPages_) { return false; } - MOZ_ASSERT(newPages <= wasm::MaxMemoryPages() && + MOZ_ASSERT(newPages <= wasm::MaxMemoryPages(t) && newPages.byteLength() < ArrayBufferObject::maxBufferByteLength()); // We have checked against the clamped maximum and so we know we can convert @@ -198,7 +200,7 @@ size_t mappedSizeWithHeader = mappedSize_ + gc::SystemPageSize(); // This was the final reference, so release the buffer. - UnmapBufferMemory(basePointer(), mappedSizeWithHeader); + UnmapBufferMemory(wasmIndexType(), basePointer(), mappedSizeWithHeader); } static bool IsSharedArrayBuffer(HandleValue v) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/vm/SharedArrayObject.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/vm/SharedArrayObject.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/vm/SharedArrayObject.h 2021-10-17 14:42:45.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/vm/SharedArrayObject.h 2021-10-20 19:28:30.000000000 +0000 @@ -153,7 +153,8 @@ void tryGrowMaxPagesInPlace(wasm::Pages deltaMaxPages); - bool wasmGrowToPagesInPlace(const Lock&, wasm::Pages newPages); + bool wasmGrowToPagesInPlace(const Lock&, wasm::IndexType t, + wasm::Pages newPages); uint32_t refcount() const { return refcount_; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/vm/StructuredClone.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/vm/StructuredClone.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/vm/StructuredClone.cpp 2021-10-17 14:42:46.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/vm/StructuredClone.cpp 2021-10-20 19:28:30.000000000 +0000 @@ -1397,7 +1397,7 @@ } // If this changes, might need to change what we write. - MOZ_ASSERT(WasmMemoryObject::RESERVED_SLOTS == 2); + MOZ_ASSERT(WasmMemoryObject::RESERVED_SLOTS == 3); Rooted memoryObj(context(), &obj->unwrapAs()); @@ -1405,6 +1405,7 @@ context(), &memoryObj->buffer().as()); return out.writePair(SCTAG_SHARED_WASM_MEMORY_OBJECT, 0) && + out.writePair(SCTAG_BOOLEAN, memoryObj->isHuge()) && writeSharedArrayBuffer(sab); } @@ -2448,6 +2449,12 @@ return false; } + // Read the isHuge flag + RootedValue isHuge(cx); + if (!startRead(&isHuge)) { + return false; + } + // Read the SharedArrayBuffer object. RootedValue payload(cx); if (!startRead(&payload)) { @@ -2466,7 +2473,8 @@ // Construct the memory. RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmMemory)); - RootedObject memory(cx, WasmMemoryObject::create(cx, sab, proto)); + RootedObject memory( + cx, WasmMemoryObject::create(cx, sab, isHuge.toBoolean(), proto)); if (!memory) { return false; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/AsmJS.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/AsmJS.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/AsmJS.cpp 2021-10-17 14:42:46.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/AsmJS.cpp 2021-10-20 19:28:30.000000000 +0000 @@ -6918,7 +6918,8 @@ return false; } - imports.get().memory = WasmMemoryObject::create(cx, buffer, nullptr); + imports.get().memory = + WasmMemoryObject::create(cx, buffer, /* hugeMemory= */ false, nullptr); if (!imports.get().memory) { return false; } @@ -7350,8 +7351,8 @@ return false; } - // The heap length is limited by what wasm can handle. - if (length > MaxMemoryBytes()) { + // The heap length is limited by what a wasm memory32 can handle. + if (length > MaxMemoryBytes(IndexType::I32)) { return false; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/cranelift/baldrapi.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/cranelift/baldrapi.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/cranelift/baldrapi.h 2021-10-17 14:42:46.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/cranelift/baldrapi.h 2021-10-20 19:28:31.000000000 +0000 @@ -182,14 +182,14 @@ // ToSymbolicAddress in WasmCraneliftCompile. enum class BD_SymbolicAddress : uint32_t { - MemoryGrow = 0, - MemorySize, - MemoryCopy, - MemoryCopyShared, + MemoryGrow = 0, /* MemoryGrowM32 */ + MemorySize, /* MemorySizeM32 */ + MemoryCopy, /* MemoryCopyM32 */ + MemoryCopyShared, /* MemoryCopySharedM32 */ DataDrop, - MemoryFill, - MemoryFillShared, - MemoryInit, + MemoryFill, /* MemoryFillM32 */ + MemoryFillShared, /* MemoryFillSharedM32 */ + MemoryInit, /* MemoryInitM32 */ TableSize, TableGrow, TableGet, @@ -209,9 +209,9 @@ TruncF64, PreBarrier, PostBarrier, - WaitI32, - WaitI64, - Wake, + WaitI32, /* WaitI32M32 */ + WaitI64, /* WaitI64M32 */ + Wake, /* WakeM32 */ Limit }; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmBaselineCompile.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmBaselineCompile.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmBaselineCompile.cpp 2021-10-17 14:42:46.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmBaselineCompile.cpp 2021-10-20 19:28:29.000000000 +0000 @@ -291,6 +291,31 @@ #endif } +#ifdef JS_CODEGEN_X86 +void BaseCompiler::stashI64(RegPtr regForTls, RegI64 r) { + MOZ_ASSERT(sizeof(TlsData::baselineScratch) >= 8); + MOZ_ASSERT(regForTls != r.low && regForTls != r.high); + fr.loadTlsPtr(regForTls); + masm.store32(r.low, Address(regForTls, offsetof(TlsData, baselineScratch))); + masm.store32(r.high, + Address(regForTls, offsetof(TlsData, baselineScratch) + 4)); +} + +void BaseCompiler::unstashI64(RegPtr regForTls, RegI64 r) { + MOZ_ASSERT(sizeof(TlsData::baselineScratch) >= 8); + fr.loadTlsPtr(regForTls); + if (regForTls == r.low) { + masm.load32(Address(regForTls, offsetof(TlsData, baselineScratch) + 4), + r.high); + masm.load32(Address(regForTls, offsetof(TlsData, baselineScratch)), r.low); + } else { + masm.load32(Address(regForTls, offsetof(TlsData, baselineScratch)), r.low); + masm.load32(Address(regForTls, offsetof(TlsData, baselineScratch) + 4), + r.high); + } +} +#endif + ////////////////////////////////////////////////////////////////////////////// // // Function entry and exit @@ -5426,20 +5451,22 @@ // Bulk memory operations. bool BaseCompiler::emitMemoryGrow() { - return emitInstanceCallOp(SASigMemoryGrow, [this]() -> bool { - Nothing arg; - return iter_.readMemoryGrow(&arg); - }); + return emitInstanceCallOp( + !usesMemory() || isMem32() ? SASigMemoryGrowM32 : SASigMemoryGrowM64, + [this]() -> bool { + Nothing arg; + return iter_.readMemoryGrow(&arg); + }); } bool BaseCompiler::emitMemorySize() { return emitInstanceCallOp( - SASigMemorySize, [this]() -> bool { return iter_.readMemorySize(); }); + !usesMemory() || isMem32() ? SASigMemorySizeM32 : SASigMemorySizeM64, + [this]() -> bool { return iter_.readMemorySize(); }); } bool BaseCompiler::emitMemCopy() { uint32_t lineOrBytecode = readCallSiteLineOrBytecode(); - uint32_t dstMemOrTableIndex = 0; uint32_t srcMemOrTableIndex = 0; Nothing nothing; @@ -5447,63 +5474,68 @@ &srcMemOrTableIndex, ¬hing, ¬hing)) { return false; } - if (deadCode_) { return true; } - int32_t signedLength; - if (peekConst(&signedLength) && signedLength != 0 && - uint32_t(signedLength) <= MaxInlineMemoryCopyLength) { - emitMemCopyInline(); - return true; + if (isMem32()) { + int32_t signedLength; + if (peekConst(&signedLength) && signedLength != 0 && + uint32_t(signedLength) <= MaxInlineMemoryCopyLength) { + memCopyInlineM32(); + return true; + } } - return emitMemCopyCall(lineOrBytecode); + return memCopyCall(lineOrBytecode); } -bool BaseCompiler::emitMemCopyCall(uint32_t lineOrBytecode) { +bool BaseCompiler::memCopyCall(uint32_t lineOrBytecode) { pushHeapBase(); - return emitInstanceCall(lineOrBytecode, usesSharedMemory() - ? SASigMemCopyShared32 - : SASigMemCopy32); + return emitInstanceCall( + lineOrBytecode, + usesSharedMemory() + ? (isMem32() ? SASigMemCopySharedM32 : SASigMemCopySharedM64) + : (isMem32() ? SASigMemCopyM32 : SASigMemCopyM64)); } bool BaseCompiler::emitMemFill() { uint32_t lineOrBytecode = readCallSiteLineOrBytecode(); - Nothing nothing; if (!iter_.readMemFill(¬hing, ¬hing, ¬hing)) { return false; } - if (deadCode_) { return true; } - int32_t signedLength; - int32_t signedValue; - if (peek2xConst(&signedLength, &signedValue) && signedLength != 0 && - uint32_t(signedLength) <= MaxInlineMemoryFillLength) { - emitMemFillInline(); - return true; + if (isMem32()) { + int32_t signedLength; + int32_t signedValue; + if (peek2xConst(&signedLength, &signedValue) && signedLength != 0 && + uint32_t(signedLength) <= MaxInlineMemoryFillLength) { + memFillInlineM32(); + return true; + } } - return emitMemFillCall(lineOrBytecode); + return memFillCall(lineOrBytecode); } -bool BaseCompiler::emitMemFillCall(uint32_t lineOrBytecode) { +bool BaseCompiler::memFillCall(uint32_t lineOrBytecode) { pushHeapBase(); - return emitInstanceCall(lineOrBytecode, usesSharedMemory() - ? SASigMemFillShared32 - : SASigMemFill32); + return emitInstanceCall( + lineOrBytecode, + usesSharedMemory() + ? (isMem32() ? SASigMemFillSharedM32 : SASigMemFillSharedM64) + : (isMem32() ? SASigMemFillM32 : SASigMemFillM64)); } bool BaseCompiler::emitMemInit() { return emitInstanceCallOp( - SASigMemInit32, [this](uint32_t* segIndex) -> bool { - uint32_t dstTableIndex; + (!usesMemory() || isMem32() ? SASigMemInitM32 : SASigMemInitM64), + [this](uint32_t* segIndex) -> bool { Nothing nothing; - if (iter_.readMemOrTableInit(/*isMem*/ true, segIndex, &dstTableIndex, + if (iter_.readMemOrTableInit(/*isMem*/ true, segIndex, nullptr, ¬hing, ¬hing, ¬hing)) { return true; } @@ -9713,6 +9745,9 @@ } bool BaseCompiler::init() { + // We may lift this restriction in the future. + MOZ_ASSERT_IF(usesMemory() && isMem64(), !moduleEnv_.hugeMemoryEnabled()); + ra.init(this); if (!SigD_.append(ValType::F64)) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmBCClass.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmBCClass.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmBCClass.h 2021-10-17 14:42:46.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmBCClass.h 2021-10-20 19:28:31.000000000 +0000 @@ -320,7 +320,10 @@ inline const FuncType& funcType() const; inline const TypeIdDesc& funcTypeId() const; + inline bool usesMemory() const; inline bool usesSharedMemory() const; + inline bool isMem32() const; + inline bool isMem64() const; // The casts are used by some of the ScratchRegister implementations. operator MacroAssembler&() const { return masm; } @@ -455,15 +458,20 @@ // Return the 32-bit low part of the 64-bit register, do not free anything. inline RegI32 fromI64(RegI64 r); + // If r is valid, return fromI64(r), otherwise an invalid RegI32. + inline RegI32 maybeFromI64(RegI64 r); + #ifdef JS_PUNBOX64 // On 64-bit systems, reinterpret r as 64-bit. inline RegI64 fromI32(RegI32 r); #endif // Widen r to 64 bits; this may allocate another register to form a pair. + // Note this does not generate code for sign/zero extension. inline RegI64 widenI32(RegI32 r); - // Narrow r to 32 bits; this may free part of a pair. + // Narrow r to 32 bits; this may free part of a pair. Note this does not + // generate code to canonicalize the value on 64-bit systems. inline RegI32 narrowI64(RegI64 r); inline RegI32 narrowRef(RegRef r); @@ -687,6 +695,7 @@ // Constant poppers will return true and pop the value if the stack top is a // constant of the appropriate type; otherwise pop nothing and return false. + [[nodiscard]] inline bool hasConst() const; [[nodiscard]] inline bool popConst(int32_t* c); [[nodiscard]] inline bool popConst(int64_t* c); [[nodiscard]] inline bool peekConst(int32_t* c); @@ -737,8 +746,9 @@ // register. inline void peekRefAt(uint32_t depth, RegRef dest); - // DOCUMENTME - [[nodiscard]] inline bool peekLocalI32(uint32_t* local); + // Peek at the value on the top of the stack and return true if it is a Local + // of any type. + [[nodiscard]] inline bool peekLocal(uint32_t* local); //////////////////////////////////////////////////////////////////////////// // @@ -1015,6 +1025,16 @@ inline void branchTo(Assembler::Condition c, RegRef lhs, ImmWord rhs, Label* l); +#ifdef JS_CODEGEN_X86 + // Store r in tls scratch storage after first loading the tls from the frame + // into the regForTls. regForTls must be neither of the registers in r. + void stashI64(RegPtr regForTls, RegI64 r); + + // Load r from the tls scratch storage after first loading the tls from the + // frame into the regForTls. regForTls can be one of the registers in r. + void unstashI64(RegPtr regForTls, RegI64 r); +#endif + ////////////////////////////////////////////////////////////////////// // // Code generators for actual operations. @@ -1058,49 +1078,107 @@ void bceCheckLocal(MemoryAccessDesc* access, AccessCheck* check, uint32_t local); void bceLocalIsUpdated(uint32_t local); + + // Fold offsets into ptr and bounds check as necessary. The tls will be valid + // in cases where it's needed. + template void prepareMemoryAccess(MemoryAccessDesc* access, AccessCheck* check, - RegPtr tls, RegI32 ptr); + RegPtr tls, RegIndexType ptr); + + void branchAddNoOverflow(Imm32 offset, RegI32 ptr, Label* ok); + void branchTestLowZero(RegI32 ptr, Imm32 mask, Label* ok); + void boundsCheck4GBOrLargerAccess(RegPtr tls, RegI32 ptr, Label* ok); + void boundsCheckBelow4GBAccess(RegPtr tls, RegI32 ptr, Label* ok); + + void branchAddNoOverflow(Imm32 offset, RegI64 ptr, Label* ok); + void branchTestLowZero(RegI64 ptr, Imm32 mask, Label* ok); + void boundsCheck4GBOrLargerAccess(RegPtr tls, RegI64 ptr, Label* ok); + void boundsCheckBelow4GBAccess(RegPtr tls, RegI64 ptr, Label* ok); #if defined(RABALDR_HAS_HEAPREG) + template BaseIndex prepareAtomicMemoryAccess(MemoryAccessDesc* access, AccessCheck* check, RegPtr tls, - RegI32 ptr); + RegIndexType ptr); #else // Some consumers depend on the returned Address not incorporating tls, as tls // may be the scratch register. + template Address prepareAtomicMemoryAccess(MemoryAccessDesc* access, - AccessCheck* check, RegPtr tls, RegI32 ptr); + AccessCheck* check, RegPtr tls, + RegIndexType ptr); #endif + + template void computeEffectiveAddress(MemoryAccessDesc* access); + [[nodiscard]] bool needTlsForAccess(const AccessCheck& check); // ptr and dest may be the same iff dest is I32. // This may destroy ptr even if ptr and dest are not the same. + void executeLoad(MemoryAccessDesc* access, AccessCheck* check, RegPtr tls, + RegI32 ptr, AnyReg dest, RegI32 temp); void load(MemoryAccessDesc* access, AccessCheck* check, RegPtr tls, RegI32 ptr, AnyReg dest, RegI32 temp); +#ifdef ENABLE_WASM_MEMORY64 + void load(MemoryAccessDesc* access, AccessCheck* check, RegPtr tls, + RegI64 ptr, AnyReg dest, RegI64 temp); +#endif + + template + void doLoadCommon(MemoryAccessDesc* access, AccessCheck check, ValType type); + + void loadCommon(MemoryAccessDesc* access, AccessCheck check, ValType type); // ptr and src must not be the same register. // This may destroy ptr and src. + void executeStore(MemoryAccessDesc* access, AccessCheck* check, RegPtr tls, + RegI32 ptr, AnyReg src, RegI32 temp); void store(MemoryAccessDesc* access, AccessCheck* check, RegPtr tls, RegI32 ptr, AnyReg src, RegI32 temp); +#ifdef ENABLE_WASM_MEMORY64 + void store(MemoryAccessDesc* access, AccessCheck* check, RegPtr tls, + RegI64 ptr, AnyReg src, RegI64 temp); +#endif + + template + void doStoreCommon(MemoryAccessDesc* access, AccessCheck check, + ValType resultType); - void loadCommon(MemoryAccessDesc* access, AccessCheck check, ValType type); void storeCommon(MemoryAccessDesc* access, AccessCheck check, ValType resultType); void atomicLoad(MemoryAccessDesc* access, ValType type); +#if !defined(JS_64BIT) + template + void atomicLoad64(MemoryAccessDesc* desc); +#endif + void atomicStore(MemoryAccessDesc* access, ValType type); + void atomicRMW(MemoryAccessDesc* access, ValType type, AtomicOp op); + template void atomicRMW32(MemoryAccessDesc* access, ValType type, AtomicOp op); + template void atomicRMW64(MemoryAccessDesc* access, ValType type, AtomicOp op); + void atomicXchg(MemoryAccessDesc* desc, ValType type); + template void atomicXchg64(MemoryAccessDesc* access, WantResult wantResult); + template void atomicXchg32(MemoryAccessDesc* access, ValType type); + void atomicCmpXchg(MemoryAccessDesc* access, ValType type); + template void atomicCmpXchg32(MemoryAccessDesc* access, ValType type); + template void atomicCmpXchg64(MemoryAccessDesc* access, ValType type); - RegI32 popMemory32Access(MemoryAccessDesc* access, AccessCheck* check); + template + RegType popConstMemoryAccess(MemoryAccessDesc* access, AccessCheck* check); + template + RegType popMemoryAccess(MemoryAccessDesc* access, AccessCheck* check); + void pushHeapBase(); //////////////////////////////////////////////////////////////////////////// @@ -1436,13 +1514,13 @@ [[nodiscard]] bool emitAtomicXchg(ValType type, Scalar::Type viewType); [[nodiscard]] bool emitMemInit(); [[nodiscard]] bool emitMemCopy(); - [[nodiscard]] bool emitMemCopyCall(uint32_t lineOrBytecode); - void emitMemCopyInline(); + [[nodiscard]] bool memCopyCall(uint32_t lineOrBytecode); + void memCopyInlineM32(); [[nodiscard]] bool emitTableCopy(); [[nodiscard]] bool emitDataOrElemDrop(bool isData); [[nodiscard]] bool emitMemFill(); - [[nodiscard]] bool emitMemFillCall(uint32_t lineOrBytecode); - void emitMemFillInline(); + [[nodiscard]] bool memFillCall(uint32_t lineOrBytecode); + void memFillInlineM32(); [[nodiscard]] bool emitTableInit(); [[nodiscard]] bool emitTableFill(); [[nodiscard]] bool emitTableGet(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmBCClass-inl.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmBCClass-inl.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmBCClass-inl.h 2021-10-17 14:42:45.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmBCClass-inl.h 2021-10-20 19:28:30.000000000 +0000 @@ -33,6 +33,8 @@ return *moduleEnv_.funcs[func_.index].typeId; } +bool BaseCompiler::usesMemory() const { return moduleEnv_.usesMemory(); } + bool BaseCompiler::usesSharedMemory() const { return moduleEnv_.usesSharedMemory(); } @@ -53,6 +55,14 @@ return iter_.bytecodeOffset(); } +bool BaseCompiler::isMem32() const { + return moduleEnv_.memory->indexType() == IndexType::I32; +} + +bool BaseCompiler::isMem64() const { + return moduleEnv_.memory->indexType() == IndexType::I64; +} + } // namespace wasm } // namespace js diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmBCMemory.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmBCMemory.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmBCMemory.cpp 2021-10-17 14:42:45.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmBCMemory.cpp 2021-10-20 19:28:30.000000000 +0000 @@ -96,6 +96,9 @@ uint32_t offsetGuardLimit = GetMaxOffsetGuardLimit(moduleEnv_.hugeMemoryEnabled()); + // 64-bit offset will be supported later. + static_assert(sizeof(access->offset()) == sizeof(uint32_t)); + if ((bceSafe_ & (BCESet(1) << local)) && access->offset() < offsetGuardLimit) { check->omitBoundsCheck = true; @@ -123,43 +126,87 @@ // (In addition, alignment checking of the pointer can be omitted if the pointer // has been checked in dominating code, but we don't do that yet.) -RegI32 BaseCompiler::popMemory32Access(MemoryAccessDesc* access, - AccessCheck* check) { - check->onlyPointerAlignment = - (access->offset() & (access->byteSize() - 1)) == 0; - +template <> +RegI32 BaseCompiler::popConstMemoryAccess(MemoryAccessDesc* access, + AccessCheck* check) { int32_t addrTemp; - if (popConst(&addrTemp)) { - uint32_t addr = addrTemp; + MOZ_ALWAYS_TRUE(popConst(&addrTemp)); + uint32_t addr = addrTemp; + + uint32_t offsetGuardLimit = + GetMaxOffsetGuardLimit(moduleEnv_.hugeMemoryEnabled()); + + // 64-bit offset will be supported later. + static_assert(sizeof(access->offset()) == sizeof(uint32_t)); + + uint64_t ea = uint64_t(addr) + uint64_t(access->offset()); + uint64_t limit = moduleEnv_.memory->initialLength32() + offsetGuardLimit; - uint32_t offsetGuardLimit = - GetMaxOffsetGuardLimit(moduleEnv_.hugeMemoryEnabled()); + check->omitBoundsCheck = ea < limit; + check->omitAlignmentCheck = (ea & (access->byteSize() - 1)) == 0; - uint64_t ea = uint64_t(addr) + uint64_t(access->offset()); - uint64_t limit = moduleEnv_.memory->initialLength32() + offsetGuardLimit; + // Fold the offset into the pointer if we can, as this is always + // beneficial. + if (ea <= UINT32_MAX) { + addr = uint32_t(ea); + access->clearOffset(); + } + + RegI32 r = needI32(); + moveImm32(int32_t(addr), r); + return r; +} + +template <> +RegI64 BaseCompiler::popConstMemoryAccess(MemoryAccessDesc* access, + AccessCheck* check) { + int64_t addrTemp; + MOZ_ALWAYS_TRUE(popConst(&addrTemp)); + uint64_t addr = addrTemp; + + uint32_t offsetGuardLimit = + GetMaxOffsetGuardLimit(moduleEnv_.hugeMemoryEnabled()); + // 64-bit offset will be supported later. + static_assert(sizeof(access->offset()) == sizeof(uint32_t)); + + uint64_t ea = addr + uint64_t(access->offset()); + bool overflow = ea < addr; + uint64_t limit = moduleEnv_.memory->initialLength64() + offsetGuardLimit; + + if (!overflow) { check->omitBoundsCheck = ea < limit; check->omitAlignmentCheck = (ea & (access->byteSize() - 1)) == 0; // Fold the offset into the pointer if we can, as this is always // beneficial. + addr = uint64_t(ea); + access->clearOffset(); + } - if (ea <= UINT32_MAX) { - addr = uint32_t(ea); - access->clearOffset(); - } + RegI64 r = needI64(); + moveImm64(int64_t(addr), r); + return r; +} + +template +RegType BaseCompiler::popMemoryAccess(MemoryAccessDesc* access, + AccessCheck* check) { + check->onlyPointerAlignment = + (access->offset() & (access->byteSize() - 1)) == 0; - RegI32 r = needI32(); - moveImm32(int32_t(addr), r); - return r; + // If there's a constant it will have the correct type for RegType. + if (hasConst()) { + return popConstMemoryAccess(access, check); } + // If there's a local it will have the correct type for RegType. uint32_t local; - if (peekLocalI32(&local)) { + if (peekLocal(&local)) { bceCheckLocal(access, check, local); } - return popI32(); + return pop(); } #ifdef JS_64BIT @@ -195,18 +242,105 @@ } #endif +void BaseCompiler::branchAddNoOverflow(Imm32 offset, RegI32 ptr, Label* ok) { + masm.branchAdd32(Assembler::CarryClear, offset, ptr, ok); +} + +void BaseCompiler::branchAddNoOverflow(Imm32 offset, RegI64 ptr, Label* ok) { +#if defined(JS_64BIT) + masm.branchAddPtr(Assembler::CarryClear, offset, Register64(ptr).reg, ok); +#else + masm.branchAdd64(Assembler::CarryClear, offset, ptr, ok); +#endif +} + +void BaseCompiler::branchTestLowZero(RegI32 ptr, Imm32 mask, Label* ok) { + masm.branchTest32(Assembler::Zero, ptr, mask, ok); +} + +void BaseCompiler::branchTestLowZero(RegI64 ptr, Imm32 mask, Label* ok) { +#ifdef JS_64BIT + masm.branchTestPtr(Assembler::Zero, Register64(ptr).reg, mask, ok); +#else + masm.branchTestPtr(Assembler::Zero, ptr.low, mask, ok); +#endif +} + +void BaseCompiler::boundsCheck4GBOrLargerAccess(RegPtr tls, RegI32 ptr, + Label* ok) { +#ifdef JS_64BIT + // Extend the value to 64 bits, check the 64-bit value against the 64-bit + // bound, then chop back to 32 bits. On most platform the extending and + // chopping are no-ops. It's important that the value we end up with has + // flowed through the Spectre mask + + // Note, ptr and ptr64 are the same register. + RegI64 ptr64 = fromI32(ptr); + + // In principle there may be non-zero bits in the upper bits of the + // register; clear them. +# ifdef RABALDR_ZERO_EXTENDS + masm.assertCanonicalInt32(ptr); +# else + MOZ_CRASH("Platform code needed here"); +# endif + + boundsCheck4GBOrLargerAccess(tls, ptr64, ok); + + // Restore the value to the canonical form for a 32-bit value in a + // 64-bit register and/or the appropriate form for further use in the + // indexing instruction. +# ifdef RABALDR_ZERO_EXTENDS + // The canonical value is zero-extended; we already have that. +# else + MOZ_CRASH("Platform code needed here"); +# endif +#else + // No support needed, we have max 2GB heap on 32-bit + MOZ_CRASH("No 32-bit support"); +#endif +} + +void BaseCompiler::boundsCheckBelow4GBAccess(RegPtr tls, RegI32 ptr, + Label* ok) { + // If the memory's max size is known to be smaller than 64K pages exactly, + // we can use a 32-bit check and avoid extension and wrapping. + masm.wasmBoundsCheck32(Assembler::Below, ptr, + Address(tls, offsetof(TlsData, boundsCheckLimit)), ok); +} + +void BaseCompiler::boundsCheck4GBOrLargerAccess(RegPtr tls, RegI64 ptr, + Label* ok) { + // Any Spectre mitigation will appear to update the ptr64 register. + masm.wasmBoundsCheck64(Assembler::Below, ptr, + Address(tls, offsetof(TlsData, boundsCheckLimit)), ok); +} + +void BaseCompiler::boundsCheckBelow4GBAccess(RegPtr tls, RegI64 ptr, + Label* ok) { + // The bounds check limit is valid to 64 bits, so there's no sense in doing + // anything complicated here. There may be optimization paths here in the + // future and they may differ on 32-bit and 64-bit. + boundsCheck4GBOrLargerAccess(tls, ptr, ok); +} + +// RegIndexType is RegI32 for Memory32 and RegI64 for Memory64. +template void BaseCompiler::prepareMemoryAccess(MemoryAccessDesc* access, AccessCheck* check, RegPtr tls, - RegI32 ptr) { + RegIndexType ptr) { uint32_t offsetGuardLimit = GetMaxOffsetGuardLimit(moduleEnv_.hugeMemoryEnabled()); + // 64-bit offset will be supported later. + static_assert(sizeof(access->offset()) == sizeof(uint32_t)); + // Fold offset if necessary for further computations. if (access->offset() >= offsetGuardLimit || (access->isAtomic() && !check->omitAlignmentCheck && !check->onlyPointerAlignment)) { Label ok; - masm.branchAdd32(Assembler::CarryClear, Imm32(access->offset()), ptr, &ok); + branchAddNoOverflow(Imm32(access->offset()), ptr, &ok); masm.wasmTrap(Trap::OutOfBounds, bytecodeOffset()); masm.bind(&ok); access->clearOffset(); @@ -219,7 +353,7 @@ MOZ_ASSERT(check->onlyPointerAlignment); // We only care about the low pointer bits here. Label ok; - masm.branchTest32(Assembler::Zero, ptr, Imm32(access->byteSize() - 1), &ok); + branchTestLowZero(ptr, Imm32(access->byteSize() - 1), &ok); masm.wasmTrap(Trap::UnalignedAccess, bytecodeOffset()); masm.bind(&ok); } @@ -241,63 +375,35 @@ if (!moduleEnv_.hugeMemoryEnabled() && !check->omitBoundsCheck) { Label ok; #ifdef JS_64BIT - // If the bounds check uses the full 64 bits of the bounds check limit, - // then the index must be zero-extended to 64 bits before checking and - // wrapped back to 32-bits after Spectre masking. (And it's important - // that the value we end up with has flowed through the Spectre mask.) - // - // If the memory's max size is known to be smaller than 64K pages exactly, - // we can use a 32-bit check and avoid extension and wrapping. + // The checking depends on how many bits are in the pointer and how many + // bits are in the bound. if (!moduleEnv_.memory->boundsCheckLimitIs32Bits() && ArrayBufferObject::maxBufferByteLength() >= 0x100000000) { - // Note, ptr and ptr64 are the same register. - RegI64 ptr64 = fromI32(ptr); - - // In principle there may be non-zero bits in the upper bits of the - // register; clear them. -# ifdef RABALDR_ZERO_EXTENDS - masm.assertCanonicalInt32(ptr); -# else - MOZ_CRASH("Platform code needed here"); -# endif - - // Any Spectre mitigation will appear to update the ptr64 register. - masm.wasmBoundsCheck64(Assembler::Below, ptr64, - Address(tls, offsetof(TlsData, boundsCheckLimit)), - &ok); - - // Restore the value to the canonical form for a 32-bit value in a - // 64-bit register and/or the appropriate form for further use in the - // indexing instruction. -# ifdef RABALDR_ZERO_EXTENDS - // The canonical value is zero-extended; we already have that. -# else - MOZ_CRASH("Platform code needed here"); -# endif + boundsCheck4GBOrLargerAccess(tls, ptr, &ok); } else { - masm.wasmBoundsCheck32(Assembler::Below, ptr, - Address(tls, offsetof(TlsData, boundsCheckLimit)), - &ok); + boundsCheckBelow4GBAccess(tls, ptr, &ok); } #else - masm.wasmBoundsCheck32(Assembler::Below, ptr, - Address(tls, offsetof(TlsData, boundsCheckLimit)), - &ok); + boundsCheckBelow4GBAccess(tls, ptr, &ok); #endif masm.wasmTrap(Trap::OutOfBounds, bytecodeOffset()); masm.bind(&ok); } } +template void BaseCompiler::computeEffectiveAddress(MemoryAccessDesc* access) { + // 64-bit offset will be supported later. + static_assert(sizeof(access->offset()) == sizeof(uint32_t)); + if (access->offset()) { Label ok; - RegI32 ptr = popI32(); - masm.branchAdd32(Assembler::CarryClear, Imm32(access->offset()), ptr, &ok); + RegIndexType ptr = pop(); + branchAddNoOverflow(Imm32(access->offset()), ptr, &ok); masm.wasmTrap(Trap::OutOfBounds, bytecodeOffset()); masm.bind(&ok); access->clearOffset(); - pushI32(ptr); + push(ptr); } } @@ -332,11 +438,11 @@ // // Load and store. -// ptr and dest may be the same iff dest is I32. -// This may destroy ptr even if ptr and dest are not the same. -void BaseCompiler::load(MemoryAccessDesc* access, AccessCheck* check, - RegPtr tls, RegI32 ptr, AnyReg dest, RegI32 temp) { - prepareMemoryAccess(access, check, tls, ptr); +void BaseCompiler::executeLoad(MemoryAccessDesc* access, AccessCheck* check, + RegPtr tls, RegI32 ptr, AnyReg dest, + RegI32 temp) { + // 64-bit offset will be supported later. + static_assert(sizeof(access->offset()) == sizeof(uint32_t)); #if defined(JS_CODEGEN_X64) MOZ_ASSERT(temp.isInvalid()); @@ -404,12 +510,42 @@ #endif } -// ptr and src must not be the same register. -// This may destroy ptr and src. -void BaseCompiler::store(MemoryAccessDesc* access, AccessCheck* check, - RegPtr tls, RegI32 ptr, AnyReg src, RegI32 temp) { +// ptr and dest may be the same iff dest is I32. +// This may destroy ptr even if ptr and dest are not the same. +void BaseCompiler::load(MemoryAccessDesc* access, AccessCheck* check, + RegPtr tls, RegI32 ptr, AnyReg dest, RegI32 temp) { + prepareMemoryAccess(access, check, tls, ptr); + executeLoad(access, check, tls, ptr, dest, temp); +} + +#ifdef ENABLE_WASM_MEMORY64 +void BaseCompiler::load(MemoryAccessDesc* access, AccessCheck* check, + RegPtr tls, RegI64 ptr, AnyReg dest, RegI64 temp) { prepareMemoryAccess(access, check, tls, ptr); +# if !defined(JS_64BIT) + // On 32-bit systems we have a maximum 2GB heap and bounds checking has + // been applied to ensure that the 64-bit pointer is valid. + return executeLoad(access, check, tls, RegI32(ptr.low), dest, + maybeFromI64(temp)); +# elif defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM64) + // On x64 and arm64 the 32-bit code simply assumes that the high bits of the + // 64-bit pointer register are zero and performs a 64-bit add. Thus the code + // generated is the same for the 64-bit and the 32-bit case. + return executeLoad(access, check, tls, RegI32(ptr.reg), dest, + maybeFromI64(temp)); +# else + MOZ_CRASH("Missing platform hook"); +# endif +} +#endif + +void BaseCompiler::executeStore(MemoryAccessDesc* access, AccessCheck* check, + RegPtr tls, RegI32 ptr, AnyReg src, + RegI32 temp) { + // 64-bit offset will be supported later. + static_assert(sizeof(access->offset()) == sizeof(uint32_t)); + // Emit the store #if defined(JS_CODEGEN_X64) MOZ_ASSERT(temp.isInvalid()); @@ -488,65 +624,93 @@ #endif } -void BaseCompiler::loadCommon(MemoryAccessDesc* access, AccessCheck check, - ValType type) { +// ptr and src must not be the same register. +// This may destroy ptr and src. +void BaseCompiler::store(MemoryAccessDesc* access, AccessCheck* check, + RegPtr tls, RegI32 ptr, AnyReg src, RegI32 temp) { + prepareMemoryAccess(access, check, tls, ptr); + executeStore(access, check, tls, ptr, src, temp); +} + +#ifdef ENABLE_WASM_MEMORY64 +void BaseCompiler::store(MemoryAccessDesc* access, AccessCheck* check, + RegPtr tls, RegI64 ptr, AnyReg src, RegI64 temp) { + prepareMemoryAccess(access, check, tls, ptr); + // See comments in load() +# if !defined(JS_64BIT) + return executeStore(access, check, tls, RegI32(ptr.low), src, + maybeFromI64(temp)); +# elif defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM64) + return executeStore(access, check, tls, RegI32(ptr.reg), src, + maybeFromI64(temp)); +# else + MOZ_CRASH("Missing platform hook"); +# endif +} +#endif + +template +void BaseCompiler::doLoadCommon(MemoryAccessDesc* access, AccessCheck check, + ValType type) { RegPtr tls; - RegI32 temp; + RegType temp; #if defined(JS_CODEGEN_MIPS64) - temp = needI32(); + temp = need(); #endif switch (type.kind()) { case ValType::I32: { - RegI32 rp = popMemory32Access(access, &check); + RegType rp = popMemoryAccess(access, &check); + RegI32 rv = needI32(); tls = maybeLoadTlsForAccess(check); - load(access, &check, tls, rp, AnyReg(rp), temp); - pushI32(rp); + load(access, &check, tls, rp, AnyReg(rv), temp); + push(rv); + free(rp); break; } case ValType::I64: { RegI64 rv; - RegI32 rp; + RegType rp; #ifdef JS_CODEGEN_X86 rv = specific_.abiReturnRegI64; needI64(rv); - rp = popMemory32Access(access, &check); + rp = popMemoryAccess(access, &check); #else - rp = popMemory32Access(access, &check); + rp = popMemoryAccess(access, &check); rv = needI64(); #endif tls = maybeLoadTlsForAccess(check); load(access, &check, tls, rp, AnyReg(rv), temp); - pushI64(rv); - freeI32(rp); + push(rv); + free(rp); break; } case ValType::F32: { - RegI32 rp = popMemory32Access(access, &check); + RegType rp = popMemoryAccess(access, &check); RegF32 rv = needF32(); tls = maybeLoadTlsForAccess(check); load(access, &check, tls, rp, AnyReg(rv), temp); - pushF32(rv); - freeI32(rp); + push(rv); + free(rp); break; } case ValType::F64: { - RegI32 rp = popMemory32Access(access, &check); + RegType rp = popMemoryAccess(access, &check); RegF64 rv = needF64(); tls = maybeLoadTlsForAccess(check); load(access, &check, tls, rp, AnyReg(rv), temp); - pushF64(rv); - freeI32(rp); + push(rv); + free(rp); break; } #ifdef ENABLE_WASM_SIMD case ValType::V128: { - RegI32 rp = popMemory32Access(access, &check); + RegType rp = popMemoryAccess(access, &check); RegV128 rv = needV128(); tls = maybeLoadTlsForAccess(check); load(access, &check, tls, rp, AnyReg(rv), temp); - pushV128(rv); - freeI32(rp); + push(rv); + free(rp); break; } #endif @@ -559,59 +723,73 @@ maybeFree(temp); } -void BaseCompiler::storeCommon(MemoryAccessDesc* access, AccessCheck check, - ValType resultType) { +void BaseCompiler::loadCommon(MemoryAccessDesc* access, AccessCheck check, + ValType type) { + if (isMem32()) { + doLoadCommon(access, check, type); + } else { +#ifdef ENABLE_WASM_MEMORY64 + doLoadCommon(access, check, type); +#else + MOZ_CRASH("Memory64 not enabled / supported on this platform"); +#endif + } +} + +template +void BaseCompiler::doStoreCommon(MemoryAccessDesc* access, AccessCheck check, + ValType resultType) { RegPtr tls; - RegI32 temp; + RegType temp; #if defined(JS_CODEGEN_MIPS64) - temp = needI32(); + temp = need(); #endif switch (resultType.kind()) { case ValType::I32: { RegI32 rv = popI32(); - RegI32 rp = popMemory32Access(access, &check); + RegType rp = popMemoryAccess(access, &check); tls = maybeLoadTlsForAccess(check); store(access, &check, tls, rp, AnyReg(rv), temp); - freeI32(rp); - freeI32(rv); + free(rp); + free(rv); break; } case ValType::I64: { RegI64 rv = popI64(); - RegI32 rp = popMemory32Access(access, &check); + RegType rp = popMemoryAccess(access, &check); tls = maybeLoadTlsForAccess(check); store(access, &check, tls, rp, AnyReg(rv), temp); - freeI32(rp); - freeI64(rv); + free(rp); + free(rv); break; } case ValType::F32: { RegF32 rv = popF32(); - RegI32 rp = popMemory32Access(access, &check); + RegType rp = popMemoryAccess(access, &check); tls = maybeLoadTlsForAccess(check); store(access, &check, tls, rp, AnyReg(rv), temp); - freeI32(rp); - freeF32(rv); + free(rp); + free(rv); break; } case ValType::F64: { RegF64 rv = popF64(); - RegI32 rp = popMemory32Access(access, &check); + RegType rp = popMemoryAccess(access, &check); tls = maybeLoadTlsForAccess(check); store(access, &check, tls, rp, AnyReg(rv), temp); - freeI32(rp); - freeF64(rv); + free(rp); + free(rv); break; } #ifdef ENABLE_WASM_SIMD case ValType::V128: { RegV128 rv = popV128(); - RegI32 rp = popMemory32Access(access, &check); + RegType rp = popMemoryAccess(access, &check); tls = maybeLoadTlsForAccess(check); store(access, &check, tls, rp, AnyReg(rv), temp); - freeI32(rp); - freeV128(rv); + free(rp); + free(rv); break; } #endif @@ -624,6 +802,29 @@ maybeFree(temp); } +void BaseCompiler::storeCommon(MemoryAccessDesc* access, AccessCheck check, + ValType type) { + if (isMem32()) { + doStoreCommon(access, check, type); + } else { +#ifdef ENABLE_WASM_MEMORY64 + doStoreCommon(access, check, type); +#else + MOZ_CRASH("Memory64 not enabled / supported on this platform"); +#endif + } +} + +// Convert something that may contain a heap index into a Register that can be +// used in an access. + +static inline Register ToRegister(RegI32 r) { return Register(r); } +#ifdef JS_PUNBOX64 +static inline Register ToRegister(RegI64 r) { return r.reg; } +#else +static inline Register ToRegister(RegI64 r) { return r.low; } +#endif + ////////////////////////////////////////////////////////////////////////////// // // Atomic operations. @@ -638,25 +839,37 @@ #ifdef RABALDR_HAS_HEAPREG +// RegIndexType is RegI32 for Memory32 and RegI64 for Memory64. +template BaseIndex BaseCompiler::prepareAtomicMemoryAccess(MemoryAccessDesc* access, AccessCheck* check, - RegPtr tls, RegI32 ptr) { + RegPtr tls, + RegIndexType ptr) { + // 64-bit offset will be supported later. + static_assert(sizeof(access->offset()) == sizeof(uint32_t)); + MOZ_ASSERT(needTlsForAccess(*check) == tls.isValid()); prepareMemoryAccess(access, check, tls, ptr); - return BaseIndex(HeapReg, ptr, TimesOne, access->offset()); + return BaseIndex(HeapReg, ToRegister(ptr), TimesOne, access->offset()); } #else // Some consumers depend on the returned Address not incorporating tls, as tls // may be the scratch register. +// +// RegIndexType is RegI32 for Memory32 and RegI64 for Memory64. +template Address BaseCompiler::prepareAtomicMemoryAccess(MemoryAccessDesc* access, AccessCheck* check, RegPtr tls, - RegI32 ptr) { + RegIndexType ptr) { + // 64-bit offset will be supported later. + static_assert(sizeof(access->offset()) == sizeof(uint32_t)); + MOZ_ASSERT(needTlsForAccess(*check) == tls.isValid()); prepareMemoryAccess(access, check, tls, ptr); - masm.addPtr(Address(tls, offsetof(TlsData, memoryBase)), ptr); - return Address(ptr, access->offset()); + masm.addPtr(Address(tls, offsetof(TlsData, memoryBase)), ToRegister(ptr)); + return Address(ToRegister(ptr), access->offset()); } #endif @@ -709,21 +922,14 @@ } // namespace atomic_load64 -void BaseCompiler::atomicLoad(MemoryAccessDesc* access, ValType type) { - Scalar::Type viewType = access->type(); - if (Scalar::byteSize(viewType) <= sizeof(void*)) { - loadCommon(access, AccessCheck(), type); - return; - } - - MOZ_ASSERT(type == ValType::I64 && Scalar::byteSize(viewType) == 8); - #if !defined(JS_64BIT) +template +void BaseCompiler::atomicLoad64(MemoryAccessDesc* access) { RegI64 rd, temp; atomic_load64::Allocate(this, &rd, &temp); AccessCheck check; - RegI32 rp = popMemory32Access(access, &check); + RegIndexType rp = popMemoryAccess(access, &check); # ifdef RABALDR_HAS_HEAPREG RegPtr tls = maybeLoadTlsForAccess(check); @@ -738,10 +944,30 @@ MOZ_ASSERT(tls == scratch); # endif - freeI32(rp); + free(rp); atomic_load64::Deallocate(this, temp); pushI64(rd); -#endif // JS_64BIT +} +#endif + +void BaseCompiler::atomicLoad(MemoryAccessDesc* access, ValType type) { + Scalar::Type viewType = access->type(); + if (Scalar::byteSize(viewType) <= sizeof(void*)) { + loadCommon(access, AccessCheck(), type); + return; + } + + MOZ_ASSERT(type == ValType::I64 && Scalar::byteSize(viewType) == 8); + +#if !defined(JS_64BIT) + if (isMem32()) { + atomicLoad64(access); + } else { + atomicLoad64(access); + } +#else + MOZ_CRASH("Should not happen"); +#endif } void BaseCompiler::atomicStore(MemoryAccessDesc* access, ValType type) { @@ -754,10 +980,14 @@ MOZ_ASSERT(type == ValType::I64 && Scalar::byteSize(viewType) == 8); -#ifdef JS_64BIT - MOZ_CRASH("Should not happen"); +#if !defined(JS_64BIT) + if (isMem32()) { + atomicXchg64(access, WantResult(false)); + } else { + atomicXchg64(access, WantResult(false)); + } #else - atomicXchg64(access, WantResult(false)); + MOZ_CRASH("Should not happen"); #endif } @@ -769,10 +999,18 @@ AtomicOp op) { Scalar::Type viewType = access->type(); if (Scalar::byteSize(viewType) <= 4) { - atomicRMW32(access, type, op); + if (isMem32()) { + atomicRMW32(access, type, op); + } else { + atomicRMW32(access, type, op); + } } else { MOZ_ASSERT(type == ValType::I64 && Scalar::byteSize(viewType) == 8); - atomicRMW64(access, type, op); + if (isMem32()) { + atomicRMW64(access, type, op); + } else { + atomicRMW64(access, type, op); + } } } @@ -781,7 +1019,11 @@ #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) struct Temps { + // On x86 we use the ScratchI32 for the temp, otherwise we'd run out of + // registers for 64-bit operations. +# if defined(JS_CODEGEN_X64) RegI32 t0; +# endif }; static void PopAndAllocate(BaseCompiler* bc, ValType type, @@ -808,13 +1050,7 @@ *rv = bc->popI32(); } *rd = bc->specific_.eax; -# if defined(JS_CODEGEN_X86) - // Single-byte is a special case handled very locally with ScratchI8, see - // AtomicRMW32 below. - if (Scalar::byteSize(viewType) > 1) { - temps->t0 = bc->needI32(); - } -# else +# ifdef JS_CODEGEN_X64 temps->t0 = bc->needI32(); # endif } @@ -823,15 +1059,13 @@ template static void Perform(BaseCompiler* bc, const MemoryAccessDesc& access, T srcAddr, AtomicOp op, RegI32 rv, RegI32 rd, const Temps& temps) { +# ifdef JS_CODEGEN_X64 RegI32 temp = temps.t0; -# ifdef JS_CODEGEN_X86 - ScratchI8 scratch(*bc); - if (access.type() == Scalar::Uint8) { - // The temp, if used, must be a byte register. - MOZ_ASSERT(temp.isInvalid()); - if (op != AtomicFetchAddOp && op != AtomicFetchSubOp) { - temp = scratch; - } +# else + RegI32 temp; + ScratchI32 scratch(*bc); + if (op != AtomicFetchAddOp && op != AtomicFetchSubOp) { + temp = scratch; } # endif bc->masm.wasmAtomicFetchOp(access, op, rv, srcAddr, temp, rd); @@ -841,7 +1075,9 @@ if (rv != bc->specific_.eax) { bc->freeI32(rv); } +# ifdef JS_CODEGEN_X64 bc->maybeFree(temps.t0); +# endif } #elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) @@ -917,6 +1153,7 @@ } // namespace atomic_rmw32 +template void BaseCompiler::atomicRMW32(MemoryAccessDesc* access, ValType type, AtomicOp op) { Scalar::Type viewType = access->type(); @@ -925,7 +1162,7 @@ atomic_rmw32::PopAndAllocate(this, type, viewType, op, &rd, &rv, &temps); AccessCheck check; - RegI32 rp = popMemory32Access(access, &check); + RegIndexType rp = popMemoryAccess(access, &check); RegPtr tls = maybeLoadTlsForAccess(check); auto memaddr = prepareAtomicMemoryAccess(access, &check, tls, rp); @@ -933,7 +1170,7 @@ maybeFree(tls); atomic_rmw32::Deallocate(this, rv, temps); - freeI32(rp); + free(rp); if (type == ValType::I64) { pushU32AsI64(rd); @@ -976,22 +1213,26 @@ #elif defined(JS_CODEGEN_X86) -// Here we'll use cmpxchg8b, so we need rd=edx:eax and value=ecx:ebx. However, -// ebx is in use also for the address, so we first pop the value to ecx:tmp, -// whence it will be stored in memory before the operation, and ecx:ebx is then -// free. The temp variable goes unused here. +// Register allocation is tricky, see comments at atomic_xchg64 below. +// +// - Initially rv=ecx:edx and eax is reserved, rd=unallocated. +// - Then rp is popped into esi+edi because those are the only available. +// - The Setup operation makes rd=edx:eax. +// - Deallocation then frees only the ecx part of rv. +// +// The temp is unused here. static void PopAndAllocate(BaseCompiler* bc, AtomicOp op, RegI64* rd, RegI64* rv, RegI64*) { - bc->needI64(bc->specific_.edx_eax); - *rd = bc->specific_.edx_eax; - + bc->needI32(bc->specific_.eax); bc->needI32(bc->specific_.ecx); - RegI32 rvLow = bc->needI32(); - *rv = RegI64(Register64(bc->specific_.ecx, rvLow)); + bc->needI32(bc->specific_.edx); + *rv = RegI64(Register64(bc->specific_.ecx, bc->specific_.edx)); bc->popI64ToSpecific(*rv); } +static void Setup(BaseCompiler* bc, RegI64* rd) { *rd = bc->specific_.edx_eax; } + static void Perform(BaseCompiler* bc, const MemoryAccessDesc& access, Address srcAddr, AtomicOp op, RegI64 rv, RegI64, RegI64 rd, const ScratchAtomicNoHeapReg& scratch) { @@ -1008,8 +1249,8 @@ bc->fr.popBytes(8); } -static void Deallocate(BaseCompiler* bc, AtomicOp op, RegI64 rv, RegI64) { - bc->freeI64(rv); +static void Deallocate(BaseCompiler* bc, AtomicOp, RegI64, RegI64) { + bc->freeI32(bc->specific_.ecx); } #elif defined(JS_CODEGEN_ARM) @@ -1068,13 +1309,14 @@ } // namespace atomic_rmw64 +template void BaseCompiler::atomicRMW64(MemoryAccessDesc* access, ValType type, AtomicOp op) { RegI64 rd, rv, temp; atomic_rmw64::PopAndAllocate(this, op, &rd, &rv, &temp); AccessCheck check; - RegI32 rp = popMemory32Access(access, &check); + RegIndexType rp = popMemoryAccess(access, &check); #if defined(RABALDR_HAS_HEAPREG) RegPtr tls = maybeLoadTlsForAccess(check); @@ -1085,11 +1327,12 @@ ScratchAtomicNoHeapReg scratch(*this); RegPtr tls = maybeLoadTlsForAccess(check, RegIntptrToRegPtr(scratch)); auto memaddr = prepareAtomicMemoryAccess(access, &check, tls, rp); + atomic_rmw64::Setup(this, &rd); atomic_rmw64::Perform(this, *access, memaddr, op, rv, temp, rd, scratch); MOZ_ASSERT(tls == scratch); #endif - freeI32(rp); + free(rp); atomic_rmw64::Deallocate(this, op, rv, temp); pushI64(rd); @@ -1102,10 +1345,18 @@ void BaseCompiler::atomicXchg(MemoryAccessDesc* access, ValType type) { Scalar::Type viewType = access->type(); if (Scalar::byteSize(viewType) <= 4) { - atomicXchg32(access, type); + if (isMem32()) { + atomicXchg32(access, type); + } else { + atomicXchg32(access, type); + } } else { MOZ_ASSERT(type == ValType::I64 && Scalar::byteSize(viewType) == 8); - atomicXchg64(access, WantResult(true)); + if (isMem32()) { + atomicXchg64(access, WantResult(true)); + } else { + atomicXchg64(access, WantResult(true)); + } } } @@ -1222,6 +1473,7 @@ } // namespace atomic_xchg32 +template void BaseCompiler::atomicXchg32(MemoryAccessDesc* access, ValType type) { Scalar::Type viewType = access->type(); @@ -1231,14 +1483,14 @@ AccessCheck check; - RegI32 rp = popMemory32Access(access, &check); + RegIndexType rp = popMemoryAccess(access, &check); RegPtr tls = maybeLoadTlsForAccess(check); auto memaddr = prepareAtomicMemoryAccess(access, &check, tls, rp); atomic_xchg32::Perform(this, *access, memaddr, rv, rd, temps); maybeFree(tls); - freeI32(rp); + free(rp); atomic_xchg32::Deallocate(this, rv, temps); if (type == ValType::I64) { @@ -1263,29 +1515,45 @@ #elif defined(JS_CODEGEN_X86) -// We'll use cmpxchg8b, so rv must be in ecx:ebx, and rd must be edx:eax. We -// can't use ebx for rv here because we need ebx for a scratch also, so use a -// separate temp and move the value to ebx just before the operation. +// Register allocation is tricky in several ways. +// +// - For a 64-bit access on memory64 we need six registers for rd, rv, and rp, +// but have only five (as the temp ebx is needed too), so we target all +// registers explicitly to make sure there's space. +// +// - We'll be using cmpxchg8b, and when we do the operation, rv must be in +// ecx:ebx, and rd must be edx:eax. We can't use ebx for rv initially because +// we need ebx for a scratch also, so use a separate temp and move the value +// to ebx just before the operation. +// +// In sum: +// +// - Initially rv=ecx:edx and eax is reserved, rd=unallocated. +// - Then rp is popped into esi+edi because those are the only available. +// - The Setup operation makes rv=ecx:ebx and rd=edx:eax and moves edx->ebx. +// - Deallocation then frees only the ecx part of rv. + static void PopAndAllocate(BaseCompiler* bc, RegI64* rd, RegI64* rv) { bc->needI32(bc->specific_.ecx); - bc->needI64(bc->specific_.edx_eax); - RegI32 tmp = bc->needI32(); - *rv = RegI64(Register64(bc->specific_.ecx, tmp)); + bc->needI32(bc->specific_.edx); + bc->needI32(bc->specific_.eax); + *rv = RegI64(Register64(bc->specific_.ecx, bc->specific_.edx)); bc->popI64ToSpecific(*rv); - *rd = bc->specific_.edx_eax; } -static RegI64 Setup(BaseCompiler* bc, RegI64 rv, - const ScratchAtomicNoHeapReg& scratch) { - MOZ_ASSERT(rv.high == bc->specific_.ecx); +static void Setup(BaseCompiler* bc, RegI64* rv, RegI64* rd, + const ScratchAtomicNoHeapReg& scratch) { + MOZ_ASSERT(rv->high == bc->specific_.ecx); MOZ_ASSERT(Register(scratch) == js::jit::ebx); - bc->masm.move32(rv.low, scratch); - return bc->specific_.ecx_ebx; + bc->masm.move32(rv->low, scratch); + *rv = bc->specific_.ecx_ebx; + *rd = bc->specific_.edx_eax; } static void Deallocate(BaseCompiler* bc, RegI64 rd, RegI64 rv) { - bc->freeI64(rv); + MOZ_ASSERT(rd == bc->specific_.edx_eax || rd == RegI64::Invalid()); bc->maybeFree(rd); + bc->freeI32(bc->specific_.ecx); } #elif defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_MIPS64) @@ -1322,29 +1590,31 @@ } // namespace atomic_xchg64 +template void BaseCompiler::atomicXchg64(MemoryAccessDesc* access, WantResult wantResult) { RegI64 rd, rv; atomic_xchg64::PopAndAllocate(this, &rd, &rv); AccessCheck check; - RegI32 rp = popMemory32Access(access, &check); + RegIndexType rp = popMemoryAccess(access, &check); #ifdef RABALDR_HAS_HEAPREG RegPtr tls = maybeLoadTlsForAccess(check); - auto memaddr = prepareAtomicMemoryAccess(access, &check, tls, rp); + auto memaddr = + prepareAtomicMemoryAccess(access, &check, tls, rp); masm.wasmAtomicExchange64(*access, memaddr, rv, rd); maybeFree(tls); #else ScratchAtomicNoHeapReg scratch(*this); RegPtr tls = maybeLoadTlsForAccess(check, RegIntptrToRegPtr(scratch)); - auto memaddr = prepareAtomicMemoryAccess(access, &check, tls, rp); - RegI64 rvNew = atomic_xchg64::Setup(this, rv, scratch); - masm.wasmAtomicExchange64(*access, memaddr, rvNew, rd); + Address memaddr = prepareAtomicMemoryAccess(access, &check, tls, rp); + atomic_xchg64::Setup(this, &rv, &rd, scratch); + masm.wasmAtomicExchange64(*access, memaddr, rv, rd); MOZ_ASSERT(tls == scratch); #endif - freeI32(rp); + free(rp); if (wantResult) { pushI64(rd); rd = RegI64::Invalid(); @@ -1359,10 +1629,18 @@ void BaseCompiler::atomicCmpXchg(MemoryAccessDesc* access, ValType type) { Scalar::Type viewType = access->type(); if (Scalar::byteSize(viewType) <= 4) { - atomicCmpXchg32(access, type); + if (isMem32()) { + atomicCmpXchg32(access, type); + } else { + atomicCmpXchg32(access, type); + } } else { MOZ_ASSERT(type == ValType::I64 && Scalar::byteSize(viewType) == 8); - atomicCmpXchg64(access, type); + if (isMem32()) { + atomicCmpXchg64(access, type); + } else { + atomicCmpXchg64(access, type); + } } } @@ -1493,6 +1771,7 @@ } // namespace atomic_cmpxchg32 +template void BaseCompiler::atomicCmpXchg32(MemoryAccessDesc* access, ValType type) { Scalar::Type viewType = access->type(); RegI32 rexpect, rnew, rd; @@ -1501,14 +1780,14 @@ &temps); AccessCheck check; - RegI32 rp = popMemory32Access(access, &check); + RegIndexType rp = popMemoryAccess(access, &check); RegPtr tls = maybeLoadTlsForAccess(check); auto memaddr = prepareAtomicMemoryAccess(access, &check, tls, rp); atomic_cmpxchg32::Perform(this, *access, memaddr, rexpect, rnew, rd, temps); maybeFree(tls); - freeI32(rp); + free(rp); atomic_cmpxchg32::Deallocate(this, rexpect, rnew, temps); if (type == ValType::I64) { @@ -1520,8 +1799,19 @@ namespace atomic_cmpxchg64 { +// The templates are needed for x86 code generation, which needs complicated +// register allocation for memory64. + +template +static void PopAndAllocate(BaseCompiler* bc, RegI64* rexpect, RegI64* rnew, + RegI64* rd); + +template +static void Deallocate(BaseCompiler* bc, RegI64 rexpect, RegI64 rnew); + #if defined(JS_CODEGEN_X64) +template static void PopAndAllocate(BaseCompiler* bc, RegI64* rexpect, RegI64* rnew, RegI64* rd) { // For cmpxchg, the expected value and the result are both in rax. @@ -1536,30 +1826,38 @@ bc->masm.wasmCompareExchange64(access, srcAddr, rexpect, rnew, rd); } +template static void Deallocate(BaseCompiler* bc, RegI64 rexpect, RegI64 rnew) { bc->freeI64(rnew); } #elif defined(JS_CODEGEN_X86) -static void PopAndAllocate(BaseCompiler* bc, RegI64* rexpect, RegI64* rnew, - RegI64* rd) { - // For cmpxchg8b, the expected value and the result are both in edx:eax, and - // the replacement value is in ecx:ebx. But we can't allocate ebx here - // because we need it later for a scratch, so instead we allocate a temp to - // hold the low word of 'new'. +template +static void Perform(BaseCompiler* bc, const MemoryAccessDesc& access, + Address srcAddr, RegI64 rexpect, RegI64 rnew, RegI64 rd, + ScratchAtomicNoHeapReg& scratch); + +// Memory32: For cmpxchg8b, the expected value and the result are both in +// edx:eax, and the replacement value is in ecx:ebx. But we can't allocate ebx +// initially because we need it later for a scratch, so instead we allocate a +// temp to hold the low word of 'new'. + +template <> +void PopAndAllocate(BaseCompiler* bc, RegI64* rexpect, RegI64* rnew, + RegI64* rd) { bc->needI64(bc->specific_.edx_eax); bc->needI32(bc->specific_.ecx); - RegI32 tmp = bc->needI32(); *rnew = bc->popI64ToSpecific(RegI64(Register64(bc->specific_.ecx, tmp))); *rexpect = bc->popI64ToSpecific(bc->specific_.edx_eax); *rd = *rexpect; } -static void Perform(BaseCompiler* bc, const MemoryAccessDesc& access, - Address srcAddr, RegI64 rexpect, RegI64 rnew, RegI64 rd, - ScratchAtomicNoHeapReg& scratch) { +template <> +void Perform(BaseCompiler* bc, const MemoryAccessDesc& access, + Address srcAddr, RegI64 rexpect, RegI64 rnew, RegI64 rd, + ScratchAtomicNoHeapReg& scratch) { MOZ_ASSERT(Register(scratch) == js::jit::ebx); MOZ_ASSERT(rnew.high == bc->specific_.ecx); bc->masm.move32(rnew.low, ebx); @@ -1567,12 +1865,62 @@ bc->specific_.ecx_ebx, rd); } -static void Deallocate(BaseCompiler* bc, RegI64 rexpect, RegI64 rnew) { +template <> +void Deallocate(BaseCompiler* bc, RegI64 rexpect, RegI64 rnew) { bc->freeI64(rnew); } +// Memory64: Register allocation is particularly hairy here. With memory64, we +// have up to seven live values: i64 expected-value, i64 new-value, i64 pointer, +// and tls. The tls can use the scratch but there's no avoiding that we'll run +// out of registers. +// +// Unlike for the rmw ops, we can't use edx as the rnew.low since it's used +// for the rexpect.high. And we can't push anything onto the stack while we're +// popping the memory address because the memory address may be on the stack. + +template <> +void PopAndAllocate(BaseCompiler* bc, RegI64* rexpect, RegI64* rnew, + RegI64* rd) { + // We reserve these (and ebx). The 64-bit pointer will end up in esi+edi. + bc->needI32(bc->specific_.eax); + bc->needI32(bc->specific_.ecx); + bc->needI32(bc->specific_.edx); + + // Pop the 'new' value and stash it in the Tls scratch area. Do not + // initialize *rnew to anything. + RegI64 tmp(Register64(bc->specific_.ecx, bc->specific_.edx)); + bc->popI64ToSpecific(tmp); + { + ScratchPtr tlsScratch(*bc); + bc->stashI64(tlsScratch, tmp); + } + + *rexpect = bc->popI64ToSpecific(bc->specific_.edx_eax); + *rd = *rexpect; +} + +template <> +void Perform(BaseCompiler* bc, const MemoryAccessDesc& access, + Address srcAddr, RegI64 rexpect, RegI64 rnew, RegI64 rd, + ScratchAtomicNoHeapReg& scratch) { + MOZ_ASSERT(rnew.isInvalid()); + rnew = bc->specific_.ecx_ebx; + + bc->unstashI64(RegPtr(Register(bc->specific_.ecx)), rnew); + bc->masm.wasmCompareExchange64(access, srcAddr, rexpect, rnew, rd); +} + +template <> +void Deallocate(BaseCompiler* bc, RegI64 rexpect, RegI64 rnew) { + // edx:ebx have been pushed as the result, and the pointer was freed + // separately in the caller, so just free ecx. + bc->free(bc->specific_.ecx); +} + #elif defined(JS_CODEGEN_ARM) +template static void PopAndAllocate(BaseCompiler* bc, RegI64* rexpect, RegI64* rnew, RegI64* rd) { // The replacement value and the result must both be odd/even pairs. @@ -1586,6 +1934,7 @@ bc->masm.wasmCompareExchange64(access, srcAddr, rexpect, rnew, rd); } +template static void Deallocate(BaseCompiler* bc, RegI64 rexpect, RegI64 rnew) { bc->freeI64(rexpect); bc->freeI64(rnew); @@ -1593,6 +1942,7 @@ #elif defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_MIPS64) +template static void PopAndAllocate(BaseCompiler* bc, RegI64* rexpect, RegI64* rnew, RegI64* rd) { *rnew = bc->popI64(); @@ -1605,6 +1955,7 @@ bc->masm.wasmCompareExchange64(access, srcAddr, rexpect, rnew, rd); } +template static void Deallocate(BaseCompiler* bc, RegI64 rexpect, RegI64 rnew) { bc->freeI64(rexpect); bc->freeI64(rnew); @@ -1612,23 +1963,26 @@ #elif defined(JS_CODEGEN_NONE) +template static void PopAndAllocate(BaseCompiler* bc, RegI64* rexpect, RegI64* rnew, RegI64* rd) {} static void Perform(BaseCompiler* bc, const MemoryAccessDesc& access, BaseIndex srcAddr, RegI64 rexpect, RegI64 rnew, RegI64 rd) { } +template static void Deallocate(BaseCompiler* bc, RegI64 rexpect, RegI64 rnew) {} #endif } // namespace atomic_cmpxchg64 +template void BaseCompiler::atomicCmpXchg64(MemoryAccessDesc* access, ValType type) { RegI64 rexpect, rnew, rd; - atomic_cmpxchg64::PopAndAllocate(this, &rexpect, &rnew, &rd); + atomic_cmpxchg64::PopAndAllocate(this, &rexpect, &rnew, &rd); AccessCheck check; - RegI32 rp = popMemory32Access(access, &check); + RegIndexType rp = popMemoryAccess(access, &check); #ifdef RABALDR_HAS_HEAPREG RegPtr tls = maybeLoadTlsForAccess(check); @@ -1638,13 +1992,14 @@ #else ScratchAtomicNoHeapReg scratch(*this); RegPtr tls = maybeLoadTlsForAccess(check, RegIntptrToRegPtr(scratch)); - auto memaddr = prepareAtomicMemoryAccess(access, &check, tls, rp); - atomic_cmpxchg64::Perform(this, *access, memaddr, rexpect, rnew, rd, scratch); + Address memaddr = prepareAtomicMemoryAccess(access, &check, tls, rp); + atomic_cmpxchg64::Perform(this, *access, memaddr, rexpect, rnew, + rd, scratch); MOZ_ASSERT(tls == scratch); #endif - freeI32(rp); - atomic_cmpxchg64::Deallocate(this, rexpect, rnew); + free(rp); + atomic_cmpxchg64::Deallocate(this, rexpect, rnew); pushI64(rd); } @@ -1660,12 +2015,17 @@ RegI64 timeout = popI64(); RegI32 val = popI32(); - computeEffectiveAddress(access); + if (isMem32()) { + computeEffectiveAddress(access); + } else { + computeEffectiveAddress(access); + } pushI32(val); pushI64(timeout); - if (!emitInstanceCall(lineOrBytecode, SASigWaitI32)) { + if (!emitInstanceCall(lineOrBytecode, + isMem32() ? SASigWaitI32M32 : SASigWaitI32M64)) { return false; } break; @@ -1674,12 +2034,31 @@ RegI64 timeout = popI64(); RegI64 val = popI64(); - computeEffectiveAddress(access); + if (isMem32()) { + computeEffectiveAddress(access); + } else { +#ifdef JS_CODEGEN_X86 + { + ScratchPtr scratch(*this); + stashI64(scratch, val); + freeI64(val); + } +#endif + computeEffectiveAddress(access); +#ifdef JS_CODEGEN_X86 + { + ScratchPtr scratch(*this); + val = needI64(); + unstashI64(scratch, val); + } +#endif + } pushI64(val); pushI64(timeout); - if (!emitInstanceCall(lineOrBytecode, SASigWaitI64)) { + if (!emitInstanceCall(lineOrBytecode, + isMem32() ? SASigWaitI64M32 : SASigWaitI64M64)) { return false; } break; @@ -1695,17 +2074,22 @@ uint32_t lineOrBytecode) { RegI32 count = popI32(); - computeEffectiveAddress(access); + if (isMem32()) { + computeEffectiveAddress(access); + } else { + computeEffectiveAddress(access); + } pushI32(count); - return emitInstanceCall(lineOrBytecode, SASigWake); + return emitInstanceCall(lineOrBytecode, + isMem32() ? SASigWakeM32 : SASigWakeM64); } ////////////////////////////////////////////////////////////////////////////// // // Bulk memory. -void BaseCompiler::emitMemCopyInline() { +void BaseCompiler::memCopyInlineM32() { MOZ_ASSERT(MaxInlineMemoryCopyLength != 0); int32_t signedLength; @@ -1910,7 +2294,7 @@ freeI32(src); } -void BaseCompiler::emitMemFillInline() { +void BaseCompiler::memFillInlineM32() { MOZ_ASSERT(MaxInlineMemoryFillLength != 0); int32_t signedLength; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmBCRegMgmt-inl.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmBCRegMgmt-inl.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmBCRegMgmt-inl.h 2021-10-17 14:42:46.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmBCRegMgmt-inl.h 2021-10-20 19:28:31.000000000 +0000 @@ -212,6 +212,13 @@ RegI32 BaseCompiler::fromI64(RegI64 r) { return RegI32(lowPart(r)); } +RegI32 BaseCompiler::maybeFromI64(RegI64 r) { + if (!r.isValid()) { + return RegI32::Invalid(); + } + return fromI64(r); +} + #ifdef JS_PUNBOX64 RegI64 BaseCompiler::fromI32(RegI32 r) { return RegI64(Register64(r)); } #endif diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmBCStkMgmt-inl.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmBCStkMgmt-inl.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmBCStkMgmt-inl.h 2021-10-17 14:42:46.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmBCStkMgmt-inl.h 2021-10-20 19:28:30.000000000 +0000 @@ -1052,6 +1052,23 @@ return specific; } +bool BaseCompiler::hasConst() const { + const Stk& v = stk_.back(); + switch (v.kind()) { + case Stk::ConstI32: + case Stk::ConstI64: + case Stk::ConstF32: + case Stk::ConstF64: +#ifdef ENABLE_WASM_SIMD + case Stk::ConstV128: +#endif + case Stk::ConstRef: + return true; + default: + return false; + } +} + bool BaseCompiler::popConst(int32_t* c) { Stk& v = stk_.back(); if (v.kind() != Stk::ConstI32) { @@ -1196,9 +1213,10 @@ return narrowI64(rd); } -bool BaseCompiler::peekLocalI32(uint32_t* local) { +bool BaseCompiler::peekLocal(uint32_t* local) { Stk& v = stk_.back(); - if (v.kind() != Stk::LocalI32) { + // See hasLocal() for documentation of this logic. + if (v.kind() <= Stk::MemLast || v.kind() > Stk::LocalLast) { return false; } *local = v.slot(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmBuiltins.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmBuiltins.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmBuiltins.cpp 2021-10-17 14:42:45.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmBuiltins.cpp 2021-10-20 19:28:30.000000000 +0000 @@ -114,54 +114,104 @@ SymbolicAddress::PowD, _F64, _Infallible, 2, {_F64, _F64, _END}}; const SymbolicAddressSignature SASigATan2D = { SymbolicAddress::ATan2D, _F64, _Infallible, 2, {_F64, _F64, _END}}; -const SymbolicAddressSignature SASigMemoryGrow = { - SymbolicAddress::MemoryGrow, _I32, _Infallible, 2, {_PTR, _I32, _END}}; -const SymbolicAddressSignature SASigMemorySize = { - SymbolicAddress::MemorySize, _I32, _Infallible, 1, {_PTR, _END}}; -const SymbolicAddressSignature SASigWaitI32 = {SymbolicAddress::WaitI32, - _I32, - _FailOnNegI32, - 4, - {_PTR, _I32, _I32, _I64, _END}}; -const SymbolicAddressSignature SASigWaitI64 = {SymbolicAddress::WaitI64, - _I32, - _FailOnNegI32, - 4, - {_PTR, _I32, _I64, _I64, _END}}; -const SymbolicAddressSignature SASigWake = { - SymbolicAddress::Wake, _I32, _FailOnNegI32, 3, {_PTR, _I32, _I32, _END}}; -const SymbolicAddressSignature SASigMemCopy32 = { - SymbolicAddress::MemCopy32, +const SymbolicAddressSignature SASigMemoryGrowM32 = { + SymbolicAddress::MemoryGrowM32, _I32, _Infallible, 2, {_PTR, _I32, _END}}; +const SymbolicAddressSignature SASigMemoryGrowM64 = { + SymbolicAddress::MemoryGrowM64, _I64, _Infallible, 2, {_PTR, _I64, _END}}; +const SymbolicAddressSignature SASigMemorySizeM32 = { + SymbolicAddress::MemorySizeM32, _I32, _Infallible, 1, {_PTR, _END}}; +const SymbolicAddressSignature SASigMemorySizeM64 = { + SymbolicAddress::MemorySizeM64, _I64, _Infallible, 1, {_PTR, _END}}; +const SymbolicAddressSignature SASigWaitI32M32 = { + SymbolicAddress::WaitI32M32, + _I32, + _FailOnNegI32, + 4, + {_PTR, _I32, _I32, _I64, _END}}; +const SymbolicAddressSignature SASigWaitI32M64 = { + SymbolicAddress::WaitI32M64, + _I32, + _FailOnNegI32, + 4, + {_PTR, _I64, _I32, _I64, _END}}; +const SymbolicAddressSignature SASigWaitI64M32 = { + SymbolicAddress::WaitI64M32, + _I32, + _FailOnNegI32, + 4, + {_PTR, _I32, _I64, _I64, _END}}; +const SymbolicAddressSignature SASigWaitI64M64 = { + SymbolicAddress::WaitI64M64, + _I32, + _FailOnNegI32, + 4, + {_PTR, _I64, _I64, _I64, _END}}; +const SymbolicAddressSignature SASigWakeM32 = { + SymbolicAddress::WakeM32, _I32, _FailOnNegI32, 3, {_PTR, _I32, _I32, _END}}; +const SymbolicAddressSignature SASigWakeM64 = { + SymbolicAddress::WakeM64, _I32, _FailOnNegI32, 3, {_PTR, _I64, _I32, _END}}; +const SymbolicAddressSignature SASigMemCopyM32 = { + SymbolicAddress::MemCopyM32, _VOID, _FailOnNegI32, 5, {_PTR, _I32, _I32, _I32, _PTR, _END}}; -const SymbolicAddressSignature SASigMemCopyShared32 = { - SymbolicAddress::MemCopyShared32, +const SymbolicAddressSignature SASigMemCopySharedM32 = { + SymbolicAddress::MemCopySharedM32, _VOID, _FailOnNegI32, 5, {_PTR, _I32, _I32, _I32, _PTR, _END}}; +const SymbolicAddressSignature SASigMemCopyM64 = { + SymbolicAddress::MemCopyM64, + _VOID, + _FailOnNegI32, + 5, + {_PTR, _I64, _I64, _I64, _PTR, _END}}; +const SymbolicAddressSignature SASigMemCopySharedM64 = { + SymbolicAddress::MemCopySharedM64, + _VOID, + _FailOnNegI32, + 5, + {_PTR, _I64, _I64, _I64, _PTR, _END}}; const SymbolicAddressSignature SASigDataDrop = { SymbolicAddress::DataDrop, _VOID, _FailOnNegI32, 2, {_PTR, _I32, _END}}; -const SymbolicAddressSignature SASigMemFill32 = { - SymbolicAddress::MemFill32, +const SymbolicAddressSignature SASigMemFillM32 = { + SymbolicAddress::MemFillM32, _VOID, _FailOnNegI32, 5, {_PTR, _I32, _I32, _I32, _PTR, _END}}; -const SymbolicAddressSignature SASigMemFillShared32 = { - SymbolicAddress::MemFillShared32, +const SymbolicAddressSignature SASigMemFillSharedM32 = { + SymbolicAddress::MemFillSharedM32, _VOID, _FailOnNegI32, 5, {_PTR, _I32, _I32, _I32, _PTR, _END}}; -const SymbolicAddressSignature SASigMemInit32 = { - SymbolicAddress::MemInit32, +const SymbolicAddressSignature SASigMemFillM64 = { + SymbolicAddress::MemFillM64, + _VOID, + _FailOnNegI32, + 5, + {_PTR, _I64, _I32, _I64, _PTR, _END}}; +const SymbolicAddressSignature SASigMemFillSharedM64 = { + SymbolicAddress::MemFillSharedM64, + _VOID, + _FailOnNegI32, + 5, + {_PTR, _I64, _I32, _I64, _PTR, _END}}; +const SymbolicAddressSignature SASigMemInitM32 = { + SymbolicAddress::MemInitM32, _VOID, _FailOnNegI32, 5, {_PTR, _I32, _I32, _I32, _I32, _END}}; +const SymbolicAddressSignature SASigMemInitM64 = { + SymbolicAddress::MemInitM64, + _VOID, + _FailOnNegI32, + 5, + {_PTR, _I64, _I32, _I32, _I32, _END}}; const SymbolicAddressSignature SASigTableCopy = { SymbolicAddress::TableCopy, _VOID, @@ -1079,50 +1129,90 @@ *abiType = Args_Double_DoubleDouble; return FuncCast(ecmaAtan2, *abiType); - case SymbolicAddress::MemoryGrow: + case SymbolicAddress::MemoryGrowM32: *abiType = Args_Int32_GeneralInt32; - MOZ_ASSERT(*abiType == ToABIType(SASigMemoryGrow)); - return FuncCast(Instance::memoryGrow_i32, *abiType); - case SymbolicAddress::MemorySize: + MOZ_ASSERT(*abiType == ToABIType(SASigMemoryGrowM32)); + return FuncCast(Instance::memoryGrow_m32, *abiType); + case SymbolicAddress::MemoryGrowM64: + *abiType = Args_Int64_GeneralInt64; + MOZ_ASSERT(*abiType == ToABIType(SASigMemoryGrowM64)); + return FuncCast(Instance::memoryGrow_m64, *abiType); + case SymbolicAddress::MemorySizeM32: *abiType = Args_Int32_General; - MOZ_ASSERT(*abiType == ToABIType(SASigMemorySize)); - return FuncCast(Instance::memorySize_i32, *abiType); - case SymbolicAddress::WaitI32: + MOZ_ASSERT(*abiType == ToABIType(SASigMemorySizeM32)); + return FuncCast(Instance::memorySize_m32, *abiType); + case SymbolicAddress::MemorySizeM64: + *abiType = Args_Int64_General; + MOZ_ASSERT(*abiType == ToABIType(SASigMemorySizeM64)); + return FuncCast(Instance::memorySize_m64, *abiType); + case SymbolicAddress::WaitI32M32: *abiType = Args_Int32_GeneralInt32Int32Int64; - MOZ_ASSERT(*abiType == ToABIType(SASigWaitI32)); - return FuncCast(Instance::wait_i32, *abiType); - case SymbolicAddress::WaitI64: + MOZ_ASSERT(*abiType == ToABIType(SASigWaitI32M32)); + return FuncCast(Instance::wait_i32_m32, *abiType); + case SymbolicAddress::WaitI32M64: + *abiType = Args_Int32_GeneralInt64Int32Int64; + MOZ_ASSERT(*abiType == ToABIType(SASigWaitI32M64)); + return FuncCast(Instance::wait_i32_m64, *abiType); + case SymbolicAddress::WaitI64M32: *abiType = Args_Int32_GeneralInt32Int64Int64; - MOZ_ASSERT(*abiType == ToABIType(SASigWaitI64)); - return FuncCast(Instance::wait_i64, *abiType); - case SymbolicAddress::Wake: + MOZ_ASSERT(*abiType == ToABIType(SASigWaitI64M32)); + return FuncCast(Instance::wait_i64_m32, *abiType); + case SymbolicAddress::WaitI64M64: + *abiType = Args_Int32_GeneralInt64Int64Int64; + MOZ_ASSERT(*abiType == ToABIType(SASigWaitI64M64)); + return FuncCast(Instance::wait_i64_m64, *abiType); + case SymbolicAddress::WakeM32: *abiType = Args_Int32_GeneralInt32Int32; - MOZ_ASSERT(*abiType == ToABIType(SASigWake)); - return FuncCast(Instance::wake, *abiType); - case SymbolicAddress::MemCopy32: + MOZ_ASSERT(*abiType == ToABIType(SASigWakeM32)); + return FuncCast(Instance::wake_m32, *abiType); + case SymbolicAddress::WakeM64: + *abiType = Args_Int32_GeneralInt64Int32; + MOZ_ASSERT(*abiType == ToABIType(SASigWakeM64)); + return FuncCast(Instance::wake_m64, *abiType); + case SymbolicAddress::MemCopyM32: *abiType = Args_Int32_GeneralInt32Int32Int32General; - MOZ_ASSERT(*abiType == ToABIType(SASigMemCopy32)); - return FuncCast(Instance::memCopy32, *abiType); - case SymbolicAddress::MemCopyShared32: + MOZ_ASSERT(*abiType == ToABIType(SASigMemCopyM32)); + return FuncCast(Instance::memCopy_m32, *abiType); + case SymbolicAddress::MemCopySharedM32: *abiType = Args_Int32_GeneralInt32Int32Int32General; - MOZ_ASSERT(*abiType == ToABIType(SASigMemCopyShared32)); - return FuncCast(Instance::memCopyShared32, *abiType); + MOZ_ASSERT(*abiType == ToABIType(SASigMemCopySharedM32)); + return FuncCast(Instance::memCopyShared_m32, *abiType); + case SymbolicAddress::MemCopyM64: + *abiType = Args_Int32_GeneralInt64Int64Int64General; + MOZ_ASSERT(*abiType == ToABIType(SASigMemCopyM64)); + return FuncCast(Instance::memCopy_m64, *abiType); + case SymbolicAddress::MemCopySharedM64: + *abiType = Args_Int32_GeneralInt64Int64Int64General; + MOZ_ASSERT(*abiType == ToABIType(SASigMemCopySharedM64)); + return FuncCast(Instance::memCopyShared_m64, *abiType); case SymbolicAddress::DataDrop: *abiType = Args_Int32_GeneralInt32; MOZ_ASSERT(*abiType == ToABIType(SASigDataDrop)); return FuncCast(Instance::dataDrop, *abiType); - case SymbolicAddress::MemFill32: + case SymbolicAddress::MemFillM32: *abiType = Args_Int32_GeneralInt32Int32Int32General; - MOZ_ASSERT(*abiType == ToABIType(SASigMemFill32)); - return FuncCast(Instance::memFill32, *abiType); - case SymbolicAddress::MemFillShared32: + MOZ_ASSERT(*abiType == ToABIType(SASigMemFillM32)); + return FuncCast(Instance::memFill_m32, *abiType); + case SymbolicAddress::MemFillSharedM32: *abiType = Args_Int32_GeneralInt32Int32Int32General; - MOZ_ASSERT(*abiType == ToABIType(SASigMemFillShared32)); - return FuncCast(Instance::memFillShared32, *abiType); - case SymbolicAddress::MemInit32: + MOZ_ASSERT(*abiType == ToABIType(SASigMemFillSharedM32)); + return FuncCast(Instance::memFillShared_m32, *abiType); + case SymbolicAddress::MemFillM64: + *abiType = Args_Int32_GeneralInt64Int32Int64General; + MOZ_ASSERT(*abiType == ToABIType(SASigMemFillM64)); + return FuncCast(Instance::memFill_m64, *abiType); + case SymbolicAddress::MemFillSharedM64: + *abiType = Args_Int32_GeneralInt64Int32Int64General; + MOZ_ASSERT(*abiType == ToABIType(SASigMemFillSharedM64)); + return FuncCast(Instance::memFillShared_m64, *abiType); + case SymbolicAddress::MemInitM32: *abiType = Args_Int32_GeneralInt32Int32Int32Int32; - MOZ_ASSERT(*abiType == ToABIType(SASigMemInit32)); - return FuncCast(Instance::memInit32, *abiType); + MOZ_ASSERT(*abiType == ToABIType(SASigMemInitM32)); + return FuncCast(Instance::memInit_m32, *abiType); + case SymbolicAddress::MemInitM64: + *abiType = Args_Int32_GeneralInt64Int32Int32Int32; + MOZ_ASSERT(*abiType == ToABIType(SASigMemInitM64)); + return FuncCast(Instance::memInit_m64, *abiType); case SymbolicAddress::TableCopy: *abiType = Args_Int32_GeneralInt32Int32Int32Int32Int32; MOZ_ASSERT(*abiType == ToABIType(SASigTableCopy)); @@ -1322,19 +1412,29 @@ case SymbolicAddress::LogD: case SymbolicAddress::PowD: case SymbolicAddress::ATan2D: - case SymbolicAddress::MemoryGrow: - case SymbolicAddress::MemorySize: - case SymbolicAddress::WaitI32: - case SymbolicAddress::WaitI64: - case SymbolicAddress::Wake: + case SymbolicAddress::MemoryGrowM32: + case SymbolicAddress::MemoryGrowM64: + case SymbolicAddress::MemorySizeM32: + case SymbolicAddress::MemorySizeM64: + case SymbolicAddress::WaitI32M32: + case SymbolicAddress::WaitI32M64: + case SymbolicAddress::WaitI64M32: + case SymbolicAddress::WaitI64M64: + case SymbolicAddress::WakeM32: + case SymbolicAddress::WakeM64: case SymbolicAddress::CoerceInPlace_JitEntry: case SymbolicAddress::ReportV128JSCall: - case SymbolicAddress::MemCopy32: - case SymbolicAddress::MemCopyShared32: + case SymbolicAddress::MemCopyM32: + case SymbolicAddress::MemCopySharedM32: + case SymbolicAddress::MemCopyM64: + case SymbolicAddress::MemCopySharedM64: case SymbolicAddress::DataDrop: - case SymbolicAddress::MemFill32: - case SymbolicAddress::MemFillShared32: - case SymbolicAddress::MemInit32: + case SymbolicAddress::MemFillM32: + case SymbolicAddress::MemFillSharedM32: + case SymbolicAddress::MemFillM64: + case SymbolicAddress::MemFillSharedM64: + case SymbolicAddress::MemInitM32: + case SymbolicAddress::MemInitM64: case SymbolicAddress::TableCopy: case SymbolicAddress::ElemDrop: case SymbolicAddress::TableFill: diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmBuiltins.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmBuiltins.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmBuiltins.h 2021-10-17 14:42:46.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmBuiltins.h 2021-10-20 19:28:31.000000000 +0000 @@ -87,17 +87,27 @@ Uint64ToDouble, Int64ToFloat32, Int64ToDouble, - MemoryGrow, - MemorySize, - WaitI32, - WaitI64, - Wake, - MemCopy32, - MemCopyShared32, + MemoryGrowM32, + MemoryGrowM64, + MemorySizeM32, + MemorySizeM64, + WaitI32M32, + WaitI32M64, + WaitI64M32, + WaitI64M64, + WakeM32, + WakeM64, + MemCopyM32, + MemCopySharedM32, + MemCopyM64, + MemCopySharedM64, DataDrop, - MemFill32, - MemFillShared32, - MemInit32, + MemFillM32, + MemFillSharedM32, + MemFillM64, + MemFillSharedM64, + MemInitM32, + MemInitM64, TableCopy, ElemDrop, TableFill, @@ -203,17 +213,27 @@ extern const SymbolicAddressSignature SASigLogD; extern const SymbolicAddressSignature SASigPowD; extern const SymbolicAddressSignature SASigATan2D; -extern const SymbolicAddressSignature SASigMemoryGrow; -extern const SymbolicAddressSignature SASigMemorySize; -extern const SymbolicAddressSignature SASigWaitI32; -extern const SymbolicAddressSignature SASigWaitI64; -extern const SymbolicAddressSignature SASigWake; -extern const SymbolicAddressSignature SASigMemCopy32; -extern const SymbolicAddressSignature SASigMemCopyShared32; +extern const SymbolicAddressSignature SASigMemoryGrowM32; +extern const SymbolicAddressSignature SASigMemoryGrowM64; +extern const SymbolicAddressSignature SASigMemorySizeM32; +extern const SymbolicAddressSignature SASigMemorySizeM64; +extern const SymbolicAddressSignature SASigWaitI32M32; +extern const SymbolicAddressSignature SASigWaitI32M64; +extern const SymbolicAddressSignature SASigWaitI64M32; +extern const SymbolicAddressSignature SASigWaitI64M64; +extern const SymbolicAddressSignature SASigWakeM32; +extern const SymbolicAddressSignature SASigWakeM64; +extern const SymbolicAddressSignature SASigMemCopyM32; +extern const SymbolicAddressSignature SASigMemCopySharedM32; +extern const SymbolicAddressSignature SASigMemCopyM64; +extern const SymbolicAddressSignature SASigMemCopySharedM64; extern const SymbolicAddressSignature SASigDataDrop; -extern const SymbolicAddressSignature SASigMemFill32; -extern const SymbolicAddressSignature SASigMemFillShared32; -extern const SymbolicAddressSignature SASigMemInit32; +extern const SymbolicAddressSignature SASigMemFillM32; +extern const SymbolicAddressSignature SASigMemFillSharedM32; +extern const SymbolicAddressSignature SASigMemFillM64; +extern const SymbolicAddressSignature SASigMemFillSharedM64; +extern const SymbolicAddressSignature SASigMemInitM32; +extern const SymbolicAddressSignature SASigMemInitM64; extern const SymbolicAddressSignature SASigTableCopy; extern const SymbolicAddressSignature SASigElemDrop; extern const SymbolicAddressSignature SASigTableFill; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmCode.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmCode.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmCode.cpp 2021-10-17 14:42:46.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmCode.cpp 2021-10-20 19:28:30.000000000 +0000 @@ -1208,7 +1208,8 @@ return true; } -bool Code::setTier2(UniqueCodeTier tier2, const LinkData& linkData) const { +bool Code::setAndBorrowTier2(UniqueCodeTier tier2, const LinkData& linkData, + const CodeTier** borrowedTier) const { MOZ_RELEASE_ASSERT(!hasTier2()); MOZ_RELEASE_ASSERT(tier2->tier() == Tier::Optimized && tier1_->tier() == Tier::Baseline); @@ -1218,15 +1219,20 @@ } tier2_ = std::move(tier2); + *borrowedTier = &*tier2_; return true; } void Code::commitTier2() const { MOZ_RELEASE_ASSERT(!hasTier2()); - MOZ_RELEASE_ASSERT(tier2_.get()); hasTier2_ = true; MOZ_ASSERT(hasTier2()); + + // To maintain the invariant that tier2_ is never read without the tier having + // been committed, this checks tier2_ here instead of before setting hasTier2_ + // (as would be natural). See comment in WasmCode.h. + MOZ_RELEASE_ASSERT(tier2_.get()); } uint32_t Code::getFuncIndex(JSFunction* fun) const { @@ -1273,11 +1279,13 @@ MOZ_ASSERT(tier1_->initialized()); return *tier1_; } - if (tier2_) { - MOZ_ASSERT(tier2_->initialized()); - return *tier2_; - } - MOZ_CRASH("No code segment at this tier"); + // It is incorrect to ask for the optimized tier without there being such + // a tier and the tier having been committed. The guard here could + // instead be `if (hasTier2()) ... ` but codeTier(t) should not be called + // in contexts where that test is necessary. + MOZ_RELEASE_ASSERT(hasTier2()); + MOZ_ASSERT(tier2_->initialized()); + return *tier2_; } MOZ_CRASH(); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmCode.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmCode.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmCode.h 2021-10-17 14:42:46.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmCode.h 2021-10-20 19:28:30.000000000 +0000 @@ -741,8 +741,27 @@ class Code : public ShareableBase { UniqueCodeTier tier1_; - mutable UniqueConstCodeTier tier2_; // Access only when hasTier2() is true + + // [SMDOC] Tier-2 data + // + // hasTier2_ and tier2_ implement a three-state protocol for broadcasting + // tier-2 data; this also amounts to a single-writer/multiple-reader setup. + // + // Initially hasTier2_ is false and tier2_ is null. + // + // While hasTier2_ is false, *no* thread may read tier2_, but one thread may + // make tier2_ non-null (this will be the tier-2 compiler thread). That same + // thread must then later set hasTier2_ to true to broadcast the tier2_ value + // and its availability. Note that the writing thread may not itself read + // tier2_ before setting hasTier2_, in order to simplify reasoning about + // global invariants. + // + // Once hasTier2_ is true, *no* thread may write tier2_ and *no* thread may + // read tier2_ without having observed hasTier2_ as true first. Once + // hasTier2_ is true, it stays true. + mutable UniqueConstCodeTier tier2_; mutable Atomic hasTier2_; + SharedMetadata metadata_; ExclusiveData profilingLabels_; JumpTables jumpTables_; @@ -770,7 +789,12 @@ } uint32_t getFuncIndex(JSFunction* fun) const; - bool setTier2(UniqueCodeTier tier2, const LinkData& linkData) const; + // Install the tier2 code without committing it. To maintain the invariant + // that tier2_ is never accessed without the tier having been committed, this + // returns a pointer to the installed tier that the caller can use for + // subsequent operations. + bool setAndBorrowTier2(UniqueCodeTier tier2, const LinkData& linkData, + const CodeTier** borrowedTier) const; void commitTier2() const; bool hasTier2() const { return hasTier2_; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmCompileArgs.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmCompileArgs.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmCompileArgs.h 2021-10-17 14:42:46.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmCompileArgs.h 2021-10-20 19:28:31.000000000 +0000 @@ -90,7 +90,6 @@ JS_FOR_WASM_FEATURES(WASM_FEATURE, WASM_FEATURE) #undef WASM_FEATURE sharedMemory(Shareable::False), - hugeMemory(false), simdWormhole(false), intrinsics(false) { } @@ -105,7 +104,6 @@ #undef WASM_FEATURE Shareable sharedMemory; - bool hugeMemory; bool simdWormhole; bool intrinsics; }; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmCompile.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmCompile.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmCompile.cpp 2021-10-17 14:42:45.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmCompile.cpp 2021-10-20 19:28:31.000000000 +0000 @@ -87,7 +87,6 @@ features.sharedMemory = wasm::ThreadsAvailable(cx) ? Shareable::True : Shareable::False; - features.hugeMemory = wasm::IsHugeMemoryEnabled(); // See comments in WasmConstants.h regarding the meaning of the wormhole // options. diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmCraneliftCompile.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmCraneliftCompile.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmCraneliftCompile.cpp 2021-10-17 14:42:46.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmCraneliftCompile.cpp 2021-10-20 19:28:29.000000000 +0000 @@ -47,21 +47,21 @@ case BD_SymbolicAddress::RefFunc: return SymbolicAddress::RefFunc; case BD_SymbolicAddress::MemoryGrow: - return SymbolicAddress::MemoryGrow; + return SymbolicAddress::MemoryGrowM32; case BD_SymbolicAddress::MemorySize: - return SymbolicAddress::MemorySize; + return SymbolicAddress::MemorySizeM32; case BD_SymbolicAddress::MemoryCopy: - return SymbolicAddress::MemCopy32; + return SymbolicAddress::MemCopyM32; case BD_SymbolicAddress::MemoryCopyShared: - return SymbolicAddress::MemCopyShared32; + return SymbolicAddress::MemCopySharedM32; case BD_SymbolicAddress::DataDrop: return SymbolicAddress::DataDrop; case BD_SymbolicAddress::MemoryFill: - return SymbolicAddress::MemFill32; + return SymbolicAddress::MemFillM32; case BD_SymbolicAddress::MemoryFillShared: - return SymbolicAddress::MemFillShared32; + return SymbolicAddress::MemFillSharedM32; case BD_SymbolicAddress::MemoryInit: - return SymbolicAddress::MemInit32; + return SymbolicAddress::MemInitM32; case BD_SymbolicAddress::TableCopy: return SymbolicAddress::TableCopy; case BD_SymbolicAddress::ElemDrop: @@ -99,11 +99,11 @@ case BD_SymbolicAddress::PostBarrier: return SymbolicAddress::PostBarrierFiltering; case BD_SymbolicAddress::WaitI32: - return SymbolicAddress::WaitI32; + return SymbolicAddress::WaitI32M32; case BD_SymbolicAddress::WaitI64: - return SymbolicAddress::WaitI64; + return SymbolicAddress::WaitI64M32; case BD_SymbolicAddress::Wake: - return SymbolicAddress::Wake; + return SymbolicAddress::WakeM32; case BD_SymbolicAddress::Limit: break; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmFrameIter.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmFrameIter.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmFrameIter.cpp 2021-10-17 14:42:45.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmFrameIter.cpp 2021-10-20 19:28:30.000000000 +0000 @@ -1462,30 +1462,48 @@ return "call to asm.js native f64 Math.pow"; case SymbolicAddress::ATan2D: return "call to asm.js native f64 Math.atan2"; - case SymbolicAddress::MemoryGrow: - return "call to native memory.grow (in wasm)"; - case SymbolicAddress::MemorySize: - return "call to native memory.size (in wasm)"; - case SymbolicAddress::WaitI32: - return "call to native i32.wait (in wasm)"; - case SymbolicAddress::WaitI64: - return "call to native i64.wait (in wasm)"; - case SymbolicAddress::Wake: - return "call to native wake (in wasm)"; + case SymbolicAddress::MemoryGrowM32: + return "call to native memory.grow m32 (in wasm)"; + case SymbolicAddress::MemoryGrowM64: + return "call to native memory.grow m64 (in wasm)"; + case SymbolicAddress::MemorySizeM32: + return "call to native memory.size m32 (in wasm)"; + case SymbolicAddress::MemorySizeM64: + return "call to native memory.size m64 (in wasm)"; + case SymbolicAddress::WaitI32M32: + return "call to native i32.wait m32 (in wasm)"; + case SymbolicAddress::WaitI32M64: + return "call to native i32.wait m64 (in wasm)"; + case SymbolicAddress::WaitI64M32: + return "call to native i64.wait m32 (in wasm)"; + case SymbolicAddress::WaitI64M64: + return "call to native i64.wait m64 (in wasm)"; + case SymbolicAddress::WakeM32: + return "call to native wake m32 (in wasm)"; + case SymbolicAddress::WakeM64: + return "call to native wake m64 (in wasm)"; case SymbolicAddress::CoerceInPlace_JitEntry: return "out-of-line coercion for jit entry arguments (in wasm)"; case SymbolicAddress::ReportV128JSCall: return "jit call to v128 wasm function"; - case SymbolicAddress::MemCopy32: - case SymbolicAddress::MemCopyShared32: - return "call to native memory.copy function"; + case SymbolicAddress::MemCopyM32: + case SymbolicAddress::MemCopySharedM32: + return "call to native memory.copy m32 function"; + case SymbolicAddress::MemCopyM64: + case SymbolicAddress::MemCopySharedM64: + return "call to native memory.copy m64 function"; case SymbolicAddress::DataDrop: return "call to native data.drop function"; - case SymbolicAddress::MemFill32: - case SymbolicAddress::MemFillShared32: - return "call to native memory.fill function"; - case SymbolicAddress::MemInit32: - return "call to native memory.init function"; + case SymbolicAddress::MemFillM32: + case SymbolicAddress::MemFillSharedM32: + return "call to native memory.fill m32 function"; + case SymbolicAddress::MemFillM64: + case SymbolicAddress::MemFillSharedM64: + return "call to native memory.fill m64 function"; + case SymbolicAddress::MemInitM32: + return "call to native memory.init m32 function"; + case SymbolicAddress::MemInitM64: + return "call to native memory.init m64 function"; case SymbolicAddress::TableCopy: return "call to native table.copy function"; case SymbolicAddress::TableFill: diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmInstance.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmInstance.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmInstance.cpp 2021-10-17 14:42:45.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmInstance.cpp 2021-10-20 19:28:30.000000000 +0000 @@ -326,8 +326,8 @@ // // Atomic operations and shared memory. -template -static int32_t PerformWait(Instance* instance, uint32_t byteOffset, T value, +template +static int32_t PerformWait(Instance* instance, PtrT byteOffset, ValT value, int64_t timeout_ns) { JSContext* cx = TlsContext.get(); @@ -337,13 +337,13 @@ return -1; } - if (byteOffset & (sizeof(T) - 1)) { + if (byteOffset & (sizeof(ValT) - 1)) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_UNALIGNED_ACCESS); return -1; } - if (byteOffset + sizeof(T) > instance->memory()->volatileMemoryLength()) { + if (byteOffset + sizeof(ValT) > instance->memory()->volatileMemoryLength()) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_OUT_OF_BOUNDS); return -1; @@ -355,8 +355,9 @@ mozilla::TimeDuration::FromMicroseconds(timeout_ns / 1000)); } - switch (atomics_wait_impl(cx, instance->sharedMemoryBuffer(), byteOffset, - value, timeout)) { + MOZ_ASSERT(byteOffset <= SIZE_MAX, "Bounds check is broken"); + switch (atomics_wait_impl(cx, instance->sharedMemoryBuffer(), + size_t(byteOffset), value, timeout)) { case FutexThread::WaitResult::OK: return 0; case FutexThread::WaitResult::NotEqual: @@ -370,22 +371,36 @@ } } -/* static */ int32_t Instance::wait_i32(Instance* instance, uint32_t byteOffset, - int32_t value, int64_t timeout_ns) { - MOZ_ASSERT(SASigWaitI32.failureMode == FailureMode::FailOnNegI32); - return PerformWait(instance, byteOffset, value, timeout_ns); +/* static */ int32_t Instance::wait_i32_m32(Instance* instance, + uint32_t byteOffset, int32_t value, + int64_t timeout_ns) { + MOZ_ASSERT(SASigWaitI32M32.failureMode == FailureMode::FailOnNegI32); + return PerformWait(instance, byteOffset, value, timeout_ns); } -/* static */ int32_t Instance::wait_i64(Instance* instance, uint32_t byteOffset, - int64_t value, int64_t timeout_ns) { - MOZ_ASSERT(SASigWaitI64.failureMode == FailureMode::FailOnNegI32); - return PerformWait(instance, byteOffset, value, timeout_ns); +/* static */ int32_t Instance::wait_i32_m64(Instance* instance, + uint64_t byteOffset, int32_t value, + int64_t timeout_ns) { + MOZ_ASSERT(SASigWaitI32M64.failureMode == FailureMode::FailOnNegI32); + return PerformWait(instance, byteOffset, value, timeout_ns); } -/* static */ int32_t Instance::wake(Instance* instance, uint32_t byteOffset, - int32_t count) { - MOZ_ASSERT(SASigWake.failureMode == FailureMode::FailOnNegI32); +/* static */ int32_t Instance::wait_i64_m32(Instance* instance, + uint32_t byteOffset, int64_t value, + int64_t timeout_ns) { + MOZ_ASSERT(SASigWaitI64M32.failureMode == FailureMode::FailOnNegI32); + return PerformWait(instance, byteOffset, value, timeout_ns); +} + +/* static */ int32_t Instance::wait_i64_m64(Instance* instance, + uint64_t byteOffset, int64_t value, + int64_t timeout_ns) { + MOZ_ASSERT(SASigWaitI64M64.failureMode == FailureMode::FailOnNegI32); + return PerformWait(instance, byteOffset, value, timeout_ns); +} +template +static int32_t PerformWake(Instance* instance, PtrT byteOffset, int32_t count) { JSContext* cx = TlsContext.get(); // The alignment guard is not in the wasm spec as of 2017-11-02, but is @@ -408,8 +423,9 @@ return 0; } + MOZ_ASSERT(byteOffset <= SIZE_MAX, "Bounds check is broken"); int64_t woken = atomics_notify_impl(instance->sharedMemoryBuffer(), - byteOffset, int64_t(count)); + size_t(byteOffset), int64_t(count)); if (woken > INT32_MAX) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, @@ -420,19 +436,50 @@ return int32_t(woken); } +/* static */ int32_t Instance::wake_m32(Instance* instance, uint32_t byteOffset, + int32_t count) { + MOZ_ASSERT(SASigWakeM32.failureMode == FailureMode::FailOnNegI32); + return PerformWake(instance, byteOffset, count); +} + +/* static */ int32_t Instance::wake_m64(Instance* instance, uint64_t byteOffset, + int32_t count) { + MOZ_ASSERT(SASigWakeM32.failureMode == FailureMode::FailOnNegI32); + return PerformWake(instance, byteOffset, count); +} + ////////////////////////////////////////////////////////////////////////////// // // Bulk memory operations. -/* static */ uint32_t Instance::memoryGrow_i32(Instance* instance, +/* static */ uint32_t Instance::memoryGrow_m32(Instance* instance, uint32_t delta) { - MOZ_ASSERT(SASigMemoryGrow.failureMode == FailureMode::Infallible); + MOZ_ASSERT(SASigMemoryGrowM32.failureMode == FailureMode::Infallible); + MOZ_ASSERT(!instance->isAsmJS()); + + JSContext* cx = TlsContext.get(); + RootedWasmMemoryObject memory(cx, instance->memory_); + + // It is safe to cast to uint32_t, as all limits have been checked inside + // grow() and will not have been exceeded for a 32-bit memory. + uint32_t ret = uint32_t(WasmMemoryObject::grow(memory, uint64_t(delta), cx)); + + // If there has been a moving grow, this Instance should have been notified. + MOZ_RELEASE_ASSERT(instance->tlsData()->memoryBase == + instance->memory_->buffer().dataPointerEither()); + + return ret; +} + +/* static */ uint64_t Instance::memoryGrow_m64(Instance* instance, + uint64_t delta) { + MOZ_ASSERT(SASigMemoryGrowM64.failureMode == FailureMode::Infallible); MOZ_ASSERT(!instance->isAsmJS()); JSContext* cx = TlsContext.get(); RootedWasmMemoryObject memory(cx, instance->memory_); - uint32_t ret = WasmMemoryObject::grow(memory, delta, cx); + uint64_t ret = WasmMemoryObject::grow(memory, delta, cx); // If there has been a moving grow, this Instance should have been notified. MOZ_RELEASE_ASSERT(instance->tlsData()->memoryBase == @@ -441,8 +488,8 @@ return ret; } -/* static */ uint32_t Instance::memorySize_i32(Instance* instance) { - MOZ_ASSERT(SASigMemorySize.failureMode == FailureMode::Infallible); +/* static */ uint32_t Instance::memorySize_m32(Instance* instance) { + MOZ_ASSERT(SASigMemorySizeM32.failureMode == FailureMode::Infallible); // This invariant must hold when running Wasm code. Assert it here so we can // write tests for cross-realm calls. @@ -456,44 +503,66 @@ return uint32_t(pages.value()); } -template -inline int32_t WasmMemoryCopy32(T memBase, size_t memLen, - uint32_t dstByteOffset, uint32_t srcByteOffset, - uint32_t len, F memMove) { - // Bounds check and deal with arithmetic overflow. +/* static */ uint64_t Instance::memorySize_m64(Instance* instance) { + MOZ_ASSERT(SASigMemorySizeM64.failureMode == FailureMode::Infallible); + + // This invariant must hold when running Wasm code. Assert it here so we can + // write tests for cross-realm calls. + MOZ_ASSERT(TlsContext.get()->realm() == instance->realm()); + + Pages pages = instance->memory()->volatilePages(); +#ifdef JS_64BIT + MOZ_ASSERT(pages <= Pages(MaxMemory64LimitField)); +#endif + return pages.value(); +} + +static inline bool BoundsCheckCopy(uint32_t dstByteOffset, + uint32_t srcByteOffset, uint32_t len, + size_t memLen) { uint64_t dstOffsetLimit = uint64_t(dstByteOffset) + uint64_t(len); uint64_t srcOffsetLimit = uint64_t(srcByteOffset) + uint64_t(len); - if (dstOffsetLimit > memLen || srcOffsetLimit > memLen) { + return dstOffsetLimit > memLen || srcOffsetLimit > memLen; +} + +static inline bool BoundsCheckCopy(uint64_t dstByteOffset, + uint64_t srcByteOffset, uint64_t len, + size_t memLen) { + uint64_t dstOffsetLimit = dstByteOffset + len; + uint64_t srcOffsetLimit = srcByteOffset + len; + + return dstOffsetLimit < dstByteOffset || dstOffsetLimit > memLen || + srcOffsetLimit < srcByteOffset || srcOffsetLimit > memLen; +} + +template +inline int32_t WasmMemoryCopy(T memBase, size_t memLen, I dstByteOffset, + I srcByteOffset, I len, F memMove) { + if (BoundsCheckCopy(dstByteOffset, srcByteOffset, len, memLen)) { JSContext* cx = TlsContext.get(); JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_OUT_OF_BOUNDS); return -1; } - memMove(memBase + dstByteOffset, memBase + srcByteOffset, size_t(len)); + memMove(memBase + uintptr_t(dstByteOffset), + memBase + uintptr_t(srcByteOffset), size_t(len)); return 0; } -/* static */ int32_t Instance::memCopy32(Instance* instance, - uint32_t dstByteOffset, - uint32_t srcByteOffset, uint32_t len, - uint8_t* memBase) { - MOZ_ASSERT(SASigMemCopy32.failureMode == FailureMode::FailOnNegI32); - +template +inline int32_t MemoryCopy(I dstByteOffset, I srcByteOffset, I len, + uint8_t* memBase) { const WasmArrayRawBuffer* rawBuf = WasmArrayRawBuffer::fromDataPtr(memBase); size_t memLen = rawBuf->byteLength(); - - return WasmMemoryCopy32(memBase, memLen, dstByteOffset, srcByteOffset, len, - memmove); + return WasmMemoryCopy(memBase, memLen, dstByteOffset, srcByteOffset, len, + memmove); } -/* static */ int32_t Instance::memCopyShared32(Instance* instance, - uint32_t dstByteOffset, - uint32_t srcByteOffset, - uint32_t len, uint8_t* memBase) { - MOZ_ASSERT(SASigMemCopyShared32.failureMode == FailureMode::FailOnNegI32); - +template +inline int32_t MemoryCopyShared(I dstByteOffset, I srcByteOffset, I len, + uint8_t* memBase) { using RacyMemMove = void (*)(SharedMem, SharedMem, size_t); @@ -501,18 +570,61 @@ SharedArrayRawBuffer::fromDataPtr(memBase); size_t memLen = rawBuf->volatileByteLength(); - return WasmMemoryCopy32, RacyMemMove>( + return WasmMemoryCopy, RacyMemMove>( SharedMem::shared(memBase), memLen, dstByteOffset, srcByteOffset, len, AtomicOperations::memmoveSafeWhenRacy); } -template -inline int32_t WasmMemoryFill32(T memBase, size_t memLen, uint32_t byteOffset, - uint32_t value, uint32_t len, F memSet) { - // Bounds check and deal with arithmetic overflow. +/* static */ int32_t Instance::memCopy_m32(Instance* instance, + uint32_t dstByteOffset, + uint32_t srcByteOffset, uint32_t len, + uint8_t* memBase) { + MOZ_ASSERT(SASigMemCopyM32.failureMode == FailureMode::FailOnNegI32); + return MemoryCopy(dstByteOffset, srcByteOffset, len, memBase); +} + +/* static */ int32_t Instance::memCopyShared_m32(Instance* instance, + uint32_t dstByteOffset, + uint32_t srcByteOffset, + uint32_t len, + uint8_t* memBase) { + MOZ_ASSERT(SASigMemCopySharedM32.failureMode == FailureMode::FailOnNegI32); + return MemoryCopyShared(dstByteOffset, srcByteOffset, len, memBase); +} + +/* static */ int32_t Instance::memCopy_m64(Instance* instance, + uint64_t dstByteOffset, + uint64_t srcByteOffset, uint64_t len, + uint8_t* memBase) { + MOZ_ASSERT(SASigMemCopyM64.failureMode == FailureMode::FailOnNegI32); + return MemoryCopy(dstByteOffset, srcByteOffset, len, memBase); +} + +/* static */ int32_t Instance::memCopyShared_m64(Instance* instance, + uint64_t dstByteOffset, + uint64_t srcByteOffset, + uint64_t len, + uint8_t* memBase) { + MOZ_ASSERT(SASigMemCopySharedM64.failureMode == FailureMode::FailOnNegI32); + return MemoryCopyShared(dstByteOffset, srcByteOffset, len, memBase); +} + +static inline bool BoundsCheckFill(uint32_t byteOffset, uint32_t len, + size_t memLen) { uint64_t offsetLimit = uint64_t(byteOffset) + uint64_t(len); + return offsetLimit > memLen; +} + +static inline bool BoundsCheckFill(uint64_t byteOffset, uint64_t len, + size_t memLen) { + uint64_t offsetLimit = byteOffset + len; + return offsetLimit < byteOffset || offsetLimit > memLen; +} - if (offsetLimit > memLen) { +template +inline int32_t WasmMemoryFill(T memBase, size_t memLen, I byteOffset, + uint32_t value, I len, F memSet) { + if (BoundsCheckFill(byteOffset, len, memLen)) { JSContext* cx = TlsContext.get(); JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_OUT_OF_BOUNDS); @@ -521,45 +633,80 @@ // The required write direction is upward, but that is not currently // observable as there are no fences nor any read/write protect operation. - memSet(memBase + byteOffset, int(value), size_t(len)); + memSet(memBase + uintptr_t(byteOffset), int(value), size_t(len)); return 0; } -/* static */ int32_t Instance::memFill32(Instance* instance, - uint32_t byteOffset, uint32_t value, - uint32_t len, uint8_t* memBase) { - MOZ_ASSERT(SASigMemFill32.failureMode == FailureMode::FailOnNegI32); - +template +inline int32_t MemoryFill(I byteOffset, uint32_t value, I len, + uint8_t* memBase) { const WasmArrayRawBuffer* rawBuf = WasmArrayRawBuffer::fromDataPtr(memBase); size_t memLen = rawBuf->byteLength(); - - return WasmMemoryFill32(memBase, memLen, byteOffset, value, len, memset); + return WasmMemoryFill(memBase, memLen, byteOffset, value, len, memset); } -/* static */ int32_t Instance::memFillShared32(Instance* instance, - uint32_t byteOffset, - uint32_t value, uint32_t len, - uint8_t* memBase) { - MOZ_ASSERT(SASigMemFillShared32.failureMode == FailureMode::FailOnNegI32); - +template +inline int32_t MemoryFillShared(I byteOffset, uint32_t value, I len, + uint8_t* memBase) { const SharedArrayRawBuffer* rawBuf = SharedArrayRawBuffer::fromDataPtr(memBase); size_t memLen = rawBuf->volatileByteLength(); + return WasmMemoryFill(SharedMem::shared(memBase), memLen, + byteOffset, value, len, + AtomicOperations::memsetSafeWhenRacy); +} - return WasmMemoryFill32(SharedMem::shared(memBase), memLen, - byteOffset, value, len, - AtomicOperations::memsetSafeWhenRacy); +/* static */ int32_t Instance::memFill_m32(Instance* instance, + uint32_t byteOffset, uint32_t value, + uint32_t len, uint8_t* memBase) { + MOZ_ASSERT(SASigMemFillM32.failureMode == FailureMode::FailOnNegI32); + return MemoryFill(byteOffset, value, len, memBase); } -/* static */ int32_t Instance::memInit32(Instance* instance, uint32_t dstOffset, - uint32_t srcOffset, uint32_t len, - uint32_t segIndex) { - MOZ_ASSERT(SASigMemInit32.failureMode == FailureMode::FailOnNegI32); +/* static */ int32_t Instance::memFillShared_m32(Instance* instance, + uint32_t byteOffset, + uint32_t value, uint32_t len, + uint8_t* memBase) { + MOZ_ASSERT(SASigMemFillSharedM32.failureMode == FailureMode::FailOnNegI32); + return MemoryFillShared(byteOffset, value, len, memBase); +} - MOZ_RELEASE_ASSERT(size_t(segIndex) < instance->passiveDataSegments_.length(), - "ensured by validation"); +/* static */ int32_t Instance::memFill_m64(Instance* instance, + uint64_t byteOffset, uint32_t value, + uint64_t len, uint8_t* memBase) { + MOZ_ASSERT(SASigMemFillM64.failureMode == FailureMode::FailOnNegI32); + return MemoryFill(byteOffset, value, len, memBase); +} + +/* static */ int32_t Instance::memFillShared_m64(Instance* instance, + uint64_t byteOffset, + uint32_t value, uint64_t len, + uint8_t* memBase) { + MOZ_ASSERT(SASigMemFillSharedM64.failureMode == FailureMode::FailOnNegI32); + return MemoryFillShared(byteOffset, value, len, memBase); +} - if (!instance->passiveDataSegments_[segIndex]) { +static bool BoundsCheckInit(uint32_t dstOffset, uint32_t srcOffset, + uint32_t len, size_t memLen, uint32_t segLen) { + uint64_t dstOffsetLimit = uint64_t(dstOffset) + uint64_t(len); + uint64_t srcOffsetLimit = uint64_t(srcOffset) + uint64_t(len); + + return dstOffsetLimit > memLen || srcOffsetLimit > segLen; +} + +static bool BoundsCheckInit(uint64_t dstOffset, uint32_t srcOffset, + uint32_t len, size_t memLen, uint32_t segLen) { + uint64_t dstOffsetLimit = dstOffset + uint64_t(len); + uint64_t srcOffsetLimit = uint64_t(srcOffset) + uint64_t(len); + + return dstOffsetLimit < dstOffset || dstOffsetLimit > memLen || + srcOffsetLimit > segLen; +} + +template +static int32_t MemoryInit(Instance* instance, I dstOffset, uint32_t srcOffset, + uint32_t len, const DataSegment* maybeSeg) { + if (!maybeSeg) { if (len == 0 && srcOffset == 0) { return 0; } @@ -569,7 +716,7 @@ return -1; } - const DataSegment& seg = *instance->passiveDataSegments_[segIndex]; + const DataSegment& seg = *maybeSeg; MOZ_RELEASE_ASSERT(!seg.active()); const uint32_t segLen = seg.bytes.length(); @@ -583,11 +730,7 @@ // to // memoryBase[ dstOffset .. dstOffset + len - 1 ] - // Bounds check and deal with arithmetic overflow. - uint64_t dstOffsetLimit = uint64_t(dstOffset) + uint64_t(len); - uint64_t srcOffsetLimit = uint64_t(srcOffset) + uint64_t(len); - - if (dstOffsetLimit > memLen || srcOffsetLimit > segLen) { + if (BoundsCheckInit(dstOffset, srcOffset, len, memLen, segLen)) { JS_ReportErrorNumberASCII(TlsContext.get(), GetErrorMessage, nullptr, JSMSG_WASM_OUT_OF_BOUNDS); return -1; @@ -598,14 +741,40 @@ SharedMem dataPtr = mem->buffer().dataPointerEither(); if (mem->isShared()) { AtomicOperations::memcpySafeWhenRacy( - dataPtr + dstOffset, (uint8_t*)seg.bytes.begin() + srcOffset, len); + dataPtr + uintptr_t(dstOffset), (uint8_t*)seg.bytes.begin() + srcOffset, + len); } else { uint8_t* rawBuf = dataPtr.unwrap(/*Unshared*/); - memcpy(rawBuf + dstOffset, (const char*)seg.bytes.begin() + srcOffset, len); + memcpy(rawBuf + uintptr_t(dstOffset), + (const char*)seg.bytes.begin() + srcOffset, len); } return 0; } +/* static */ int32_t Instance::memInit_m32(Instance* instance, + uint32_t dstOffset, + uint32_t srcOffset, uint32_t len, + uint32_t segIndex) { + MOZ_ASSERT(SASigMemInitM32.failureMode == FailureMode::FailOnNegI32); + MOZ_RELEASE_ASSERT(size_t(segIndex) < instance->passiveDataSegments_.length(), + "ensured by validation"); + + return MemoryInit(instance, dstOffset, srcOffset, len, + instance->passiveDataSegments_[segIndex]); +} + +/* static */ int32_t Instance::memInit_m64(Instance* instance, + uint64_t dstOffset, + uint32_t srcOffset, uint32_t len, + uint32_t segIndex) { + MOZ_ASSERT(SASigMemInitM64.failureMode == FailureMode::FailOnNegI32); + MOZ_RELEASE_ASSERT(size_t(segIndex) < instance->passiveDataSegments_.length(), + "ensured by validation"); + + return MemoryInit(instance, dstOffset, srcOffset, len, + instance->passiveDataSegments_[segIndex]); +} + ////////////////////////////////////////////////////////////////////////////// // // Bulk table operations. diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmInstance.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmInstance.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmInstance.h 2021-10-17 14:42:45.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmInstance.h 2021-10-20 19:28:30.000000000 +0000 @@ -193,31 +193,42 @@ public: // Functions to be called directly from wasm code. static int32_t callImport_general(Instance*, int32_t, int32_t, uint64_t*); - static uint32_t memoryGrow_i32(Instance* instance, uint32_t delta); - static uint32_t memorySize_i32(Instance* instance); - static int32_t wait_i32(Instance* instance, uint32_t byteOffset, - int32_t value, int64_t timeout); - static int32_t wait_i64(Instance* instance, uint32_t byteOffset, - int64_t value, int64_t timeout); - static int32_t wake(Instance* instance, uint32_t byteOffset, int32_t count); - static int32_t memCopy32(Instance* instance, uint32_t dstByteOffset, - uint32_t srcByteOffset, uint32_t len, - uint8_t* memBase); - static int32_t memCopyShared32(Instance* instance, uint32_t dstByteOffset, - uint32_t srcByteOffset, uint32_t len, - uint8_t* memBase); + static uint32_t memoryGrow_m32(Instance* instance, uint32_t delta); + static uint64_t memoryGrow_m64(Instance* instance, uint64_t delta); + static uint32_t memorySize_m32(Instance* instance); + static uint64_t memorySize_m64(Instance* instance); + static int32_t memCopy_m32(Instance* instance, uint32_t dstByteOffset, + uint32_t srcByteOffset, uint32_t len, + uint8_t* memBase); + static int32_t memCopyShared_m32(Instance* instance, uint32_t dstByteOffset, + uint32_t srcByteOffset, uint32_t len, + uint8_t* memBase); + static int32_t memCopy_m64(Instance* instance, uint64_t dstByteOffset, + uint64_t srcByteOffset, uint64_t len, + uint8_t* memBase); + static int32_t memCopyShared_m64(Instance* instance, uint64_t dstByteOffset, + uint64_t srcByteOffset, uint64_t len, + uint8_t* memBase); + static int32_t memFill_m32(Instance* instance, uint32_t byteOffset, + uint32_t value, uint32_t len, uint8_t* memBase); + static int32_t memFillShared_m32(Instance* instance, uint32_t byteOffset, + uint32_t value, uint32_t len, + uint8_t* memBase); + static int32_t memFill_m64(Instance* instance, uint64_t byteOffset, + uint32_t value, uint64_t len, uint8_t* memBase); + static int32_t memFillShared_m64(Instance* instance, uint64_t byteOffset, + uint32_t value, uint64_t len, + uint8_t* memBase); + static int32_t memInit_m32(Instance* instance, uint32_t dstOffset, + uint32_t srcOffset, uint32_t len, + uint32_t segIndex); + static int32_t memInit_m64(Instance* instance, uint64_t dstOffset, + uint32_t srcOffset, uint32_t len, + uint32_t segIndex); static int32_t dataDrop(Instance* instance, uint32_t segIndex); - static int32_t memFill32(Instance* instance, uint32_t byteOffset, - uint32_t value, uint32_t len, uint8_t* memBase); - static int32_t memFillShared32(Instance* instance, uint32_t byteOffset, - uint32_t value, uint32_t len, - uint8_t* memBase); - static int32_t memInit32(Instance* instance, uint32_t dstOffset, - uint32_t srcOffset, uint32_t len, uint32_t segIndex); static int32_t tableCopy(Instance* instance, uint32_t dstOffset, uint32_t srcOffset, uint32_t len, uint32_t dstTableIndex, uint32_t srcTableIndex); - static int32_t elemDrop(Instance* instance, uint32_t segIndex); static int32_t tableFill(Instance* instance, uint32_t start, void* value, uint32_t len, uint32_t tableIndex); static void* tableGet(Instance* instance, uint32_t index, @@ -230,6 +241,19 @@ static int32_t tableInit(Instance* instance, uint32_t dstOffset, uint32_t srcOffset, uint32_t len, uint32_t segIndex, uint32_t tableIndex); + static int32_t elemDrop(Instance* instance, uint32_t segIndex); + static int32_t wait_i32_m32(Instance* instance, uint32_t byteOffset, + int32_t value, int64_t timeout); + static int32_t wait_i32_m64(Instance* instance, uint64_t byteOffset, + int32_t value, int64_t timeout); + static int32_t wait_i64_m32(Instance* instance, uint32_t byteOffset, + int64_t value, int64_t timeout); + static int32_t wait_i64_m64(Instance* instance, uint64_t byteOffset, + int64_t value, int64_t timeout); + static int32_t wake_m32(Instance* instance, uint32_t byteOffset, + int32_t count); + static int32_t wake_m64(Instance* instance, uint64_t byteOffset, + int32_t count); static void* refFunc(Instance* instance, uint32_t funcIndex); static void preBarrierFiltering(Instance* instance, gc::Cell** location); static void postBarrier(Instance* instance, gc::Cell** location); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmIonCompile.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmIonCompile.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmIonCompile.cpp 2021-10-17 14:42:46.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmIonCompile.cpp 2021-10-20 19:28:30.000000000 +0000 @@ -854,11 +854,7 @@ // If the bounds checking strategy requires it, load the bounds check limit // from the Tls. MWasmLoadTls* maybeLoadBoundsCheckLimit(MIRType type) { -#ifdef JS_64BIT MOZ_ASSERT(type == MIRType::Int32 || type == MIRType::Int64); -#else - MOZ_ASSERT(type == MIRType::Int32); -#endif if (moduleEnv_.hugeMemoryEnabled()) { return nullptr; } @@ -886,8 +882,14 @@ // If the EA is known and aligned it will need no checks. if (base->isConstant()) { - int32_t ptr = base->toConstant()->toInt32(); - // OK to wrap around the address computation here. + // We only care about the low bits, so overflow is OK, as is chopping off + // the high bits of an i64 pointer. + uint32_t ptr = 0; + if (isMem64()) { + ptr = uint32_t(base->toConstant()->toInt64()); + } else { + ptr = base->toConstant()->toInt32(); + } if (((ptr + access->offset()) & (access->byteSize() - 1)) == 0) { return false; } @@ -908,14 +910,27 @@ GetMaxOffsetGuardLimit(moduleEnv_.hugeMemoryEnabled()); if ((*base)->isConstant()) { - uint32_t basePtr = (*base)->toConstant()->toInt32(); + uint64_t basePtr = 0; + if (isMem64()) { + basePtr = uint64_t((*base)->toConstant()->toInt64()); + } else { + basePtr = uint64_t(int64_t((*base)->toConstant()->toInt32())); + } + uint32_t offset = access->offset(); if (offset < offsetGuardLimit && basePtr < offsetGuardLimit - offset) { - auto* ins = MConstant::New(alloc(), Int32Value(0), MIRType::Int32); + offset += uint32_t(basePtr); + access->setOffset(offset); + + MConstant* ins = nullptr; + if (isMem64()) { + ins = MConstant::NewInt64(alloc(), 0); + } else { + ins = MConstant::New(alloc(), Int32Value(0), MIRType::Int32); + } curBlock_->add(ins); *base = ins; - access->setOffset(access->offset() + basePtr); } } } @@ -933,7 +948,7 @@ } } - MWasmLoadTls* needBoundsCheck(bool* limitIs64Bits_) { + MWasmLoadTls* needBoundsCheck() { #ifdef JS_64BIT // For 32-bit base pointers: // @@ -944,38 +959,35 @@ // // If the memory's max size is known to be smaller than 64K pages exactly, // we can use a 32-bit check and avoid extension and wrapping. - bool limitIs64Bits = - !moduleEnv_.memory->boundsCheckLimitIs32Bits() && + bool mem32LimitIs64Bits = + isMem32() && !moduleEnv_.memory->boundsCheckLimitIs32Bits() && ArrayBufferObject::maxBufferByteLength() >= 0x100000000; #else // On 32-bit platforms we have no more than 2GB memory and the limit for a // 32-bit base pointer is never a 64-bit value. - bool limitIs64Bits = false; + bool mem32LimitIs64Bits = false; #endif - MWasmLoadTls* boundsCheckLimit = maybeLoadBoundsCheckLimit( - limitIs64Bits ? MIRType::Int64 : MIRType::Int32); - if (boundsCheckLimit) { - *limitIs64Bits_ = limitIs64Bits; - } - return boundsCheckLimit; + return maybeLoadBoundsCheckLimit( + mem32LimitIs64Bits || isMem64() ? MIRType::Int64 : MIRType::Int32); } - void performBoundsCheck(MDefinition** base, MWasmLoadTls* boundsCheckLimit, - bool limitIs64Bits) { - // At the outset, actualBase could be the result of pretty much any i32 - // operation, or it could be the load of an i32 constant. We may assume - // the value has a canonical representation for the platform, see doc - // block in MacroAssembler.h. + void performBoundsCheck(MDefinition** base, MWasmLoadTls* boundsCheckLimit) { + // At the outset, actualBase could be the result of pretty much any integer + // operation, or it could be the load of an integer constant. If its type + // is i32, we may assume the value has a canonical representation for the + // platform, see doc block in MacroAssembler.h. MDefinition* actualBase = *base; - // Extend the index value to perform a 64-bit bounds check if the memory - // can be 4GB. - - if (limitIs64Bits) { + // Extend an i32 index value to perform a 64-bit bounds check if the memory + // can be 4GB or larger. + bool extendAndWrapIndex = + isMem32() && boundsCheckLimit->type() == MIRType::Int64; + if (extendAndWrapIndex) { auto* extended = MWasmExtendU32Index::New(alloc(), actualBase); curBlock_->add(extended); actualBase = extended; } + auto* ins = MWasmBoundsCheck::New(alloc(), actualBase, boundsCheckLimit, bytecodeOffset()); curBlock_->add(ins); @@ -984,9 +996,8 @@ // If we're masking, then we update *base to create a dependency chain // through the masked index. But we will first need to wrap the index // value if it was extended above. - if (JitOptions.spectreIndexMasking) { - if (limitIs64Bits) { + if (extendAndWrapIndex) { auto* wrapped = MWasmWrapU32Index::New(alloc(), actualBase); curBlock_->add(wrapped); actualBase = wrapped; @@ -997,6 +1008,12 @@ // Perform all necessary checking before a wasm heap access, based on the // attributes of the access and base pointer. + // + // For 64-bit indices on platforms that are limited to indices that fit into + // 32 bits (all 32-bit platforms and mips64), this returns a bounds-checked + // `base` that has type Int32. Lowering code depends on this and will assert + // that the base has this type. See the end of this function. + void checkOffsetAndAlignmentAndBounds(MemoryAccessDesc* access, MDefinition** base) { MOZ_ASSERT(!inDeadCode()); @@ -1025,11 +1042,25 @@ // Emit the bounds check if necessary; it traps if it fails. This may // update *base. - bool limitIs64Bits = false; - MWasmLoadTls* boundsCheckLimit = needBoundsCheck(&limitIs64Bits); + MWasmLoadTls* boundsCheckLimit = needBoundsCheck(); if (boundsCheckLimit) { - performBoundsCheck(base, boundsCheckLimit, limitIs64Bits); + performBoundsCheck(base, boundsCheckLimit); } + +#ifndef JS_64BIT + if (isMem64()) { + // We must have had an explicit bounds check (or one was elided if it was + // proved redundant), and on 32-bit systems the index will for sure fit in + // 32 bits: the max memory is 2GB. So chop the index down to 32-bit to + // simplify the back-end. + MOZ_ASSERT((*base)->type() == MIRType::Int64); + MOZ_ASSERT(!moduleEnv_.hugeMemoryEnabled()); + auto* chopped = MWasmWrapU32Index::New(alloc(), *base); + MOZ_ASSERT(chopped->type() == MIRType::Int32); + curBlock_->add(chopped); + *base = chopped; + } +#endif } bool isSmallerAccessForI64(ValType result, const MemoryAccessDesc* access) { @@ -1042,6 +1073,9 @@ } public: + bool isMem32() { return moduleEnv_.memory->indexType() == IndexType::I32; } + bool isMem64() { return moduleEnv_.memory->indexType() == IndexType::I64; } + // Add the offset into the pointer to yield the EA; trap on overflow. MDefinition* computeEffectiveAddress(MDefinition* base, MemoryAccessDesc* access) { @@ -1074,6 +1108,9 @@ access->type()); } else { checkOffsetAndAlignmentAndBounds(access, &base); +#ifndef JS_64BIT + MOZ_ASSERT(base->type() == MIRType::Int32); +#endif load = MWasmLoad::New(alloc(), memoryBase, base, *access, ToMIRType(result)); } @@ -1099,6 +1136,9 @@ access->type(), v); } else { checkOffsetAndAlignmentAndBounds(access, &base); +#ifndef JS_64BIT + MOZ_ASSERT(base->type() == MIRType::Int32); +#endif store = MWasmStore::New(alloc(), memoryBase, base, *access, v); } if (!store) { @@ -1116,6 +1156,9 @@ } checkOffsetAndAlignmentAndBounds(access, &base); +#ifndef JS_64BIT + MOZ_ASSERT(base->type() == MIRType::Int32); +#endif if (isSmallerAccessForI64(result, access)) { auto* cvtOldv = @@ -1153,6 +1196,9 @@ } checkOffsetAndAlignmentAndBounds(access, &base); +#ifndef JS_64BIT + MOZ_ASSERT(base->type() == MIRType::Int32); +#endif if (isSmallerAccessForI64(result, access)) { auto* cvtValue = @@ -1186,6 +1232,9 @@ } checkOffsetAndAlignmentAndBounds(access, &base); +#ifndef JS_64BIT + MOZ_ASSERT(base->type() == MIRType::Int32); +#endif if (isSmallerAccessForI64(result, access)) { auto* cvtValue = @@ -1279,6 +1328,9 @@ MDefinition* base = addr.base; MOZ_ASSERT(!moduleEnv_.isAsmJS()); checkOffsetAndAlignmentAndBounds(&access, &base); +# ifndef JS_64BIT + MOZ_ASSERT(base->type() == MIRType::Int32); +# endif MInstruction* load = MWasmLoadLaneSimd128::New( alloc(), memoryBase, base, access, laneSize, laneIndex, src); if (!load) { @@ -1300,6 +1352,9 @@ MDefinition* base = addr.base; MOZ_ASSERT(!moduleEnv_.isAsmJS()); checkOffsetAndAlignmentAndBounds(&access, &base); +# ifndef JS_64BIT + MOZ_ASSERT(base->type() == MIRType::Int32); +# endif MInstruction* store = MWasmStoreLaneSimd128::New( alloc(), memoryBase, base, access, laneSize, laneIndex, src); if (!store) { @@ -3287,6 +3342,7 @@ return false; } + MOZ_ASSERT(f.isMem32()); // asm.js opcode MemoryAccessDesc access(viewType, addr.align, addr.offset, f.bytecodeIfNotAsmJS()); @@ -3311,6 +3367,7 @@ MOZ_CRASH("unexpected coerced store"); } + MOZ_ASSERT(f.isMem32()); // asm.js opcode MemoryAccessDesc access(viewType, addr.align, addr.offset, f.bytecodeIfNotAsmJS()); @@ -3411,7 +3468,9 @@ static bool EmitMemoryGrow(FunctionCompiler& f) { uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode(); - const SymbolicAddressSignature& callee = SASigMemoryGrow; + const SymbolicAddressSignature& callee = + !f.moduleEnv().usesMemory() || f.isMem32() ? SASigMemoryGrowM32 + : SASigMemoryGrowM64; CallCompileState args; if (!f.passInstance(callee.argTypes[0], &args)) { return false; @@ -3440,7 +3499,9 @@ static bool EmitMemorySize(FunctionCompiler& f) { uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode(); - const SymbolicAddressSignature& callee = SASigMemorySize; + const SymbolicAddressSignature& callee = + !f.moduleEnv().usesMemory() || f.isMem32() ? SASigMemorySizeM32 + : SASigMemorySizeM64; CallCompileState args; if (!f.iter().readMemorySize()) { @@ -3542,7 +3603,8 @@ uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode(); const SymbolicAddressSignature& callee = - type == ValType::I32 ? SASigWaitI32 : SASigWaitI64; + f.isMem32() ? (type == ValType::I32 ? SASigWaitI32M32 : SASigWaitI64M32) + : (type == ValType::I32 ? SASigWaitI32M64 : SASigWaitI64M64); CallCompileState args; if (!f.passInstance(callee.argTypes[0], &args)) { return false; @@ -3600,7 +3662,8 @@ static bool EmitWake(FunctionCompiler& f) { uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode(); - const SymbolicAddressSignature& callee = SASigWake; + const SymbolicAddressSignature& callee = + f.isMem32() ? SASigWakeM32 : SASigWakeM64; CallCompileState args; if (!f.passInstance(callee.argTypes[0], &args)) { return false; @@ -3664,8 +3727,9 @@ uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode(); const SymbolicAddressSignature& callee = - (f.moduleEnv().usesSharedMemory() ? SASigMemCopyShared32 - : SASigMemCopy32); + (f.moduleEnv().usesSharedMemory() + ? (f.isMem32() ? SASigMemCopySharedM32 : SASigMemCopySharedM64) + : (f.isMem32() ? SASigMemCopyM32 : SASigMemCopyM64)); CallCompileState args; if (!f.passInstance(callee.argTypes[0], &args)) { return false; @@ -3691,8 +3755,8 @@ return f.builtinInstanceMethodCall(callee, lineOrBytecode, args); } -static bool EmitMemCopyInline(FunctionCompiler& f, MDefinition* dst, - MDefinition* src, MDefinition* len) { +static bool EmitMemCopyInlineM32(FunctionCompiler& f, MDefinition* dst, + MDefinition* src, MDefinition* len) { MOZ_ASSERT(MaxInlineMemoryCopyLength != 0); MOZ_ASSERT(len->isConstant() && len->type() == MIRType::Int32); @@ -3841,10 +3905,12 @@ return true; } - if (len->isConstant() && len->type() == MIRType::Int32 && - len->toConstant()->toInt32() != 0 && - uint32_t(len->toConstant()->toInt32()) <= MaxInlineMemoryCopyLength) { - return EmitMemCopyInline(f, dst, src, len); + if (f.isMem32()) { + if (len->isConstant() && len->type() == MIRType::Int32 && + len->toConstant()->toInt32() != 0 && + uint32_t(len->toConstant()->toInt32()) <= MaxInlineMemoryCopyLength) { + return EmitMemCopyInlineM32(f, dst, src, len); + } } return EmitMemCopyCall(f, dst, src, len); } @@ -3937,7 +4003,9 @@ uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode(); const SymbolicAddressSignature& callee = - f.moduleEnv().usesSharedMemory() ? SASigMemFillShared32 : SASigMemFill32; + (f.moduleEnv().usesSharedMemory() + ? (f.isMem32() ? SASigMemFillSharedM32 : SASigMemFillSharedM64) + : (f.isMem32() ? SASigMemFillM32 : SASigMemFillM64)); CallCompileState args; if (!f.passInstance(callee.argTypes[0], &args)) { return false; @@ -3964,8 +4032,8 @@ return f.builtinInstanceMethodCall(callee, lineOrBytecode, args); } -static bool EmitMemFillInline(FunctionCompiler& f, MDefinition* start, - MDefinition* val, MDefinition* len) { +static bool EmitMemFillInlineM32(FunctionCompiler& f, MDefinition* start, + MDefinition* val, MDefinition* len) { MOZ_ASSERT(MaxInlineMemoryFillLength != 0); MOZ_ASSERT(len->isConstant() && len->type() == MIRType::Int32 && @@ -4069,11 +4137,13 @@ return true; } - if (len->isConstant() && len->type() == MIRType::Int32 && - len->toConstant()->toInt32() != 0 && - uint32_t(len->toConstant()->toInt32()) <= MaxInlineMemoryFillLength && - val->isConstant() && val->type() == MIRType::Int32) { - return EmitMemFillInline(f, start, val, len); + if (f.isMem32()) { + if (len->isConstant() && len->type() == MIRType::Int32 && + len->toConstant()->toInt32() != 0 && + uint32_t(len->toConstant()->toInt32()) <= MaxInlineMemoryFillLength && + val->isConstant() && val->type() == MIRType::Int32) { + return EmitMemFillInlineM32(f, start, val, len); + } } return EmitMemFillCall(f, start, val, len); } @@ -4093,7 +4163,8 @@ uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode(); const SymbolicAddressSignature& callee = - isMem ? SASigMemInit32 : SASigTableInit; + isMem ? (f.isMem32() ? SASigMemInitM32 : SASigMemInitM64) + : SASigTableInit; CallCompileState args; if (!f.passInstance(callee.argTypes[0], &args)) { return false; @@ -5885,7 +5956,11 @@ MIRGenerator mir(nullptr, options, &alloc, &graph, &compileInfo, IonOptimizations.get(OptimizationLevel::Wasm)); if (moduleEnv.usesMemory()) { - mir.initMinWasmHeapLength(moduleEnv.memory->initialLength32()); + if (moduleEnv.memory->indexType() == IndexType::I32) { + mir.initMinWasmHeapLength(moduleEnv.memory->initialLength32()); + } else { + mir.initMinWasmHeapLength(moduleEnv.memory->initialLength64()); + } } // Build MIR graph diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmJS.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmJS.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmJS.cpp 2021-10-17 14:42:46.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmJS.cpp 2021-10-20 19:28:29.000000000 +0000 @@ -779,10 +779,6 @@ // be used into CompileAndSerialize(). compileArgs->ionEnabled = true; - // The caller must ensure that huge memory support is configured the same in - // the receiving process of this serialized module. - compileArgs->features.hugeMemory = wasm::IsHugeMemoryEnabled(); - SerializeListener listener(serialized); UniqueChars error; @@ -1146,6 +1142,7 @@ } static JSObject* MemoryTypeToObject(JSContext* cx, bool shared, + wasm::IndexType indexType, wasm::Pages minPages, Maybe maxPages) { Rooted props(cx, IdValueVector(cx)); @@ -1165,6 +1162,16 @@ return nullptr; } +# ifdef ENABLE_WASM_MEMORY64 + RootedString it( + cx, JS_NewStringCopyZ(cx, indexType == IndexType::I32 ? "i32" : "i64")); + if (!props.append( + IdValuePair(NameToId(cx->names().index), StringValue(it)))) { + ReportOutOfMemory(cx); + return nullptr; + } +# endif + if (!props.append( IdValuePair(NameToId(cx->names().shared), BooleanValue(shared)))) { ReportOutOfMemory(cx); @@ -1450,8 +1457,8 @@ MOZ_ASSERT(memoryIndex == 0); const MemoryDesc& memory = *metadata.memory; typeObj = - MemoryTypeToObject(cx, memory.isShared(), memory.initialPages(), - memory.maximumPages()); + MemoryTypeToObject(cx, memory.isShared(), memory.indexType(), + memory.initialPages(), memory.maximumPages()); break; } case DefinitionKind::Global: { @@ -1557,8 +1564,8 @@ case DefinitionKind::Memory: { const MemoryDesc& memory = *metadata.memory; typeObj = - MemoryTypeToObject(cx, memory.isShared(), memory.initialPages(), - memory.maximumPages()); + MemoryTypeToObject(cx, memory.isShared(), memory.indexType(), + memory.initialPages(), memory.maximumPages()); break; } case DefinitionKind::Global: { @@ -2575,7 +2582,7 @@ /* static */ WasmMemoryObject* WasmMemoryObject::create( - JSContext* cx, HandleArrayBufferObjectMaybeShared buffer, + JSContext* cx, HandleArrayBufferObjectMaybeShared buffer, bool isHuge, HandleObject proto) { AutoSetNewObjectMetadata metadata(cx); auto* obj = NewObjectWithGivenProto(cx, proto); @@ -2584,6 +2591,7 @@ } obj->initReservedSlot(BUFFER_SLOT, ObjectValue(*buffer)); + obj->initReservedSlot(ISHUGE_SLOT, BooleanValue(isHuge)); MOZ_ASSERT(!obj->hasObservers()); return obj; @@ -2615,7 +2623,7 @@ return false; } - if (Pages(limits.initial) > MaxMemoryPages()) { + if (Pages(limits.initial) > MaxMemoryPages(limits.indexType)) { JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_MEM_IMP_LIMIT); return false; @@ -2623,7 +2631,7 @@ MemoryDesc memory(limits); RootedArrayBufferObjectMaybeShared buffer(cx); - if (!CreateWasmBuffer32(cx, memory, &buffer)) { + if (!CreateWasmBuffer(cx, memory, &buffer)) { return false; } @@ -2634,8 +2642,9 @@ return false; } - RootedWasmMemoryObject memoryObj(cx, - WasmMemoryObject::create(cx, buffer, proto)); + RootedWasmMemoryObject memoryObj( + cx, WasmMemoryObject::create( + cx, buffer, IsHugeMemoryEnabled(limits.indexType), proto)); if (!memoryObj) { return false; } @@ -2747,9 +2756,10 @@ bool WasmMemoryObject::typeImpl(JSContext* cx, const CallArgs& args) { RootedWasmMemoryObject memoryObj( cx, &args.thisv().toObject().as()); - RootedObject typeObj(cx, MemoryTypeToObject(cx, memoryObj->isShared(), - memoryObj->volatilePages(), - memoryObj->sourceMaxPages())); + RootedObject typeObj( + cx, MemoryTypeToObject(cx, memoryObj->isShared(), memoryObj->indexType(), + memoryObj->volatilePages(), + memoryObj->sourceMaxPages())); if (!typeObj) { return false; } @@ -2829,15 +2839,7 @@ } bool WasmMemoryObject::isHuge() const { -#ifdef WASM_SUPPORTS_HUGE_MEMORY - // TODO: Turn this into a static_assert, if we are able to make - // MaxMemoryBytes() constexpr once the dust settles for the 4GB heaps. - MOZ_ASSERT(MaxMemoryBytes() < HugeMappedSize, - "Non-huge buffer may be confused as huge"); - return buffer().wasmMappedSize() >= HugeMappedSize; -#else - return false; -#endif + return getReservedSlot(ISHUGE_SLOT).toBoolean(); } bool WasmMemoryObject::movingGrowable() const { @@ -2860,7 +2862,7 @@ MOZ_ASSERT(mappedSize >= wasm::GuardSize); MOZ_ASSERT(wasm::IsValidBoundsCheckImmediate(mappedSize - wasm::GuardSize)); size_t limit = mappedSize - wasm::GuardSize; - MOZ_ASSERT(limit <= MaxMemoryBoundsCheckLimit()); + MOZ_ASSERT(limit <= MaxMemoryBoundsCheckLimit(indexType())); return limit; } @@ -2882,30 +2884,28 @@ } /* static */ -uint32_t WasmMemoryObject::growShared(HandleWasmMemoryObject memory, - uint32_t delta) { +uint64_t WasmMemoryObject::growShared(HandleWasmMemoryObject memory, + uint64_t delta) { SharedArrayRawBuffer* rawBuf = memory->sharedArrayRawBuffer(); SharedArrayRawBuffer::Lock lock(rawBuf); Pages oldNumPages = rawBuf->volatileWasmPages(); Pages newPages = oldNumPages; if (!newPages.checkedIncrement(Pages(delta))) { - return -1; + return uint64_t(int64_t(-1)); } - if (!rawBuf->wasmGrowToPagesInPlace(lock, newPages)) { - return -1; + if (!rawBuf->wasmGrowToPagesInPlace(lock, memory->indexType(), newPages)) { + return uint64_t(int64_t(-1)); } // New buffer objects will be created lazily in all agents (including in // this agent) by bufferGetterImpl, above, so no more work to do here. - // It is safe to cast to uint32_t, as oldNumPages was within our - // implementation limits of MaxMemoryPages(), which is within uint32_t. - return uint32_t(oldNumPages.value()); + return oldNumPages.value(); } /* static */ -uint32_t WasmMemoryObject::grow(HandleWasmMemoryObject memory, uint32_t delta, +uint64_t WasmMemoryObject::grow(HandleWasmMemoryObject memory, uint64_t delta, JSContext* cx) { if (memory->isShared()) { return growShared(memory, delta); @@ -2917,29 +2917,27 @@ // TODO (large ArrayBuffer): For Cranelift, limit the memory size to something // that fits in a uint32_t. See more information at the definition of // MaxMemoryBytes(). - // - // TODO: Turn this into a static_assert, if we are able to make - // MaxMemoryBytes() constexpr once the dust settles for the 4GB heaps. - MOZ_ASSERT(MaxMemoryBytes() <= UINT32_MAX, "Avoid 32-bit overflows"); + MOZ_ASSERT(MaxMemoryBytes(memory->indexType()) <= UINT32_MAX, + "Avoid 32-bit overflows"); #endif Pages oldNumPages = oldBuf->wasmPages(); Pages newPages = oldNumPages; if (!newPages.checkedIncrement(Pages(delta))) { - return -1; + return uint64_t(int64_t(-1)); } RootedArrayBufferObject newBuf(cx); if (memory->movingGrowable()) { MOZ_ASSERT(!memory->isHuge()); - if (!ArrayBufferObject::wasmMovingGrowToPages(newPages, oldBuf, &newBuf, - cx)) { - return -1; - } - } else if (!ArrayBufferObject::wasmGrowToPagesInPlace(newPages, oldBuf, - &newBuf, cx)) { - return -1; + if (!ArrayBufferObject::wasmMovingGrowToPages(memory->indexType(), newPages, + oldBuf, &newBuf, cx)) { + return uint64_t(int64_t(-1)); + } + } else if (!ArrayBufferObject::wasmGrowToPagesInPlace( + memory->indexType(), newPages, oldBuf, &newBuf, cx)) { + return uint64_t(int64_t(-1)); } memory->setReservedSlot(BUFFER_SLOT, ObjectValue(*newBuf)); @@ -2953,9 +2951,7 @@ } } - // It is safe to cast to uint32_t, as oldNumPages was within our - // implementation limits of MaxMemoryPages(), which is within uint32_t. - return uint32_t(oldNumPages.value()); + return oldNumPages.value(); } bool js::wasm::IsSharedWasmMemoryObject(JSObject* obj) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmJS.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmJS.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmJS.h 2021-10-17 14:42:45.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmJS.h 2021-10-20 19:28:30.000000000 +0000 @@ -365,6 +365,7 @@ class WasmMemoryObject : public NativeObject { static const unsigned BUFFER_SLOT = 0; static const unsigned OBSERVERS_SLOT = 1; + static const unsigned ISHUGE_SLOT = 2; static const JSClassOps classOps_; static const ClassSpec classSpec_; static void finalize(JSFreeOp* fop, JSObject* obj); @@ -374,7 +375,7 @@ static bool type(JSContext* cx, unsigned argc, Value* vp); static bool growImpl(JSContext* cx, const CallArgs& args); static bool grow(JSContext* cx, unsigned argc, Value* vp); - static uint32_t growShared(HandleWasmMemoryObject memory, uint32_t delta); + static uint64_t growShared(HandleWasmMemoryObject memory, uint64_t delta); using InstanceSet = JS::WeakCache buffer, - HandleObject proto); + bool isHuge, HandleObject proto); // `buffer()` returns the current buffer object always. If the buffer // represents shared memory then `buffer().byteLength()` never changes, and @@ -430,7 +431,7 @@ SharedArrayRawBuffer* sharedArrayRawBuffer() const; bool addMovingGrowObserver(JSContext* cx, WasmInstanceObject* instance); - static uint32_t grow(HandleWasmMemoryObject memory, uint32_t delta, + static uint64_t grow(HandleWasmMemoryObject memory, uint64_t delta, JSContext* cx); }; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmMemory.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmMemory.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmMemory.cpp 2021-10-17 14:42:45.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmMemory.cpp 2021-10-20 19:28:30.000000000 +0000 @@ -21,6 +21,7 @@ #include "mozilla/MathAlgorithms.h" #include "vm/ArrayBufferObject.h" #include "wasm/WasmCodegenTypes.h" +#include "wasm/WasmProcess.h" using mozilla::IsPowerOfTwo; @@ -182,6 +183,68 @@ * to be aligned, only the pointer need be checked. */ +// Bounds checks always compare the base of the memory access with the bounds +// check limit. If the memory access is unaligned, this means that, even if the +// bounds check succeeds, a few bytes of the access can extend past the end of +// memory. To guard against this, extra space is included in the guard region to +// catch the overflow. MaxMemoryAccessSize is a conservative approximation of +// the maximum guard space needed to catch all unaligned overflows. +// +// Also see "Linear memory addresses and bounds checking" above. + +static const unsigned MaxMemoryAccessSize = LitVal::sizeofLargestValue(); + +// All plausible targets must be able to do at least IEEE754 double +// loads/stores, hence the lower limit of 8. Some Intel processors support +// AVX-512 loads/stores, hence the upper limit of 64. +static_assert(MaxMemoryAccessSize >= 8, "MaxMemoryAccessSize too low"); +static_assert(MaxMemoryAccessSize <= 64, "MaxMemoryAccessSize too high"); +static_assert((MaxMemoryAccessSize & (MaxMemoryAccessSize - 1)) == 0, + "MaxMemoryAccessSize is not a power of two"); + +#ifdef WASM_SUPPORTS_HUGE_MEMORY + +static_assert(MaxMemoryAccessSize <= HugeUnalignedGuardPage, + "rounded up to static page size"); +static_assert(HugeOffsetGuardLimit < UINT32_MAX, + "checking for overflow against OffsetGuardLimit is enough."); + +// We have only tested huge memory on x64 and arm64. +# if !(defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM64)) +# error "Not an expected configuration" +# endif + +#endif + +// On !WASM_SUPPORTS_HUGE_MEMORY platforms: +// - To avoid OOM in ArrayBuffer::prepareForAsmJS, asm.js continues to use the +// original ArrayBuffer allocation which has no guard region at all. +// - For WebAssembly memories, an additional GuardSize is mapped after the +// accessible region of the memory to catch folded (base+offset) accesses +// where `offset < OffsetGuardLimit` as well as the overflow from unaligned +// accesses, as described above for MaxMemoryAccessSize. + +static const size_t OffsetGuardLimit = PageSize - MaxMemoryAccessSize; + +static_assert(MaxMemoryAccessSize < GuardSize, + "Guard page handles partial out-of-bounds"); +static_assert(OffsetGuardLimit < UINT32_MAX, + "checking for overflow against OffsetGuardLimit is enough."); + +size_t wasm::GetMaxOffsetGuardLimit(bool hugeMemory) { +#ifdef WASM_SUPPORTS_HUGE_MEMORY + return hugeMemory ? HugeOffsetGuardLimit : OffsetGuardLimit; +#else + return OffsetGuardLimit; +#endif +} + +// Assert that our minimum offset guard limit covers our inline +// memory.copy/fill optimizations. +static const size_t MinOffsetGuardLimit = OffsetGuardLimit; +static_assert(MaxInlineMemoryCopyLength < MinOffsetGuardLimit, "precondition"); +static_assert(MaxInlineMemoryFillLength < MinOffsetGuardLimit, "precondition"); + #ifdef JS_64BIT # ifdef ENABLE_WASM_CRANELIFT // TODO (large ArrayBuffer): Cranelift needs to be updated to use more than the @@ -191,29 +254,41 @@ // The "-2" here accounts for the !huge-memory case in CreateSpecificWasmBuffer, // which is guarding against an overflow. Also see // WasmMemoryObject::boundsCheckLimit() for related assertions. -wasm::Pages wasm::MaxMemoryPages() { +wasm::Pages wasm::MaxMemoryPages(IndexType) { size_t desired = MaxMemory32LimitField - 2; size_t actual = ArrayBufferObject::maxBufferByteLength() / PageSize; return wasm::Pages(std::min(desired, actual)); } - -size_t wasm::MaxMemoryBoundsCheckLimit() { - return UINT32_MAX - 2 * PageSize + 1; -} # else -wasm::Pages wasm::MaxMemoryPages() { - size_t desired = MaxMemory32LimitField; - size_t actual = ArrayBufferObject::maxBufferByteLength() / PageSize; +wasm::Pages wasm::MaxMemoryPages(IndexType t) { + size_t offsetGuardAllowance = 0; + if (t == IndexType::I64) { + // For memory64, the max memory size is larger than any reasonable buffer + // size. The guard pages must fit within the confines of the buffer, so + // reduce the maximum actual memory size accordingly. Since this memory is + // !huge, there is no extra page to deal with unaligned accesses. + MOZ_ASSERT(!IsHugeMemoryEnabled(t)); + offsetGuardAllowance = OffsetGuardLimit; + } + + size_t desired = + t == IndexType::I64 ? MaxMemory64LimitField : MaxMemory32LimitField; + size_t actual = + (ArrayBufferObject::maxBufferByteLength() - offsetGuardAllowance) / + PageSize; return wasm::Pages(std::min(desired, actual)); } - -size_t wasm::MaxMemoryBoundsCheckLimit() { return size_t(UINT32_MAX) + 1; } # endif + +size_t wasm::MaxMemoryBoundsCheckLimit(IndexType t) { + return MaxMemoryPages(t).byteLength(); +} + #else // On 32-bit systems, the heap limit must be representable in the nonnegative // range of an int32_t, which means the maximum heap size as observed by wasm // code is one wasm page less than 2GB. -wasm::Pages wasm::MaxMemoryPages() { +wasm::Pages wasm::MaxMemoryPages(IndexType t) { MOZ_ASSERT(ArrayBufferObject::maxBufferByteLength() >= INT32_MAX / PageSize); return wasm::Pages(INT32_MAX / PageSize); } @@ -221,7 +296,7 @@ // The max bounds check limit can be larger than the MaxMemoryPages because it // is really MaxMemoryPages rounded up to the next valid bounds check immediate, // see ComputeMappedSize(). -size_t wasm::MaxMemoryBoundsCheckLimit() { +size_t wasm::MaxMemoryBoundsCheckLimit(IndexType t) { size_t boundsCheckLimit = size_t(INT32_MAX) + 1; MOZ_ASSERT(IsValidBoundsCheckImmediate(boundsCheckLimit)); return boundsCheckLimit; @@ -262,7 +337,7 @@ return i; } -Pages wasm::ClampedMaxPages(Pages initialPages, +Pages wasm::ClampedMaxPages(IndexType t, Pages initialPages, const Maybe& sourceMaxPages, bool useHugeMemory) { Pages clampedMaxPages; @@ -270,7 +345,7 @@ if (sourceMaxPages.isSome()) { // There is a specified maximum, clamp it to the implementation limit of // maximum pages - clampedMaxPages = std::min(*sourceMaxPages, wasm::MaxMemoryPages()); + clampedMaxPages = std::min(*sourceMaxPages, wasm::MaxMemoryPages(t)); #if defined(JS_64BIT) && defined(ENABLE_WASM_CRANELIFT) // On 64-bit platforms when we aren't using huge memory and we're using @@ -285,10 +360,10 @@ #ifndef JS_64BIT static_assert(sizeof(uintptr_t) == 4, "assuming not 64 bit implies 32 bit"); - // On 32-bit platforms, prevent applications specifying a large max - // (like MaxMemory32Pages) from unintentially OOMing the browser: they just - // want "a lot of memory". Maintain the invariant that - // initialPages <= clampedMaxPages. + // On 32-bit platforms, prevent applications specifying a large max (like + // MaxMemoryPages()) from unintentially OOMing the browser: they just want + // "a lot of memory". Maintain the invariant that initialPages <= + // clampedMaxPages. static const uint64_t OneGib = 1 << 30; static const Pages OneGibPages = Pages(OneGib >> wasm::PageBits); static_assert(HighestValidARMImmediate > OneGib, @@ -300,13 +375,13 @@ } else { // There is not a specified maximum, fill it in with the implementation // limit of maximum pages - clampedMaxPages = wasm::MaxMemoryPages(); + clampedMaxPages = wasm::MaxMemoryPages(t); } // Double-check our invariants MOZ_RELEASE_ASSERT(sourceMaxPages.isNothing() || clampedMaxPages <= *sourceMaxPages); - MOZ_RELEASE_ASSERT(clampedMaxPages <= wasm::MaxMemoryPages()); + MOZ_RELEASE_ASSERT(clampedMaxPages <= wasm::MaxMemoryPages(t)); MOZ_RELEASE_ASSERT(initialPages <= clampedMaxPages); return clampedMaxPages; @@ -317,9 +392,9 @@ // implementation limits. size_t maxSize = clampedMaxPages.byteLength(); - // It is the bounds-check limit, not the mapped size, that gets used by - // code. Thus round up the maxSize to the next valid immediate value *before* - // adding in the guard page. + // It is the bounds-check limit, not the mapped size, that gets baked into + // code. Thus round up the maxSize to the next valid immediate value + // *before* adding in the guard page. // // Also see "Wasm Linear Memory Structure" in vm/ArrayBufferObject.cpp. uint64_t boundsCheckLimit = RoundUpToNextValidBoundsCheckImmediate(maxSize); @@ -345,73 +420,3 @@ return i; #endif } - -// Bounds checks always compare the base of the memory access with the bounds -// check limit. If the memory access is unaligned, this means that, even if the -// bounds check succeeds, a few bytes of the access can extend past the end of -// memory. To guard against this, extra space is included in the guard region to -// catch the overflow. MaxMemoryAccessSize is a conservative approximation of -// the maximum guard space needed to catch all unaligned overflows. -// -// Also see "Linear memory addresses and bounds checking" above. - -static const unsigned MaxMemoryAccessSize = LitVal::sizeofLargestValue(); - -// All plausible targets must be able to do at least IEEE754 double -// loads/stores, hence the lower limit of 8. Some Intel processors support -// AVX-512 loads/stores, hence the upper limit of 64. -static_assert(MaxMemoryAccessSize >= 8, "MaxMemoryAccessSize too low"); -static_assert(MaxMemoryAccessSize <= 64, "MaxMemoryAccessSize too high"); -static_assert((MaxMemoryAccessSize & (MaxMemoryAccessSize - 1)) == 0, - "MaxMemoryAccessSize is not a power of two"); - -#ifdef WASM_SUPPORTS_HUGE_MEMORY - -static_assert(MaxMemoryAccessSize <= HugeUnalignedGuardPage, - "rounded up to static page size"); -static_assert(HugeOffsetGuardLimit < UINT32_MAX, - "checking for overflow against OffsetGuardLimit is enough."); - -// We have only tested huge memory on x64 and arm64. -# if !(defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM64)) -# error "Not an expected configuration" -# endif - -// TODO: We want this static_assert back, but it reqires MaxMemory32Bytes to be -// a constant or constexpr function, not a regular function as now. -// -// The assert is also present in WasmMemoryObject::isHuge and -// WasmMemoryObject::grow, so it's OK to comment out here for now. - -// static_assert(MaxMemory32Bytes < HugeMappedSize(), -// "Normal array buffer could be confused with huge memory"); -#endif - -// On !WASM_SUPPORTS_HUGE_MEMORY platforms: -// - To avoid OOM in ArrayBuffer::prepareForAsmJS, asm.js continues to use the -// original ArrayBuffer allocation which has no guard region at all. -// - For WebAssembly memories, an additional GuardSize is mapped after the -// accessible region of the memory to catch folded (base+offset) accesses -// where `offset < OffsetGuardLimit` as well as the overflow from unaligned -// accesses, as described above for MaxMemoryAccessSize. - -static const size_t OffsetGuardLimit = PageSize - MaxMemoryAccessSize; - -static_assert(MaxMemoryAccessSize < GuardSize, - "Guard page handles partial out-of-bounds"); -static_assert(OffsetGuardLimit < UINT32_MAX, - "checking for overflow against OffsetGuardLimit is enough."); - -size_t wasm::GetMaxOffsetGuardLimit(bool hugeMemory) { -#ifdef WASM_SUPPORTS_HUGE_MEMORY - return hugeMemory ? HugeOffsetGuardLimit : OffsetGuardLimit; -#else - return OffsetGuardLimit; -#endif -} - -// Assert that our minimum offset guard limit covers our inline -// memory.copy/fill optimizations. -static const size_t MinOffsetGuardLimit = OffsetGuardLimit; -static_assert(MaxInlineMemoryCopyLength < MinOffsetGuardLimit, "precondition"); -static_assert(MaxInlineMemoryFillLength < MinOffsetGuardLimit, "precondition"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmMemory.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmMemory.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmMemory.h 2021-10-17 14:42:46.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmMemory.h 2021-10-20 19:28:31.000000000 +0000 @@ -57,7 +57,7 @@ // We represent byte lengths using the native word size, as it is assumed that // consumers of this API will only need byte lengths once it is time to // allocate memory, at which point the pages will be checked against the -// implementation limits `MaxMemory32Pages()` and will then be guaranteed to +// implementation limits `MaxMemoryPages()` and will then be guaranteed to // fit in a native word. struct Pages { private: @@ -120,19 +120,19 @@ }; // The largest number of pages the application can request. -extern Pages MaxMemoryPages(); +extern Pages MaxMemoryPages(IndexType t); -// The byte value of MaxMemoryPages(). -static inline size_t MaxMemoryBytes() { return MaxMemoryPages().byteLength(); } +// The byte value of MaxMemoryPages(t). +static inline size_t MaxMemoryBytes(IndexType t) { + return MaxMemoryPages(t).byteLength(); +} -// A value at least as large as MaxMemoryBytes() representing the largest valid +// A value at least as large as MaxMemoryBytes(t) representing the largest valid // bounds check limit on the system. (It can be larger than MaxMemoryBytes() // because bounds check limits are rounded up to fit formal requirements on some // platforms. Also see ComputeMappedSize().) -extern size_t MaxMemoryBoundsCheckLimit(); +extern size_t MaxMemoryBoundsCheckLimit(IndexType t); -// The largest value allowed for the 'maximum' field of a memory of the given -// type. static inline uint64_t MaxMemoryLimitField(IndexType indexType) { return indexType == IndexType::I32 ? MaxMemory32LimitField : MaxMemory64LimitField; @@ -140,7 +140,7 @@ // Compute the 'clamped' maximum size of a memory. See // 'WASM Linear Memory structure' in ArrayBufferObject.cpp for background. -extern Pages ClampedMaxPages(Pages initialPages, +extern Pages ClampedMaxPages(IndexType t, Pages initialPages, const mozilla::Maybe& sourceMaxPages, bool useHugeMemory); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmModule.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmModule.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmModule.cpp 2021-10-17 14:42:46.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmModule.cpp 2021-10-20 19:28:30.000000000 +0000 @@ -168,7 +168,8 @@ // Install the data in the data structures. They will not be visible // until commitTier2(). - if (!code().setTier2(std::move(code2), linkData2)) { + const CodeTier* borrowedTier2; + if (!code().setAndBorrowTier2(std::move(code2), linkData2, &borrowedTier2)) { return false; } @@ -186,7 +187,7 @@ const MetadataTier& metadataTier1 = metadata(Tier::Baseline); auto stubs1 = code().codeTier(Tier::Baseline).lazyStubs().lock(); - auto stubs2 = code().codeTier(Tier::Optimized).lazyStubs().lock(); + auto stubs2 = borrowedTier2->lazyStubs().lock(); MOZ_ASSERT(stubs2->empty()); @@ -204,10 +205,8 @@ } } - const CodeTier& tier2 = code().codeTier(Tier::Optimized); - Maybe stub2Index; - if (!stubs2->createTier2(funcExportIndices, tier2, &stub2Index)) { + if (!stubs2->createTier2(funcExportIndices, *borrowedTier2, &stub2Index)) { return false; } @@ -424,7 +423,7 @@ uint32_t cpu = ObservedCPUFeatures(); if (!buildId->reserve(buildId->length() + - 12 /* "()" + 8 nibbles + "m[+-]" */)) { + 13 /* "()" + 8 nibbles + "m[+-][+-]" */)) { return false; } @@ -436,7 +435,10 @@ buildId->infallibleAppend(')'); buildId->infallibleAppend('m'); - buildId->infallibleAppend(wasm::IsHugeMemoryEnabled() ? '+' : '-'); + buildId->infallibleAppend(wasm::IsHugeMemoryEnabled(IndexType::I32) ? '+' + : '-'); + buildId->infallibleAppend(wasm::IsHugeMemoryEnabled(IndexType::I64) ? '+' + : '-'); return true; } @@ -769,7 +771,7 @@ } if (!CheckLimits(cx, desc.initialPages(), desc.maximumPages(), - /* defaultMax */ MaxMemoryPages(), + /* defaultMax */ MaxMemoryPages(desc.indexType()), /* actualLength */ memory->volatilePages(), memory->sourceMaxPages(), metadata().isAsmJS(), "Memory")) { @@ -782,19 +784,20 @@ } else { MOZ_ASSERT(!metadata().isAsmJS()); - if (desc.initialPages() > MaxMemoryPages()) { + if (desc.initialPages() > MaxMemoryPages(desc.indexType())) { JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_MEM_IMP_LIMIT); return false; } RootedArrayBufferObjectMaybeShared buffer(cx); - if (!CreateWasmBuffer32(cx, desc, &buffer)) { + if (!CreateWasmBuffer(cx, desc, &buffer)) { return false; } RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmMemory)); - memory.set(WasmMemoryObject::create(cx, buffer, proto)); + memory.set(WasmMemoryObject::create( + cx, buffer, IsHugeMemoryEnabled(desc.indexType()), proto)); if (!memory) { return false; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmModuleTypes.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmModuleTypes.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmModuleTypes.h 2021-10-17 14:42:45.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmModuleTypes.h 2021-10-20 19:28:30.000000000 +0000 @@ -517,6 +517,11 @@ return limits.initial * PageSize; } + uint64_t initialLength64() const { + MOZ_ASSERT(indexType() == IndexType::I64); + return limits.initial * PageSize; + } + MemoryDesc() = default; explicit MemoryDesc(Limits limits) : limits(limits) {} }; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmProcess.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmProcess.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmProcess.cpp 2021-10-17 14:42:45.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmProcess.cpp 2021-10-20 19:28:30.000000000 +0000 @@ -334,25 +334,50 @@ size_t(1) << MinAddressBitsForHugeMemory; #endif -ExclusiveData sHugeMemoryEnabled(mutexid::WasmHugeMemoryEnabled); +ExclusiveData sHugeMemoryEnabled32( + mutexid::WasmHugeMemoryEnabled); +ExclusiveData sHugeMemoryEnabled64( + mutexid::WasmHugeMemoryEnabled); -static bool IsHugeMemoryEnabledHelper() { - auto state = sHugeMemoryEnabled.lock(); +static bool IsHugeMemoryEnabledHelper32() { + auto state = sHugeMemoryEnabled32.lock(); return state->get(); } -bool wasm::IsHugeMemoryEnabled() { - static bool enabled = IsHugeMemoryEnabledHelper(); - return enabled; +static bool IsHugeMemoryEnabledHelper64() { + auto state = sHugeMemoryEnabled64.lock(); + return state->get(); +} + +bool wasm::IsHugeMemoryEnabled(wasm::IndexType t) { + static bool enabled32 = IsHugeMemoryEnabledHelper32(); + static bool enabled64 = IsHugeMemoryEnabledHelper64(); + return t == IndexType::I32 ? enabled32 : enabled64; } bool wasm::DisableHugeMemory() { - auto state = sHugeMemoryEnabled.lock(); - return state->set(false); + bool ok = true; + { + auto state = sHugeMemoryEnabled64.lock(); + ok = ok && state->set(false); + } + { + auto state = sHugeMemoryEnabled32.lock(); + ok = ok && state->set(false); + } + return ok; } void ConfigureHugeMemory() { #ifdef WASM_SUPPORTS_HUGE_MEMORY + bool ok = true; + + { + // Currently no huge memory for IndexType::I64, so always set to false. + auto state = sHugeMemoryEnabled64.lock(); + ok = ok && state->set(false); + } + if (gc::SystemAddressBits() < MinAddressBitsForHugeMemory) { return; } @@ -362,9 +387,12 @@ return; } - auto state = sHugeMemoryEnabled.lock(); - bool set = state->set(true); - MOZ_RELEASE_ASSERT(set); + { + auto state = sHugeMemoryEnabled32.lock(); + ok = ok && state->set(true); + } + + MOZ_RELEASE_ASSERT(ok); #endif } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmProcess.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmProcess.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmProcess.h 2021-10-17 14:42:46.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmProcess.h 2021-10-20 19:28:31.000000000 +0000 @@ -21,6 +21,8 @@ #include "mozilla/Atomics.h" +#include "wasm/WasmMemory.h" + namespace js { namespace wasm { @@ -53,9 +55,11 @@ void UnregisterCodeSegment(const CodeSegment* cs); -// Whether this process is configured to use huge memory or not. +// Whether this process is configured to use huge memory or not. Note that this +// is not precise enough to tell whether a particular memory uses huge memory, +// there are additional conditions for that. -bool IsHugeMemoryEnabled(); +bool IsHugeMemoryEnabled(IndexType t); [[nodiscard]] bool DisableHugeMemory(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmTlsData.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmTlsData.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmTlsData.h 2021-10-17 14:42:45.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmTlsData.h 2021-10-20 19:28:30.000000000 +0000 @@ -102,6 +102,10 @@ // baseline-compiled function. void** jumpTable; + // General scratch storage for the baseline compiler, which can't always use + // the stack for this. + uint32_t baselineScratch[2]; + // The globalArea must be the last field. Globals for the module start here // and are inline in this structure. 16-byte alignment is required for SIMD // data. diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmValidate.h firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmValidate.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/src/wasm/WasmValidate.h 2021-10-17 14:42:45.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/src/wasm/WasmValidate.h 2021-10-20 19:28:30.000000000 +0000 @@ -28,6 +28,7 @@ #include "wasm/WasmCompile.h" #include "wasm/WasmCompileArgs.h" #include "wasm/WasmModuleTypes.h" +#include "wasm/WasmProcess.h" #include "wasm/WasmTypeDef.h" namespace js { @@ -97,8 +98,8 @@ #undef WASM_FEATURE Shareable sharedMemoryEnabled() const { return features.sharedMemory; } bool hugeMemoryEnabled() const { - return !isAsmJS() && features.hugeMemory && usesMemory() && - memory->indexType() == IndexType::I32; + return !isAsmJS() && usesMemory() && + IsHugeMemoryEnabled(memory->indexType()); } bool simdWormholeEnabled() const { return features.simdWormhole; } bool intrinsicsEnabled() const { return features.intrinsics; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/xpconnect/loader/ChromeScriptLoader.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/xpconnect/loader/ChromeScriptLoader.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/xpconnect/loader/ChromeScriptLoader.cpp 2021-10-17 14:42:45.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/xpconnect/loader/ChromeScriptLoader.cpp 2021-10-20 19:28:30.000000000 +0000 @@ -290,10 +290,13 @@ : mParent(aParent), mStencil(aStencil), mURL(aOptions.filename()), - mHasReturnValue(!aOptions.noScriptRval), - mLazilyParse(!aOptions.forceFullParse()) { + mHasReturnValue(!aOptions.noScriptRval) { MOZ_ASSERT(aParent); MOZ_ASSERT(aStencil); +#ifdef DEBUG + JS::InstantiateOptions options(aOptions); + options.assertDefault(); +#endif }; void PrecompiledScript::ExecuteInGlobal(JSContext* aCx, HandleObject aGlobal, @@ -303,13 +306,8 @@ RootedObject targetObj(aCx, JS_FindCompilationScope(aCx, aGlobal)); JSAutoRealm ar(aCx, targetObj); - CompileOptions options(aCx); - if (!mLazilyParse) { - // See AsyncScriptCompiler::Start. - options.setForceFullParse(); - } - // mHasReturnValue (noScriptRval) is unused during instantiation. - + // See assertion in constructor. + JS::InstantiateOptions options; Rooted script( aCx, JS::InstantiateGlobalStencil(aCx, options, mStencil)); if (!script) { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/xpconnect/loader/mozJSComponentLoader.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/xpconnect/loader/mozJSComponentLoader.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/xpconnect/loader/mozJSComponentLoader.cpp 2021-10-17 14:42:46.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/xpconnect/loader/mozJSComponentLoader.cpp 2021-10-20 19:28:29.000000000 +0000 @@ -819,7 +819,9 @@ } } - RootedScript script(cx, JS::InstantiateGlobalStencil(cx, options, stencil)); + JS::InstantiateOptions instantiateOptions(options); + RootedScript script( + cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil)); if (!script) { // Propagate the exception, if one exists. Also, don't leave the stale // exception on this context. diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/xpconnect/loader/mozJSSubScriptLoader.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/xpconnect/loader/mozJSSubScriptLoader.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/xpconnect/loader/mozJSSubScriptLoader.cpp 2021-10-17 14:42:46.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/xpconnect/loader/mozJSSubScriptLoader.cpp 2021-10-20 19:28:30.000000000 +0000 @@ -131,8 +131,9 @@ JS::Stencil* stencil) { MOZ_ASSERT(!js::IsWrapper(targetObj)); - JS::RootedScript script(cx, - JS::InstantiateGlobalStencil(cx, options, stencil)); + JS::InstantiateOptions instantiateOptions(options); + JS::RootedScript script( + cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil)); if (!script) { return false; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/xpconnect/loader/PrecompiledScript.h firefox-trunk-95.0~a1~hg20211020r596404/js/xpconnect/loader/PrecompiledScript.h --- firefox-trunk-95.0~a1~hg20211017r596111/js/xpconnect/loader/PrecompiledScript.h 2021-10-17 14:42:46.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/xpconnect/loader/PrecompiledScript.h 2021-10-20 19:28:30.000000000 +0000 @@ -54,7 +54,6 @@ RefPtr mStencil; nsCString mURL; const bool mHasReturnValue; - const bool mLazilyParse; }; } // namespace dom diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/xpconnect/src/XPCJSContext.cpp firefox-trunk-95.0~a1~hg20211020r596404/js/xpconnect/src/XPCJSContext.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/js/xpconnect/src/XPCJSContext.cpp 2021-10-17 14:42:46.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/xpconnect/src/XPCJSContext.cpp 2021-10-20 19:28:29.000000000 +0000 @@ -588,7 +588,7 @@ // Now is a good time to turn on profiling if it's pending. PROFILER_JS_INTERRUPT_CALLBACK(); - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled()) { nsDependentCString filename("unknown file"); JS::AutoFilename scriptFilename; // Computing the line number can be very expensive (see bug 1330231 for diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/js/xpconnect/tests/chrome/test_bug996069.xhtml firefox-trunk-95.0~a1~hg20211020r596404/js/xpconnect/tests/chrome/test_bug996069.xhtml --- firefox-trunk-95.0~a1~hg20211017r596111/js/xpconnect/tests/chrome/test_bug996069.xhtml 2021-10-17 14:42:46.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/js/xpconnect/tests/chrome/test_bug996069.xhtml 2021-10-20 19:28:31.000000000 +0000 @@ -42,11 +42,11 @@ }; var newWin = Cu.evalInSandbox( - "window.open('http://example.org/chrome/js/xpconnect/tests/chrome/file_bug996069.html');", + "window.open('https://example.org/chrome/js/xpconnect/tests/chrome/file_bug996069.html');", sb); } ]]> - `); - } - response.write(``); - response.finish(); - }, DELAY, Ci.nsITimer.TYPE_ONE_SHOT); + timer = new nsTimer( + () => { + if (request.queryString.includes("with-iframe")) { + response.write(``); + } + response.write(``); + response.finish(); + }, + DELAY, + Ci.nsITimer.TYPE_ONE_SHOT + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/mobile/android/geckoview/src/androidTest/assets/www/simple_redirect.sjs firefox-trunk-95.0~a1~hg20211020r596404/mobile/android/geckoview/src/androidTest/assets/www/simple_redirect.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/mobile/android/geckoview/src/androidTest/assets/www/simple_redirect.sjs 2021-10-17 14:42:48.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/mobile/android/geckoview/src/androidTest/assets/www/simple_redirect.sjs 2021-10-20 19:28:33.000000000 +0000 @@ -1,5 +1,4 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setStatusLine(request.httpVersion, 301, "Moved Permanently"); response.setHeader("Location", request.queryString, false); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/modules/libpref/init/all.js firefox-trunk-95.0~a1~hg20211020r596404/modules/libpref/init/all.js --- firefox-trunk-95.0~a1~hg20211017r596111/modules/libpref/init/all.js 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/modules/libpref/init/all.js 2021-10-20 19:28:32.000000000 +0000 @@ -4491,7 +4491,11 @@ pref("devtools.jsonview.enabled", true); // Default theme ("auto", "dark" or "light"). -pref("devtools.theme", "auto", sticky); +#ifdef MOZ_DEV_EDITION + pref("devtools.theme", "dark", sticky); +#else + pref("devtools.theme", "light", sticky); +#endif // Completely disable DevTools entry points, as well as all DevTools command // line arguments This should be merged with devtools.enabled, see Bug 1440675. diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/modules/libpref/init/StaticPrefList.yaml firefox-trunk-95.0~a1~hg20211020r596404/modules/libpref/init/StaticPrefList.yaml --- firefox-trunk-95.0~a1~hg20211017r596111/modules/libpref/init/StaticPrefList.yaml 2021-10-17 14:42:48.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/modules/libpref/init/StaticPrefList.yaml 2021-10-20 19:28:34.000000000 +0000 @@ -1312,6 +1312,13 @@ value: false mirror: always +# Testing-only pref to cause PBrowser creation for a specific BrowsingContext to +# fail, to test the errored codepath. +- name: browser.tabs.remote.testOnly.failPBrowserCreation.enabled + type: bool + value: false + mirror: always + - name: browser.tabs.remote.desktopbehavior type: bool value: false @@ -3753,6 +3760,14 @@ value: false mirror: always +# Points in the native bounds geometry are required to be quantized +# sufficiently to prevent fingerprinting. The WebXR spec suggests +# quantizing to the nearest 5 centimeters. +- name: dom.vr.webxr.quantization + type: AtomicFloat + value: 0.05f + mirror: always + #ifdef XP_WIN # Control firing WidgetMouseEvent by handling Windows pointer messages or # mouse messages. @@ -4920,6 +4935,13 @@ value: true mirror: once +#if !defined(MOZ_WIDGET_ANDROID) +- name: gfx.egl.prefer-gles.enabled + type: bool + value: false + mirror: once +#endif + # [Windows] Whether registry FontSubstitutes entries are used unconditionally, # or only if the original font is not available. #if defined(XP_WIN) @@ -5046,6 +5068,22 @@ value: 0 mirror: always +# SwapInterval +- name: gfx.swap-interval.glx + type: RelaxedAtomicBool + value: true + mirror: always + +- name: gfx.swap-interval.egl + type: RelaxedAtomicBool + mirror: always +#ifdef MOZ_WIDGET_ANDROID + value: true +#else + value: false +#endif + + # Log severe performance warnings to the error console and profiles. # This should be use to quickly find which slow paths are used by test cases. - name: gfx.perf-warnings.enabled @@ -6208,7 +6246,7 @@ #if defined(ENABLE_WASM_MEMORY64) - name: javascript.options.wasm_memory64 type: bool - value: true + value: false mirror: always #endif // defined(ENABLE_WASM_MEMORY64) @@ -6346,15 +6384,6 @@ #endif mirror: always -- name: layers.enable-tiles - type: bool -#if defined(XP_MACOSX) || defined (XP_OPENBSD) - value: true -#else - value: false -#endif - mirror: once - # Force all possible layers to be always active layers. - name: layers.force-active type: bool @@ -6503,30 +6532,6 @@ value: true mirror: always -# We allow for configurable and rectangular tile size to avoid wasting memory -# on devices whose screen size does not align nicely to the default tile size. -# Although layers can be any size, they are often the same size as the screen, -# especially for width. -- name: layers.tile-width - type: int32_t - value: 512 - mirror: once - -- name: layers.tile-height - type: int32_t - value: 512 - mirror: once - -# If this is set the tile size will only be treated as a suggestion. -# On B2G we will round this to the stride of the underlying allocation. -# On any platform we may later use the screen size and ignore -# tile-width/tile-height entirely. Its recommended to turn this off -# if you change the tile size. -- name: layers.tiles.adjust - type: bool - value: true - mirror: once - - name: layers.recycle-allocator-rdd type: bool value: true @@ -6539,7 +6544,7 @@ - name: layers.iosurfaceimage.use-nv12 type: bool - value: false + value: true mirror: once #--------------------------------------------------------------------------- @@ -8833,7 +8838,9 @@ - name: media.webrtc.platformencoder type: bool -#if defined(MOZ_WIDGET_ANDROID) || defined(NIGHTLY_BUILD) +#if defined(MOZ_WIDGET_ANDROID) + value: true +#elif defined(NIGHTLY_BUILD) && !defined(MOZ_WIDGET_GTK) value: true #else value: false @@ -10267,11 +10274,6 @@ # Prefs starting with "privacy." #--------------------------------------------------------------------------- -- name: privacy.file_unique_origin - type: bool - value: true - mirror: always - - name: privacy.fuzzyfox.clockgrainus type: RelaxedAtomicUint32 value: 100 diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/mozglue/baseprofiler/public/BaseProfilerMarkers.h firefox-trunk-95.0~a1~hg20211020r596404/mozglue/baseprofiler/public/BaseProfilerMarkers.h --- firefox-trunk-95.0~a1~hg20211017r596111/mozglue/baseprofiler/public/BaseProfilerMarkers.h 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/mozglue/baseprofiler/public/BaseProfilerMarkers.h 2021-10-20 19:28:32.000000000 +0000 @@ -89,7 +89,12 @@ #ifndef MOZ_GECKO_PROFILER return {}; #else - if (!baseprofiler::profiler_can_accept_markers()) { + if ((aOptions.ThreadId().IsUnspecified() || + aOptions.ThreadId().ThreadId() == profiler_current_thread_id()) + ? !baseprofiler::profiler_thread_is_being_profiled() + // If targetting another thread, we can only check if the profiler + // is active&unpaused. + : !baseprofiler::detail::RacyFeatures::IsActiveAndUnpaused()) { return {}; } return ::mozilla::baseprofiler::AddMarkerToBuffer( diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/mozglue/baseprofiler/public/BaseProfilerState.h firefox-trunk-95.0~a1~hg20211020r596404/mozglue/baseprofiler/public/BaseProfilerState.h --- firefox-trunk-95.0~a1~hg20211017r596111/mozglue/baseprofiler/public/BaseProfilerState.h 2021-10-17 14:42:48.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/mozglue/baseprofiler/public/BaseProfilerState.h 2021-10-20 19:28:34.000000000 +0000 @@ -31,6 +31,7 @@ namespace mozilla::baseprofiler { [[nodiscard]] inline bool profiler_is_active() { return false; } +[[nodiscard]] inline bool profiler_is_active_and_unpaused() { return false; } } // namespace mozilla::baseprofiler @@ -309,21 +310,16 @@ return baseprofiler::detail::RacyFeatures::IsActive(); } -// Same as profiler_is_active(), but with the same extra checks that determine -// if the profiler would currently store markers. So this should be used before -// doing some potentially-expensive work that's used in a marker. E.g.: -// -// if (profiler_can_accept_markers()) { -// BASE_PROFILER_MARKER(name, OTHER, SomeMarkerType, expensivePayload); -// } -[[nodiscard]] inline bool profiler_can_accept_markers() { +// Same as profiler_is_active(), but also checks if the profiler is not paused. +[[nodiscard]] inline bool profiler_is_active_and_unpaused() { return baseprofiler::detail::RacyFeatures::IsActiveAndUnpaused(); } -// Is the profiler active, and is the current thread being profiled? -// (Same caveats and recommented usage as profiler_is_active().) +// Is the profiler active and unpaused, and is the current thread being +// profiled? (Same caveats and recommented usage as profiler_is_active().) [[nodiscard]] inline bool profiler_thread_is_being_profiled() { - return profiler_is_active() && baseprofiler::detail::IsThreadBeingProfiled(); + return baseprofiler::detail::RacyFeatures::IsActiveAndUnpaused() && + baseprofiler::detail::IsThreadBeingProfiled(); } // Is the profiler active and paused? Returns false if the profiler is inactive. diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/mozglue/build/TsanOptions.cpp firefox-trunk-95.0~a1~hg20211020r596404/mozglue/build/TsanOptions.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/mozglue/build/TsanOptions.cpp 2021-10-17 14:42:48.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/mozglue/build/TsanOptions.cpp 2021-10-20 19:28:34.000000000 +0000 @@ -231,12 +231,6 @@ // Bug 1606803 "race:ipv6_is_present\n" - // Bug 1606864 - "race:nsSocketTransport::Close\n" - "race:nsSocketTransport::OnSocketDetached\n" - "race:nsSocketTransport::OnMsgInputClosed\n" - "race:nsSocketTransport::OpenOutputStream\n" - // Bug 1615017 "race:CacheFileMetadata::SetHash\n" "race:CacheFileMetadata::OnDataWritten\n" diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/mozglue/tests/TestBaseProfiler.cpp firefox-trunk-95.0~a1~hg20211020r596404/mozglue/tests/TestBaseProfiler.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/mozglue/tests/TestBaseProfiler.cpp 2021-10-17 14:42:48.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/mozglue/tests/TestBaseProfiler.cpp 2021-10-20 19:28:34.000000000 +0000 @@ -4082,6 +4082,8 @@ printf("baseprofiler_pause()...\n"); baseprofiler::profiler_pause(); + MOZ_RELEASE_ASSERT(!baseprofiler::profiler_thread_is_being_profiled()); + Maybe info = baseprofiler::profiler_get_buffer_info(); MOZ_RELEASE_ASSERT(info.isSome()); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/base/BackgroundFileSaver.cpp firefox-trunk-95.0~a1~hg20211020r596404/netwerk/base/BackgroundFileSaver.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/base/BackgroundFileSaver.cpp 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/base/BackgroundFileSaver.cpp 2021-10-20 19:28:32.000000000 +0000 @@ -121,8 +121,7 @@ NS_IMETHODIMP BackgroundFileSaver::GetObserver(nsIBackgroundFileSaverObserver** aObserver) { NS_ENSURE_ARG_POINTER(aObserver); - *aObserver = mObserver; - NS_IF_ADDREF(*aObserver); + *aObserver = do_AddRef(mObserver).take(); return NS_OK; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/base/LoadInfo.cpp firefox-trunk-95.0~a1~hg20211020r596404/netwerk/base/LoadInfo.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/base/LoadInfo.cpp 2021-10-17 14:42:48.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/base/LoadInfo.cpp 2021-10-20 19:28:34.000000000 +0000 @@ -815,7 +815,7 @@ NS_IMETHODIMP LoadInfo::GetLoadingPrincipal(nsIPrincipal** aLoadingPrincipal) { - NS_IF_ADDREF(*aLoadingPrincipal = mLoadingPrincipal); + *aLoadingPrincipal = do_AddRef(mLoadingPrincipal).take(); return NS_OK; } @@ -833,7 +833,7 @@ NS_IMETHODIMP LoadInfo::GetPrincipalToInherit(nsIPrincipal** aPrincipalToInherit) { - NS_IF_ADDREF(*aPrincipalToInherit = mPrincipalToInherit); + *aPrincipalToInherit = do_AddRef(mPrincipalToInherit).take(); return NS_OK; } @@ -1752,7 +1752,7 @@ NS_IMETHODIMP LoadInfo::GetResultPrincipalURI(nsIURI** aURI) { - NS_IF_ADDREF(*aURI = mResultPrincipalURI); + *aURI = do_AddRef(mResultPrincipalURI).take(); return NS_OK; } @@ -1893,7 +1893,7 @@ NS_IMETHODIMP LoadInfo::GetCspEventListener(nsICSPEventListener** aCSPEventListener) { - NS_IF_ADDREF(*aCSPEventListener = mCSPEventListener); + *aCSPEventListener = do_AddRef(mCSPEventListener).take(); return NS_OK; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/base/nsDNSPrefetch.cpp firefox-trunk-95.0~a1~hg20211020r596404/netwerk/base/nsDNSPrefetch.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/base/nsDNSPrefetch.cpp 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/base/nsDNSPrefetch.cpp 2021-10-20 19:28:33.000000000 +0000 @@ -16,19 +16,17 @@ #include "mozilla/Atomics.h" #include "mozilla/Preferences.h" -static nsIDNSService* sDNSService = nullptr; +static mozilla::StaticRefPtr sDNSService; nsresult nsDNSPrefetch::Initialize(nsIDNSService* aDNSService) { MOZ_ASSERT(NS_IsMainThread()); - NS_IF_RELEASE(sDNSService); sDNSService = aDNSService; - NS_IF_ADDREF(sDNSService); return NS_OK; } nsresult nsDNSPrefetch::Shutdown() { - NS_IF_RELEASE(sDNSService); + sDNSService = nullptr; return NS_OK; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/base/nsIncrementalStreamLoader.cpp firefox-trunk-95.0~a1~hg20211020r596404/netwerk/base/nsIncrementalStreamLoader.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/base/nsIncrementalStreamLoader.cpp 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/base/nsIncrementalStreamLoader.cpp 2021-10-20 19:28:34.000000000 +0000 @@ -41,7 +41,7 @@ /* readonly attribute nsIRequest request; */ NS_IMETHODIMP nsIncrementalStreamLoader::GetRequest(nsIRequest** aRequest) { - NS_IF_ADDREF(*aRequest = mRequest); + *aRequest = do_AddRef(mRequest).take(); return NS_OK; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/base/nsINetUtil.idl firefox-trunk-95.0~a1~hg20211020r596404/netwerk/base/nsINetUtil.idl --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/base/nsINetUtil.idl 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/base/nsINetUtil.idl 2021-10-20 19:28:33.000000000 +0000 @@ -157,6 +157,9 @@ /** Skip C0 and DEL from unescaping */ const unsigned long ESCAPE_URL_SKIP_CONTROL = 1 << 15; + /** %XX-escape external protocol handler URL */ + const unsigned long ESCAPE_URL_EXT_HANDLER = 1 << 17; + /** * %XX-Escape invalid chars in a URL segment. * diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/base/nsInputStreamChannel.cpp firefox-trunk-95.0~a1~hg20211020r596404/netwerk/base/nsInputStreamChannel.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/base/nsInputStreamChannel.cpp 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/base/nsInputStreamChannel.cpp 2021-10-20 19:28:33.000000000 +0000 @@ -55,7 +55,7 @@ NS_IMETHODIMP nsInputStreamChannel::GetContentStream(nsIInputStream** stream) { - NS_IF_ADDREF(*stream = mContentStream); + *stream = do_AddRef(mContentStream).take(); return NS_OK; } @@ -87,8 +87,7 @@ NS_IMETHODIMP nsInputStreamChannel::GetBaseURI(nsIURI** aBaseURI) { - *aBaseURI = mBaseURI; - NS_IF_ADDREF(*aBaseURI); + *aBaseURI = do_AddRef(mBaseURI).take(); return NS_OK; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/base/nsInputStreamPump.cpp firefox-trunk-95.0~a1~hg20211020r596404/netwerk/base/nsInputStreamPump.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/base/nsInputStreamPump.cpp 2021-10-17 14:42:48.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/base/nsInputStreamPump.cpp 2021-10-20 19:28:34.000000000 +0000 @@ -256,7 +256,7 @@ nsInputStreamPump::GetLoadGroup(nsILoadGroup** aLoadGroup) { RecursiveMutexAutoLock lock(mMutex); - NS_IF_ADDREF(*aLoadGroup = mLoadGroup); + *aLoadGroup = do_AddRef(mLoadGroup).take(); return NS_OK; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/base/nsMIMEInputStream.cpp firefox-trunk-95.0~a1~hg20211020r596404/netwerk/base/nsMIMEInputStream.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/base/nsMIMEInputStream.cpp 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/base/nsMIMEInputStream.cpp 2021-10-20 19:28:34.000000000 +0000 @@ -166,8 +166,7 @@ NS_IMETHODIMP nsMIMEInputStream::GetData(nsIInputStream** aStream) { NS_ENSURE_ARG_POINTER(aStream); - *aStream = mStream; - NS_IF_ADDREF(*aStream); + *aStream = do_AddRef(mStream).take(); return NS_OK; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/base/nsNetUtil.cpp firefox-trunk-95.0~a1~hg20211020r596404/netwerk/base/nsNetUtil.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/base/nsNetUtil.cpp 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/base/nsNetUtil.cpp 2021-10-20 19:28:34.000000000 +0000 @@ -2530,37 +2530,6 @@ return false; } - if (!StaticPrefs::privacy_file_unique_origin()) { - // - // If the file to be loaded is in a subdirectory of the source - // (or same-dir if source is not a directory) then it will - // inherit its source principal and be scriptable by that source. - // - bool sourceIsDir; - bool allowed = false; - nsresult rv = sourceFile->IsDirectory(&sourceIsDir); - if (NS_SUCCEEDED(rv) && sourceIsDir) { - rv = sourceFile->Contains(targetFile, &allowed); - } else { - nsCOMPtr sourceParent; - rv = sourceFile->GetParent(getter_AddRefs(sourceParent)); - if (NS_SUCCEEDED(rv) && sourceParent) { - rv = sourceParent->Equals(targetFile, &allowed); - if (NS_FAILED(rv) || !allowed) { - rv = sourceParent->Contains(targetFile, &allowed); - } else { - MOZ_ASSERT(aAllowDirectoryTarget, - "sourceFile->Parent == targetFile, but targetFile " - "should've been disallowed if it is a directory"); - } - } - } - - if (NS_SUCCEEDED(rv) && allowed) { - return true; - } - } - return false; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/base/nsRedirectHistoryEntry.cpp firefox-trunk-95.0~a1~hg20211020r596404/netwerk/base/nsRedirectHistoryEntry.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/base/nsRedirectHistoryEntry.cpp 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/base/nsRedirectHistoryEntry.cpp 2021-10-20 19:28:34.000000000 +0000 @@ -29,13 +29,13 @@ NS_IMETHODIMP nsRedirectHistoryEntry::GetReferrerURI(nsIURI** referrer) { - NS_IF_ADDREF(*referrer = mReferrer); + *referrer = do_AddRef(mReferrer).take(); return NS_OK; } NS_IMETHODIMP nsRedirectHistoryEntry::GetPrincipal(nsIPrincipal** principal) { - NS_IF_ADDREF(*principal = mPrincipal); + *principal = do_AddRef(mPrincipal).take(); return NS_OK; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/base/nsSocketTransport2.cpp firefox-trunk-95.0~a1~hg20211020r596404/netwerk/base/nsSocketTransport2.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/base/nsSocketTransport2.cpp 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/base/nsSocketTransport2.cpp 2021-10-20 19:28:33.000000000 +0000 @@ -2371,14 +2371,14 @@ NS_IMETHODIMP nsSocketTransport::GetSecurityInfo(nsISupports** secinfo) { MutexAutoLock lock(mLock); - NS_IF_ADDREF(*secinfo = mSecInfo); + *secinfo = do_AddRef(mSecInfo).take(); return NS_OK; } NS_IMETHODIMP nsSocketTransport::GetSecurityCallbacks(nsIInterfaceRequestor** callbacks) { MutexAutoLock lock(mLock); - NS_IF_ADDREF(*callbacks = mCallbacks); + *callbacks = do_AddRef(mCallbacks).take(); return NS_OK; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/base/nsSocketTransport2.h firefox-trunk-95.0~a1~hg20211020r596404/netwerk/base/nsSocketTransport2.h --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/base/nsSocketTransport2.h 2021-10-17 14:42:48.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/base/nsSocketTransport2.h 2021-10-20 19:28:34.000000000 +0000 @@ -303,6 +303,9 @@ return (!mProxyHost.IsEmpty() && !mProxyTransparent) ? mProxyHost : mHost; } + Atomic mInputClosed{true}; + Atomic mOutputClosed{true}; + //------------------------------------------------------------------------- // members accessible only on the socket transport thread: // (the exception being initialization/shutdown time) @@ -311,8 +314,6 @@ // socket state vars: uint32_t mState{STATE_CLOSED}; // STATE_??? flags bool mAttached{false}; - bool mInputClosed{true}; - bool mOutputClosed{true}; // this flag is used to determine if the results of a host lookup arrive // recursively or not. this flag is not protected by any lock. @@ -456,7 +457,7 @@ int32_t mKeepaliveRetryIntervalS{-1}; int32_t mKeepaliveProbeCount{-1}; - bool mDoNotRetryToConnect{false}; + Atomic mDoNotRetryToConnect{false}; // Whether the port remapping has already been applied. We definitely want to // prevent duplicate calls in case of chaining remapping. diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/base/nsSocketTransportService2.cpp firefox-trunk-95.0~a1~hg20211020r596404/netwerk/base/nsSocketTransportService2.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/base/nsSocketTransportService2.cpp 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/base/nsSocketTransportService2.cpp 2021-10-20 19:28:34.000000000 +0000 @@ -686,7 +686,7 @@ if (pollTimeout != PR_INTERVAL_NO_WAIT) { profiler_thread_wake(); } - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled()) { PROFILER_MARKER_TEXT( "SocketTransportService::Poll", NETWORK, MarkerTiming::IntervalUntilNowFrom(startTime), diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/base/nsUDPSocket.cpp firefox-trunk-95.0~a1~hg20211020r596404/netwerk/base/nsUDPSocket.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/base/nsUDPSocket.cpp 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/base/nsUDPSocket.cpp 2021-10-20 19:28:34.000000000 +0000 @@ -205,7 +205,7 @@ NS_IMETHODIMP nsUDPMessage::GetOutputStream(nsIOutputStream** aOutputStream) { NS_ENSURE_ARG_POINTER(aOutputStream); - NS_IF_ADDREF(*aOutputStream = mOutputStream); + *aOutputStream = do_AddRef(mOutputStream).take(); return NS_OK; } @@ -369,7 +369,7 @@ NS_IMETHODIMP UDPMessageProxy::GetOutputStream(nsIOutputStream** aOutputStream) { NS_ENSURE_ARG_POINTER(aOutputStream); - NS_IF_ADDREF(*aOutputStream = mOutputStream); + *aOutputStream = do_AddRef(mOutputStream).take(); return NS_OK; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/base/nsURLHelper.cpp firefox-trunk-95.0~a1~hg20211020r596404/netwerk/base/nsURLHelper.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/base/nsURLHelper.cpp 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/base/nsURLHelper.cpp 2021-10-20 19:28:34.000000000 +0000 @@ -35,9 +35,9 @@ //---------------------------------------------------------------------------- static bool gInitialized = false; -static nsIURLParser* gNoAuthURLParser = nullptr; -static nsIURLParser* gAuthURLParser = nullptr; -static nsIURLParser* gStdURLParser = nullptr; +static StaticRefPtr gNoAuthURLParser; +static StaticRefPtr gAuthURLParser; +static StaticRefPtr gStdURLParser; static void InitGlobals() { nsCOMPtr parser; @@ -45,22 +45,19 @@ parser = do_GetService(NS_NOAUTHURLPARSER_CONTRACTID); NS_ASSERTION(parser, "failed getting 'noauth' url parser"); if (parser) { - gNoAuthURLParser = parser.get(); - NS_ADDREF(gNoAuthURLParser); + gNoAuthURLParser = parser; } parser = do_GetService(NS_AUTHURLPARSER_CONTRACTID); NS_ASSERTION(parser, "failed getting 'auth' url parser"); if (parser) { - gAuthURLParser = parser.get(); - NS_ADDREF(gAuthURLParser); + gAuthURLParser = parser; } parser = do_GetService(NS_STDURLPARSER_CONTRACTID); NS_ASSERTION(parser, "failed getting 'std' url parser"); if (parser) { - gStdURLParser = parser.get(); - NS_ADDREF(gStdURLParser); + gStdURLParser = parser; } gInitialized = true; @@ -68,11 +65,11 @@ void net_ShutdownURLHelper() { if (gInitialized) { - NS_IF_RELEASE(gNoAuthURLParser); - NS_IF_RELEASE(gAuthURLParser); - NS_IF_RELEASE(gStdURLParser); gInitialized = false; } + gNoAuthURLParser = nullptr; + gAuthURLParser = nullptr; + gStdURLParser = nullptr; } //---------------------------------------------------------------------------- diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/base/TLSServerSocket.cpp firefox-trunk-95.0~a1~hg20211020r596404/netwerk/base/TLSServerSocket.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/base/TLSServerSocket.cpp 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/base/TLSServerSocket.cpp 2021-10-20 19:28:33.000000000 +0000 @@ -134,8 +134,7 @@ if (NS_WARN_IF(!aCert)) { return NS_ERROR_INVALID_POINTER; } - *aCert = mServerCert; - NS_IF_ADDREF(*aCert); + *aCert = do_AddRef(mServerCert).take(); return NS_OK; } @@ -305,8 +304,7 @@ if (NS_WARN_IF(!aSocket)) { return NS_ERROR_INVALID_POINTER; } - *aSocket = mServerSocket; - NS_IF_ADDREF(*aSocket); + *aSocket = do_AddRef(mServerSocket).take(); return NS_OK; } @@ -315,8 +313,7 @@ if (NS_WARN_IF(!aStatus)) { return NS_ERROR_INVALID_POINTER; } - *aStatus = this; - NS_IF_ADDREF(*aStatus); + *aStatus = do_AddRef(this).take(); return NS_OK; } @@ -325,8 +322,7 @@ if (NS_WARN_IF(!aCert)) { return NS_ERROR_INVALID_POINTER; } - *aCert = mPeerCert; - NS_IF_ADDREF(*aCert); + *aCert = do_AddRef(mPeerCert).take(); return NS_OK; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/cache2/CacheEntry.cpp firefox-trunk-95.0~a1~hg20211020r596404/netwerk/cache2/CacheEntry.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/cache2/CacheEntry.cpp 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/cache2/CacheEntry.cpp 2021-10-20 19:28:33.000000000 +0000 @@ -1325,7 +1325,7 @@ { mozilla::MutexAutoLock lock(mLock); if (mSecurityInfoLoaded) { - NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo); + *aSecurityInfo = do_AddRef(mSecurityInfo).take(); return NS_OK; } } @@ -1350,7 +1350,7 @@ mSecurityInfo.swap(secInfo); mSecurityInfoLoaded = true; - NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo); + *aSecurityInfo = do_AddRef(mSecurityInfo).take(); } return NS_OK; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/cookie/test/browser/oversize.sjs firefox-trunk-95.0~a1~hg20211020r596404/netwerk/cookie/test/browser/oversize.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/cookie/test/browser/oversize.sjs 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/cookie/test/browser/oversize.sjs 2021-10-20 19:28:33.000000000 +0000 @@ -4,6 +4,14 @@ const maxBytesPerCookie = 4096; const maxBytesPerCookiePath = 1024; - aResponse.setHeader("Set-Cookie", "a=" + Array(maxBytesPerCookie + 1).join('x'), true); - aResponse.setHeader("Set-Cookie", "b=c; path=/" + Array(maxBytesPerCookiePath + 1).join('x'), true); + aResponse.setHeader( + "Set-Cookie", + "a=" + Array(maxBytesPerCookie + 1).join("x"), + true + ); + aResponse.setHeader( + "Set-Cookie", + "b=c; path=/" + Array(maxBytesPerCookiePath + 1).join("x"), + true + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/cookie/test/browser/server.sjs firefox-trunk-95.0~a1~hg20211020r596404/netwerk/cookie/test/browser/server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/cookie/test/browser/server.sjs 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/cookie/test/browser/server.sjs 2021-10-20 19:28:34.000000000 +0000 @@ -1,6 +1,6 @@ function handleRequest(aRequest, aResponse) { aResponse.setStatusLine(aRequest.httpVersion, 200); - if (aRequest.hasHeader('Cookie')) { + if (aRequest.hasHeader("Cookie")) { aResponse.write("cookie-present"); } else { aResponse.setHeader("Set-Cookie", "foopy=1"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/cookie/test/mochitest/cookie.sjs firefox-trunk-95.0~a1~hg20211020r596404/netwerk/cookie/test/mochitest/cookie.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/cookie/test/mochitest/cookie.sjs 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/cookie/test/mochitest/cookie.sjs 2021-10-20 19:28:34.000000000 +0000 @@ -9,24 +9,35 @@ } if (parts.includes("fetch")) { - setState("data", JSON.stringify({type: "fetch", hasCookie: aRequest.hasHeader("Cookie") })); + setState( + "data", + JSON.stringify({ type: "fetch", hasCookie: aRequest.hasHeader("Cookie") }) + ); aResponse.write("Hello world!"); return; } if (parts.includes("xhr")) { - setState("data", JSON.stringify({type: "xhr", hasCookie: aRequest.hasHeader("Cookie") })); + setState( + "data", + JSON.stringify({ type: "xhr", hasCookie: aRequest.hasHeader("Cookie") }) + ); aResponse.write("Hello world!"); return; } if (parts.includes("image")) { - setState("data", JSON.stringify({type: "image", hasCookie: aRequest.hasHeader("Cookie") })); + setState( + "data", + JSON.stringify({ type: "image", hasCookie: aRequest.hasHeader("Cookie") }) + ); // A 1x1 PNG image. // Source: https://commons.wikimedia.org/wiki/File:1x1.png (Public Domain) - const IMAGE = atob("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAA" + - "ACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII="); + const IMAGE = atob( + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAA" + + "ACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=" + ); aResponse.setHeader("Content-Type", "image/png", false); aResponse.write(IMAGE); @@ -34,7 +45,13 @@ } if (parts.includes("script")) { - setState("data", JSON.stringify({type: "script", hasCookie: aRequest.hasHeader("Cookie") })); + setState( + "data", + JSON.stringify({ + type: "script", + hasCookie: aRequest.hasHeader("Cookie"), + }) + ); aResponse.setHeader("Content-Type", "text/javascript", false); aResponse.write("window.scriptLoaded();"); @@ -42,7 +59,13 @@ } if (parts.includes("worker")) { - setState("data", JSON.stringify({type: "worker", hasCookie: aRequest.hasHeader("Cookie") })); + setState( + "data", + JSON.stringify({ + type: "worker", + hasCookie: aRequest.hasHeader("Cookie"), + }) + ); function w() { onmessage = e => { @@ -53,7 +76,9 @@ } if (e.data == "fetch") { - fetch("cookie.sjs?fetch&" + Math.random()).then(r => r.text()).then(_ => postMessage(42)); + fetch("cookie.sjs?fetch&" + Math.random()) + .then(r => r.text()) + .then(_ => postMessage(42)); return; } @@ -65,7 +90,7 @@ } }; postMessage(42); - }; + } aResponse.setHeader("Content-Type", "text/javascript", false); aResponse.write(w.toString() + "; w();"); @@ -73,14 +98,26 @@ } if (parts.includes("subworker")) { - setState("data", JSON.stringify({type: "subworker", hasCookie: aRequest.hasHeader("Cookie") })); + setState( + "data", + JSON.stringify({ + type: "subworker", + hasCookie: aRequest.hasHeader("Cookie"), + }) + ); aResponse.setHeader("Content-Type", "text/javascript", false); aResponse.write("42"); return; } if (parts.includes("sharedworker")) { - setState("data", JSON.stringify({type: "sharedworker", hasCookie: aRequest.hasHeader("Cookie") })); + setState( + "data", + JSON.stringify({ + type: "sharedworker", + hasCookie: aRequest.hasHeader("Cookie"), + }) + ); function w() { onconnect = e => { @@ -92,7 +129,9 @@ } if (evt.data == "fetch") { - fetch("cookie.sjs?fetch&" + Math.random()).then(r => r.text()).then(_ => e.ports[0].postMessage(42)); + fetch("cookie.sjs?fetch&" + Math.random()) + .then(r => r.text()) + .then(_ => e.ports[0].postMessage(42)); return; } @@ -105,7 +144,7 @@ }; e.ports[0].postMessage(42); }; - }; + } aResponse.setHeader("Content-Type", "text/javascript", false); aResponse.write(w.toString() + "; w();"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/dns/effective_tld_names.dat firefox-trunk-95.0~a1~hg20211020r596404/netwerk/dns/effective_tld_names.dat --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/dns/effective_tld_names.dat 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/dns/effective_tld_names.dat 2021-10-20 19:28:34.000000000 +0000 @@ -842,7 +842,13 @@ inf.cu // cv : https://en.wikipedia.org/wiki/.cv +// cv : http://www.dns.cv/tldcv_portal/do?com=DS;5446457100;111;+PAGE(4000018)+K-CAT-CODIGO(RDOM)+RCNT(100); <- registration rules cv +com.cv +edu.cv +int.cv +nome.cv +org.cv // cw : http://www.una.cw/cw_registry/ // Confirmed by registry 2013-03-26 @@ -1179,6 +1185,7 @@ web.gu // gw : https://en.wikipedia.org/wiki/.gw +// gw : https://nic.gw/regras/ gw // gy : https://en.wikipedia.org/wiki/.gy @@ -5853,7 +5860,7 @@ org.ps net.ps -// pt : http://online.dns.pt/dns/start_dns +// pt : https://www.dns.pt/en/domain/pt-terms-and-conditions-registration-rules/ pt net.pt gov.pt @@ -10791,6 +10798,10 @@ // Submitted by Apigee Security Team apigee.io +// Apphud : https://apphud.com +// Submitted by Alexander Selivanov +siiites.com + // Appspace : https://www.appspace.com // Submitted by Appspace Security Team appspacehosted.com @@ -11665,10 +11676,6 @@ onred.one staging.onred.one -// One.com: https://www.one.com/ -// Submitted by Jacob Bunk Nielsen -service.one - // EU.org https://eu.org/ // Submitted by Pierre Beyssac eu.org @@ -12912,6 +12919,10 @@ // Submitted by Vicary Archangel omniwe.site +// One.com: https://www.one.com/ +// Submitted by Jacob Bunk Nielsen +service.one + // One Fold Media : http://www.onefoldmedia.com/ // Submitted by Eddie Jones nid.io @@ -13466,6 +13477,11 @@ // Submitted by Bjoern Henke taifun-dns.de +// Tailscale Inc. : https://www.tailscale.com +// Submitted by David Anderson +beta.tailscale.net +ts.net + // TASK geographical domains (www.task.gda.pl/uslugi/dns) gda.pl gdansk.pl diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/ipc/DocumentLoadListener.cpp firefox-trunk-95.0~a1~hg20211020r596404/netwerk/ipc/DocumentLoadListener.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/ipc/DocumentLoadListener.cpp 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/ipc/DocumentLoadListener.cpp 2021-10-20 19:28:34.000000000 +0000 @@ -1467,6 +1467,21 @@ } } +DocumentLoadListener::ProcessBehavior GetProcessSwitchBehavior( + Element* aBrowserElement) { + if (aBrowserElement->HasAttribute(u"maychangeremoteness"_ns)) { + return DocumentLoadListener::ProcessBehavior::PROCESS_BEHAVIOR_STANDARD; + } + nsCOMPtr browser = aBrowserElement->AsBrowser(); + bool isRemoteBrowser = false; + browser->GetIsRemoteBrowser(&isRemoteBrowser); + if (isRemoteBrowser) { + return DocumentLoadListener::ProcessBehavior:: + PROCESS_BEHAVIOR_SUBFRAME_ONLY; + } + return DocumentLoadListener::ProcessBehavior::PROCESS_BEHAVIOR_DISABLED; +} + static bool ContextCanProcessSwitch(CanonicalBrowsingContext* aBrowsingContext, WindowGlobalParent* aParentWindow) { if (NS_WARN_IF(!aBrowsingContext)) { @@ -1507,26 +1522,20 @@ return false; } - nsIBrowser::ProcessBehavior processBehavior = - nsIBrowser::PROCESS_BEHAVIOR_DISABLED; - nsresult rv = browser->GetProcessSwitchBehavior(&processBehavior); - if (NS_FAILED(rv)) { - MOZ_ASSERT_UNREACHABLE( - "nsIBrowser::GetProcessSwitchBehavior shouldn't fail"); - MOZ_LOG(gProcessIsolationLog, LogLevel::Warning, - ("Process Switch Abort: failed to get process switch behavior")); - return false; - } + DocumentLoadListener::ProcessBehavior processBehavior = + GetProcessSwitchBehavior(browserElement); // Check if the process switch we're considering is disabled by the // 's process behavior. - if (processBehavior == nsIBrowser::PROCESS_BEHAVIOR_DISABLED) { + if (processBehavior == + DocumentLoadListener::ProcessBehavior::PROCESS_BEHAVIOR_DISABLED) { MOZ_LOG(gProcessIsolationLog, LogLevel::Warning, ("Process Switch Abort: switch disabled by ")); return false; } - if (!aParentWindow && - processBehavior == nsIBrowser::PROCESS_BEHAVIOR_SUBFRAME_ONLY) { + if (!aParentWindow && processBehavior == + DocumentLoadListener::ProcessBehavior:: + PROCESS_BEHAVIOR_SUBFRAME_ONLY) { MOZ_LOG(gProcessIsolationLog, LogLevel::Warning, ("Process Switch Abort: toplevel switch disabled by ")); return false; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/ipc/DocumentLoadListener.h firefox-trunk-95.0~a1~hg20211020r596404/netwerk/ipc/DocumentLoadListener.h --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/ipc/DocumentLoadListener.h 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/ipc/DocumentLoadListener.h 2021-10-20 19:28:34.000000000 +0000 @@ -289,6 +289,25 @@ bool IsDocumentLoad() const { return mIsDocumentLoad; } + // Determine what process switching behavior a browser element should have. + enum ProcessBehavior : uint8_t { + // Gecko won't automatically change which process this frame, or it's + // subframes, are loaded in. + PROCESS_BEHAVIOR_DISABLED, + + // If `useRemoteTabs` is enabled, Gecko will change which process this frame + // is loaded in automatically, without calling `performProcessSwitch`. + // When `useRemoteSubframes` is enabled, subframes will change processes. + PROCESS_BEHAVIOR_STANDARD, + + // Gecko won't automatically change which process this frame is loaded, but + // when `useRemoteSubframes` is enabled, subframes will change processes. + // + // NOTE: This configuration is included only for backwards compatibility, + // and will be removed, as it can easily lead to invalid behavior. + PROCESS_BEHAVIOR_SUBFRAME_ONLY, + }; + protected: virtual ~DocumentLoadListener(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/protocol/file/nsFileChannel.cpp firefox-trunk-95.0~a1~hg20211020r596404/netwerk/protocol/file/nsFileChannel.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/protocol/file/nsFileChannel.cpp 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/protocol/file/nsFileChannel.cpp 2021-10-20 19:28:33.000000000 +0000 @@ -513,6 +513,6 @@ NS_IMETHODIMP nsFileChannel::GetUploadStream(nsIInputStream** result) { - NS_IF_ADDREF(*result = mUploadStream); + *result = do_AddRef(mUploadStream).take(); return NS_OK; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/protocol/gio/nsGIOProtocolHandler.cpp firefox-trunk-95.0~a1~hg20211020r596404/netwerk/protocol/gio/nsGIOProtocolHandler.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/protocol/gio/nsGIOProtocolHandler.cpp 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/protocol/gio/nsGIOProtocolHandler.cpp 2021-10-20 19:28:34.000000000 +0000 @@ -164,18 +164,7 @@ NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIINPUTSTREAM - explicit nsGIOInputStream(const nsCString& uriSpec) - : mSpec(uriSpec), - mChannel(nullptr), - mHandle(nullptr), - mStream(nullptr), - mBytesRemaining(UINT64_MAX), - mStatus(NS_OK), - mDirList(nullptr), - mDirListPtr(nullptr), - mDirBufCursor(0), - mDirOpen(false), - mMonitorMountInProgress("GIOInputStream::MountFinished") {} + explicit nsGIOInputStream(const nsCString& uriSpec) : mSpec(uriSpec) {} void SetChannel(nsIChannel* channel) { // We need to hold an owning reference to our channel. This is done @@ -191,7 +180,7 @@ // cycle since the channel likely owns this stream. This reference // cycle is broken in our Close method. - NS_ADDREF(mChannel = channel); + mChannel = do_AddRef(channel).take(); } void SetMountResult(MountOperationResult result, gint error_code); @@ -204,19 +193,19 @@ nsresult DoOpenDirectory(); nsresult DoOpenFile(GFileInfo* info); nsCString mSpec; - nsIChannel* mChannel; // manually refcounted - GFile* mHandle; - GFileInputStream* mStream; - uint64_t mBytesRemaining; - nsresult mStatus; - GList* mDirList; - GList* mDirListPtr; + nsIChannel* mChannel{nullptr}; // manually refcounted + GFile* mHandle{nullptr}; + GFileInputStream* mStream{nullptr}; + uint64_t mBytesRemaining{UINT64_MAX}; + nsresult mStatus{NS_OK}; + GList* mDirList{nullptr}; + GList* mDirListPtr{nullptr}; nsCString mDirBuf; - uint32_t mDirBufCursor; - bool mDirOpen; + uint32_t mDirBufCursor{0}; + bool mDirOpen{false}; MountOperationResult mMountRes = MountOperationResult::MOUNT_OPERATION_SUCCESS; - mozilla::Monitor mMonitorMountInProgress; + mozilla::Monitor mMonitorMountInProgress{"GIOInputStream::MountFinished"}; gint mMountErrorCode{}; }; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/protocol/http/ClassifierDummyChannel.cpp firefox-trunk-95.0~a1~hg20211020r596404/netwerk/protocol/http/ClassifierDummyChannel.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/protocol/http/ClassifierDummyChannel.cpp 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/protocol/http/ClassifierDummyChannel.cpp 2021-10-20 19:28:33.000000000 +0000 @@ -96,12 +96,6 @@ SetLoadInfo(aLoadInfo); } -ClassifierDummyChannel::~ClassifierDummyChannel() { - NS_ReleaseOnMainThread("ClassifierDummyChannel::mURI", mURI.forget()); - NS_ReleaseOnMainThread("ClassifierDummyChannel::mTopWindowURI", - mTopWindowURI.forget()); -} - void ClassifierDummyChannel::AddClassificationFlags( uint32_t aClassificationFlags, bool aThirdParty) { if (aThirdParty) { @@ -117,7 +111,7 @@ NS_IMETHODIMP ClassifierDummyChannel::GetOriginalURI(nsIURI** aOriginalURI) { - NS_IF_ADDREF(*aOriginalURI = mURI); + *aOriginalURI = do_AddRef(mURI).take(); return NS_OK; } @@ -129,7 +123,7 @@ NS_IMETHODIMP ClassifierDummyChannel::GetURI(nsIURI** aURI) { - NS_IF_ADDREF(*aURI = mURI); + *aURI = do_AddRef(mURI).take(); return NS_OK; } @@ -239,7 +233,7 @@ NS_IMETHODIMP ClassifierDummyChannel::GetLoadInfo(nsILoadInfo** aLoadInfo) { - NS_IF_ADDREF(*aLoadInfo = mLoadInfo); + *aLoadInfo = do_AddRef(mLoadInfo).take(); return NS_OK; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/protocol/http/ClassifierDummyChannel.h firefox-trunk-95.0~a1~hg20211020r596404/netwerk/protocol/http/ClassifierDummyChannel.h --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/protocol/http/ClassifierDummyChannel.h 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/protocol/http/ClassifierDummyChannel.h 2021-10-20 19:28:34.000000000 +0000 @@ -71,7 +71,7 @@ void AddClassificationFlags(uint32_t aClassificationFlags, bool aThirdParty); private: - ~ClassifierDummyChannel(); + ~ClassifierDummyChannel() = default; nsCOMPtr mLoadInfo; nsCOMPtr mURI; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/protocol/http/HttpBaseChannel.cpp firefox-trunk-95.0~a1~hg20211020r596404/netwerk/protocol/http/HttpBaseChannel.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/protocol/http/HttpBaseChannel.cpp 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/protocol/http/HttpBaseChannel.cpp 2021-10-20 19:28:34.000000000 +0000 @@ -434,8 +434,7 @@ NS_IMETHODIMP HttpBaseChannel::GetLoadGroup(nsILoadGroup** aLoadGroup) { NS_ENSURE_ARG_POINTER(aLoadGroup); - *aLoadGroup = mLoadGroup; - NS_IF_ADDREF(*aLoadGroup); + *aLoadGroup = do_AddRef(mLoadGroup).take(); return NS_OK; } @@ -542,8 +541,7 @@ NS_IMETHODIMP HttpBaseChannel::GetOwner(nsISupports** aOwner) { NS_ENSURE_ARG_POINTER(aOwner); - *aOwner = mOwner; - NS_IF_ADDREF(*aOwner); + *aOwner = do_AddRef(mOwner).take(); return NS_OK; } @@ -562,7 +560,7 @@ NS_IMETHODIMP HttpBaseChannel::GetLoadInfo(nsILoadInfo** aLoadInfo) { - NS_IF_ADDREF(*aLoadInfo = mLoadInfo); + *aLoadInfo = do_AddRef(mLoadInfo).take(); return NS_OK; } @@ -573,8 +571,7 @@ NS_IMETHODIMP HttpBaseChannel::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks) { - *aCallbacks = mCallbacks; - NS_IF_ADDREF(*aCallbacks); + *aCallbacks = do_AddRef(mCallbacks).take(); return NS_OK; } @@ -797,8 +794,7 @@ NS_IMETHODIMP HttpBaseChannel::GetUploadStream(nsIInputStream** stream) { NS_ENSURE_ARG_POINTER(stream); - *stream = mUploadStream; - NS_IF_ADDREF(*stream); + *stream = do_AddRef(mUploadStream).take(); return NS_OK; } @@ -1280,8 +1276,7 @@ if (val) LOG(("Unknown content encoding '%s', ignoring\n", val)); } } - *aNewNextListener = nextListener; - NS_IF_ADDREF(*aNewNextListener); + *aNewNextListener = do_AddRef(nextListener).take(); return NS_OK; } @@ -2124,15 +2119,14 @@ #endif } } - NS_IF_ADDREF(*aTopWindowURI = mTopWindowURI); + *aTopWindowURI = do_AddRef(mTopWindowURI).take(); return rv; } NS_IMETHODIMP HttpBaseChannel::GetDocumentURI(nsIURI** aDocumentURI) { NS_ENSURE_ARG_POINTER(aDocumentURI); - *aDocumentURI = mDocumentURI; - NS_IF_ADDREF(*aDocumentURI); + *aDocumentURI = do_AddRef(mDocumentURI).take(); return NS_OK; } @@ -3414,7 +3408,7 @@ NS_IMETHODIMP HttpBaseChannel::GetApiRedirectToURI(nsIURI** aResult) { NS_ENSURE_ARG_POINTER(aResult); - NS_IF_ADDREF(*aResult = mAPIRedirectToURI); + *aResult = do_AddRef(mAPIRedirectToURI).take(); return NS_OK; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/protocol/http/HttpChannelChild.cpp firefox-trunk-95.0~a1~hg20211020r596404/netwerk/protocol/http/HttpChannelChild.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/protocol/http/HttpChannelChild.cpp 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/protocol/http/HttpChannelChild.cpp 2021-10-20 19:28:34.000000000 +0000 @@ -867,7 +867,7 @@ mCacheReadStart = aTiming.cacheReadStart(); mCacheReadEnd = aTiming.cacheReadEnd(); - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled()) { nsAutoCString requestMethod; GetRequestMethod(requestMethod); nsAutoCString contentType; @@ -1357,7 +1357,7 @@ ResourceTimingStructArgsToTimingsStruct(timing, mTransactionTimings); - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled()) { nsAutoCString requestMethod; GetRequestMethod(requestMethod); nsAutoCString contentType; @@ -1664,7 +1664,7 @@ */ mLastStatusReported = TimeStamp::Now(); - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled()) { nsAutoCString requestMethod; GetRequestMethod(requestMethod); @@ -1886,7 +1886,7 @@ NS_IMETHODIMP HttpChannelChild::GetSecurityInfo(nsISupports** aSecurityInfo) { NS_ENSURE_ARG_POINTER(aSecurityInfo); - NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo); + *aSecurityInfo = do_AddRef(mSecurityInfo).take(); return NS_OK; } @@ -1990,7 +1990,7 @@ gHttpHandler->OnOpeningRequest(this); mLastStatusReported = TimeStamp::Now(); - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled()) { nsAutoCString requestMethod; GetRequestMethod(requestMethod); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/protocol/http/HttpConnectionBase.h firefox-trunk-95.0~a1~hg20211020r596404/netwerk/protocol/http/HttpConnectionBase.h --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/protocol/http/HttpConnectionBase.h 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/protocol/http/HttpConnectionBase.h 2021-10-20 19:28:34.000000000 +0000 @@ -105,7 +105,7 @@ virtual bool CanAcceptWebsocket() { return false; } void GetConnectionInfo(nsHttpConnectionInfo** ci) { - NS_IF_ADDREF(*ci = mConnInfo); + *ci = do_AddRef(mConnInfo).take(); } virtual void GetSecurityInfo(nsISupports** result) = 0; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/protocol/http/InterceptedHttpChannel.cpp firefox-trunk-95.0~a1~hg20211020r596404/netwerk/protocol/http/InterceptedHttpChannel.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/protocol/http/InterceptedHttpChannel.cpp 2021-10-17 14:42:48.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/protocol/http/InterceptedHttpChannel.cpp 2021-10-20 19:28:34.000000000 +0000 @@ -92,7 +92,7 @@ // We save this timestamp from outside of the if block in case we enable the // profiler after AsyncOpen(). mLastStatusReported = TimeStamp::Now(); - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled()) { nsAutoCString requestMethod; GetRequestMethod(requestMethod); @@ -505,7 +505,7 @@ mCanceled = true; - if (mLastStatusReported && profiler_can_accept_markers()) { + if (mLastStatusReported && profiler_thread_is_being_profiled()) { // These do allocations/frees/etc; avoid if not active // mLastStatusReported can be null if Cancel is called before we added the // start marker. @@ -675,7 +675,7 @@ mLoadFlags); NS_ENSURE_SUCCESS(rv, rv); - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled()) { nsAutoCString requestMethod; GetRequestMethod(requestMethod); @@ -1043,7 +1043,7 @@ // Register entry to the PerformanceStorage resource timing MaybeReportTimingData(); - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled()) { // These do allocations/frees/etc; avoid if not active nsAutoCString requestMethod; GetRequestMethod(requestMethod); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/protocol/http/NetworkMarker.cpp firefox-trunk-95.0~a1~hg20211020r596404/netwerk/protocol/http/NetworkMarker.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/protocol/http/NetworkMarker.cpp 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/protocol/http/NetworkMarker.cpp 2021-10-20 19:28:34.000000000 +0000 @@ -23,7 +23,7 @@ UniquePtr aSource, const Maybe& aContentType, nsIURI* aRedirectURI, uint32_t aRedirectFlags, uint64_t aRedirectChannelId) { - if (!profiler_can_accept_markers()) { + if (!profiler_thread_is_being_profiled()) { return; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/protocol/http/nsHttpChannel.cpp firefox-trunk-95.0~a1~hg20211020r596404/netwerk/protocol/http/nsHttpChannel.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/protocol/http/nsHttpChannel.cpp 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/protocol/http/nsHttpChannel.cpp 2021-10-20 19:28:34.000000000 +0000 @@ -5014,7 +5014,7 @@ "[this=%p newChannel=%p preserveMethod=%d]", this, newChannel, preserveMethod)); - if (!mEndMarkerAdded && profiler_can_accept_markers()) { + if (!mEndMarkerAdded && profiler_thread_is_being_profiled()) { mEndMarkerAdded = true; nsAutoCString requestMethod; @@ -5542,7 +5542,7 @@ mStatus = NS_FAILED(status) ? status : NS_ERROR_ABORT; if (mLastStatusReported && !mEndMarkerAdded && - profiler_can_accept_markers()) { + profiler_thread_is_being_profiled()) { // These do allocations/frees/etc; avoid if not active // mLastStatusReported can be null if Cancel is called before we added the // start marker. @@ -5733,8 +5733,7 @@ NS_IMETHODIMP nsHttpChannel::GetSecurityInfo(nsISupports** securityInfo) { NS_ENSURE_ARG_POINTER(securityInfo); - *securityInfo = mSecurityInfo; - NS_IF_ADDREF(*securityInfo); + *securityInfo = do_AddRef(mSecurityInfo).take(); return NS_OK; } @@ -5862,7 +5861,7 @@ // We save this timestamp from outside of the if block in case we enable the // profiler after AsyncOpen(). mLastStatusReported = TimeStamp::Now(); - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled()) { nsAutoCString requestMethod; GetRequestMethod(requestMethod); @@ -6552,11 +6551,10 @@ NS_IMETHODIMP nsHttpChannel::GetProxyInfo(nsIProxyInfo** result) { if (!mConnectionInfo) { - *result = mProxyInfo; + *result = do_AddRef(mProxyInfo).take(); } else { - *result = mConnectionInfo->ProxyInfo(); + *result = do_AddRef(mConnectionInfo->ProxyInfo()).take(); } - NS_IF_ADDREF(*result); return NS_OK; } @@ -7487,7 +7485,7 @@ MaybeFlushConsoleReports(); - if (!mEndMarkerAdded && profiler_can_accept_markers()) { + if (!mEndMarkerAdded && profiler_thread_is_being_profiled()) { // These do allocations/frees/etc; avoid if not active mEndMarkerAdded = true; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/protocol/http/nsHttp.cpp firefox-trunk-95.0~a1~hg20211020r596404/netwerk/protocol/http/nsHttp.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/protocol/http/nsHttp.cpp 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/protocol/http/nsHttp.cpp 2021-10-20 19:28:34.000000000 +0000 @@ -49,44 +49,8 @@ }; #undef HTTP_ATOM -class nsCaseInsentitiveHashKey : public PLDHashEntryHdr { - public: - using KeyType = const nsACString&; - using KeyTypePointer = const nsACString*; - - explicit nsCaseInsentitiveHashKey(KeyTypePointer aStr) : mStr(*aStr) { - // take it easy just deal HashKey - } - - nsCaseInsentitiveHashKey(const nsCaseInsentitiveHashKey&) = delete; - nsCaseInsentitiveHashKey(nsCaseInsentitiveHashKey&& aToMove) noexcept - : PLDHashEntryHdr(std::move(aToMove)), mStr(aToMove.mStr) {} - ~nsCaseInsentitiveHashKey() = default; - - KeyType GetKey() const { return mStr; } - bool KeyEquals(const KeyTypePointer aKey) const { - return mStr.Equals(*aKey, nsCaseInsensitiveCStringComparator); - } - - static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; } - static PLDHashNumber HashKey(const KeyTypePointer aKey) { - nsAutoCString tmKey(*aKey); - ToLowerCase(tmKey); - return mozilla::HashString(tmKey); - } - enum { ALLOW_MEMMOVE = false }; - - // To avoid double-counting, only measure the string if it is unshared. - size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const { - return GetKey().SizeOfExcludingThisIfUnshared(aMallocSizeOf); - } - - private: - const nsCString mStr; -}; - -static StaticDataMutex> sAtomTable( - "nsHttp::sAtomTable"); +static StaticDataMutex> + sAtomTable("nsHttp::sAtomTable"); // This is set to true in DestroyAtomTable so we don't try to repopulate the // table if ResolveAtom gets called during shutdown for some reason. @@ -95,7 +59,8 @@ // We put the atoms in a hash table for speedy lookup.. see ResolveAtom. namespace nsHttp { -nsresult CreateAtomTable(nsTHashtable& base) { +nsresult CreateAtomTable( + nsTHashtable& base) { if (sTableDestroyed) { return NS_ERROR_ILLEGAL_DURING_SHUTDOWN; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/protocol/http/NullHttpChannel.cpp firefox-trunk-95.0~a1~hg20211020r596404/netwerk/protocol/http/NullHttpChannel.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/protocol/http/NullHttpChannel.cpp 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/protocol/http/NullHttpChannel.cpp 2021-10-20 19:28:34.000000000 +0000 @@ -292,7 +292,7 @@ NS_IMETHODIMP NullHttpChannel::GetOriginalURI(nsIURI** aOriginalURI) { - NS_IF_ADDREF(*aOriginalURI = mOriginalURI); + *aOriginalURI = do_AddRef(mOriginalURI).take(); return NS_OK; } @@ -304,7 +304,7 @@ NS_IMETHODIMP NullHttpChannel::GetURI(nsIURI** aURI) { - NS_IF_ADDREF(*aURI = mURI); + *aURI = do_AddRef(mURI).take(); return NS_OK; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/protocol/http/TRRServiceChannel.cpp firefox-trunk-95.0~a1~hg20211020r596404/netwerk/protocol/http/TRRServiceChannel.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/protocol/http/TRRServiceChannel.cpp 2021-10-17 14:42:48.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/protocol/http/TRRServiceChannel.cpp 2021-10-20 19:28:34.000000000 +0000 @@ -162,8 +162,7 @@ NS_IMETHODIMP TRRServiceChannel::GetSecurityInfo(nsISupports** securityInfo) { NS_ENSURE_ARG_POINTER(securityInfo); - *securityInfo = mSecurityInfo; - NS_IF_ADDREF(*securityInfo); + *securityInfo = do_AddRef(mSecurityInfo).take(); return NS_OK; } @@ -1339,11 +1338,10 @@ NS_IMETHODIMP TRRServiceChannel::GetProxyInfo(nsIProxyInfo** result) { if (!mConnectionInfo) { - *result = mProxyInfo; + *result = do_AddRef(mProxyInfo).take(); } else { - *result = mConnectionInfo->ProxyInfo(); + *result = do_AddRef(mConnectionInfo->ProxyInfo()).take(); } - NS_IF_ADDREF(*result); return NS_OK; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/protocol/viewsource/nsViewSourceChannel.cpp firefox-trunk-95.0~a1~hg20211020r596404/netwerk/protocol/viewsource/nsViewSourceChannel.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/protocol/viewsource/nsViewSourceChannel.cpp 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/protocol/viewsource/nsViewSourceChannel.cpp 2021-10-20 19:28:33.000000000 +0000 @@ -659,8 +659,7 @@ return isc->GetBaseURI(aBaseURI); } } - *aBaseURI = mBaseURI; - NS_IF_ADDREF(*aBaseURI); + *aBaseURI = do_AddRef(mBaseURI).take(); return NS_OK; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/protocol/websocket/BaseWebSocketChannel.cpp firefox-trunk-95.0~a1~hg20211020r596404/netwerk/protocol/websocket/BaseWebSocketChannel.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/protocol/websocket/BaseWebSocketChannel.cpp 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/protocol/websocket/BaseWebSocketChannel.cpp 2021-10-20 19:28:34.000000000 +0000 @@ -94,7 +94,7 @@ BaseWebSocketChannel::GetNotificationCallbacks( nsIInterfaceRequestor** aNotificationCallbacks) { LOG(("BaseWebSocketChannel::GetNotificationCallbacks() %p\n", this)); - NS_IF_ADDREF(*aNotificationCallbacks = mCallbacks); + *aNotificationCallbacks = do_AddRef(mCallbacks).take(); return NS_OK; } @@ -109,7 +109,7 @@ NS_IMETHODIMP BaseWebSocketChannel::GetLoadGroup(nsILoadGroup** aLoadGroup) { LOG(("BaseWebSocketChannel::GetLoadGroup() %p\n", this)); - NS_IF_ADDREF(*aLoadGroup = mLoadGroup); + *aLoadGroup = do_AddRef(mLoadGroup).take(); return NS_OK; } @@ -129,7 +129,7 @@ NS_IMETHODIMP BaseWebSocketChannel::GetLoadInfo(nsILoadInfo** aLoadInfo) { - NS_IF_ADDREF(*aLoadInfo = mLoadInfo); + *aLoadInfo = do_AddRef(mLoadInfo).take(); return NS_OK; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/streamconv/converters/nsDirIndexParser.cpp firefox-trunk-95.0~a1~hg20211020r596404/netwerk/streamconv/converters/nsDirIndexParser.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/streamconv/converters/nsDirIndexParser.cpp 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/streamconv/converters/nsDirIndexParser.cpp 2021-10-20 19:28:34.000000000 +0000 @@ -115,7 +115,7 @@ NS_IMETHODIMP nsDirIndexParser::GetListener(nsIDirIndexListener** aListener) { - NS_IF_ADDREF(*aListener = mListener.get()); + *aListener = do_AddRef(mListener).take(); return NS_OK; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/streamconv/converters/nsMultiMixedConv.cpp firefox-trunk-95.0~a1~hg20211020r596404/netwerk/streamconv/converters/nsMultiMixedConv.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/streamconv/converters/nsMultiMixedConv.cpp 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/streamconv/converters/nsMultiMixedConv.cpp 2021-10-20 19:28:33.000000000 +0000 @@ -212,9 +212,7 @@ NS_IMETHODIMP nsPartChannel::GetLoadGroup(nsILoadGroup** aLoadGroup) { - *aLoadGroup = mLoadGroup; - NS_IF_ADDREF(*aLoadGroup); - + *aLoadGroup = do_AddRef(mLoadGroup).take(); return NS_OK; } @@ -375,8 +373,7 @@ nsPartChannel::GetBaseChannel(nsIChannel** aReturn) { NS_ENSURE_ARG_POINTER(aReturn); - *aReturn = mMultipartChannel; - NS_IF_ADDREF(*aReturn); + *aReturn = do_AddRef(mMultipartChannel).take(); return NS_OK; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/test/browser/auth_post.sjs firefox-trunk-95.0~a1~hg20211020r596404/netwerk/test/browser/auth_post.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/test/browser/auth_post.sjs 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/test/browser/auth_post.sjs 2021-10-20 19:28:34.000000000 +0000 @@ -1,7 +1,9 @@ const CC = Components.Constructor; -const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", - "setInputStream"); +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); function readStream(inputStream) { let available = 0; @@ -10,11 +12,10 @@ result.push(inputStream.readBytes(available)); } - return result.join(''); + return result.join(""); } -function handleRequest(request, response) -{ +function handleRequest(request, response) { if (request.method != "POST") { response.setStatusLine(request.httpVersion, 405, "Method Not Allowed"); return; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/test/browser/browser_fetch_lnk.js firefox-trunk-95.0~a1~hg20211020r596404/netwerk/test/browser/browser_fetch_lnk.js --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/test/browser/browser_fetch_lnk.js 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/test/browser/browser_fetch_lnk.js 2021-10-20 19:28:34.000000000 +0000 @@ -4,10 +4,6 @@ "use strict"; add_task(async () => { - await SpecialPowers.pushPrefEnv({ - set: [["privacy.file_unique_origin", false]], - }); - const FILE_PAGE = Services.io.newFileURI( new FileUtils.File(getTestFilePath("dummy.html")) ).spec; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/test/browser/redirect.sjs firefox-trunk-95.0~a1~hg20211020r596404/netwerk/test/browser/redirect.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/test/browser/redirect.sjs 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/test/browser/redirect.sjs 2021-10-20 19:28:34.000000000 +0000 @@ -1,5 +1,4 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setStatusLine(request.httpVersion, 301, "Moved Permanently"); let location = request.queryString; response.setHeader("Location", location, false); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/test/httpserver/test/data/sjs/cgi.sjs firefox-trunk-95.0~a1~hg20211020r596404/netwerk/test/httpserver/test/data/sjs/cgi.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/test/httpserver/test/data/sjs/cgi.sjs 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/test/httpserver/test/data/sjs/cgi.sjs 2021-10-20 19:28:34.000000000 +0000 @@ -1,7 +1,7 @@ -function handleRequest(request, response) -{ - if (request.queryString == "throw") +function handleRequest(request, response) { + if (request.queryString == "throw") { throw "monkey wrench!"; + } response.setHeader("Content-Type", "text/plain", false); response.write("PASS"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/test/httpserver/test/data/sjs/object-state.sjs firefox-trunk-95.0~a1~hg20211020r596404/netwerk/test/httpserver/test/data/sjs/object-state.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/test/httpserver/test/data/sjs/object-state.sjs 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/test/httpserver/test/data/sjs/object-state.sjs 2021-10-20 19:28:34.000000000 +0000 @@ -1,13 +1,12 @@ -function parseQueryString(str) -{ +function parseQueryString(str) { var paramArray = str.split("&"); var regex = /^([^=]+)=(.*)$/; var params = {}; - for (var i = 0, sz = paramArray.length; i < sz; i++) - { + for (var i = 0, sz = paramArray.length; i < sz; i++) { var match = regex.exec(paramArray[i]); - if (!match) + if (!match) { throw "Bad parameter in queryString! '" + paramArray[i] + "'"; + } params[decodeURIComponent(match[1])] = decodeURIComponent(match[2]); } @@ -22,10 +21,9 @@ * best of my knowledge there's no way to force data flow at all those levels, * so this is the best we can do. */ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setHeader("Cache-Control", "no-cache", false); - + /* * NB: A Content-Type header is *necessary* to avoid content-sniffing, which * will delay onStartRequest past the the point where the entire head of @@ -35,31 +33,21 @@ var params = parseQueryString(request.queryString); - switch (params.state) - { + switch (params.state) { case "initial": response.processAsync(); response.write("do"); - var state = - { - QueryInterface: function(iid) - { - if (iid.equals(Components.interfaces.nsISupports)) - return this; - throw Components.results.NS_ERROR_NO_INTERFACE; - }, - end: function() - { - response.write("ne"); - response.finish(); - } - }; + var state = { + QueryInterface: ChromeUtils.generateQI([]), + end() { + response.write("ne"); + response.finish(); + }, + }; state.wrappedJSObject = state; setObjectState("object-state-test", state); - getObjectState("object-state-test", function(obj) - { - if (obj !== state) - { + getObjectState("object-state-test", function(obj) { + if (obj !== state) { response.write("FAIL bad state save"); response.finish(); } @@ -72,8 +60,7 @@ case "trigger": response.write("trigger"); - getObjectState("object-state-test", function(obj) - { + getObjectState("object-state-test", function(obj) { obj.wrappedJSObject.end(); setObjectState("object-state-test", null); }); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/test/httpserver/test/data/sjs/qi.sjs firefox-trunk-95.0~a1~hg20211020r596404/netwerk/test/httpserver/test/data/sjs/qi.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/test/httpserver/test/data/sjs/qi.sjs 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/test/httpserver/test/data/sjs/qi.sjs 2021-10-20 19:28:33.000000000 +0000 @@ -1,43 +1,40 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { var exstr, qid; response.setStatusLine(request.httpVersion, 500, "FAIL"); var passed = false; - try - { + try { qid = request.QueryInterface(Ci.nsIHttpRequest); passed = qid === request; - } - catch (e) - { + } catch (e) { exstr = ("" + e).split(/[\x09\x20-\x7f\x81-\xff]+/)[0]; - response.setStatusLine(request.httpVersion, 500, - "request doesn't QI: " + exstr); + response.setStatusLine( + request.httpVersion, + 500, + "request doesn't QI: " + exstr + ); return; } - if (!passed) - { + if (!passed) { response.setStatusLine(request.httpVersion, 500, "request QI'd wrongly?"); return; } passed = false; - try - { + try { qid = response.QueryInterface(Ci.nsIHttpResponse); passed = qid === response; - } - catch (e) - { + } catch (e) { exstr = ("" + e).split(/[\x09\x20-\x7f\x81-\xff]+/)[0]; - response.setStatusLine(request.httpVersion, 500, - "response doesn't QI: " + exstr); + response.setStatusLine( + request.httpVersion, + 500, + "response doesn't QI: " + exstr + ); return; } - if (!passed) - { + if (!passed) { response.setStatusLine(request.httpVersion, 500, "response QI'd wrongly?"); return; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/test/httpserver/test/data/sjs/range-checker.sjs firefox-trunk-95.0~a1~hg20211020r596404/netwerk/test/httpserver/test/data/sjs/range-checker.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/test/httpserver/test/data/sjs/range-checker.sjs 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/test/httpserver/test/data/sjs/range-checker.sjs 2021-10-20 19:28:33.000000000 +0000 @@ -1,3 +1 @@ -function handleRequest(request, response) -{ -} +function handleRequest(request, response) {} diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/test/httpserver/test/data/sjs/state1.sjs firefox-trunk-95.0~a1~hg20211020r596404/netwerk/test/httpserver/test/data/sjs/state1.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/test/httpserver/test/data/sjs/state1.sjs 2021-10-17 14:42:48.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/test/httpserver/test/data/sjs/state1.sjs 2021-10-20 19:28:34.000000000 +0000 @@ -1,21 +1,19 @@ -function parseQueryString(str) -{ +function parseQueryString(str) { var paramArray = str.split("&"); var regex = /^([^=]+)=(.*)$/; var params = {}; - for (var i = 0, sz = paramArray.length; i < sz; i++) - { + for (var i = 0, sz = paramArray.length; i < sz; i++) { var match = regex.exec(paramArray[i]); - if (!match) + if (!match) { throw "Bad parameter in queryString! '" + paramArray[i] + "'"; + } params[decodeURIComponent(match[1])] = decodeURIComponent(match[2]); } return params; } -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setHeader("Cache-Control", "no-cache", false); var params = parseQueryString(request.queryString); @@ -24,8 +22,7 @@ response.setHeader("X-Old-Shared-Value", oldShared, false); var newShared = params.newShared; - if (newShared !== undefined) - { + if (newShared !== undefined) { setSharedState("shared-value", newShared); response.setHeader("X-New-Shared-Value", newShared, false); } @@ -34,8 +31,7 @@ response.setHeader("X-Old-Private-Value", oldPrivate, false); var newPrivate = params.newPrivate; - if (newPrivate !== undefined) - { + if (newPrivate !== undefined) { setState("private-value", newPrivate); response.setHeader("X-New-Private-Value", newPrivate, false); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/test/httpserver/test/data/sjs/state2.sjs firefox-trunk-95.0~a1~hg20211020r596404/netwerk/test/httpserver/test/data/sjs/state2.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/test/httpserver/test/data/sjs/state2.sjs 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/test/httpserver/test/data/sjs/state2.sjs 2021-10-20 19:28:34.000000000 +0000 @@ -1,21 +1,19 @@ -function parseQueryString(str) -{ +function parseQueryString(str) { var paramArray = str.split("&"); var regex = /^([^=]+)=(.*)$/; var params = {}; - for (var i = 0, sz = paramArray.length; i < sz; i++) - { + for (var i = 0, sz = paramArray.length; i < sz; i++) { var match = regex.exec(paramArray[i]); - if (!match) + if (!match) { throw "Bad parameter in queryString! '" + paramArray[i] + "'"; + } params[decodeURIComponent(match[1])] = decodeURIComponent(match[2]); } return params; } -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setHeader("Cache-Control", "no-cache", false); var params = parseQueryString(request.queryString); @@ -24,8 +22,7 @@ response.setHeader("X-Old-Shared-Value", oldShared, false); var newShared = params.newShared; - if (newShared !== undefined) - { + if (newShared !== undefined) { setSharedState("shared-value", newShared); response.setHeader("X-New-Shared-Value", newShared, false); } @@ -34,8 +31,7 @@ response.setHeader("X-Old-Private-Value", oldPrivate, false); var newPrivate = params.newPrivate; - if (newPrivate !== undefined) - { + if (newPrivate !== undefined) { setState("private-value", newPrivate); response.setHeader("X-New-Private-Value", newPrivate, false); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/test/httpserver/test/data/sjs/thrower.sjs firefox-trunk-95.0~a1~hg20211020r596404/netwerk/test/httpserver/test/data/sjs/thrower.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/test/httpserver/test/data/sjs/thrower.sjs 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/test/httpserver/test/data/sjs/thrower.sjs 2021-10-20 19:28:34.000000000 +0000 @@ -1,6 +1,6 @@ -function handleRequest(request, response) -{ - if (request.queryString == "throw") +function handleRequest(request, response) { + if (request.queryString == "throw") { undefined[5]; + } response.setHeader("X-Test-Status", "PASS", false); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/test/mochitests/file_1502055.sjs firefox-trunk-95.0~a1~hg20211020r596404/netwerk/test/mochitests/file_1502055.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/test/mochitests/file_1502055.sjs 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/test/mochitests/file_1502055.sjs 2021-10-20 19:28:34.000000000 +0000 @@ -1,18 +1,17 @@ -function handleRequest(request, response) -{ - var count = parseInt(getState('count')); +function handleRequest(request, response) { + var count = parseInt(getState("count")); if (!count) { count = 0; } if (count == 0) { - response.setStatusLine(request.httpVersion, '200', 'OK'); - response.setHeader('Content-Type', 'text/html', false); - response.write('Hello world!'); - setState('count', '1'); + response.setStatusLine(request.httpVersion, "200", "OK"); + response.setHeader("Content-Type", "text/html", false); + response.write("Hello world!"); + setState("count", "1"); return; } response.setStatusLine(request.httpVersion, "304", "Not Modified"); - response.setHeader('Clear-Site-Data', '"storage"'); + response.setHeader("Clear-Site-Data", '"storage"'); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/test/mochitests/file_1503201.sjs firefox-trunk-95.0~a1~hg20211020r596404/netwerk/test/mochitests/file_1503201.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/test/mochitests/file_1503201.sjs 2021-10-17 14:42:48.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/test/mochitests/file_1503201.sjs 2021-10-20 19:28:34.000000000 +0000 @@ -1,4 +1,4 @@ function handleRequest(request, response) { response.setStatusLine(request.httpVersion, 401, "Unauthorized"); - response.setHeader("WWW-Authenticate", "Bearer realm=\"foo\""); + response.setHeader("WWW-Authenticate", 'Bearer realm="foo"'); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/test/mochitests/file_loadinfo_redirectchain.sjs firefox-trunk-95.0~a1~hg20211020r596404/netwerk/test/mochitests/file_loadinfo_redirectchain.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/test/mochitests/file_loadinfo_redirectchain.sjs 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/test/mochitests/file_loadinfo_redirectchain.sjs 2021-10-20 19:28:33.000000000 +0000 @@ -4,8 +4,8 @@ */ function createIframeContent(aQuery) { - - var content =` + var content = + ` @@ -14,7 +14,9 @@ \r\n\r\n Web Packaged App Index\r\n\r\n", type: "text/html" }, - { headers: ["Content-Location: /scripts/app.js", "Content-Type: text/javascript"], data: "module Math from '/scripts/helpers/math.js';\r\n...\r\n", type: "text/javascript" }, - { headers: ["Content-Location: /scripts/helpers/math.js", "Content-Type: text/javascript"], data: "export function sum(nums) { ... }\r\n...\r\n", type: "text/javascript" } + { + headers: ["Content-Location: /index.html", "Content-Type: text/html"], + data: + "\r\n \r\n \r\n\r\n Web Packaged App Index\r\n\r\n", + type: "text/html", + }, + { + headers: [ + "Content-Location: /scripts/app.js", + "Content-Type: text/javascript", + ], + data: "module Math from '/scripts/helpers/math.js';\r\n...\r\n", + type: "text/javascript", + }, + { + headers: [ + "Content-Location: /scripts/helpers/math.js", + "Content-Type: text/javascript", + ], + data: "export function sum(nums) { ... }\r\n...\r\n", + type: "text/javascript", + }, ], - token : "gc0pJq0M:08jU534c0p", - getData: function() { + token: "gc0pJq0M:08jU534c0p", + getData() { var str = ""; for (var i in this.content) { str += "--" + this.token + "\r\n"; @@ -27,5 +44,5 @@ str += "--" + this.token + "--"; return str; - } -} + }, +}; diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/netwerk/url-classifier/AsyncUrlChannelClassifier.cpp firefox-trunk-95.0~a1~hg20211020r596404/netwerk/url-classifier/AsyncUrlChannelClassifier.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/netwerk/url-classifier/AsyncUrlChannelClassifier.cpp 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/netwerk/url-classifier/AsyncUrlChannelClassifier.cpp 2021-10-20 19:28:34.000000000 +0000 @@ -76,7 +76,7 @@ private: URIData(); - ~URIData(); + ~URIData() = default; nsCOMPtr mURI; nsCString mURISpec; @@ -117,8 +117,6 @@ URIData::URIData() { MOZ_ASSERT(NS_IsMainThread()); } -URIData::~URIData() { NS_ReleaseOnMainThread("URIData:mURI", mURI.forget()); } - bool URIData::IsEqual(nsIURI* aURI) const { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aURI); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/parser/htmlparser/tests/mochitest/dir_bug534293/file_bug534293.sjs firefox-trunk-95.0~a1~hg20211020r596404/parser/htmlparser/tests/mochitest/dir_bug534293/file_bug534293.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/parser/htmlparser/tests/mochitest/dir_bug534293/file_bug534293.sjs 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/parser/htmlparser/tests/mochitest/dir_bug534293/file_bug534293.sjs 2021-10-20 19:28:35.000000000 +0000 @@ -1,11 +1,10 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setHeader("Content-Type", "text/javascript", false); if (request.queryString.indexOf("report") != -1) { if (getState("loaded") == "loaded") { - response.write("ok(true, 'This script was supposed to get fetched.');"); + response.write("ok(true, 'This script was supposed to get fetched.');"); } else { - response.write("ok(false, 'This script was supposed to get fetched.');"); + response.write("ok(false, 'This script was supposed to get fetched.');"); } } else { setState("loaded", "loaded"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/parser/htmlparser/tests/mochitest/file_async_bug1104732.sjs firefox-trunk-95.0~a1~hg20211020r596404/parser/htmlparser/tests/mochitest/file_async_bug1104732.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/parser/htmlparser/tests/mochitest/file_async_bug1104732.sjs 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/parser/htmlparser/tests/mochitest/file_async_bug1104732.sjs 2021-10-20 19:28:34.000000000 +0000 @@ -1,14 +1,19 @@ var timer = null; -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.processAsync(); response.setHeader("Content-Type", "application/javascript", false); response.write("asyncState = 'mid-async';\n"); - timer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer); - timer.initWithCallback(function() { - response.write("asyncState = 'loaded';\n"); - response.finish(); - }, 5 * 1000 /* milliseconds */, timer.TYPE_ONE_SHOT); + timer = Components.classes["@mozilla.org/timer;1"].createInstance( + Components.interfaces.nsITimer + ); + timer.initWithCallback( + function() { + response.write("asyncState = 'loaded';\n"); + response.finish(); + }, + 5 * 1000 /* milliseconds */, + timer.TYPE_ONE_SHOT + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/parser/htmlparser/tests/mochitest/file_bug102699.sjs firefox-trunk-95.0~a1~hg20211020r596404/parser/htmlparser/tests/mochitest/file_bug102699.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/parser/htmlparser/tests/mochitest/file_bug102699.sjs 2021-10-17 14:42:50.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/parser/htmlparser/tests/mochitest/file_bug102699.sjs 2021-10-20 19:28:34.000000000 +0000 @@ -1,15 +1,19 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setHeader("Content-Type", "text/javascript", false); if (request.queryString.indexOf("report") != -1) { if (getState("loaded") == "loaded") { - response.write("ok(false, 'This script was not supposed to get fetched.'); continueAfterReport();"); + response.write( + "ok(false, 'This script was not supposed to get fetched.'); continueAfterReport();" + ); } else { - response.write("ok(true, 'This script was not supposed to get fetched.'); continueAfterReport();"); + response.write( + "ok(true, 'This script was not supposed to get fetched.'); continueAfterReport();" + ); } } else { setState("loaded", "loaded"); - response.write('document.documentElement.setAttribute("data-fail", "FAIL");'); + response.write( + 'document.documentElement.setAttribute("data-fail", "FAIL");' + ); } } - diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/parser/htmlparser/tests/mochitest/file_bug534293.sjs firefox-trunk-95.0~a1~hg20211020r596404/parser/htmlparser/tests/mochitest/file_bug534293.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/parser/htmlparser/tests/mochitest/file_bug534293.sjs 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/parser/htmlparser/tests/mochitest/file_bug534293.sjs 2021-10-20 19:28:34.000000000 +0000 @@ -1,11 +1,14 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setHeader("Content-Type", "text/javascript", false); if (request.queryString.indexOf("report") != -1) { if (getState("loaded") == "loaded") { - response.write("ok(false, 'This script was not supposed to get fetched.');"); + response.write( + "ok(false, 'This script was not supposed to get fetched.');" + ); } else { - response.write("ok(true, 'This script was not supposed to get fetched.');"); + response.write( + "ok(true, 'This script was not supposed to get fetched.');" + ); } } else { setState("loaded", "loaded"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/parser/htmlparser/tests/mochitest/file_bug534293-slow.sjs firefox-trunk-95.0~a1~hg20211020r596404/parser/htmlparser/tests/mochitest/file_bug534293-slow.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/parser/htmlparser/tests/mochitest/file_bug534293-slow.sjs 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/parser/htmlparser/tests/mochitest/file_bug534293-slow.sjs 2021-10-20 19:28:33.000000000 +0000 @@ -1,14 +1,18 @@ var timer; -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setHeader("Cache-Control", "no-cache", false); response.setHeader("Content-Type", "text/javascript", false); response.write("ok(true, 'Slow script ran.');"); response.processAsync(); - timer = Components.classes["@mozilla.org/timer;1"] - .createInstance(Components.interfaces.nsITimer); - timer.initWithCallback(function() { + timer = Components.classes["@mozilla.org/timer;1"].createInstance( + Components.interfaces.nsITimer + ); + timer.initWithCallback( + function() { response.finish(); - }, 500, Components.interfaces.nsITimer.TYPE_ONE_SHOT); + }, + 500, + Components.interfaces.nsITimer.TYPE_ONE_SHOT + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/parser/htmlparser/tests/mochitest/file_bug543062.sjs firefox-trunk-95.0~a1~hg20211020r596404/parser/htmlparser/tests/mochitest/file_bug543062.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/parser/htmlparser/tests/mochitest/file_bug543062.sjs 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/parser/htmlparser/tests/mochitest/file_bug543062.sjs 2021-10-20 19:28:34.000000000 +0000 @@ -1,20 +1,29 @@ var timer; function armTimer(response) { - timer = Components.classes["@mozilla.org/timer;1"] - .createInstance(Components.interfaces.nsITimer); - timer.initWithCallback(function() { - if (getState("docwritepreloadssecond") == "second" && getState("docwritepreloadsthird") == "third") { - response.write("ok(true, 'Second and third scripts should have started loading while the first one is loading');"); + timer = Components.classes["@mozilla.org/timer;1"].createInstance( + Components.interfaces.nsITimer + ); + timer.initWithCallback( + function() { + if ( + getState("docwritepreloadssecond") == "second" && + getState("docwritepreloadsthird") == "third" + ) { + response.write( + "ok(true, 'Second and third scripts should have started loading while the first one is loading');" + ); response.finish(); } else { armTimer(response); } - }, 20, Components.interfaces.nsITimer.TYPE_ONE_SHOT); + }, + 20, + Components.interfaces.nsITimer.TYPE_ONE_SHOT + ); } -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setHeader("Cache-Control", "no-cache", false); response.setHeader("Content-Type", "text/javascript", false); if (request.queryString.indexOf("first") != -1) { @@ -29,4 +38,3 @@ setState("docwritepreloadsthird", "third"); } } - diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/parser/htmlparser/tests/mochitest/file_bug568470-script.sjs firefox-trunk-95.0~a1~hg20211020r596404/parser/htmlparser/tests/mochitest/file_bug568470-script.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/parser/htmlparser/tests/mochitest/file_bug568470-script.sjs 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/parser/htmlparser/tests/mochitest/file_bug568470-script.sjs 2021-10-20 19:28:35.000000000 +0000 @@ -1,16 +1,19 @@ var timer = null; // Declare outside to prevent premature GC -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setHeader("Cache-Control", "no-cache", false); response.setHeader("Content-Type", "text/javascript", false); response.write("var i = 0;"); response.bodyOutputStream.flush(); response.processAsync(); - timer = Components.classes["@mozilla.org/timer;1"] - .createInstance(Components.interfaces.nsITimer); - timer.initWithCallback(function() { + timer = Components.classes["@mozilla.org/timer;1"].createInstance( + Components.interfaces.nsITimer + ); + timer.initWithCallback( + function() { response.finish(); - }, 500, Components.interfaces.nsITimer.TYPE_ONE_SHOT); + }, + 500, + Components.interfaces.nsITimer.TYPE_ONE_SHOT + ); } - diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/parser/htmlparser/tests/mochitest/file_bug568470.sjs firefox-trunk-95.0~a1~hg20211020r596404/parser/htmlparser/tests/mochitest/file_bug568470.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/parser/htmlparser/tests/mochitest/file_bug568470.sjs 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/parser/htmlparser/tests/mochitest/file_bug568470.sjs 2021-10-20 19:28:34.000000000 +0000 @@ -1,21 +1,24 @@ var timer; // Place timer in global scope to avoid it getting GC'ed prematurely -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setHeader("Cache-Control", "no-cache", false); response.setHeader("Content-Type", "text/html", false); response.write(""); response.write("
"); - for (var i = 0; i < 2000; i++) { + for (var i = 0; i < 2000; i++) { response.write("Lorem ipsum dolor sit amet. "); } response.write("
"); response.bodyOutputStream.flush(); response.processAsync(); - timer = Components.classes["@mozilla.org/timer;1"] - .createInstance(Components.interfaces.nsITimer); - timer.initWithCallback(function() { + timer = Components.classes["@mozilla.org/timer;1"].createInstance( + Components.interfaces.nsITimer + ); + timer.initWithCallback( + function() { response.finish(); - }, 1200, Components.interfaces.nsITimer.TYPE_ONE_SHOT); + }, + 1200, + Components.interfaces.nsITimer.TYPE_ONE_SHOT + ); } - diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/parser/htmlparser/tests/mochitest/file_bug642908.sjs firefox-trunk-95.0~a1~hg20211020r596404/parser/htmlparser/tests/mochitest/file_bug642908.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/parser/htmlparser/tests/mochitest/file_bug642908.sjs 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/parser/htmlparser/tests/mochitest/file_bug642908.sjs 2021-10-20 19:28:34.000000000 +0000 @@ -1,16 +1,19 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { if (request.queryString.indexOf("report") != -1) { response.setHeader("Content-Type", "text/javascript", false); if (getState("loaded") == "loaded") { - response.write("ok(false, 'There was an attempt to preload the image.');"); + response.write( + "ok(false, 'There was an attempt to preload the image.');" + ); } else { - response.write("ok(true, 'There was no attempt to preload the image.');"); + response.write("ok(true, 'There was no attempt to preload the image.');"); } response.write("SimpleTest.finish();"); } else { setState("loaded", "loaded"); response.setHeader("Content-Type", "image/svg", false); - response.write("Not supposed to load this"); + response.write( + "Not supposed to load this" + ); } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/parser/htmlparser/tests/mochitest/file_bug655682.sjs firefox-trunk-95.0~a1~hg20211020r596404/parser/htmlparser/tests/mochitest/file_bug655682.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/parser/htmlparser/tests/mochitest/file_bug655682.sjs 2021-10-17 14:42:50.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/parser/htmlparser/tests/mochitest/file_bug655682.sjs 2021-10-20 19:28:34.000000000 +0000 @@ -1,8 +1,7 @@ var timer; var callback; -function handleRequest(request, response) -{ +function handleRequest(request, response) { if (request.queryString.indexOf("trigger") != -1) { setState("triggered", "triggered"); response.setHeader("Cache-Control", "no-cache", false); @@ -17,21 +16,26 @@ response.setHeader("Content-Type", "text/html; charset=utf-8", false); response.write(" "); response.bodyOutputStream.flush(); - timer = Components.classes["@mozilla.org/timer;1"] - .createInstance(Components.interfaces.nsITimer); + timer = Components.classes["@mozilla.org/timer;1"].createInstance( + Components.interfaces.nsITimer + ); callback = function() { if (getState("triggered") == "triggered") { response.write("
AB
"); response.finish(); } else { - timer.initWithCallback(callback, - 10, - Components.interfaces.nsITimer.TYPE_ONE_SHOT); + timer.initWithCallback( + callback, + 10, + Components.interfaces.nsITimer.TYPE_ONE_SHOT + ); } - } - timer.initWithCallback(callback, - 10, - Components.interfaces.nsITimer.TYPE_ONE_SHOT); - } + }; + timer.initWithCallback( + callback, + 10, + Components.interfaces.nsITimer.TYPE_ONE_SHOT + ); + } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/parser/htmlparser/tests/mochitest/file_img_picture_preload.sjs firefox-trunk-95.0~a1~hg20211020r596404/parser/htmlparser/tests/mochitest/file_img_picture_preload.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/parser/htmlparser/tests/mochitest/file_img_picture_preload.sjs 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/parser/htmlparser/tests/mochitest/file_img_picture_preload.sjs 2021-10-20 19:28:35.000000000 +0000 @@ -1,8 +1,7 @@ // Return a PNG, saving an array of query strings we see as state. When query // string is 'status', return array as JSON -function handleRequest(request, response) -{ +function handleRequest(request, response) { var seenImages = getState("seenImages"); seenImages = seenImages ? JSON.parse(seenImages) : []; @@ -20,7 +19,7 @@ // Return an image response.setStatusLine("1.1", 302, "Found"); response.setHeader("Location", "blue.png", false); - dump(request.queryString + '\n'); + dump(request.queryString + "\n"); seenImages.push(request.queryString); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/python/mach/mach/sentry.py firefox-trunk-95.0~a1~hg20211020r596404/python/mach/mach/sentry.py --- firefox-trunk-95.0~a1~hg20211017r596111/python/mach/mach/sentry.py 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/python/mach/mach/sentry.py 2021-10-20 19:28:33.000000000 +0000 @@ -54,7 +54,9 @@ global _is_unmodified_mach_core_thread _is_unmodified_mach_core_thread = Thread( - target=_is_unmodified_mach_core, args=[topsrcdir] + target=_is_unmodified_mach_core, + args=[topsrcdir], + daemon=True, ) _is_unmodified_mach_core_thread.start() diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/python/mozbuild/mozbuild/doctor.py firefox-trunk-95.0~a1~hg20211020r596404/python/mozbuild/mozbuild/doctor.py --- firefox-trunk-95.0~a1~hg20211017r596111/python/mozbuild/mozbuild/doctor.py 2021-10-17 14:42:50.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/python/mozbuild/mozbuild/doctor.py 2021-10-20 19:28:34.000000000 +0000 @@ -168,6 +168,28 @@ "hg.mozilla.org.".format(username) ], ) + + if "Mercurial access is currently disabled on your account" in proc.stdout: + return DoctorCheck( + name="ssh", + status=CheckStatus.FATAL, + display_text=[ + "You previously had push access to hgmo, but due to inactivity", + "your access was revoked. Please file a bug in Bugzilla under", + "`Infrastructure & Operations :: Infrastructure: LDAP` to request", + "access.", + ], + ) + + return DoctorCheck( + name="ssh", + status=CheckStatus.WARNING, + display_text=[ + "Unexpected output from `ssh hg.mozilla.org`:", + proc.stdout, + ], + ) + except subprocess.CalledProcessError: return DoctorCheck( name="ssh", diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/security/manager/ssl/nsSTSPreloadList.inc firefox-trunk-95.0~a1~hg20211020r596404/security/manager/ssl/nsSTSPreloadList.inc --- firefox-trunk-95.0~a1~hg20211017r596111/security/manager/ssl/nsSTSPreloadList.inc 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/security/manager/ssl/nsSTSPreloadList.inc 2021-10-20 19:28:35.000000000 +0000 @@ -8,7 +8,7 @@ /*****************************************************************************/ #include -const PRTime gPreloadListExpirationTime = INT64_C(1645094807534000); +const PRTime gPreloadListExpirationTime = INT64_C(1645440425708000); %% 0--1.de, 1 0-1.party, 1 @@ -900,6 +900,7 @@ 1234365w.com, 1 1234365x.com, 1 1234365y.com, 1 +1234365z.com, 0 12345.lv, 1 12345678365.com, 1 123456789365.com, 1 @@ -1997,8 +1998,6 @@ 26ck.com, 1 26gt.com, 1 26ja.com, 1 -26nc.com, 1 -26nd.com, 1 26pg.com, 1 26pn.com, 1 26sn.com, 1 @@ -2146,9 +2145,7 @@ 300llll.com, 1 300mmmm.com, 1 300oooo.com, 1 -300qqqq.com, 1 300rrrr.com, 1 -300uuuu.com, 1 300xxxx.com, 1 301.moe, 1 301.technology, 1 @@ -2199,7 +2196,6 @@ 3178888888.com, 1 31789999.com, 1 317899999.com, 1 -3178aaa.com, 1 3178b.com, 1 3178bbb.com, 1 3178c.com, 1 @@ -2628,7 +2624,6 @@ 375422.com, 1 377625.com, 1 377632.com, 1 -3778vip.com, 0 377ks.com, 1 377zzz.com, 1 37879.com, 0 @@ -2710,8 +2705,13 @@ 3957f.com, 1 3957g.com, 1 396228.com, 1 +396301.com, 0 +396303.com, 0 +396304.com, 0 +396305.com, 0 3963aa.com, 1 3963bb.com, 1 +3963cc.com, 0 3963dd.com, 1 396422.com, 1 3970a.com, 1 @@ -2840,7 +2840,6 @@ 3haeuserprojekt.org, 1 3haueserprojekt.org, 1 3hh365.com, 1 -3hl0.net, 1 3i-infotech.com, 1 3ii365.com, 1 3james.com, 1 @@ -2949,20 +2948,14 @@ 4000sf.com, 1 40010monogatari.com, 1 4005365.com, 1 -400bbbb.com, 1 -400cccc.com, 1 400eeee.com, 1 400gggg.com, 1 400iiii.com, 1 -400jjjj.com, 1 400llll.com, 1 400nnnn.com, 1 -400pppp.com, 1 400tttt.com, 1 400uuuu.com, 1 -400vvvv.com, 1 400yaahc.gov, 1 -400yyyy.com, 1 4025360.com, 1 4025361.com, 1 4025362.com, 1 @@ -3318,7 +3311,6 @@ 4kitchenknives.com, 1 4kpi.eu, 1 4kprojektory.cz, 1 -4kvids.com, 1 4lados.tk, 1 4lephants.tk, 1 4list.ml, 1 @@ -3385,8 +3377,6 @@ 500eeee.com, 1 500fcw.com, 1 500foods.com, 1 -500iiii.com, 1 -500jjjj.com, 1 500k.nl, 1 500k8.com, 1 500mmmm.com, 1 @@ -3394,7 +3384,6 @@ 500p.xyz, 1 500promokodov.ru, 1 500qqqq.com, 1 -500rrrr.com, 1 500tttt.com, 1 500uuuu.com, 1 500vvvv.com, 1 @@ -3781,16 +3770,13 @@ 600bbbb.com, 1 600cao.com, 1 600dddd.com, 1 -600iiii.com, 1 600k8.com, 1 600kkkk.com, 1 -600llll.com, 1 600mmmm.com, 1 600pppp.com, 1 600ssss.com, 1 600tttt.com, 1 600vvvv.com, 1 -600wwww.com, 1 600xxxx.com, 1 602422.com, 1 602yb.com, 1 @@ -3913,6 +3899,7 @@ 6396ddd.com, 1 6396eee.com, 1 6396fff.com, 1 +6396ggg.com, 0 6396iii.com, 1 6396jjj.com, 1 6396ooo.com, 1 @@ -4410,16 +4397,9 @@ 7-it.ml, 1 700.az, 1 700bbbb.com, 1 -700cccc.com, 1 700dddd.com, 1 -700gggg.com, 1 -700hhhh.com, 1 -700iiii.com, 1 -700mmmm.com, 1 700uuuu.com, 1 700wns.com, 1 -700yyyy.com, 1 -700zzzz.com, 1 701605.com, 1 701squad.tk, 1 70365365.com, 0 @@ -4716,21 +4696,17 @@ 8007d88.com, 1 800999.xyz, 1 800bbbb.com, 1 -800cccc.com, 1 800dddd.com, 1 800eeee.com, 1 800hhhh.com, 1 800iiii.com, 1 800kkkk.com, 1 800llll.com, 1 -800nnnn.com, 1 800qqqq.com, 1 800rrrr.com, 1 800sf.com, 1 800vvvv.com, 1 800wwww.com, 1 -800xxxx.com, 1 -800zzzz.com, 1 8010d88.com, 1 8012d88.com, 1 8017d.com, 1 @@ -5087,6 +5063,7 @@ 861365k.com, 1 861365l.com, 1 861365m.com, 1 +861365n.com, 0 861365o.com, 1 861365q.com, 1 861365r.com, 1 @@ -5343,9 +5320,7 @@ 9009019.com, 1 900aaaa.com, 1 900bbbb.com, 1 -900cccc.com, 1 900dddd.com, 1 -900eeee.com, 1 900gggg.com, 1 900hosting.com, 1 900iiii.com, 1 @@ -5354,11 +5329,9 @@ 900nnnn.com, 1 900pk.com, 1 900qqqq.com, 1 -900tttt.com, 1 900uuuu.com, 1 900wwww.com, 1 900yyyy.com, 1 -900zzzz.com, 1 901543.com, 1 903422.com, 1 905422.com, 1 @@ -6723,7 +6696,6 @@ aceinstituteonline.com, 1 aceitedelcampo.com, 1 aceleraguria.com.br, 1 -aceleratuweb.com, 1 acelpb.com, 1 acem.org.au, 1 acemadeira.pt, 1 @@ -6737,7 +6709,6 @@ acercapartners.com, 1 acerentalandsales.com, 1 acerislaw.com, 1 -acerosfortuna.com.mx, 1 acerostrevino.com.mx, 1 aceshop702.com, 1 acessoeducacao.com, 1 @@ -7065,6 +7036,7 @@ adamradocz.com, 1 adamraoof.tk, 1 adamricheimer.com, 1 +adams-gonczi.fun, 1 adams.dk, 1 adams.es, 1 adamsasphaltpaving.com, 1 @@ -7094,6 +7066,7 @@ adarixconsultores.com, 1 adarshcloud.in, 1 adarshthapa.in, 1 +adarshthapa.net, 0 adarsvidler.me, 1 adasbench.com, 1 adata.kz, 1 @@ -7493,7 +7466,6 @@ adventuregamers.com, 1 adventurenow.nl, 1 adventureprooutdoors.com, 1 -adventures-abroad.com, 1 adventures.com, 1 adventuresinparanoia.com, 1 adventureswithlillie.ca, 1 @@ -7532,6 +7504,7 @@ advokat-dtp.ml, 1 advokat-dtp.tk, 1 advokat-malinovskii.ml, 1 +advokat-romanov.com, 1 advokat-vvp.com.ua, 1 advokat73.gq, 1 advokati-ceva.cz, 1 @@ -8218,7 +8191,6 @@ agreor.com, 1 agri-meet.com, 1 agri.ee, 1 -agrichamber.com.ua, 1 agricult.tk, 1 agricultural-technology.tk, 1 agriculture-schools.com, 1 @@ -8687,7 +8659,6 @@ airportcoc.cf, 1 airportcoc.ga, 1 airportcoc.ml, 1 -airportlimototoronto.com, 1 airportstuttgart.com, 1 airporttaxibudapest.com, 1 airporttransferbudapest.co.uk, 1 @@ -9281,6 +9252,7 @@ alexandrastrauss.fr, 1 alexandrawett.net, 1 alexandre-acaries.fr, 1 +alexandre-barret.fr, 1 alexandre-gallais.fr, 1 alexandreguarita.com.br, 1 alexandremottier.tk, 1 @@ -10228,6 +10200,7 @@ alzashop.com, 1 alziamoiltetto.it, 1 alzon.cf, 1 +alzulej.pt, 1 am-39.com, 1 am-dd.com, 1 am-executive-consulting.com, 1 @@ -10631,6 +10604,7 @@ amp-logistics.com, 1 amped4ski.co.nz, 1 amper.kharkov.ua, 1 +amperaa.net, 1 ampersandnbspsemicolon.com, 1 ampetronic.com, 1 ampgroep.nl, 1 @@ -12407,6 +12381,7 @@ archbishop.ga, 1 archdetal.com.ua, 1 archeologicatoscana.it, 1 +archeologiegorinchem.com, 1 archerconsulting.llc, 1 archerlong.com, 1 archerlongx.com, 1 @@ -12638,6 +12613,7 @@ arizona-fake.tk, 1 arizonaautomobileclub.com, 1 arizonabondedtitle.com, 1 +arizonamasterworks.com, 1 arizonaonlinedivorce.com, 1 arizonatech.tk, 1 arjan.nl, 1 @@ -13015,6 +12991,7 @@ artlantis.nl, 1 artleading.ru, 1 artlifeisgood.com, 1 +artlinestix.com.au, 1 artlogo.biz, 1 artlogo.cz, 1 artlogo.sk, 1 @@ -13089,7 +13066,6 @@ arx-libertatis.org, 1 arx.vg, 1 arx8x.net, 1 -arxcs.com, 1 aryabusines.com, 1 aryacollege.me, 1 aryalaroca.de, 1 @@ -13256,7 +13232,7 @@ asiaflash.com, 1 asiafood-curator.com, 1 asiagate.ga, 1 -asiahabit.com, 1 +asiahabit.com, 0 asiaheavens.com, 1 asiakartu.tk, 1 asialeonding.at, 1 @@ -13335,7 +13311,8 @@ asmbsurvey.com, 1 asmdz.com, 1 asmeets.nl, 1 -asmm.cc, 0 +asmita.ru, 1 +asmm.cc, 1 asmobox.ga, 1 asmood.net, 1 asmrbuluo.com, 0 @@ -13557,7 +13534,6 @@ asua.ca, 1 asuamaytinh.com, 1 asucrews.com, 1 -asuhe.win, 1 asukalangley.tk, 1 asun.co, 1 asur.store, 1 @@ -13862,7 +13838,6 @@ atwonline.org, 1 atxchirocoverage.com, 1 atxtraumatherapycenter.com, 1 -atyafesolutions.com, 1 atyourleisureculinary.com, 1 atyourprice.net, 1 atypicom.es, 1 @@ -14219,6 +14194,7 @@ autofresh.tk, 1 autogear.ga, 1 autoglass.com.my, 1 +autograder.ml, 1 autohausmf-nord.de, 1 autohaussued.de, 1 autohit.ro, 1 @@ -14485,7 +14461,7 @@ autotitleloansnu.ga, 1 autoto.hr, 1 autotonic.tk, 1 -autotrac.com.br, 0 +autotrac.com.br, 1 autotransportquoteservices.com, 1 autotras.com, 1 autotyreprest.ro, 1 @@ -14515,7 +14491,6 @@ autowerkstatt-puchheim.de, 1 autozaz.ml, 1 autozuki.com, 1 -autre.cn, 1 autres-talents.fr, 1 autshir.com, 1 autumnhungary.tk, 1 @@ -14775,6 +14750,7 @@ avonlearningcampus.com, 1 avonture.be, 1 avonvets.co.uk, 1 +avova.de, 1 avpres.net, 0 avptp.org, 1 avqueen.cn, 1 @@ -15349,6 +15325,7 @@ b96899.com, 1 b9728.co, 1 b979333.com, 1 +b979365.com, 0 b979555.com, 1 b979666.com, 1 b979999.com, 1 @@ -15408,6 +15385,7 @@ b9999qq.com, 1 b9999tt.com, 1 b9999vv.com, 1 +b9999ww.com, 0 b9999yy.com, 1 b9999zz.com, 1 b99iosapp.com, 1 @@ -17403,7 +17381,6 @@ berightthere.eu, 1 berikod.ru, 1 beris.us, 1 -berita.press, 0 beritalima.com, 1 beritamotor.tk, 1 beritanow.tk, 1 @@ -17515,7 +17492,6 @@ besnard.me, 1 besnik.de, 0 besnik.tk, 1 -besola.de, 1 besole.ch, 1 besolov.tk, 1 besonders-s.at, 1 @@ -17866,6 +17842,7 @@ besv.com, 1 beszerzokozpont.hu, 1 bet-platform.com, 1 +bet031.com, 0 bet03vip.com, 0 bet064.com, 1 bet06vip.com, 1 @@ -18308,7 +18285,7 @@ bgmall.tk, 1 bgmedia.tk, 1 bgmn.me, 1 -bgmsquad.com, 1 +bgmsquad.com, 0 bgp.space, 1 bgr34.cz, 1 bgs-game.com, 1 @@ -18416,6 +18393,7 @@ bicicletassym.com.co, 1 bicignet.ga, 1 bicommarketing.com, 1 +bicranial.io, 0 bicromoestudio.com, 1 bicstone.me, 1 bicubic.tk, 1 @@ -18776,7 +18754,7 @@ binhdang.me, 0 binhex.net, 1 binhminhpc.com, 1 -bini-solution.com, 1 +bini-solution.com, 0 binimo.com, 1 biniou.net, 1 binkanhada.biz, 1 @@ -19191,6 +19169,7 @@ bitrush.nl, 1 bits-hr.de, 0 bitsafe.com.my, 1 +bitsalt.com, 1 bitsellx.com, 1 bitseo.ga, 1 bitseo.tk, 1 @@ -19284,7 +19263,6 @@ bizpay.su, 1 bizprom.ga, 1 bizstart.ga, 1 -bizstarter.cz, 1 bizsugar.ga, 1 bizteam.ga, 1 biztera.com, 1 @@ -19601,7 +19579,6 @@ blekingeopen.tk, 1 blenderinsider.com, 1 blenderrecipereviews.com, 1 -blenderwallet.io, 1 blendessencial.com, 1 blending.kr, 1 blendle.com, 1 @@ -19636,6 +19613,7 @@ blindfold.ga, 1 blindpigandtheacorn.com, 1 blinds-unlimited.com, 1 +blindscribblings.com, 1 blindsjoburg.com, 1 blingbusinessest.ga, 1 blingsparkleshine.com, 1 @@ -19787,6 +19765,7 @@ blogsnote.xyz, 1 blogspasest.ga, 1 blogstar.tk, 1 +blogtechnologiczny.pl, 1 blogthetindung.com, 1 blogtroterzy.pl, 1 bloguser.ru, 1 @@ -19891,7 +19870,6 @@ bluemosh.com, 1 bluemtnrentalmanagement.ca, 1 bluenailsstudio.nl, 1 -bluenet-26.com, 1 blueneuron.tk, 1 blueoakart.com, 1 blueparrotpainting.com, 1 @@ -19902,7 +19880,6 @@ bluepromocode.com, 1 bluerange.io, 1 blueridge.social, 1 -bluerootsmarketing.com, 1 blues-and-pictures.com, 1 bluesbarn.tk, 1 bluesbuyers.com, 1 @@ -19943,6 +19920,7 @@ bluffelectrician.co.za, 1 bluffplumber.co.za, 1 bluheron.ca, 1 +blui.cf, 0 blui.xyz, 1 bluiandaj.ml, 1 bluicraft.tk, 1 @@ -20111,6 +20089,7 @@ bodymassage.cf, 1 bodymod.tk, 1 bodymusclejournal.com, 1 +bodypainter.pl, 1 bodypainting.waw.pl, 1 bodyshaping.ml, 1 bodyshopnews.net, 1 @@ -20443,7 +20422,6 @@ boostdesign.tk, 1 boostgame.win, 1 boostplm.com, 1 -boostrpro.pl, 1 booths.cyou, 1 bootina.com, 1 bootlesshacker.com, 1 @@ -20674,6 +20652,7 @@ bouncingbuzzybees.co.uk, 1 bouncinghigher.co.uk, 1 bouncingscotland.com, 1 +bouncourseplanner.net, 1 bouncy-castles-surrey.co.uk, 1 bouncy-tots.co.uk, 1 bouncybaileys.co.uk, 1 @@ -21270,13 +21249,13 @@ bricmon.tk, 1 brico-volet.com, 1 bricolajeux.ch, 0 +bricolea.fr, 1 bricomag-media.com, 1 bricomium.com, 1 brid.gy, 0 bridal.tk, 1 bridalfabrics.ru, 1 bridalweddingshow.ga, 1 -bride-forever.com, 1 bride.vn, 1 bridesbouquet.ml, 1 bridestarco.com, 1 @@ -22737,7 +22716,6 @@ bypetula.cz, 1 byprata.com.br, 1 byraje.com, 1 -byrddogpaving.com, 1 byrest.com, 1 byriderfranchise.com, 1 byrko.cz, 1 @@ -23714,6 +23692,7 @@ capsulezone.tk, 1 captainark.net, 1 captainclaw.tk, 1 +captainfit.in, 1 captainjanks.tk, 1 captainratnesh.tk, 1 captainscarlet.tk, 1 @@ -23784,7 +23763,6 @@ carboneventsupport.be, 1 carboneventsupport.lu, 1 carbonholic.org, 1 -carboniaccessori.com.br, 1 carbonkiller.org, 1 carbonlib.com, 0 carbonmonoxidelawyer.net, 1 @@ -24703,6 +24681,7 @@ cbca.gov, 1 cbcentelles.tk, 1 cbcf.info, 1 +cbchslax.com, 1 cbcnet.co.za, 1 cbcnet.info, 1 cbd-natural.de, 1 @@ -24776,7 +24755,6 @@ ccc-ch.ch, 1 ccc-checker.cn, 1 ccc-cloud.de, 1 -ccc.xxx, 1 cccleaner.tk, 1 cccp-o.tk, 1 cccpublishing.com, 1 @@ -24865,6 +24843,7 @@ cdmon.tech, 1 cdn.ampproject.org, 1 cdn1shweflix.xyz, 1 +cdn6.de, 1 cdnaval.tk, 1 cdncompanies.com, 1 cdnjs.com, 1 @@ -25025,6 +25004,7 @@ cennetfm.tk, 1 cennetforum.tk, 1 censamatil.net, 1 +censeo-financial.com, 1 censored.ml, 1 censurfridns.dk, 1 censurfridns.nu, 1 @@ -25993,7 +25973,7 @@ chilian.de, 1 chilihosting.eu, 1 chilikin.pro, 1 -chilimath.com, 1 +chilimath.com, 0 chilimathwords.com, 1 chilio.net, 1 chilipepperhomes.com, 1 @@ -26483,7 +26463,6 @@ cialis-trial.gq, 1 cialisfreetrial.ga, 1 cialisvtr.com, 1 -cialona.nl, 1 cialowruchu.pl, 1 ciancaiphotobooth.com, 1 ciancode.com, 1 @@ -27049,6 +27028,7 @@ claudiney.id, 1 claudiney.info, 1 claudiohdz.com, 1 +claumarservice.com, 1 claus-bahr.de, 1 claus-cremer.tk, 1 clauseriksen.net, 1 @@ -27252,7 +27232,6 @@ climaencusco.com, 1 climateactionfestival.org, 1 climatechange2021.org, 1 -climatecrafters.com, 1 climateinteractive.org, 1 climatgate.tk, 1 climaticequipment.tk, 1 @@ -27663,7 +27642,6 @@ cnam-idf.fr, 1 cnaprograms.online, 1 cnatraining.network, 1 -cnb.ie, 1 cnbs.ch, 1 cnc-lehrgang.de, 1 cncado.net, 1 @@ -27702,6 +27680,7 @@ co2eco.cn, 0 co2fr.ee, 0 co50.com, 1 +coach-immobilier-neuf.fr, 1 coach.org.uk, 0 coachapp-ipass.herokuapp.com, 1 coachbakery.com, 1 @@ -28246,7 +28225,6 @@ columbushydroxide.net, 1 columbushydroxide.org, 1 columbusks.gov, 1 -columbuswines.com, 1 colwichks.gov, 1 colyakoomusic.com, 1 com-b.vn, 1 @@ -28774,6 +28752,7 @@ connectme.com.mx, 1 connectmy.car, 1 connecto-data.com, 1 +connecto.group, 1 connectum.eu, 1 connelink.fr, 1 conner.work, 1 @@ -29353,7 +29332,6 @@ cosmechic.fr, 1 cosmekaitori.jp, 1 cosmetic-surgery-prices.co.uk, 1 -cosmeticappraisal.com, 1 cosmeticasimple.com, 1 cosmeticenter.com.br, 1 cosmeticosdelivery.com.br, 1 @@ -29451,6 +29429,7 @@ counsellingtime.co.uk, 1 counsellingtime.com, 1 counstellor.com, 0 +count.sh, 0 countdowntrader.com, 1 counterenlol.com, 1 counterespionage.com, 1 @@ -30479,7 +30458,6 @@ ctoresms.com, 1 ctpe.info, 1 ctpe.net, 1 -ctr-sante.eu, 1 ctr.id, 0 ctrl.blog, 1 ctrl.gr, 0 @@ -31672,7 +31650,6 @@ dannyoficial.tk, 1 dannyroh.de, 1 dannyrohde.de, 1 -dannys.cloud, 0 dannys.space, 1 dannyscloud.tk, 1 dannystevens.co.uk, 1 @@ -31687,6 +31664,7 @@ danq.me, 1 danramer.tk, 1 dansa.com.co, 1 +dansage.co, 0 dansaunders.me, 1 dansdiscounttools.com, 1 dansedesalonsaintave.fr, 1 @@ -31802,7 +31780,6 @@ darkgrid.eu, 1 darkhall.tk, 1 darkhuntersworld.tk, 1 -darkillusion.us, 1 darkishgreen.com, 1 darkknights.tk, 1 darklang.com, 1 @@ -32041,7 +32018,6 @@ datenreiter.gq, 1 datenreiter.org, 1 datensalat.info, 1 -datenschutz-consult.de, 1 datenschutz-gruenwald.de, 1 datenschutz-individuell.de, 1 datenschutz-isny.de, 1 @@ -32222,7 +32198,6 @@ davy-server.com, 1 davyatletiek.tk, 1 davyjones.com.br, 1 -davyjonesatacado.com.br, 1 davypropper.com, 1 davys.com.br, 1 daware.io, 1 @@ -32624,7 +32599,6 @@ decorativeflooring.com, 1 decorator.uk, 1 decoratore.roma.it, 1 -decorauvent.ca, 1 decorestilo.com.br, 1 decorincasa.com.br, 1 decorlux.bg, 1 @@ -33415,7 +33389,6 @@ desiplex.tk, 1 desire-host.tk, 1 desish.cf, 1 -desiskinscience.com, 0 desivideos.tk, 1 deskaservices.com, 1 deskdesign.me, 1 @@ -33506,7 +33479,6 @@ detenterprise.com, 1 determapp.de, 1 determinatie.tk, 1 -dethemium.com, 1 deti-online.com, 1 deti-vse.ml, 1 deti.ga, 1 @@ -33717,6 +33689,7 @@ dexign.ro, 1 dexigner.com, 1 dexonrest.azurewebsites.net, 1 +dexonservicedeskws.azurewebsites.net, 1 dexonsoftware.com, 0 dexterseries.ru, 1 dextra.tk, 1 @@ -34002,7 +33975,6 @@ didierghez.com, 1 didierlaumen.be, 1 didigotoffer.com, 1 -didntdoitbailbonds.com, 1 didtrumpopengovernmentyet.com, 1 die-bergfuehrer.de, 1 die-besten-weisheiten.de, 1 @@ -34278,6 +34250,7 @@ digitalpiloten.org, 1 digitalplaymakers.co.uk, 1 digitalpocketpedometer.tk, 1 +digitalponsel.com, 0 digitalposition.com, 1 digitalprimate.my, 1 digitalprofilers.com, 1 @@ -34297,7 +34270,6 @@ digitalsphere.tk, 1 digitalsurge.io, 1 digitaltcertifikat.dk, 1 -digitaltepee.co.uk, 1 digitaltry.tk, 1 digitalupcoming.tk, 1 digitalvag.tk, 1 @@ -35268,7 +35240,7 @@ do13.net, 1 do67.de, 1 do67.net, 1 -doamatto.xyz, 0 +doamatto.xyz, 1 doanhai.tk, 1 dobavki.club, 1 dobbshvac.com, 1 @@ -36707,7 +36679,6 @@ dryasinakgul.com, 1 drybjed.net, 1 drybysuperior.com, 1 -drycleancoalition.org, 1 drycreekphoto.com, 1 drydensfairfax.com, 1 drydor.com, 1 @@ -36959,6 +36930,7 @@ dumbeartech.com, 1 dumberger-bau.de, 1 dumbfunded.co.uk, 1 +dumbsolpunks.com, 1 dumino.bg, 1 dummo.tk, 1 dumnezeu.tk, 1 @@ -37829,6 +37801,7 @@ eaglexiang.org, 1 eagleyecs.com, 1 eaimty.com, 1 +eajglobal.com, 1 ealadel.com, 1 ealekseyev.ml, 1 ealev.de, 1 @@ -37855,7 +37828,6 @@ earlyvoting.ml, 1 earlyyearshub.com, 1 earmarks.gov, 1 -earn99.co, 1 earnet.tk, 1 earningsgames.tk, 1 earningthatis.tk, 1 @@ -37905,7 +37877,6 @@ eaststudios.net, 1 eastvalleyautorebuild.com, 1 eastwashingtonpa.gov, 1 -eastwesttmc.com.au, 1 eastwindsorhistory.tk, 1 eastyorkshirebuses.co.uk, 1 easukasbestos.co.uk, 1 @@ -38097,6 +38068,7 @@ ebpgateway.com, 1 ebrahimhadimarket.com, 1 ebrdbusinessguide.com, 1 +ebrea.ch, 1 ebregrow.com, 1 ebrnd.de, 1 ebrowz.com, 1 @@ -38275,7 +38247,6 @@ ecopark.asia, 1 ecorak.de, 1 ecorp-australia.tk, 1 -ecos-eguilles.com, 1 ecos-ev.de, 1 ecos.srl, 1 ecosas.org, 1 @@ -38359,7 +38330,6 @@ edeals.com.co, 1 edeca.net, 1 ededdeddy.tk, 1 -edefrutos.me, 1 edefrutos2020.com, 1 edegembicycleclub.tk, 1 edegulkoyu.tk, 1 @@ -38450,7 +38420,6 @@ edrgroup.nl, 1 edrosd.cf, 1 edrost.tk, 1 -edsby.com, 1 edscolors.com, 1 edshogg.co.uk, 1 edsm.net, 1 @@ -38723,6 +38692,7 @@ eggman.tk, 1 eggqvq.com, 1 eggrolls.ml, 1 +eggy.com.au, 0 eggzr.com, 1 egh.ir, 1 egiftcards.be, 1 @@ -39078,6 +39048,7 @@ electricalconejovalley.com, 1 electricaldosvientos.com, 1 electricalengineers.tk, 1 +electricalfencingfourways.co.za, 1 electricalfencinggermiston.co.za, 1 electricalhiddenhills.com, 1 electricallakesherwood.com, 1 @@ -39097,7 +39068,6 @@ electricconejovalley.com, 1 electricdosvientos.com, 1 electricdreams.xyz, 1 -electricfencebenoni.co.za, 1 electricfenceboksburg.co.za, 1 electricfencemidrand.co.za, 1 electricfenceroodepoort.co.za, 1 @@ -39583,7 +39553,6 @@ elstopstelten.nl, 0 elsuccionador.com, 1 elsvanderlugt.nl, 1 -elswickllc.com, 1 elsword.moe, 0 elta.com.ua, 1 eltair.com, 1 @@ -40004,6 +39973,7 @@ ender.fr, 1 ender.moe, 1 ender3.info, 1 +enderandrew.com, 1 enderbycamping.com, 1 enderdrachelp.ddns.net, 1 enderhost.tk, 1 @@ -40548,7 +40518,6 @@ epvd.tk, 1 epyonsuniverse.net, 1 eq-serve.com, 1 -eqab.net, 1 eqassociates.com, 1 eqibank.com, 1 eqlplayground.io, 1 @@ -40824,6 +40793,7 @@ es888999.com, 1 es999.net, 1 esaborit.ddns.net, 0 +esadoggy.com, 0 esagente.com, 1 esajokinen.net, 1 esale.co, 1 @@ -41185,6 +41155,7 @@ estudiarenergiasrenovablesonline.es, 1 estudiaresteticaonline.es, 1 estudiarparaser.com, 1 +estudiarseguridadprivada.es, 1 estudiaryaprenderingles.com, 1 estudiemosvirtualmente.com, 1 estudio21pattern.com, 0 @@ -41225,7 +41196,6 @@ etaoinwu.win, 1 etath.com, 1 etaxigraz.com, 1 -etbtoursegypt.com, 1 etccooperative.org, 0 etch.co, 1 etch44.com, 1 @@ -41327,7 +41297,6 @@ etiquetaunica.com.br, 1 etkaddict.com, 1 etkarle.de, 1 -etkinpatent.com, 1 etnis.id, 1 etnoforum.tk, 1 etnoria.com, 1 @@ -41527,6 +41496,7 @@ eusarse.tk, 1 euskaltzaleak.tk, 1 eusou.ml, 1 +euterpiaradio.ch, 1 eutiximo.com, 1 eutotal.com, 1 euvo.tk, 0 @@ -41617,7 +41587,7 @@ event-blick.de, 1 event-fullyyours.com, 1 event-reisen.tk, 1 -event1teamstore.com, 1 +event1teamstore.com, 0 event4fun.no, 1 eventact.com, 0 eventaro.com, 1 @@ -42140,7 +42110,6 @@ exs.lv, 1 exsanio.de, 1 exside.com, 1 -extact.com, 1 extantsoft.biz, 1 extendet.tk, 1 extendwings.com, 1 @@ -42764,7 +42733,7 @@ faktotum.tech, 0 fakturar.com, 1 fakturi.com, 1 -faktury.co, 1 +faktury.co, 0 falaeapp.org, 1 falaowang.com, 1 falasteenjobs.com, 1 @@ -43328,6 +43297,7 @@ fbigame.com, 1 fbihr.gov, 1 fbiic.gov, 1 +fbijobs.gov, 1 fbo.gov, 1 fbo.network, 1 fboerman.nl, 1 @@ -43350,7 +43320,6 @@ fcbrasov.tk, 1 fcburk.de, 1 fccarbon.com, 0 -fccpvirtual.com.ve, 1 fcdauwendaele-dames.tk, 1 fcdekampioenen.tk, 1 fcdemuis.tk, 1 @@ -43393,7 +43362,6 @@ fdresearch.ca, 1 fdsl.eu, 1 fe-data.nl, 1 -feac.us, 1 feaden.me, 1 feandc.com, 1 fear-crowd.tk, 1 @@ -44870,7 +44838,6 @@ flipsidevr.com, 1 fliptable.org, 1 flipthebrain.com, 1 -fliptracker.io, 1 flipweb.tk, 1 flirt-norden.de, 1 flirtbox.tk, 1 @@ -45650,6 +45617,7 @@ formulastudent.de, 1 formulaveevictoria.com.au, 1 formup.com.pl, 1 +formvibes.com, 1 fornarisandres.com, 1 foro-coopfuture.tk, 1 foro.io, 0 @@ -45662,6 +45630,7 @@ forocoches.com, 1 forocristiano.tk, 1 forodebanfield.tk, 1 +forodeespanol.com, 1 forodieta.com, 0 foroenguera.tk, 1 forojovensanfernando.tk, 1 @@ -46017,6 +45986,7 @@ fraho.eu, 1 frahub.com, 1 frail.gq, 1 +fralef.me, 0 fralippolippi.tk, 1 frama.link, 1 frama.site, 1 @@ -46514,6 +46484,7 @@ freezander.tk, 1 freezemea.com, 1 freezerrepairaustin.com, 1 +freezvon.com, 1 frei.social, 1 freibesetzt.tk, 1 freiboth.ddns.net, 1 @@ -47384,6 +47355,7 @@ fyol.pw, 1 fyp-aiman.com, 1 fyphb.com, 1 +fyrehost.net, 1 fyrehost.xyz, 1 fyretrine.com, 1 fyroeo.fr, 0 @@ -47470,7 +47442,6 @@ gabe.download, 1 gabe.house, 1 gabe.link, 1 -gabe.pics, 1 gabe.space, 1 gabe.watch, 1 gabe565.com, 1 @@ -47862,7 +47833,6 @@ gametium.com, 1 gametium.es, 1 gametowndev.tk, 1 -gametriot.com, 1 gametube.website, 1 gameview.tk, 1 gamewayz.online, 1 @@ -47919,7 +47889,6 @@ gandalfcz.tk, 1 gandalfservice.com, 1 gandalfthefeline.com, 1 -gandgliquors.com, 1 gandul.ro, 1 gangbangs.tk, 1 gangbangteen.net, 1 @@ -49183,7 +49152,6 @@ gianproperties.com, 1 giant-panda.com, 1 giant-tortoise.com, 1 -giantbrandsolutions.com, 1 giantratesers.ga, 1 giantratesest.ga, 1 giantslipandslide.co.uk, 1 @@ -49272,6 +49240,7 @@ gigawattz.com, 1 giggletotz.co.uk, 1 gigharborwa.gov, 1 +gigiena-ruk.ru, 1 gigiscloud.servebeer.com, 1 giglink.club, 1 gigloog.tk, 1 @@ -49434,6 +49403,7 @@ gitns.org, 1 gitstuff.tk, 1 gittigidiyor.com, 1 +gittr.ch, 1 giuem.com, 1 giuliabonati.com, 1 giuliawylde.com, 1 @@ -49825,7 +49795,6 @@ gmpartsprime.com, 1 gmslparking.co.uk, 1 gmsociety.tk, 1 -gmsurveyingms.com, 1 gmta.nl, 1 gmtplus.co.za, 1 gmuh.fr, 1 @@ -50722,7 +50691,6 @@ graphicbuffet.co.th, 1 graphicdesignresources.net, 1 graphicdream.tk, 1 -graphicnab.com, 1 graphicspace.tk, 1 graphicwallet.com, 1 graphire.io, 1 @@ -50775,7 +50743,6 @@ grattan.co.uk, 1 gratuitweb.tk, 1 graumeier.de, 1 -grauwasser-blog.de, 1 gravedigger.tk, 1 gravelshooters.com, 1 gravelshooters.net, 1 @@ -52114,7 +52081,6 @@ hairhumanextensions.tk, 1 hairloss.com, 1 hairlossadvice.tk, 1 -hairmitage.pl, 0 hairphoto.tk, 1 hairpins.tk, 1 hairsalon-wish.com, 1 @@ -52308,7 +52274,6 @@ handmadehechoamano.com, 1 handmadetutorials.ro, 1 handphones.tk, 1 -handrollschile.cl, 1 handsaccounting.com, 1 handsomeabel.tk, 1 handstandstudio.ga, 1 @@ -52613,6 +52578,7 @@ harrietjohnston.tk, 1 harrimantn.gov, 1 harringtonca.com, 1 +harrisandharris.com.au, 1 harrisconsulting.ie, 1 harrisexteriors.com, 1 harrisonar.gov, 1 @@ -53130,7 +53096,6 @@ healthhusky.ga, 1 healthiercompany.com, 1 healthiergenerations.co.uk, 1 -healthierweight.co.uk, 1 healthimagine.ga, 1 healthintergrity.ga, 1 healthiraq.ga, 1 @@ -53459,6 +53424,7 @@ heidifuller.com, 1 heidihills.com, 1 heidirange.tk, 1 +heidisheroes.org, 1 heidns.cn, 0 heightselectrical.com.au, 1 heijdel.nl, 1 @@ -53516,6 +53482,7 @@ heladospipos.ga, 1 helagotaland.ga, 1 helagotaland.gq, 1 +helali.me, 1 helastel.com, 1 helbreath.tk, 1 helco.xyz, 1 @@ -53963,8 +53930,9 @@ hexstreamsoft.com, 1 hexxagon.com, 1 hey.pw, 1 -heyapakabar.com, 0 +heyapakabar.com, 1 heybaker.com.au, 1 +heyboldface.com, 1 heybookmark.ga, 1 heybookmark.gq, 1 heybritney.tk, 1 @@ -56110,7 +56078,6 @@ hurricanelabs.com, 0 hurricaneplaneers.ga, 1 hurricaneplaneest.ga, 1 -hurrikane.us, 1 hurriyetseriilan.tk, 1 hurstiharrell.tk, 1 hurtigtinternet.dk, 1 @@ -56119,7 +56086,6 @@ hushbabysleep.com, 1 hushfile.it, 1 husic.net, 0 -husk.house, 1 husky-in-nood.tk, 1 huskyeye.de, 1 huskyinc.us, 0 @@ -56667,7 +56633,6 @@ iconecoiffure.ca, 1 iconintegration.com.au, 1 iconoarte.tk, 1 -iconomi.net, 1 icons4free.tk, 1 iconsuppstore.com, 1 iconworld.ml, 1 @@ -56685,6 +56650,7 @@ icst.tk, 1 ict-concept.nl, 1 ict-crew.nl, 1 +ict-kerk.nl, 1 ict-oldehove.nl, 1 ict-radar.com, 1 ict-radar.nl, 1 @@ -56850,6 +56816,7 @@ idonthaveawebsite.tk, 1 idontplaydarts.com, 1 idoparadoxon.hu, 1 +idowp.net, 1 idp.onl, 1 idraetsmusik.dk, 1 idratherbequilting.com, 1 @@ -57441,7 +57408,6 @@ imforza.com, 1 img.mg, 1 img.ovh, 1 -imgaa.com, 1 imgbb.com, 1 imgbu.com, 1 imgencrypt.com, 1 @@ -57933,6 +57899,7 @@ inex.one, 1 inexlog.fr, 1 inexpensivecomputers.net, 1 +inextmovies.link, 1 ineztheminiatureelephant.com, 1 inf-fusion.ca, 1 inf0sec.nl, 1 @@ -58719,6 +58686,7 @@ intelhost.com.pe, 1 intelhost.net, 1 inteli.com.pl, 1 +intelics.com.au, 1 intellar.com, 1 intelldynamics.com, 1 intellecta.is, 1 @@ -59566,6 +59534,7 @@ ishiro.me, 1 ishland.com, 1 ishopforpowerserg.com, 1 +ishotagency.com, 1 ishtyl.com, 1 isidore.uk, 1 isif-ostewg.org, 1 @@ -59796,7 +59765,6 @@ it-faul.de, 1 it-inside.ch, 1 it-jobbank.dk, 1 -it-kron.de, 1 it-maker.eu, 1 it-meneer.nl, 0 it-novosti.tk, 1 @@ -60305,6 +60273,7 @@ j9508.com, 1 j9511.com, 1 j9512.com, 1 +j9514.com, 0 j9515.com, 1 j9516.com, 1 j9517.com, 1 @@ -60769,7 +60738,6 @@ jarsater.com, 0 jas-ac.com, 1 jas-team.net, 1 -jasalokal.id, 1 jasawebbisnis.com, 0 jaseng.ga, 1 jashinchan.cn, 1 @@ -61019,7 +60987,6 @@ jdd888.cc, 1 jdecommastermind.com, 1 jdefreitas.com, 1 -jdegbau.com, 1 jdelgado.fr, 1 jdieselmusic.com, 1 jdinjury.com, 1 @@ -61237,7 +61204,6 @@ jeremywinn.xyz, 1 jericamacmillan.com, 1 jerichoproject.org, 1 -jering.tech, 1 jerisandoval.tk, 1 jerixmx.com, 1 jermann.biz, 1 @@ -61270,6 +61236,7 @@ jerusalempersonalsers.ga, 1 jerusalempersonalsest.ga, 1 jesec.cn, 1 +jesec.io, 1 jesiensredniowiecza.pl, 1 jesmh.de, 1 jesperandersson.tk, 1 @@ -61505,6 +61472,7 @@ jimvophotography.tk, 1 jimwoodrealty.com, 1 jimwoodrealty.help, 1 +jin-dan.site, 0 jinancy.fr, 1 jinanshen.com, 1 jinbijin.nl, 1 @@ -61604,7 +61572,7 @@ jlpn.eu, 1 jlpn.nl, 1 jlponsetto.com, 1 -jlqwer.com, 0 +jlqwer.com, 1 jlr-luxembourg.com, 1 jls.idv.tw, 1 jltcsecuritygroup.com, 1 @@ -62443,7 +62411,6 @@ julenlanda.com, 0 julesfrans.be, 1 julesroovers.nl, 1 -julestern.com, 1 julia-clarete.tk, 1 julia-jones.org, 1 julia-pink.org, 1 @@ -62451,7 +62418,7 @@ juliaexclusiv.com, 1 juliajuice.net, 1 julian-miller.de, 1 -julian-post.de, 1 +julian-post.de, 0 julian-uphoff.de, 1 julian-weigle.de, 1 julian.tech, 1 @@ -62691,6 +62658,7 @@ justtalk.site, 1 justthinktwice.gov, 0 justupdate.me, 1 +justyardsign.com, 1 justyy.com, 1 justzz.xyz, 1 juszczak.io, 1 @@ -63127,7 +63095,6 @@ kalex.nl, 1 kaleylocks.com, 1 kalhufvudet.se, 1 -kali.training, 1 kaliaa.fi, 1 kalian.cz, 1 kaliboairport.tk, 1 @@ -63305,7 +63272,6 @@ kanzlei-gaengler.de, 1 kanzlei-hhh.de, 1 kanzlei-oehler.com, 1 -kanzlei-sixt.de, 1 kanzshop.com, 1 kaohongshu.blog, 1 kaosintesta.tk, 1 @@ -64567,7 +64533,6 @@ kingdoms.gg, 1 kingfast.cc, 1 kingfast.eu.org, 1 -kingfin.com, 1 kingiescastles.co.uk, 1 kingjamesbibleonline.org, 1 kingjamesgospel.com, 1 @@ -64763,6 +64728,7 @@ kita-freie-schule.de, 1 kita-sun.com, 1 kitabat.com, 1 +kitabgaul.com, 0 kitabnamabayi.com, 1 kitacoffee.com, 1 kitagawa-internal-medicine-clinic.com, 1 @@ -65120,7 +65086,6 @@ knighkidoma.tk, 1 knightsblog.de, 1 knightsbridge.net, 1 -knightsbridgewine.com, 1 knightsweep.com, 1 knighulki.cf, 1 knigi-free.cf, 1 @@ -65153,7 +65118,6 @@ know.cf, 1 knowarth.com, 1 knowdebt.org, 1 -knowit-now.com, 1 knowl365.com, 1 knowledge-base.info, 0 knowledgeforce.com, 1 @@ -65297,6 +65261,7 @@ koing.de, 1 koirala.email, 1 koiro.fi, 1 +koishi.pro, 1 koizumidesign.com, 1 koj.co, 1 koji-tsujitani.net, 1 @@ -65794,6 +65759,7 @@ krattk.de, 1 krauseent.com, 0 krauskopf-it.de, 1 +krautomat.com, 1 kraynik.com, 1 krazy.net.au, 1 krazykastles.co.uk, 1 @@ -65958,7 +65924,6 @@ krozilla.tk, 1 krpaforum.org, 1 krsaustralia.com.au, 1 -krsn.de, 1 krsvrs.nl, 1 krti.com.ua, 1 krubik.tk, 1 @@ -66004,7 +65969,6 @@ krystal-framework.ml, 1 krytykawszystkiego.com, 1 krytykawszystkiego.pl, 1 -kryx.de, 1 ks-19.com, 1 ks-39.com, 1 ks-59.com, 1 @@ -66664,6 +66628,7 @@ laborriquita.tk, 1 labortogether.com, 1 labouncycastlehire.co.uk, 1 +labourreedevergheas.fr, 1 laboutiquedejuliette.com, 1 laboutiquemarocaineduconvoyeur.com, 1 laboutiquemarocaineduconvoyeur.ma, 1 @@ -67028,6 +66993,7 @@ lan4.life, 1 lana.swedbank.se, 1 lanabello.com.br, 1 +lanaengel.com, 1 lanagiselle.net, 1 lanahallen.com, 1 lanasomething.com, 1 @@ -67075,7 +67041,7 @@ landoncreekapartments.com, 1 landoverhillsmd.gov, 1 landroverexpo.com.au, 1 -landsbref.is, 1 +landsbref.is, 0 landscape-photography.org, 1 landscapelightingagoura.com, 1 landscapelightingagourahills.com, 1 @@ -67123,7 +67089,6 @@ langenu.tk, 1 langgasse-baar.ch, 1 langhof-immobilien.de, 1 -langhun.me, 1 langjp.com, 0 langley.tk, 1 langleyporter.com, 1 @@ -67405,7 +67370,6 @@ laszloinstitute.com, 1 laszlotamas.hu, 1 lat.sk, 1 -lat46.ch, 0 latabaccheria.net, 1 latabledebry.be, 1 latabledemontebello.com, 1 @@ -67441,7 +67405,6 @@ latestimmigrationnews.today, 1 latestmata.com, 1 latestmobiles.tk, 1 -latestmyanmarnews.com, 0 latestonmarketing.com, 1 latetrain.cn, 1 lathamlabs.com, 1 @@ -67671,7 +67634,6 @@ lazysoftware.fr, 1 lazytux.org, 1 lazywaves.tk, 1 -lazzzy.com, 1 lb-music.tk, 1 lb-toner.de, 1 lb366.cc, 1 @@ -68614,7 +68576,6 @@ lesptitspasdelyne.fr, 1 lesptitstutos.fr, 1 lesquerda.cat, 0 -lessavonnables.fr, 1 lessets-graphiques.com, 1 lessiamia.net, 1 lessis.moe, 1 @@ -68835,7 +68796,6 @@ lgbusiness.es, 0 lgerman.de, 1 lgesteticaautomotiva.com.br, 1 -lgf.im, 0 lghfinancialstrategy.ch, 0 lgiswa.com.au, 1 lgnsh.fr, 1 @@ -69217,7 +69177,6 @@ lightningwirelabs.com, 1 lighto.pk, 1 lightography.com, 1 -lightquantum.moe, 1 lights.co.uk, 1 lights0123.com, 1 lightscale.com, 1 @@ -69401,7 +69360,6 @@ lincolncountysheriffok.gov, 1 lincolncountytn.gov, 1 lincolncountywy.gov, 1 -lincolnfinewines.com, 1 lincolnimps.tk, 1 lincolnmoneyman.com, 1 lincolnpedsgroup.com, 1 @@ -69840,7 +69798,7 @@ littles.moe, 1 littlescallywagsplay.co.uk, 1 littleservice.cn, 1 -littlesk.in, 1 +littlesk.in, 0 littleskin.cn, 1 littleson.com.br, 1 littlesouls.ml, 1 @@ -69941,7 +69899,6 @@ livelong.tk, 1 livelonglife.tk, 1 livelovelaughlg.com, 1 -livelyapps.com, 1 liveman.dk, 1 livemomentum.ml, 1 livemosspointe.com, 1 @@ -70276,13 +70233,11 @@ locksmithgermiston24-7.co.za, 1 locksmithgrapevinetx.com, 1 locksmithhillcrest.co.za, 1 -locksmithindurban.co.za, 1 locksmithlakewaytx.com, 1 locksmithlivoniami.com, 1 locksmithmadisonheights.com, 1 locksmithmesquitetexas.com, 1 locksmithmesquitetx.com, 1 -locksmithmidrand24-7.co.za, 1 locksmithmissouricity.com, 1 locksmithopen.com, 1 locksmithresidentialspringtx.com, 1 @@ -70476,7 +70431,6 @@ lokaal.org, 1 lokal-speisen.de, 1 lokalna.net, 1 -lokan.id, 1 loker.id, 1 lokjagruktafoundation.com, 1 lokomotivaplzen.cz, 1 @@ -70494,7 +70448,6 @@ lolcosplay.ga, 1 lolcow.farm, 1 lolcow.org, 1 -lolcow.su, 1 loldudes.com, 1 lolfunny.tk, 1 loli.art, 1 @@ -70673,6 +70626,7 @@ look-books.tk, 1 look-info.tk, 1 look-like.tk, 1 +look.co.il, 1 lookae.com, 0 lookagain.co.uk, 1 lookasik.eu, 1 @@ -70694,7 +70648,7 @@ looneymooney.com, 1 loony.info, 0 loonylatke.com, 1 -loopback.kr, 0 +loopback.kr, 1 loopcore.de, 1 loopkey.com.br, 1 loopool.tk, 1 @@ -71244,7 +71198,6 @@ ludum.pl, 1 ludunwayoo.com, 1 ludwig.im, 1 -ludwiggrill.de, 1 ludwigjohnson.se, 1 ludwigpro.net, 1 lueck-bertram.de, 1 @@ -71330,6 +71283,7 @@ lukeistschuld.de, 1 lukekuza.com, 1 lukekuza.me, 1 +lukem.eu, 0 lukem.net, 1 lukeng.net, 1 lukepeltier.com, 1 @@ -71495,7 +71449,6 @@ luuppi.fi, 1 luv-scent.com, 1 luv.asn.au, 1 -luv2watchmycam.com, 1 luvare.com, 1 luvbridal.com.au, 1 luvey.com, 1 @@ -71908,7 +71861,6 @@ maden.com, 1 mader.jp, 1 maderasbrown.com, 1 -madewellwoodworks.com, 1 madewithopendata.org, 1 madge.tk, 1 madhawaweb.tk, 1 @@ -71918,6 +71870,7 @@ madirc.net, 1 madisoncountyhelps.com, 1 madisonent-facialplasticsurgery.com, 1 +madisonivy.space, 1 madisonprocaccini.tk, 1 madisonsjewelersorlando.com, 1 madisonsquarerealestate.com, 1 @@ -72043,6 +71996,7 @@ magi-cake.com, 1 magiamgiatot.tk, 1 magic-cards.info, 1 +magic-carpetcleaning.co.uk, 1 magic-cheerleading.tk, 1 magic-network.tk, 1 magic-photo-events.fr, 1 @@ -72140,6 +72094,7 @@ magnettracker.com, 1 magniezetassocies.fr, 1 magnific.tk, 1 +magnificentdata.com, 1 magniflood.com, 1 magnitgang.ml, 1 magnitola.ml, 1 @@ -73270,7 +73225,7 @@ marktguru.de, 1 markup-ua.com, 1 markus-blog.de, 1 -markus-dev.com, 1 +markus-dev.com, 0 markus-keppeler.de, 1 markus-musiker.de, 1 markus-ullmann.de, 1 @@ -74046,7 +74001,6 @@ maxratmeyer.com, 1 maxrickettsuy.com, 1 maxrider.tk, 1 -maxs.com, 1 maxtruxa.com, 1 maxundlara.at, 1 maxundlara.com, 1 @@ -75546,6 +75500,7 @@ meridiangroup.ml, 1 meridianmetals.com, 1 meridianoshop.com.br, 1 +meridianstore.com.br, 1 merikserver.tk, 1 merkattumaa.tk, 1 merkchest.tk, 1 @@ -75767,7 +75722,6 @@ metrofree.ga, 1 metroline.ml, 1 metrolush.com, 1 -metromas.com, 1 metron-eging.com, 1 metron-networks.com, 1 metron-online.com, 1 @@ -75949,7 +75903,6 @@ miankamran.tk, 1 miao.team, 1 miaoft.com, 1 -miaomiao.eu.org, 1 miaovps.com, 1 miaowo.org, 1 miap.eu, 1 @@ -75990,6 +75943,7 @@ michaelamead.com, 1 michaelasawyer.com, 1 michaelband.co, 1 +michaelband.com, 1 michaelbeer.co.uk, 1 michaelbondar.tk, 1 michaelboogerd.tk, 1 @@ -76231,7 +76185,6 @@ miguelmoura.com, 1 migueloblitas.tk, 1 miguelpallardo.tk, 1 -miguia.tv, 1 mihaiordean.com, 1 mihalgrameno.ml, 1 mihalicka.com, 1 @@ -76343,6 +76296,7 @@ mikkelvej.dk, 1 mikkohuupponen.com, 1 mikkonen.bio, 1 +mikkosa.fi, 0 miklagard.dk, 1 miklcct.com, 1 mikmik.co.il, 1 @@ -76439,7 +76393,6 @@ millerpaving.com, 1 millersminibarns.com, 1 millersprolandscape.com, 0 -millerwalker.com, 1 millettable.com, 1 milliarden-liste.de, 1 millibirlik.tk, 1 @@ -77063,7 +77016,6 @@ mjhs.org, 1 mjhsfoundation.org, 1 mjjlab.com, 1 -mjkholding.nl, 1 mjmedia.co.za, 1 mjniessen.com, 1 mjollnir.fr, 1 @@ -78399,6 +78351,7 @@ moy-gorod.od.ua, 0 moy.cat, 1 moybiznes.tk, 1 +moyer.pub, 0 moyideal.tk, 1 moylen.eu, 1 moyminsk.tk, 1 @@ -78766,6 +78719,7 @@ mudaomundo.org, 1 mudareganhar.pt, 0 mudasobwa.tk, 1 +mudaustralia.com, 1 mudbenesov.cz, 1 mudcomplex.ga, 1 mudcomplexers.ga, 1 @@ -78803,7 +78757,6 @@ muhammed.tk, 1 muhasebekurslari.tk, 1 muhcow.dk, 1 -muhelheim.com, 1 muhibbulislam.tk, 1 muhiminulhasan.me, 1 muhlenbergtwppa.gov, 1 @@ -80115,7 +80068,6 @@ n3oxid.fr, 1 n3rd0rama.tk, 1 n4mullingartolongford.ie, 1 -n4zm.com, 1 n5118.com, 1 n5197.co, 1 n61roscommon.ie, 1 @@ -80218,6 +80170,7 @@ nadjasummer.com, 1 nadlerdentistry.com, 1 nadomna-rabota.tk, 1 +nadoske.info, 1 nadsandgams.com, 1 naduvilathu.tk, 1 nadyaolcer.fr, 1 @@ -80294,7 +80247,6 @@ nakada4610.com, 1 nakagawa-d.co.jp, 1 nakagawa-s.jp, 1 -nakajims.net, 1 nakalabo.jp, 1 nakama.tv, 1 nakamastudios.com, 1 @@ -80350,6 +80302,7 @@ namepros.com, 1 nameproscdn.com, 1 namereel.com, 1 +namesbee.com, 0 nameshield.com, 1 nameshield.net, 1 namesnack.com, 1 @@ -80603,6 +80556,7 @@ natashki.tk, 1 natasjaversantvoort.nl, 1 natation-nsh.com, 0 +natchmatch.com, 1 nate.sh, 1 nateandxtina.wedding, 1 nategreen.org, 0 @@ -80634,7 +80588,6 @@ nathankonopinski.com, 0 nathanmfarrugia.com, 1 nathanphoenix.com, 1 -nathans.com.au, 1 nathansmetana.com, 1 nathenmaxwell.tk, 1 nathumarket.com.br, 1 @@ -80914,6 +80867,7 @@ ndarville.com, 1 ndbt.com, 1 ndcpolipak.com, 1 +nder.be, 1 ndev.tk, 1 ndfirefighter.com, 1 ndhlink.com, 1 @@ -81540,6 +81494,7 @@ netzona.org, 1 netzspielplatz.de, 0 netzsv.website, 1 +netztest.at, 1 netzvieh.de, 1 netzwerk-lq.com, 1 netzwerk-sozialliberal.de, 1 @@ -81715,7 +81670,7 @@ newinternet.media, 1 newizv.ru, 0 newjerseyvideography.com, 1 -newjianzhi.com, 1 +newjianzhi.com, 0 newknd.com, 1 newlegalsteroid.com, 1 newlifehempoil.com, 1 @@ -82807,6 +82762,7 @@ nomifensine.com, 1 nomik.xyz, 1 nomio.com, 1 +nomo.my, 1 nomoondev.azurewebsites.net, 1 nomsing.tk, 1 nomsy.net, 1 @@ -82923,6 +82879,7 @@ noriel.ro, 1 norikazumatsuno.tk, 1 noris.de, 0 +noriskit.nl, 1 noritakechina.com, 1 normaculta.com.br, 1 normalady.com, 1 @@ -83154,6 +83111,7 @@ notoriousdev.com, 1 nototema.com, 1 notre-planete.info, 1 +notrecinema.com, 1 notrefuse.tk, 1 notrero13.com, 1 notresiteduvercors.tk, 1 @@ -83275,7 +83233,6 @@ nowcomplete.com.br, 1 nowebsite.tk, 1 nowecor.de, 1 -noweigh.co.uk, 1 nowhere.dk, 1 nowinkijedynki.tk, 1 nowitzki.network, 1 @@ -83342,6 +83299,7 @@ nrldc.in, 0 nrm.co.nz, 1 nrmc.pt, 1 +nrnjn.xyz, 0 nrsmart.com, 1 nrsweb.org, 1 nrthcdn.me, 1 @@ -84010,7 +83968,6 @@ oceanspringsarchives.net, 1 oceansurplus.tk, 1 oceanviewde.gov, 1 -oceanvisuals.com, 1 ocebot.net, 1 ocenilla.ml, 1 ocenilla.tk, 1 @@ -84134,7 +84091,6 @@ odosblog.de, 1 odpikedoslike.com, 1 odsylvie.cz, 1 -oduachambers.com, 1 oducs.org, 1 odvps.com, 0 odxin.com, 1 @@ -84180,7 +84136,6 @@ oevkg.at, 1 of-sound-mind.com, 1 of2106.dnsalias.org, 0 -of2m.fr, 1 ofallonil.gov, 1 ofaqim.city, 1 ofasoft.com, 1 @@ -84203,7 +84158,6 @@ offentligsektormedmoln.se, 1 offer-today.ml, 1 offerhome.com, 1 -offerito.com, 1 offermann-koeln.de, 1 offers-daraghmehstores.com, 1 offerte-gas.it, 1 @@ -84346,7 +84300,7 @@ ohyooo.com, 1 ohype.ga, 1 ohype.gq, 1 -oi-wiki.org, 0 +oi-wiki.org, 1 oiahe.org.uk, 1 oic-ci.gc.ca, 1 oidrava.tk, 1 @@ -84492,7 +84446,6 @@ oldcold.co, 1 oldcraft-mc.ru, 1 olddisk.ml, 1 -oldenglishsheepdog.com.br, 1 oldenzaal.tk, 1 older-racer.com, 1 oldfarming.tk, 1 @@ -84664,6 +84617,7 @@ omachi.top, 1 omaedu.ro, 1 omag.gov, 1 +omahachapterone.org, 1 omaharoofpros.com, 1 omahcoin.com, 1 omandatapark.com, 1 @@ -84811,7 +84765,6 @@ one-cozmic.com, 1 one-dot.de, 1 one-host.ga, 1 -one-million-places.com, 1 one-news.net, 0 one-page.org, 1 one-pixel.tk, 1 @@ -85349,6 +85302,7 @@ openwaveguide.de, 1 openwifi.gr, 1 openwrt-dist.tk, 1 +opera.im, 1 operacdn.com, 1 operacionlimpieza.com, 1 operad.fr, 1 @@ -85782,7 +85736,6 @@ orum.in, 1 orunodoy.com, 1 orwell.tk, 1 -orwell1984.today, 1 oryva.com, 1 orz.uno, 1 orzechot.pl, 1 @@ -85824,6 +85777,7 @@ oscarvk.ch, 1 osceolacountyia.gov, 1 osci.io, 1 +oscie.net, 1 oscillation-services.fr, 1 oscloud.com, 1 oscom.tk, 1 @@ -85865,7 +85819,6 @@ osm.ovh, 1 osmaniyehaber.tk, 1 osmanlitakilari.tk, 1 -osmanlitorunu.com, 1 osmarks.net, 1 osmarks.tk, 1 osmdroid.net, 1 @@ -86336,7 +86289,6 @@ p-soc.com.br, 1 p-store.net, 1 p-t.io, 1 -p-vegas.com, 1 p.linode.com, 0 p.lu, 1 p.sb, 1 @@ -86433,7 +86385,6 @@ pabloroblesminister.com, 1 pablosaraiva.com, 1 pabpunk.tk, 1 -pacaom.com, 1 pacatlantic.com, 1 pacay.id, 1 pacch.io, 1 @@ -86712,7 +86663,6 @@ paliucuiciucci.tk, 1 palladium46.com, 1 palladiumprivate.com, 1 -palladiumtechs.com, 1 palletflow.com, 1 palli.ch, 0 palmaprop.com, 1 @@ -86888,7 +86838,6 @@ panzerwarmodsru.tk, 1 pao.moe, 1 paocaibang.net, 1 -paocloud.co.th, 1 paolodemichele.it, 0 paolomargari.tk, 1 paolotagliaferri.com, 1 @@ -86993,7 +86942,6 @@ paramascotas.vip, 1 parameterizer.me, 1 paramo-pineiro.tk, 1 -paramo.me, 1 paramountdentalcenter.com, 1 paramountelectronics.co.uk, 1 paranoidandroid.tk, 1 @@ -87129,6 +87077,7 @@ parmels.com.br, 1 parmoli.tk, 1 parnassys.net, 1 +parniplus.com, 1 parnizaziteksasko.cz, 1 parodesigns.com, 1 paroisses-theix-surzur.com, 1 @@ -87744,6 +87693,7 @@ pcplaza.tk, 1 pcprkolo.pl, 1 pcproblem.tk, 1 +pcpromaroc.ma, 1 pcpuhastaja.tk, 1 pcrab.ml, 1 pcrecovery.ga, 1 @@ -89160,6 +89110,7 @@ pierreyvesdick.fr, 1 piersmana.com, 1 pierson.tk, 1 +piesel-piepser.de, 1 pietbrakman.tk, 1 pietechsf.com, 0 pieter-verweij.nl, 1 @@ -89175,7 +89126,6 @@ pif.email, 1 piffer.ind.br, 1 pig-breeding.tk, 1 -pigb.net, 1 pigeonracinginformation.com, 1 pigeons-rings.com, 1 pigfox.com, 1 @@ -89223,7 +89173,6 @@ pillowfort.pub, 1 pilot-colleges.com, 1 pilot.co, 0 -pilot.com, 1 pilotandy.com, 1 pilotcareercenter.com, 1 pilotcareercentre.com, 1 @@ -89562,6 +89511,7 @@ pixshop.fr, 1 pixstash.net, 1 pixxxels.cc, 1 +pixyship.com, 1 pizala.de, 1 pizdelka.tk, 1 pizponim.co.il, 1 @@ -89778,7 +89728,6 @@ plaros.ml, 1 plasapulsa.tk, 1 plasesolev.tk, 1 -plashenkov.com, 1 plaskiewicz.pl, 1 plasofficial.it, 1 plassmann.ws, 1 @@ -90643,6 +90592,7 @@ poolspa.es, 1 pooltest.co.uk, 1 pooltools.net, 1 +poolvilla-margarita.net, 1 poopjournal.rocks, 1 poopr.ru, 1 poopthereitisla.com, 1 @@ -91050,7 +91000,6 @@ potteranderson.com, 1 potterish.com, 1 potterperfect.tk, 1 -pottersheartministry.org, 1 pottershouse.tk, 1 potterybroker.ga, 1 pottshome.co.uk, 1 @@ -91676,6 +91625,7 @@ prinesec.com, 1 prinice.org, 1 print-street.tk, 1 +print3dgifts.co.uk, 1 printbase.cz, 1 printbigjournal.tk, 1 printedmailbags.co.uk, 1 @@ -91938,7 +91888,6 @@ prodentalsantacruz.es, 1 prodesigntools.com, 1 prodesk.bg, 1 -prodevsblog.com, 1 prodhealthcare.org, 1 prodietix.cz, 1 prodigibook.com, 1 @@ -92034,6 +91983,7 @@ profmetod.com, 1 proformer.io, 1 proformi.com, 1 +proforo.co, 1 profritual.ru, 1 profsaranya.com, 1 proft.eu, 1 @@ -92210,6 +92160,7 @@ promocjedladzieci.pl, 1 promocodius.com, 1 promodance.cz, 1 +promodesigns.co.za, 1 promodoble.com, 1 promods.cn, 1 promods.download, 1 @@ -92377,6 +92328,7 @@ protectionformula.com.ua, 1 protectwrap.ml, 1 protege.moi, 1 +protegetudescanso.com, 1 proteh.com.ua, 1 protein-riegel-test.de, 1 proteinreport.org, 1 @@ -92578,6 +92530,7 @@ psihology.tk, 1 psihoterapevt1.by, 1 psihotest.tk, 1 +psikokoro.com, 1 psinergy.info, 1 psinergyhealth.com, 1 psinergytech.com, 1 @@ -92826,7 +92779,6 @@ pukfalkenberg.dk, 1 pulcinella.tk, 1 puli.com.br, 1 -pulinkai.xyz, 1 pulizia.milano.it, 1 pulizia.roma.it, 1 pulizieuffici.milano.it, 1 @@ -93105,7 +93057,6 @@ pyjy.org, 1 pylad.se, 1 pylon.bot, 1 -pymebi.cl, 1 pymescentro.net, 1 pymtreceipt.com, 1 pyopenssl.org, 1 @@ -93192,7 +93143,7 @@ qatartimes.tk, 1 qatouch.com, 1 qaz.cloud, 1 -qazweek.kz, 1 +qazweek.kz, 0 qbasic.tk, 1 qbasicsite.tk, 1 qbd.eu, 1 @@ -93393,7 +93344,6 @@ qtv.ge, 1 qtvr.com, 1 qtxh.net, 1 -qtzhi.com, 1 quackerswaterproofing.com, 1 quackquack.in, 1 quad9.net, 1 @@ -93771,6 +93721,7 @@ qxin.info, 1 qxpress.com.py, 1 qxq.moe, 0 +qxxllw.com, 1 qxzg.org, 1 qxzg.xyz, 1 qxzgssr.xyz, 1 @@ -94810,7 +94761,6 @@ readyblinds.com.au, 1 readychurchsites.com, 1 readycolorado.gov, 1 -readydedis.com, 1 readyelec.com, 1 readync.gov, 1 readyrosie.com, 1 @@ -95273,7 +95223,6 @@ redzonedaily.com, 1 reececustom.com, 1 reed-sensor.com, 1 -reedloden.com, 1 reedy.tk, 1 reeftrip.com, 1 reeladventurefishing.com, 1 @@ -95631,7 +95580,6 @@ remont-kazan.tk, 1 remont-kvartirvmoskve.ga, 1 remont-naushnikov.tk, 1 -remont-p.com, 1 remont-rollet-izgotovlenie.cf, 1 remont-rukami.tk, 1 remontdot.tk, 1 @@ -95909,7 +95857,6 @@ resolvefa.co.uk, 1 resolvefa.com, 1 resolveit.gq, 1 -resolvergroup.com.au, 1 resolvo.com, 1 resolvs.com, 1 resoplus.ch, 0 @@ -96121,6 +96068,7 @@ revers.tk, 1 reverseaustralia.com, 1 reversecanada.com, 1 +reversecrucifixkm.altervista.org, 1 reversedns.tk, 1 reverseloansolutions.com, 1 reverselookupphone.us, 1 @@ -96345,7 +96293,6 @@ riccardopiccioni.it, 1 riccy.org, 1 riceadvice.info, 1 -ricettesemplicieveloci.altervista.org, 1 rich-good.com, 1 richadams.me, 1 richamorindonesia.com, 1 @@ -96591,6 +96538,7 @@ risco.ro, 1 riscoshardware.tk, 1 rise-technologies.com, 1 +rise.africa, 1 rise.com, 1 rise.global, 1 riseandrank.com, 1 @@ -96650,7 +96598,6 @@ ritual.ml, 1 ritus.md, 1 ritzlux.com.tw, 1 -riunioni.online, 1 rivaforum.de, 1 rivals.space, 1 rivalsa.cn, 1 @@ -96665,7 +96612,6 @@ riverbednetflowsupport.com, 1 riverbendroofingnd.com, 1 rivercitybni.com, 1 -riverdale.net.au, 1 riverford.co.uk, 1 riveroaksdentaljax.com, 1 riverotravel.cl, 1 @@ -96886,7 +96832,6 @@ robot-invest.ml, 1 robot.car, 1 robotask.in, 1 -robotattack.org, 1 robotbattle.tk, 1 robotdecocinaya.com, 1 roboth.am, 1 @@ -97178,7 +97123,6 @@ romegapolice.gov, 1 romeoferraris.com, 1 romeoijulio.tk, 1 -romeroeletro.com.br, 1 rometoptentravel.com, 1 rommelhuntermusic.tk, 1 rommelmark.nl, 1 @@ -97209,7 +97153,6 @@ ronforeman.com, 1 ronghexx.com, 1 rongreenbaum.com, 1 -ronhose.com, 1 roninathletics.com, 1 roninf.ch, 1 roninitconsulting.com, 1 @@ -97277,7 +97220,6 @@ rootless.ga, 1 rootless.tk, 1 rootly.com, 1 -rootly.io, 1 rootonline.de, 1 rootpak.com, 1 rootpigeon.com, 1 @@ -97442,11 +97384,10 @@ route-wird-berechnet.de, 1 routechoices.com, 1 routerchart.com, 1 -routerclub.ru, 1 routeto.com, 1 routetracker.co, 1 routeur4g.fr, 0 -rouwcentrumterheide.be, 1 +rouwcentrumterheide.be, 0 rovatronic.tk, 1 roverglobal.ga, 1 roveridx.com, 1 @@ -97709,7 +97650,6 @@ rte1.ie, 1 rte2fm.ie, 1 rteaertel.ie, 1 -rtechservices.io, 1 rteguide.ie, 1 rteinternational.ie, 1 rtejr.ie, 1 @@ -98598,6 +98538,7 @@ saintseiya-temple.tk, 1 saintshopoficial.com.br, 1 saintvincent.tk, 1 +saintw.com, 0 sainzderozas.com, 1 saipariwar.com, 1 saipeople.net, 1 @@ -99351,7 +99292,6 @@ saturdayenterprises.ga, 1 saturnbb.com, 1 saturne.tk, 1 -saturuang.id, 1 satyamshivamsundaram.in, 1 sauber.dk, 1 saubooks.tk, 1 @@ -99551,6 +99491,7 @@ sbstattoo.com, 1 sc-artworks.co.uk, 0 sc019.com, 1 +sc2pte.eu, 0 sc5.jp, 1 scaarus.com, 1 scabieslice.com, 1 @@ -100043,6 +99984,7 @@ scour.cc, 1 scourgesofcarpathia.tk, 1 scout-korting.tk, 1 +scoutbee.com, 1 scoutbee.io, 1 scouteridano.tk, 1 scouting-kontiki.nl, 1 @@ -100081,7 +100023,6 @@ scrapcarbrampton.ca, 1 scrapcarremovalmississauga.ca, 1 scrapcars.net.au, 1 -scraperhireaustralia.com.au, 1 scrapmartine.tk, 1 scrapmycarperth.com.au, 0 scratch-ppp.jp, 1 @@ -100151,6 +100092,7 @@ sculpturesworldwide.tk, 1 sculpturos.com, 1 scungioborst.com, 1 +scunthorpemoneyman.com, 1 scuolaguidalame.ch, 0 scuolamazzini.livorno.it, 1 scuolatdm.com, 1 @@ -100304,7 +100246,6 @@ seb8iaan.com, 1 sebald.com, 1 sebald.org, 1 -sebandroid.com, 1 sebar-iklan.gq, 1 sebariklanmassal.gq, 1 sebarin.tk, 1 @@ -100918,6 +100859,7 @@ sentry.nu, 1 sentrybay.com, 1 sentworks.com, 1 +senu.pro, 1 senzaparole.de, 1 senzei.tk, 1 seo-analyse.com, 1 @@ -100938,7 +100880,6 @@ seo-smo.ml, 1 seo-smo.tk, 1 seo-url.tk, 1 -seo.consulting, 1 seo.domains, 1 seo.london, 1 seoagentur-hamburg.com, 0 @@ -101150,7 +101091,6 @@ sertasimmons.com, 1 sertim.tk, 1 seru.eu, 1 -serukan.com, 1 serval-concept.com, 1 servantweb.fr, 1 servcom.net.au, 1 @@ -101267,7 +101207,6 @@ serwetki-papierowe.pl, 1 serwis-telewizorow.pl, 1 serwis-wroclaw.pl, 1 -serwislukit.pl, 1 serwistomy.pl, 1 seryovpn.com, 1 seryox.com, 1 @@ -101430,6 +101369,7 @@ sfaparish.org, 1 sfarc.ml, 1 sfat.llc, 1 +sfaturiit.ro, 1 sfbao.com, 1 sfdcopens.com, 1 sfdev.ovh, 1 @@ -102676,7 +102616,6 @@ sigvik.ru, 1 siika.solutions, 1 siikaflix.tv, 1 -siirtutkusu.com, 0 sijbesmaverhuizingen.nl, 1 sijimi.cn, 1 sik-it.nl, 1 @@ -102898,7 +102837,6 @@ simon.butcher.name, 1 simon.lc, 1 simon3k.moe, 1 -simonagancia.com, 1 simonastallone.com, 1 simonberard.garden, 1 simonbondo.dk, 1 @@ -103295,7 +103233,6 @@ siteru.tk, 1 sites.google.com, 1 sitesara.com, 1 -sitesdel.ru, 1 sitesdesign.tk, 1 sitesforward.com, 1 sitesko.de, 1 @@ -104184,6 +104121,7 @@ smsg-dev.ch, 0 smsinger.com, 1 smsk.email, 1 +smsk.io, 1 smskeywords.co.uk, 1 smskmail.com, 1 smsprivacy.org, 1 @@ -104245,12 +104183,10 @@ snarf.in, 1 snargol.com, 1 snatch-note.tk, 1 -snatek.net, 1 snatertlc.it, 1 snatti.com, 1 snazel.ae, 1 snazel.co.il, 1 -snazel.co.uk, 1 snazel.de, 1 snazel.ee, 1 snazel.uk, 1 @@ -104434,6 +104370,7 @@ social-work-colleges.com, 1 social-work.tk, 1 social.com.co, 1 +socialab.gr, 1 socialair.tk, 1 socialblaze.com.au, 1 socialbook2015.ga, 1 @@ -104476,6 +104413,7 @@ socialtactics.ml, 1 socialtournaments.com, 0 socialtranslation.ga, 1 +socialtrends.pl, 0 socialwave.tk, 1 socialworkout.com, 1 socialworkout.net, 1 @@ -104668,7 +104606,6 @@ solaradventures.icu, 1 solarbattery.ga, 1 solareagricola.it, 1 -solarfaa.ir, 1 solarfever.ga, 1 solargaming.tk, 1 solarhome.ml, 1 @@ -104812,7 +104749,6 @@ solvewebmedia.com, 1 solviejo.tk, 1 solvin.com, 1 -solvingproblems.com.au, 1 solviq.com, 1 solvops.com, 1 solwaveovens.com, 1 @@ -104869,6 +104805,7 @@ son-tolkovatel.gq, 1 son-tolkovatel.ml, 1 son-tolkovatel.tk, 1 +son.ru, 1 sona-gaming.com, 1 sona.fr, 1 sonacupalova.cz, 1 @@ -105360,6 +105297,7 @@ spamloco.net, 1 spammable.com, 1 spamty.eu, 1 +spamwc.de, 1 spanch.cf, 1 spanch.ga, 1 spanch.gq, 1 @@ -106188,7 +106126,6 @@ ssmothership.tk, 1 ssmpuc.com, 1 ssmrca.ca, 1 -ssnet.vip, 1 ssnetwork.jp, 1 ssone.ee, 1 sspanel.host, 1 @@ -106323,7 +106260,6 @@ stalker-shop.com, 1 stalker-source.tk, 1 stalkerteam.pl, 1 -stalkr.net, 1 stalkthe.net, 1 stall-frei.de, 1 stallardjw.me, 1 @@ -106347,7 +106283,6 @@ stampederadon.com, 1 stampsbar.co.uk, 1 stamurai.com, 1 -stan.moe, 1 stanandjerre.org, 1 stanchierifamilylaw.com, 1 standard.co.uk, 1 @@ -106583,7 +106518,6 @@ statisticalsurveys.com, 1 statistician-online.com, 0 statistik-seminare.de, 1 -statistikian.com, 1 statnevlajky.sk, 1 statnivlajky.cz, 1 statrix.org, 1 @@ -106761,7 +106695,6 @@ steinmassl.org, 1 steinmetz.cloud, 1 stekelenburg.me, 1 -steklein.de, 1 stekosouthamerica.com, 1 stelfox.net, 1 stelga.ca, 1 @@ -107173,7 +107106,7 @@ stoneagehealth.com.au, 1 stonechatjewellers.ie, 1 stonecutgods.com, 1 -stonedwarf5.net, 1 +stonedwarf5.net, 0 stonedworms.de, 0 stoneedgeconcrete.com, 1 stonefoot.de, 1 @@ -108407,7 +108340,6 @@ supremestandards.com, 1 supriville.com.br, 1 supropionegocio.tk, 1 -supweb.ovh, 1 sur-v.com, 1 suranganet.tk, 1 surasak.org, 1 @@ -109146,6 +109078,7 @@ t00228.com, 1 t00ts.com, 0 t060.com, 1 +t070.com, 0 t0kie.space, 1 t0ny.name, 1 t12u.com, 1 @@ -109889,7 +109822,6 @@ taxikraken.tk, 1 taximinvody.ml, 1 taximovies.gq, 1 -taxipool.co.il, 1 taxis-collectifs.ch, 0 taxisafmatosinhos.pt, 1 taxisantandreudelabarca.es, 1 @@ -109903,7 +109835,6 @@ taxo.fi, 1 taxpackagesupport.com, 1 taxprocpa.com, 1 -taxteam.co.il, 1 tayar2u.my, 1 taybee.net, 1 tayebbayri.com, 0 @@ -110053,6 +109984,7 @@ teacherfrancis.com, 1 teachermommylife.com, 1 teacherph.com, 1 +teacherph.review, 1 teacherpowered.org, 1 teacherquinten.com, 1 teacherquotes.gq, 1 @@ -110165,7 +110097,6 @@ teamshirts.nl, 1 teamshirts.no, 1 teamshirts.se, 1 -teamsimplythebest.com, 1 teamsomeday.tk, 1 teamsudan.cf, 1 teamtomorrow.tk, 1 @@ -110281,6 +110212,7 @@ techexplorist.com, 1 techfibian.tk, 1 techfishnews.com, 1 +techformator.pl, 0 techfreepro.ml, 1 techfuze.com, 0 techfuze.io, 0 @@ -110335,7 +110267,6 @@ technicalhelps.org, 1 technicallyeasy.net, 1 technicalproblem.tk, 1 -technicalramblings.com, 1 technicaltrainer.co.za, 1 technicalustad.com, 1 techniclab.net, 0 @@ -110566,6 +110497,7 @@ teiseken.tk, 1 teixobactin.com, 1 tejas1835.com, 1 +tejaswi.biz, 1 tejo.tk, 1 teka.ro, 1 tekanswer.com, 1 @@ -110762,6 +110694,7 @@ tempus-aquilae.de, 1 tempus.tf, 1 temtekco.com, 1 +temydee.com, 1 tena.ml, 1 tena.tk, 1 tenable.com.au, 1 @@ -112016,6 +111949,7 @@ theoldmill.tk, 1 theoldnews.net, 1 theoldsewingfactory.com, 1 +theolodewijk.nl, 1 theologique.ch, 0 theomg.co, 1 theonegroup.co.uk, 0 @@ -112243,7 +112177,6 @@ thesomepeople.org, 1 thesoundstageatstrangeland.com, 1 thespacegame.tk, 1 -thesphinx.ca, 1 thespiritfm.tk, 1 thesplashlab.com, 1 thesslstore.com, 1 @@ -112378,7 +112311,6 @@ thewebmasters.tk, 1 thewebsitedoctors.co.uk, 1 thewebsitemarketingagency.com, 1 -theweddingsociety.co, 1 theweed.tk, 1 thewest.tk, 1 thewhiteboxxx.com, 1 @@ -112504,7 +112436,6 @@ thinkingplanet.net, 1 thinkmarketing.ca, 1 thinkprocedural.com, 1 -thinktac.com, 1 thinktankofthree.com, 1 thinkwisesoftware.com, 1 thinkwits.com, 1 @@ -112634,6 +112565,7 @@ threatcentral.io, 1 threatcon.io, 1 threatdetection.info, 1 +threatint.eu, 1 threatmonitor.io, 1 threatnix.io, 1 threatutic.gq, 1 @@ -113014,7 +112946,6 @@ timesheetcomics.com, 1 timeslive.co.ke, 1 timespace.eu.org, 1 -timespowerofprint.com, 1 timespreader.com, 0 timeswiki.org, 1 timetab.org, 1 @@ -113117,7 +113048,7 @@ tinyfont.cf, 1 tinyfont.ml, 1 tinyguitars.tk, 1 -tinyhouse-bimify.fr, 1 +tinyhouse-bimify.fr, 0 tinyhousebarat.com, 1 tinyhousebarat.de, 1 tinyhousefinance.com.au, 1 @@ -113165,7 +113096,6 @@ tipsypresent.com, 1 tiptoptransmissions.com, 1 tipulnagish.co.il, 1 -tipwho.com, 1 tiqets.com, 1 tir-mauperthuis.fr, 1 tiraloche.com, 1 @@ -113524,6 +113454,7 @@ togetter.com, 1 togglename.ml, 1 togoweed.co, 1 +togruta.com, 1 togtider.dk, 1 toh25unblocked.tk, 1 toheb.de, 0 @@ -113679,6 +113610,7 @@ tomik.fun, 1 tomikoyco.com, 1 tomiler.com, 1 +tomjans.nl, 1 tomjepp.uk, 1 tomjn.com, 1 tomkempers.nl, 1 @@ -113808,7 +113740,6 @@ tonytan.io, 1 tonytan.net, 1 tonytron.com.br, 1 -tonyw.xyz, 1 tonywebster.com, 1 tonyzhao.xyz, 1 too.com.ua, 1 @@ -113961,6 +113892,7 @@ topkorea.ml, 1 toplevel.mx, 1 toplifesaudaveis.com.br, 1 +toplink.co.il, 1 toplist.cz, 1 toplist.eu, 1 toplist.sk, 1 @@ -116539,6 +116471,7 @@ ufplanets.com, 1 ufroo.com, 1 ufuq.de, 1 +ugameclub.com, 1 ugb-verlag.de, 0 ugcdn.com, 1 ugeek.tk, 1 @@ -116801,7 +116734,6 @@ unblocked.nz, 1 unblocked.one, 1 unblocked.pet, 1 -unblocked.pl, 1 unblocked.pro, 1 unblocked.sh, 1 unblocked.uno, 1 @@ -117199,6 +117131,7 @@ unpleasant.tk, 1 unpluggedjuice.dk, 1 unplugstore.it, 1 +unpoditalia.se, 1 unpossible.xyz, 1 unpost.net, 1 unpr.dk, 1 @@ -117231,7 +117164,6 @@ unstoppableever.com.br, 1 unstoppableunits.com, 1 unsupervised.ca, 1 -unsuspicious.click, 1 unterhaltungsbox.com, 1 unternehmensbewertung.pro, 1 unternehmer-radio.de, 1 @@ -117320,6 +117252,7 @@ uploadbeta.com, 1 uploadbro.com, 1 uploaddatanow.com, 1 +uploads.su, 1 uploadscript.tk, 1 uploadtokiosk.com, 1 uploadyourtestament.com, 1 @@ -117367,6 +117300,7 @@ uptoplay.ovh, 1 uptownbabe.com, 1 uptownlocators.com, 1 +uptrends.com, 1 uptrends.de, 1 uptrex.co.uk, 1 upturn.org, 1 @@ -117890,7 +117824,6 @@ v33v33.com, 1 v36533.com, 1 v44v44.com, 1 -v4f.com, 1 v5017.com, 1 v5075.com, 1 v51365.com, 1 @@ -118463,7 +118396,7 @@ ve3oat.ca, 1 ve3zsh.ca, 0 veadoscomfome.tk, 1 -veast.network, 1 +veast.network, 0 vebbankir-zajm-onlajn.gq, 1 vebdengi.tk, 1 veber.bg, 1 @@ -119836,11 +119769,9 @@ vizitfree.ml, 1 vizitnik.tk, 1 vizmart.ml, 1 -vizualbits.com, 1 vjeff.com, 1 vjeff.net, 1 vjhfoundation.org, 1 -vjshi.xyz, 1 vk-agent.ru, 1 vk-group.com, 1 vk-k.com, 1 @@ -120471,6 +120402,8 @@ vulnerabilityscans.nl, 1 vulnerable.af, 1 vulners.com, 1 +vulns.sexy, 1 +vulns.xyz, 1 vulnscan.org, 1 vulpine.club, 1 vulpr.com, 1 @@ -120790,7 +120723,6 @@ wajtc.com, 1 wak.io, 1 waka-mono.com, 1 -wakandasun.com, 0 wakarandroid.com, 1 wakastream.cc, 1 wakatime.com, 1 @@ -120816,7 +120748,7 @@ walent.in, 1 walentin.co, 1 walhal.la, 1 -waligorska.pl, 0 +waligorska.pl, 1 walk.onl, 1 walkaround.tk, 1 walker-foundation.org, 1 @@ -121164,7 +121096,6 @@ waterlens.moe, 1 waterliteracy.tk, 1 waterloofaucets.com, 1 -watermarkly.com, 1 watermitigationspecialists.com, 1 watermonitor.gov, 1 wateroutlook.com, 1 @@ -121206,7 +121137,6 @@ waukeshairon.com, 1 waupacacounty-wi.gov, 1 wav-productions.com, 1 -wav.tv, 1 wave-inc.co.jp, 1 wave.is, 1 wave.red, 1 @@ -121710,7 +121640,6 @@ webs4all.ro, 0 websanlamuerte.tk, 1 webschool21.ml, 1 -websdeweb.com, 1 websec.nu, 1 websectools.com, 1 websecurity.is, 1 @@ -121718,6 +121647,7 @@ webseitenserver.com, 0 websenat.de, 1 webshan.ir, 0 +webshaped.de, 1 webshop.nl, 1 website-engineering.co.za, 0 website-traffic.shop, 1 @@ -122387,6 +122317,7 @@ whexit.nl, 1 whey-protein.ch, 1 wheyteck.com, 1 +whi.tw, 0 whichdoctor.com, 1 whichgender.today, 1 whiff-of-grape.ca, 1 @@ -122492,6 +122423,7 @@ whoagirls.com, 1 whoagirls.net, 0 whoagirls.org, 1 +whoami.eu.org, 1 whoami.io, 1 whocalld.com, 1 whocalled.us, 1 @@ -122802,6 +122734,7 @@ wildandisle.com, 1 wildandwonderfulbodycare.com, 1 wildandwonderfulketo.com, 1 +wildanfauzy.com, 0 wildbergh.tk, 1 wildberries.cf, 1 wildbirds.dk, 1 @@ -123004,7 +122937,6 @@ winek.tk, 1 wineparis.com, 1 winerytoursanfrancisco.com, 1 -wineworksonline.com, 1 winfar.co.za, 1 winfieldchen.me, 1 winfilestorage.tk, 1 @@ -123725,6 +123657,7 @@ worldnewsphoto.tk, 1 worldofarganoil.com, 1 worldofbelia.de, 1 +worldofgeekstuff.com, 1 worldofghibli.id, 1 worldofheroes.ml, 1 worldoflegion.ml, 1 @@ -123934,7 +123867,6 @@ wptotal.com, 1 wptrigone.net, 1 wpuse.ru, 1 -wpvibes.com, 1 wq.ro, 1 wqaw3.tk, 1 wr.su, 1 @@ -124313,7 +124245,6 @@ x-embed.com, 1 x-files.tk, 1 x-iweb.ru, 1 -x-lan.be, 1 x-net24.pl, 1 x-one.co.jp, 1 x-orbit.dk, 1 @@ -124677,7 +124608,6 @@ xianjianruishiyouyiyuan.com, 1 xiao-sheng.gq, 1 xiaobai.pro, 0 -xiaobude.cn, 1 xiaocg.xyz, 1 xiaodingyi.cn, 1 xiaofengsky.com, 0 @@ -124846,9 +124776,9 @@ xinpujing918.com, 0 xinsane.com, 1 xinshanla.com, 1 -xinsto.com, 1 xinu.xyz, 1 xinxin.pl, 1 +xinyezx.com, 1 xinyitour.tw, 1 xio.moe, 1 xion.nu, 1 @@ -124873,7 +124803,6 @@ xkeyc.cn, 1 xkviz.net, 1 xkwy2018.cn, 1 -xlan.be, 1 xlange.com, 1 xlaw.com.br, 1 xldl.ml, 1 @@ -124888,6 +124817,7 @@ xlyingyuan.com, 0 xm.digital, 1 xmag.pl, 1 +xmdhs.top, 1 xmediabigz.tk, 1 xmediazxy.tk, 1 xmedius.ca, 0 @@ -125983,7 +125913,6 @@ yasmingarcia.tk, 1 yasraiting.tk, 1 yasrating.tk, 1 -yassine-ayari.com, 1 yassinesmael.tk, 1 yasudaseiki.cn, 1 yasukevicious.com, 1 @@ -126396,7 +126325,6 @@ yoshitsugu.net, 1 yoshiya2020.com, 1 yoshkar-ola-city.tk, 1 -yosida-dental.com, 1 yosida95.com, 1 yospos.org, 1 yoti.com, 1 @@ -126416,7 +126344,7 @@ youber.cz, 1 youbil.com, 1 youc.ir, 1 -youcanbook.me, 1 +youcanbook.me, 0 youcanfinance.com.au, 1 youcanfuckoff.xyz, 1 youcanhelp.tk, 1 @@ -126927,7 +126855,6 @@ z-pc.net, 1 z-rejstejna.cz, 1 z-vector.com, 1 -z.ai, 1 z.cash, 1 z.is, 1 z.md, 1 @@ -127434,6 +127361,7 @@ zd9797.com, 1 zdbl.de, 1 zdenek-hejl.com, 1 +zdenekpasek.com, 1 zdenekpasek.cz, 1 zdenekspacek.cz, 1 zdenekvecera.cz, 1 @@ -127477,6 +127405,7 @@ zecuur.nl, 1 zedeko.pl, 1 zedern-welt.de, 1 +zedex.cn, 1 zednet.tk, 1 zeds-official.com, 1 zeedroom.be, 0 @@ -127725,6 +127654,7 @@ zhaopage.com, 1 zhaostephen.com, 1 zhaoxixiangban.cc, 1 +zhaozhiru.com, 1 zhattyt.com, 1 zhbot.org, 1 zhcexo.com, 1 @@ -127811,6 +127741,7 @@ zighinetto.org, 1 zigi.io, 1 zigottos.fr, 1 +zigsphere.com, 0 zigzagmart.com, 1 zihao.me, 0 zihari.com, 1 @@ -128235,6 +128166,7 @@ zseartcc.org, 1 zselicivt.hu, 1 zserver.fr, 1 +zsi.com.tr, 1 zsien.cn, 1 zskomenskeho.cz, 1 zsoltbereczki.tk, 1 @@ -128258,7 +128190,7 @@ zubilo-perm.ru, 1 zubnivodni.cz, 1 zubr.net, 1 -zubro.net, 1 +zubro.net, 0 zudomc.me, 1 zuefle.net, 1 zuehlcke.de, 1 diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/security/manager/ssl/StaticHPKPins.h firefox-trunk-95.0~a1~hg20211020r596404/security/manager/ssl/StaticHPKPins.h --- firefox-trunk-95.0~a1~hg20211017r596111/security/manager/ssl/StaticHPKPins.h 2021-10-17 14:42:50.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/security/manager/ssl/StaticHPKPins.h 2021-10-20 19:28:34.000000000 +0000 @@ -1149,4 +1149,4 @@ static const int32_t kUnknownId = -1; -static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1642675615870000); +static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1643021232351000); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/security/manager/ssl/tests/mochitest/mixedcontent/alloworigin.sjs firefox-trunk-95.0~a1~hg20211020r596404/security/manager/ssl/tests/mochitest/mixedcontent/alloworigin.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/security/manager/ssl/tests/mochitest/mixedcontent/alloworigin.sjs 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/security/manager/ssl/tests/mochitest/mixedcontent/alloworigin.sjs 2021-10-20 19:28:35.000000000 +0000 @@ -1,5 +1,4 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setStatusLine(request.httpVersion, 200, "OK"); response.setHeader("Access-Control-Allow-Origin", "*"); response.write("hello!"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/security/manager/ssl/tests/mochitest/mixedcontent/emptyimage.sjs firefox-trunk-95.0~a1~hg20211020r596404/security/manager/ssl/tests/mochitest/mixedcontent/emptyimage.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/security/manager/ssl/tests/mochitest/mixedcontent/emptyimage.sjs 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/security/manager/ssl/tests/mochitest/mixedcontent/emptyimage.sjs 2021-10-20 19:28:35.000000000 +0000 @@ -1,5 +1,4 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setStatusLine(request.httpVersion, 200, "OK"); //response.setHeader("Content-type", "image/gif"); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/security/manager/ssl/tests/mochitest/mixedcontent/hugebmp.sjs firefox-trunk-95.0~a1~hg20211020r596404/security/manager/ssl/tests/mochitest/mixedcontent/hugebmp.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/security/manager/ssl/tests/mochitest/mixedcontent/hugebmp.sjs 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/security/manager/ssl/tests/mochitest/mixedcontent/hugebmp.sjs 2021-10-20 19:28:35.000000000 +0000 @@ -1,13 +1,15 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setStatusLine(request.httpVersion, 200, "OK"); response.setHeader("Content-type", "image/bitmap"); - - let bmpheader = "\x42\x4D\x36\x10\x0E\x00\x00\x00\x00\x00\x36\x00\x00\x00\x28\x00\x00\x00\x80\x02\x00\x00\xE0\x01\x00\x00\x01\x00\x18\x00\x00\x00\x00\x00\x00\x10\x0E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; - let bmpdatapiece = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; + + let bmpheader = + "\x42\x4D\x36\x10\x0E\x00\x00\x00\x00\x00\x36\x00\x00\x00\x28\x00\x00\x00\x80\x02\x00\x00\xE0\x01\x00\x00\x01\x00\x18\x00\x00\x00\x00\x00\x00\x10\x0E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; + let bmpdatapiece = + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; response.bodyOutputStream.write(bmpheader, 54); // Fill 640*480*3 nulls - for (let i = 0; i < (640 * 480 * 3) / 64; ++i) + for (let i = 0; i < (640 * 480 * 3) / 64; ++i) { response.bodyOutputStream.write(bmpdatapiece, 64); + } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/security/manager/ssl/tests/mochitest/mixedcontent/iframesecredirect.sjs firefox-trunk-95.0~a1~hg20211020r596404/security/manager/ssl/tests/mochitest/mixedcontent/iframesecredirect.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/security/manager/ssl/tests/mochitest/mixedcontent/iframesecredirect.sjs 2021-10-17 14:42:50.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/security/manager/ssl/tests/mochitest/mixedcontent/iframesecredirect.sjs 2021-10-20 19:28:33.000000000 +0000 @@ -1,5 +1,7 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setStatusLine(request.httpVersion, 307, "Moved temporarly"); - response.setHeader("Location", "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/iframe.html"); + response.setHeader( + "Location", + "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/iframe.html" + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/security/manager/ssl/tests/mochitest/mixedcontent/iframeunsecredirect.sjs firefox-trunk-95.0~a1~hg20211020r596404/security/manager/ssl/tests/mochitest/mixedcontent/iframeunsecredirect.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/security/manager/ssl/tests/mochitest/mixedcontent/iframeunsecredirect.sjs 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/security/manager/ssl/tests/mochitest/mixedcontent/iframeunsecredirect.sjs 2021-10-20 19:28:34.000000000 +0000 @@ -1,5 +1,7 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setStatusLine(request.httpVersion, 307, "Moved temporarly"); - response.setHeader("Location", "http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/iframe.html"); + response.setHeader( + "Location", + "http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/iframe.html" + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/security/manager/ssl/tests/mochitest/mixedcontent/imgsecredirect.sjs firefox-trunk-95.0~a1~hg20211020r596404/security/manager/ssl/tests/mochitest/mixedcontent/imgsecredirect.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/security/manager/ssl/tests/mochitest/mixedcontent/imgsecredirect.sjs 2021-10-17 14:42:50.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/security/manager/ssl/tests/mochitest/mixedcontent/imgsecredirect.sjs 2021-10-20 19:28:34.000000000 +0000 @@ -1,5 +1,7 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setStatusLine(request.httpVersion, 307, "Moved temporarly"); - response.setHeader("Location", "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg"); + response.setHeader( + "Location", + "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg" + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/security/manager/ssl/tests/mochitest/mixedcontent/imgunsecredirect.sjs firefox-trunk-95.0~a1~hg20211020r596404/security/manager/ssl/tests/mochitest/mixedcontent/imgunsecredirect.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/security/manager/ssl/tests/mochitest/mixedcontent/imgunsecredirect.sjs 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/security/manager/ssl/tests/mochitest/mixedcontent/imgunsecredirect.sjs 2021-10-20 19:28:35.000000000 +0000 @@ -1,5 +1,7 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setStatusLine(request.httpVersion, 307, "Moved temporarly"); - response.setHeader("Location", "http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg"); + response.setHeader( + "Location", + "http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg" + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/security/manager/ssl/tests/mochitest/mixedcontent/nocontent.sjs firefox-trunk-95.0~a1~hg20211020r596404/security/manager/ssl/tests/mochitest/mixedcontent/nocontent.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/security/manager/ssl/tests/mochitest/mixedcontent/nocontent.sjs 2021-10-17 14:42:50.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/security/manager/ssl/tests/mochitest/mixedcontent/nocontent.sjs 2021-10-20 19:28:33.000000000 +0000 @@ -1,4 +1,3 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setStatusLine(request.httpVersion, 204, "No Content"); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/security/manager/ssl/tests/mochitest/mixedcontent/redirecttoemptyimage.sjs firefox-trunk-95.0~a1~hg20211020r596404/security/manager/ssl/tests/mochitest/mixedcontent/redirecttoemptyimage.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/security/manager/ssl/tests/mochitest/mixedcontent/redirecttoemptyimage.sjs 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/security/manager/ssl/tests/mochitest/mixedcontent/redirecttoemptyimage.sjs 2021-10-20 19:28:34.000000000 +0000 @@ -1,5 +1,7 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setStatusLine(request.httpVersion, 307, "Moved temporarly"); - response.setHeader("Location", "http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/emptyimage.sjs"); + response.setHeader( + "Location", + "http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/emptyimage.sjs" + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/security/manager/ssl/tests/unit/test_content_signing/pysign.py firefox-trunk-95.0~a1~hg20211020r596404/security/manager/ssl/tests/unit/test_content_signing/pysign.py --- firefox-trunk-95.0~a1~hg20211017r596111/security/manager/ssl/tests/unit/test_content_signing/pysign.py 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/security/manager/ssl/tests/unit/test_content_signing/pysign.py 2021-10-20 19:28:34.000000000 +0000 @@ -18,18 +18,19 @@ import base64 import binascii import hashlib -import os +import pathlib import six import sys import ecdsa -# For pykey -sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +# For pykey, find the relative file location and add it to path +toolsDir = (pathlib.Path(__file__).parents[4] / "tools").resolve() +sys.path.append(str(toolsDir)) import pykey data = sys.stdin.buffer.read() key = pykey.ECCKey("secp384r1") sig = key.signRaw(b"Content-Signature:\00" + data, pykey.HASH_SHA384) -print base64.b64encode(sig).replace("+", "-").replace("/", "_") +print(str(base64.b64encode(sig)).replace("+", "-").replace("/", "_")) diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/security/nss/automation/abi-check/previous-nss-release firefox-trunk-95.0~a1~hg20211020r596404/security/nss/automation/abi-check/previous-nss-release --- firefox-trunk-95.0~a1~hg20211017r596111/security/nss/automation/abi-check/previous-nss-release 2021-10-17 14:42:50.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/security/nss/automation/abi-check/previous-nss-release 2021-10-20 19:28:35.000000000 +0000 @@ -1 +1 @@ -NSS_3_70_BRANCH +NSS_3_71_BRANCH diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/security/nss/coreconf/coreconf.dep firefox-trunk-95.0~a1~hg20211020r596404/security/nss/coreconf/coreconf.dep --- firefox-trunk-95.0~a1~hg20211017r596111/security/nss/coreconf/coreconf.dep 2021-10-17 14:42:50.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/security/nss/coreconf/coreconf.dep 2021-10-20 19:28:35.000000000 +0000 @@ -10,3 +10,4 @@ */ #error "Do not include this header file." + diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/security/nss/lib/nss/nss.h firefox-trunk-95.0~a1~hg20211020r596404/security/nss/lib/nss/nss.h --- firefox-trunk-95.0~a1~hg20211017r596111/security/nss/lib/nss/nss.h 2021-10-17 14:42:50.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/security/nss/lib/nss/nss.h 2021-10-20 19:28:34.000000000 +0000 @@ -22,12 +22,12 @@ * The format of the version string should be * ".[.[.]][ ][ ]" */ -#define NSS_VERSION "3.71" _NSS_CUSTOMIZED +#define NSS_VERSION "3.72" _NSS_CUSTOMIZED " Beta" #define NSS_VMAJOR 3 -#define NSS_VMINOR 71 +#define NSS_VMINOR 72 #define NSS_VPATCH 0 #define NSS_VBUILD 0 -#define NSS_BETA PR_FALSE +#define NSS_BETA PR_TRUE #ifndef RC_INVOKED diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/security/nss/lib/softoken/lowpbe.c firefox-trunk-95.0~a1~hg20211020r596404/security/nss/lib/softoken/lowpbe.c --- firefox-trunk-95.0~a1~hg20211017r596111/security/nss/lib/softoken/lowpbe.c 2021-10-17 14:42:50.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/security/nss/lib/softoken/lowpbe.c 2021-10-20 19:28:35.000000000 +0000 @@ -570,7 +570,7 @@ /* Bug 1606992 - Cache the hash result for the common case that we're * asked to repeatedly compute the key for the same password item, * hash, iterations and salt. */ -#define KDF2_CACHE_COUNT 2 +#define KDF2_CACHE_COUNT 3 static struct { PZLock *lock; struct { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/security/nss/lib/softoken/softkver.h firefox-trunk-95.0~a1~hg20211020r596404/security/nss/lib/softoken/softkver.h --- firefox-trunk-95.0~a1~hg20211017r596111/security/nss/lib/softoken/softkver.h 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/security/nss/lib/softoken/softkver.h 2021-10-20 19:28:36.000000000 +0000 @@ -17,11 +17,11 @@ * The format of the version string should be * ".[.[.]][ ][ ]" */ -#define SOFTOKEN_VERSION "3.71" SOFTOKEN_ECC_STRING +#define SOFTOKEN_VERSION "3.72" SOFTOKEN_ECC_STRING " Beta" #define SOFTOKEN_VMAJOR 3 -#define SOFTOKEN_VMINOR 71 +#define SOFTOKEN_VMINOR 72 #define SOFTOKEN_VPATCH 0 #define SOFTOKEN_VBUILD 0 -#define SOFTOKEN_BETA PR_FALSE +#define SOFTOKEN_BETA PR_TRUE #endif /* _SOFTKVER_H_ */ diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/security/nss/lib/util/nssutil.h firefox-trunk-95.0~a1~hg20211020r596404/security/nss/lib/util/nssutil.h --- firefox-trunk-95.0~a1~hg20211017r596111/security/nss/lib/util/nssutil.h 2021-10-17 14:42:51.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/security/nss/lib/util/nssutil.h 2021-10-20 19:28:35.000000000 +0000 @@ -19,12 +19,12 @@ * The format of the version string should be * ".[.[.]][ ]" */ -#define NSSUTIL_VERSION "3.71" +#define NSSUTIL_VERSION "3.72 Beta" #define NSSUTIL_VMAJOR 3 -#define NSSUTIL_VMINOR 71 +#define NSSUTIL_VMINOR 72 #define NSSUTIL_VPATCH 0 #define NSSUTIL_VBUILD 0 -#define NSSUTIL_BETA PR_FALSE +#define NSSUTIL_BETA PR_TRUE SEC_BEGIN_PROTOS diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/security/nss/TAG-INFO firefox-trunk-95.0~a1~hg20211020r596404/security/nss/TAG-INFO --- firefox-trunk-95.0~a1~hg20211017r596111/security/nss/TAG-INFO 2021-10-17 14:42:49.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/security/nss/TAG-INFO 2021-10-20 19:28:35.000000000 +0000 @@ -1 +1 @@ -NSS_3_71_RTM \ No newline at end of file +de3db3a55aef \ No newline at end of file diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/security/sandbox/common/test/SandboxTestingChildTests.h firefox-trunk-95.0~a1~hg20211020r596404/security/sandbox/common/test/SandboxTestingChildTests.h --- firefox-trunk-95.0~a1~hg20211017r596111/security/sandbox/common/test/SandboxTestingChildTests.h 2021-10-17 14:42:50.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/security/sandbox/common/test/SandboxTestingChildTests.h 2021-10-20 19:28:35.000000000 +0000 @@ -29,6 +29,10 @@ # include #endif +#ifdef XP_MACOSX +# include +#endif + namespace mozilla { void RunTestsContent(SandboxTestingChild* child) { @@ -109,6 +113,21 @@ }); # endif // XP_LINUX +# ifdef XP_MACOSX + // Test that content processes can not connect to the macOS window server. + // CGSessionCopyCurrentDictionary() returns NULL when a connection to the + // window server is not available. + CFDictionaryRef windowServerDict = CGSessionCopyCurrentDictionary(); + bool gotWindowServerDetails = (windowServerDict != nullptr); + child->SendReportTestResults( + "CGSessionCopyCurrentDictionary"_ns, false, gotWindowServerDetails, + gotWindowServerDetails ? "Failed: dictionary unexpectedly returned"_ns + : "Succeeded: no dictionary returned"_ns); + if (windowServerDict != nullptr) { + CFRelease(windowServerDict); + } +# endif + #else // XP_UNIX child->ReportNoTests(); #endif // XP_UNIX diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/services/fxaccounts/tests/mochitest/file_invalidEmailCase.sjs firefox-trunk-95.0~a1~hg20211020r596404/services/fxaccounts/tests/mochitest/file_invalidEmailCase.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/services/fxaccounts/tests/mochitest/file_invalidEmailCase.sjs 2021-10-17 14:42:50.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/services/fxaccounts/tests/mochitest/file_invalidEmailCase.sjs 2021-10-20 19:28:34.000000000 +0000 @@ -21,9 +21,11 @@ */ const CC = Components.Constructor; -const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", - "setInputStream"); +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); const goodEmail = "Greta.Garbo@gmail.COM"; const badEmail = "greta.garbo@gmail.com"; @@ -77,4 +79,3 @@ messageStr = JSON.stringify(message); response.bodyOutputStream.write(messageStr, messageStr.length); } - diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/services/settings/dumps/blocklists/addons-bloomfilters.json firefox-trunk-95.0~a1~hg20211020r596404/services/settings/dumps/blocklists/addons-bloomfilters.json --- firefox-trunk-95.0~a1~hg20211017r596111/services/settings/dumps/blocklists/addons-bloomfilters.json 2021-10-17 14:42:50.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/services/settings/dumps/blocklists/addons-bloomfilters.json 2021-10-20 19:28:34.000000000 +0000 @@ -3,6 +3,36 @@ { "stash": { "blocked": [ + "vipkdy@com.vipkdy:5.5.1.32", + "vipkdy@com.vipkdy:5.5.1.26", + "vipkdy@com.vipkdy:5.5.1.8", + "@ultimateshoppingsearch:1.1.8", + "vipkdy@com.vipkdy:5.5.1.30", + "vipkdy@com.vipkdy:5.5.1.9", + "vipkdy@com.vipkdy:5.5.1.33", + "@ultimateshoppingsearch:1.1.6", + "vipkdy@com.vipkdy:5.5.1.20", + "@ultimateshoppingsearch:1.2.0", + "@ultimateshoppingsearch:1.1.9", + "vipkdy@com.vipkdy:5.5.1.28", + "vipkdy@com.vipkdy:5.5.1.36", + "vipkdy@com.vipkdy:5.5.1.3", + "@ultimateshoppingsearch:1.2.2", + "vipkdy@com.vipkdy:5.5.1.21", + "@ultimateshoppingsearch:1.2.1", + "@ultimateshoppingsearch:1.1.7" + ], + "unblocked": [] + }, + "schema": 1634206554527, + "key_format": "{guid}:{version}", + "stash_time": 1634236509443, + "id": "9737d55d-c647-485e-a801-c9ca425a5432", + "last_modified": 1634236664195 + }, + { + "stash": { + "blocked": [ "jid1-4P0kohSJxU1qGg@jetpack:1.81.165", "jid1-4P0kohSJxU1qGg@jetpack:1.96.206", "jid1-4P0kohSJxU1qGg@jetpack:1.96.743", diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/services/settings/dumps/security-state/intermediates.json firefox-trunk-95.0~a1~hg20211020r596404/services/settings/dumps/security-state/intermediates.json --- firefox-trunk-95.0~a1~hg20211017r596111/services/settings/dumps/security-state/intermediates.json 2021-10-17 14:42:51.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/services/settings/dumps/security-state/intermediates.json 2021-10-20 19:28:35.000000000 +0000 @@ -1,7 +1,313 @@ { "data": [ { - "schema": 1634131327830, + "schema": 1634309429571, + "derHash": "q97sUxSQmPigsH79lys0Wom+3o7eaXXmG+le4Cbafvo=", + "subject": "CN=Actalis Client Authentication CA G1,O=Actalis S.p.A./03358520967,L=Milano,ST=Milano,C=IT", + "subjectDN": "MIGCMQswCQYDVQQGEwJJVDEPMA0GA1UECAwGTWlsYW5vMQ8wDQYDVQQHDAZNaWxhbm8xIzAhBgNVBAoMGkFjdGFsaXMgUy5wLkEuLzAzMzU4NTIwOTY3MSwwKgYDVQQDDCNBY3RhbGlzIENsaWVudCBBdXRoZW50aWNhdGlvbiBDQSBHMQ==", + "whitelist": false, + "attachment": { + "hash": "737f0f0ce1590b65bb7efa52f0521e84ec1ac16313c8b4c6cd288064af2d127d", + "size": 2235, + "filename": "YsqG87ugr7iITRNFFaHeNX5oMmY80JlEVuqBCxAhZAM=.pem", + "location": "security-state-staging/intermediates/5df58a07-e383-4a1a-8af4-9471aa61c013.pem", + "mimetype": "application/x-pem-file" + }, + "pubKeyHash": "YsqG87ugr7iITRNFFaHeNX5oMmY80JlEVuqBCxAhZAM=", + "crlite_enrolled": false, + "id": "621a2382-280e-48de-b859-5293ba5379b7", + "last_modified": 1634309862997 + }, + { + "schema": 1634309428400, + "derHash": "d4xRba7HAO5Ys1geQR5cDdR4ZjpRY6KYlTQVB9bpZN0=", + "subject": "CN=SHECA DV Server CA G5,O=UniTrust,C=CN", + "subjectDN": "MEAxCzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlUcnVzdDEeMBwGA1UEAwwVU0hFQ0EgRFYgU2VydmVyIENBIEc1", + "whitelist": false, + "attachment": { + "hash": "4153674bf85308618da92043981bdd2d2f7ca5a4ed109d39c822cdd2408d1775", + "size": 2008, + "filename": "LSv8B00n0rDwNaioIz0qIgnC9J7YkSK9NS35qVjVZK4=.pem", + "location": "security-state-staging/intermediates/9e707aa9-a13b-417d-ac93-377ab92b3136.pem", + "mimetype": "application/x-pem-file" + }, + "pubKeyHash": "LSv8B00n0rDwNaioIz0qIgnC9J7YkSK9NS35qVjVZK4=", + "crlite_enrolled": false, + "id": "3884a378-6d9c-42ab-a578-6a20d7e609a8", + "last_modified": 1634309862974 + }, + { + "schema": 1634309626145, + "derHash": "d4xRba7HAO5Ys1geQR5cDdR4ZjpRY6KYlTQVB9bpZN0=", + "subject": "CN=SHECA DV Server CA G5,O=UniTrust,C=CN", + "subjectDN": "MEAxCzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlUcnVzdDEeMBwGA1UEAwwVU0hFQ0EgRFYgU2VydmVyIENBIEc1", + "whitelist": false, + "attachment": { + "hash": "4153674bf85308618da92043981bdd2d2f7ca5a4ed109d39c822cdd2408d1775", + "size": 2008, + "filename": "LSv8B00n0rDwNaioIz0qIgnC9J7YkSK9NS35qVjVZK4=.pem", + "location": "security-state-staging/intermediates/616b8c10-5e1d-4a6f-9d4b-0e50a8fd9dc5.pem", + "mimetype": "application/x-pem-file" + }, + "pubKeyHash": "LSv8B00n0rDwNaioIz0qIgnC9J7YkSK9NS35qVjVZK4=", + "crlite_enrolled": false, + "id": "0b34f843-08b5-4719-befd-242cfb5b537c", + "last_modified": 1634309862951 + }, + { + "schema": 1634309425878, + "derHash": "/mtvnkS2cHl9UuXxbvG7ELSes61mJSL8Yys335pQRM0=", + "subject": "CN=Staclar TLS Issuing CA R1,O=Staclar\\, Inc.,L=Claymont,ST=Delaware,C=US", + "subjectDN": "MG8xCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhEZWxhd2FyZTERMA8GA1UEBwwIQ2xheW1vbnQxFjAUBgNVBAoMDVN0YWNsYXIsIEluYy4xIjAgBgNVBAMMGVN0YWNsYXIgVExTIElzc3VpbmcgQ0EgUjE=", + "whitelist": false, + "attachment": { + "hash": "54038b6768167a2d4b10ab95d2f8e93b47ab7b8e6233e07343f621a977f51f34", + "size": 2495, + "filename": "2hrK22IRCeQc0huhCCGTSKY2R0LmyeFUaH4ArRR6YUc=.pem", + "location": "security-state-staging/intermediates/3efc8703-5c7e-45d9-b345-9857aec25425.pem", + "mimetype": "application/x-pem-file" + }, + "pubKeyHash": "2hrK22IRCeQc0huhCCGTSKY2R0LmyeFUaH4ArRR6YUc=", + "crlite_enrolled": true, + "id": "b3bfb57e-84e5-459a-93ea-751a8c706623", + "last_modified": 1634309862928 + }, + { + "schema": 1634309427798, + "derHash": "/M986IpkKHTRwQf00QxqfdM0eXY5upgTBFRWLDA68XA=", + "subject": "CN=Quantum Basic TLS DV RSA R1,O=Quantum CA Limited,C=GB", + "subjectDN": "MFAxCzAJBgNVBAYTAkdCMRswGQYDVQQKDBJRdWFudHVtIENBIExpbWl0ZWQxJDAiBgNVBAMMG1F1YW50dW0gQmFzaWMgVExTIERWIFJTQSBSMQ==", + "whitelist": false, + "attachment": { + "hash": "9a5b9cf6da2c98fb4d3adb057a8cb4c74161b112407d57a29dd0fc9cb2d24904", + "size": 2381, + "filename": "OB3HSFDxykZYkZCziJA-6l6iHYbsqNrhFIrMg_L45mU=.pem", + "location": "security-state-staging/intermediates/0f12f590-b4c0-47a3-a66e-78070f6bfe7f.pem", + "mimetype": "application/x-pem-file" + }, + "pubKeyHash": "OB3HSFDxykZYkZCziJA+6l6iHYbsqNrhFIrMg/L45mU=", + "crlite_enrolled": true, + "id": "10b10ba7-6c85-4583-9814-eda266ae2416", + "last_modified": 1634309862903 + }, + { + "schema": 1634309426524, + "derHash": "N/0pxwHWl3mY8gUVPqikwumWNU3wctSYTcXYsfdaK2E=", + "subject": "CN=SwissNS TLS Issuing RSA CA R1,O=swissns GmbH,L=Luzern,ST=Luzern,C=CH", + "subjectDN": "MG4xCzAJBgNVBAYTAkNIMQ8wDQYDVQQIDAZMdXplcm4xDzANBgNVBAcMBkx1emVybjEVMBMGA1UECgwMc3dpc3NucyBHbWJIMSYwJAYDVQQDDB1Td2lzc05TIFRMUyBJc3N1aW5nIFJTQSBDQSBSMQ==", + "whitelist": false, + "attachment": { + "hash": "712a44aa441a1572aa92968b2b418a8c979bfb72b6b17653fed5888d76210828", + "size": 2544, + "filename": "KAda-LJFeGtGthCqCejer2EUArFZNe-fmlkLdhkdcJc=.pem", + "location": "security-state-staging/intermediates/2b761dc1-930d-4dc0-ba17-650c271bfb95.pem", + "mimetype": "application/x-pem-file" + }, + "pubKeyHash": "KAda+LJFeGtGthCqCejer2EUArFZNe+fmlkLdhkdcJc=", + "crlite_enrolled": true, + "id": "32fe68ce-7d4c-4d54-bab1-b2aab2d02223", + "last_modified": 1634309862879 + }, + { + "schema": 1634291406255, + "derHash": "NtGanN5gKdFNpNlRc1GQu0zG2yBtZD9kdNdzlHSRIpk=", + "subject": "CN=Quantum Secure Site OV Pro TLS CN RSA R1,O=Quantum CA Limited,C=GB", + "subjectDN": "MF0xCzAJBgNVBAYTAkdCMRswGQYDVQQKDBJRdWFudHVtIENBIExpbWl0ZWQxMTAvBgNVBAMMKFF1YW50dW0gU2VjdXJlIFNpdGUgT1YgUHJvIFRMUyBDTiBSU0EgUjE=", + "whitelist": false, + "attachment": { + "hash": "37e8e536a1b1e7f4996f53b272104ddf40c3497a8ed88f0be057d22bdedfb8ad", + "size": 2402, + "filename": "wmkhucJk0k_u-vh155sN1sgtdzW537s_eYqcbeYm4PU=.pem", + "location": "security-state-staging/intermediates/271cdd84-3255-4317-bf48-4451169b8dae.pem", + "mimetype": "application/x-pem-file" + }, + "pubKeyHash": "wmkhucJk0k/u+vh155sN1sgtdzW537s/eYqcbeYm4PU=", + "crlite_enrolled": true, + "id": "0dbf6d16-da28-4b79-98b6-478b71d6b156", + "last_modified": 1634309862856 + }, + { + "schema": 1634309425224, + "derHash": "5Hft8R61T28u3VaYez2/ivprhVB655KsY81BzkGsE5c=", + "subject": "CN=Quantum Secure Site DV TLS CN RSA R1,O=Quantum CA Limited,C=GB", + "subjectDN": "MFkxCzAJBgNVBAYTAkdCMRswGQYDVQQKDBJRdWFudHVtIENBIExpbWl0ZWQxLTArBgNVBAMMJFF1YW50dW0gU2VjdXJlIFNpdGUgRFYgVExTIENOIFJTQSBSMQ==", + "whitelist": false, + "attachment": { + "hash": "4a8292642e07c7a31a3e646b5dee0077bb31ab11611979dd57528a38af3c0f4e", + "size": 2398, + "filename": "iZz0oapRjKvmsylcqDwbwcsBGfrjtSeUvrbqeWauFlo=.pem", + "location": "security-state-staging/intermediates/3884e68e-5ff3-4f81-8755-d774a966e23c.pem", + "mimetype": "application/x-pem-file" + }, + "pubKeyHash": "iZz0oapRjKvmsylcqDwbwcsBGfrjtSeUvrbqeWauFlo=", + "crlite_enrolled": true, + "id": "5730e704-8d00-4b00-acc9-6c9fb8d6a310", + "last_modified": 1634309862829 + }, + { + "schema": 1634309427217, + "derHash": "VYRK03slu02z/+fc3DkCOkGzxIikepX60fz61Qi3AoU=", + "subject": "CN=Dodo Sign TLS ICA RSA R1,O=Dodo Sign Ltd,L=Ebene,ST=Plaines Wilhems,C=MU", + "subjectDN": "MHIxCzAJBgNVBAYTAk1VMRgwFgYDVQQIDA9QbGFpbmVzIFdpbGhlbXMxDjAMBgNVBAcMBUViZW5lMRYwFAYDVQQKDA1Eb2RvIFNpZ24gTHRkMSEwHwYDVQQDDBhEb2RvIFNpZ24gVExTIElDQSBSU0EgUjE=", + "whitelist": false, + "attachment": { + "hash": "9bbab79fdec346cdda828bc4486cedf2582bee9d80c23587c1c2f05407854b6e", + "size": 2438, + "filename": "QBni1na5RD10V0ehoagk-O8mlEaC8kPw1gH8_uoYYTY=.pem", + "location": "security-state-staging/intermediates/512c8ed9-74cb-4a34-a0b5-79f8a0f953b2.pem", + "mimetype": "application/x-pem-file" + }, + "pubKeyHash": "QBni1na5RD10V0ehoagk+O8mlEaC8kPw1gH8/uoYYTY=", + "crlite_enrolled": true, + "id": "d63929e7-804a-4e7f-9657-ccb49438e6ad", + "last_modified": 1634309862789 + }, + { + "schema": 1634309424611, + "derHash": "lItxEa9C9UbVec/1ziveyCE03ZkUhCvdsMUocutgTjk=", + "subject": "CN=SSL.com SSL Intermediate CA ECC R2,O=SSL Corp,L=Houston,ST=Texas,C=US", + "subjectDN": "MG8xCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjERMA8GA1UECgwIU1NMIENvcnAxKzApBgNVBAMMIlNTTC5jb20gU1NMIEludGVybWVkaWF0ZSBDQSBFQ0MgUjI=", + "whitelist": false, + "attachment": { + "hash": "29138ba36283c37133babfb45ad6da91b908056c4bc777229a9b6114c29bb9d7", + "size": 1264, + "filename": "zGgA4OU4DjJdvpRYUqbi5Vh2g9W5Oc_PgKihy9mkLsE=.pem", + "location": "security-state-staging/intermediates/583bc538-020a-40af-92a9-40c3653a90e8.pem", + "mimetype": "application/x-pem-file" + }, + "pubKeyHash": "zGgA4OU4DjJdvpRYUqbi5Vh2g9W5Oc/PgKihy9mkLsE=", + "crlite_enrolled": true, + "id": "a0acdb7c-e477-4e0c-8f54-b838e76497f5", + "last_modified": 1634309862765 + }, + { + "schema": 1634309428987, + "derHash": "UnpgsCq/OkpVGcT2L7vVYOMDQHTu7IuHmaqTaGk/420=", + "subject": "CN=SSL.com RSA SSL subCA,O=SSL Corporation,L=Houston,ST=Texas,C=US", + "subjectDN": "MGkxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMR4wHAYDVQQDDBVTU0wuY29tIFJTQSBTU0wgc3ViQ0E=", + "whitelist": false, + "attachment": { + "hash": "19883ec948ba65cabab87774d50633120945ef053a62f8362d2e09b43597fe1a", + "size": 2292, + "filename": "7LcB5Z8ATVz4rcQtIY5xJir8_-F3e_HPi8IDdCnjCaE=.pem", + "location": "security-state-staging/intermediates/23914bfd-6f12-4a9d-8c1a-a2c9e445a1bf.pem", + "mimetype": "application/x-pem-file" + }, + "pubKeyHash": "7LcB5Z8ATVz4rcQtIY5xJir8/+F3e/HPi8IDdCnjCaE=", + "crlite_enrolled": true, + "id": "b976a3a6-3c96-4aca-9c5f-fc654e3c4f6c", + "last_modified": 1634309862738 + }, + { + "schema": 1634309430157, + "derHash": "N/0pxwHWl3mY8gUVPqikwumWNU3wctSYTcXYsfdaK2E=", + "subject": "CN=SwissNS TLS Issuing RSA CA R1,O=swissns GmbH,L=Luzern,ST=Luzern,C=CH", + "subjectDN": "MG4xCzAJBgNVBAYTAkNIMQ8wDQYDVQQIDAZMdXplcm4xDzANBgNVBAcMBkx1emVybjEVMBMGA1UECgwMc3dpc3NucyBHbWJIMSYwJAYDVQQDDB1Td2lzc05TIFRMUyBJc3N1aW5nIFJTQSBDQSBSMQ==", + "whitelist": false, + "attachment": { + "hash": "712a44aa441a1572aa92968b2b418a8c979bfb72b6b17653fed5888d76210828", + "size": 2544, + "filename": "KAda-LJFeGtGthCqCejer2EUArFZNe-fmlkLdhkdcJc=.pem", + "location": "security-state-staging/intermediates/c7fb95b1-4e6e-44da-b797-1b64a448dbfb.pem", + "mimetype": "application/x-pem-file" + }, + "pubKeyHash": "KAda+LJFeGtGthCqCejer2EUArFZNe+fmlkLdhkdcJc=", + "crlite_enrolled": true, + "id": "1c713ec1-22b4-4bb9-a642-d8188603e3c9", + "last_modified": 1634309862715 + }, + { + "schema": 1634287827544, + "derHash": "irOgrPKJ5u91S+RJI2hD1n9FwZG93WZIS4Xm5gVWqa8=", + "subject": "CN=SHECA OV Server CA G5,O=UniTrust,C=CN", + "subjectDN": "MEAxCzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlUcnVzdDEeMBwGA1UEAwwVU0hFQ0EgT1YgU2VydmVyIENBIEc1", + "whitelist": false, + "attachment": { + "hash": "f80efdafc27d6bb8367919d6877b83658178c312d7ff4c7951be66667c2a033e", + "size": 2008, + "filename": "Ml9jtIo6CaZwLt7q6tlW9x4oQNlrHC-AQOHP17SXtCk=.pem", + "location": "security-state-staging/intermediates/ecb85ef3-60a6-486c-a07a-96efc94a8698.pem", + "mimetype": "application/x-pem-file" + }, + "pubKeyHash": "Ml9jtIo6CaZwLt7q6tlW9x4oQNlrHC+AQOHP17SXtCk=", + "crlite_enrolled": false, + "id": "317681eb-a6d1-4ecd-bf18-a7af01dde81d", + "last_modified": 1634288243929 + }, + { + "schema": 1634288024219, + "derHash": "irOgrPKJ5u91S+RJI2hD1n9FwZG93WZIS4Xm5gVWqa8=", + "subject": "CN=SHECA OV Server CA G5,O=UniTrust,C=CN", + "subjectDN": "MEAxCzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlUcnVzdDEeMBwGA1UEAwwVU0hFQ0EgT1YgU2VydmVyIENBIEc1", + "whitelist": false, + "attachment": { + "hash": "f80efdafc27d6bb8367919d6877b83658178c312d7ff4c7951be66667c2a033e", + "size": 2008, + "filename": "Ml9jtIo6CaZwLt7q6tlW9x4oQNlrHC-AQOHP17SXtCk=.pem", + "location": "security-state-staging/intermediates/9bd0ec19-9f61-45d1-a9f4-651e6aa64389.pem", + "mimetype": "application/x-pem-file" + }, + "pubKeyHash": "Ml9jtIo6CaZwLt7q6tlW9x4oQNlrHC+AQOHP17SXtCk=", + "crlite_enrolled": false, + "id": "61ad8f77-7825-42e3-a828-72ff88095f6e", + "last_modified": 1634288243904 + }, + { + "schema": 1634266228965, + "derHash": "6uWboswzKBpc3exQz7/Z3QylVl8872mMk2ahDOwIf5c=", + "subject": "CN=Domain The Net Technologies Ltd CA for SSL R2,O=Domain The Net Technologies Ltd,C=IL", + "subjectDN": "MG8xCzAJBgNVBAYTAklMMSgwJgYDVQQKDB9Eb21haW4gVGhlIE5ldCBUZWNobm9sb2dpZXMgTHRkMTYwNAYDVQQDDC1Eb21haW4gVGhlIE5ldCBUZWNobm9sb2dpZXMgTHRkIENBIGZvciBTU0wgUjI=", + "whitelist": false, + "attachment": { + "hash": "dc6270566751505a7936c7847e06bc81b9c0a9ca4b21fd6512fbeaa3d7d3bc7d", + "size": 2483, + "filename": "1FBqLyRsP8ibxXXsW64LYWGTeYMGSsUTMFEetUQakD8=.pem", + "location": "security-state-staging/intermediates/c052ede4-8c2e-4eed-97b4-5d3e127c78f8.pem", + "mimetype": "application/x-pem-file" + }, + "pubKeyHash": "1FBqLyRsP8ibxXXsW64LYWGTeYMGSsUTMFEetUQakD8=", + "crlite_enrolled": false, + "id": "461724c9-5ae7-4021-be3d-b5c081e3b8d7", + "last_modified": 1634266676073 + }, + { + "schema": 1634266231951, + "derHash": "NsfjKXUKQD2wuvBpyanzOWPlyYpmqKtXAW22+IzFYU0=", + "subject": "CN=MilleniumSign SSL Certificate CA RSA R2,O=MilleniumSign Limited,L=Ebene,ST=Plaines Wilhems,C=MU", + "subjectDN": "MIGJMQswCQYDVQQGEwJNVTEYMBYGA1UECAwPUGxhaW5lcyBXaWxoZW1zMQ4wDAYDVQQHDAVFYmVuZTEeMBwGA1UECgwVTWlsbGVuaXVtU2lnbiBMaW1pdGVkMTAwLgYDVQQDDCdNaWxsZW5pdW1TaWduIFNTTCBDZXJ0aWZpY2F0ZSBDQSBSU0EgUjI=", + "whitelist": false, + "attachment": { + "hash": "3c3182c3f974165726acb2e2b6c35f9306b120ba4684e22ac91eef26e5ef70a1", + "size": 2552, + "filename": "1jNByu9N5tc5kL0IfWW4-AIUQnp6ljVLB3ROX-cA6b4=.pem", + "location": "security-state-staging/intermediates/03421df1-3916-4c0a-87eb-0d83ea93a06f.pem", + "mimetype": "application/x-pem-file" + }, + "pubKeyHash": "1jNByu9N5tc5kL0IfWW4+AIUQnp6ljVLB3ROX+cA6b4=", + "crlite_enrolled": false, + "id": "17e6f300-d0c1-4ef6-ae0f-36ecd026b133", + "last_modified": 1634266676048 + }, + { + "schema": 1634223513998, + "derHash": "zJ9yVE/2F8FM6+goMZSnDx6Jgc8WPVo/2NzP2EbV3Bo=", + "subject": "CN=NetLock OnlineSSL (Class Online) Tanúsítványkiadó,OU=Tanúsítványkiadók (Certification Services),O=NetLock Kft.,L=Budapest,C=HU", + "subjectDN": "MIGwMQswCQYDVQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzKTE+MDwGA1UEAww1TmV0TG9jayBPbmxpbmVTU0wgKENsYXNzIE9ubGluZSkgVGFuw7pzw610dsOhbnlraWFkw7M=", + "whitelist": false, + "attachment": { + "hash": "72ae8f0a0087c7b13f79d2f02193b9bca142172f3053cad4b927af716cd0534a", + "size": 2190, + "filename": "He6eOdoX1CHCLhtg75AtdDdI18-x-VfWOYW5_LCt5kk=.pem", + "location": "security-state-staging/intermediates/6bab6451-8f63-49f3-a556-455c6b868bd1.pem", + "mimetype": "application/x-pem-file" + }, + "pubKeyHash": "He6eOdoX1CHCLhtg75AtdDdI18+x+VfWOYW5/LCt5kk=", + "crlite_enrolled": false, + "id": "ca8f61c9-fe67-45a0-8bc2-f0a8348e2cdb", + "last_modified": 1634245155970 + }, + { + "schema": 1634206552959, "derHash": "ClUqZfIv+CDn7D1Du/iLAqvDS9JH4MNQWJG2NC8WpfI=", "subject": "CN=SHECA RSA Domain Validation Server CA G3,O=UniTrust,C=CN", "subjectDN": "MFMxCzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlUcnVzdDExMC8GA1UEAwwoU0hFQ0EgUlNBIERvbWFpbiBWYWxpZGF0aW9uIFNlcnZlciBDQSBHMw==", @@ -14,9 +320,9 @@ "mimetype": "application/x-pem-file" }, "pubKeyHash": "N6OrM0KKQgR1zORoDKkLLFEKAYCmS/84dpbLl/qNOnU=", - "crlite_enrolled": false, + "crlite_enrolled": true, "id": "4b6a2421-1677-41c0-8103-bc13fb7e09f9", - "last_modified": 1634201855983 + "last_modified": 1634223512841 }, { "schema": 1634079014715, @@ -5815,78 +6121,6 @@ "last_modified": 1631735851199 }, { - "schema": 1631713981163, - "derHash": "d4xRba7HAO5Ys1geQR5cDdR4ZjpRY6KYlTQVB9bpZN0=", - "subject": "CN=SHECA DV Server CA G5,O=UniTrust,C=CN", - "subjectDN": "MEAxCzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlUcnVzdDEeMBwGA1UEAwwVU0hFQ0EgRFYgU2VydmVyIENBIEc1", - "whitelist": false, - "attachment": { - "hash": "4153674bf85308618da92043981bdd2d2f7ca5a4ed109d39c822cdd2408d1775", - "size": 2008, - "filename": "LSv8B00n0rDwNaioIz0qIgnC9J7YkSK9NS35qVjVZK4=.pem", - "location": "security-state-staging/intermediates/616b8c10-5e1d-4a6f-9d4b-0e50a8fd9dc5.pem", - "mimetype": "application/x-pem-file" - }, - "pubKeyHash": "LSv8B00n0rDwNaioIz0qIgnC9J7YkSK9NS35qVjVZK4=", - "crlite_enrolled": true, - "id": "0b34f843-08b5-4719-befd-242cfb5b537c", - "last_modified": 1631714250659 - }, - { - "schema": 1631713814405, - "derHash": "d4xRba7HAO5Ys1geQR5cDdR4ZjpRY6KYlTQVB9bpZN0=", - "subject": "CN=SHECA DV Server CA G5,O=UniTrust,C=CN", - "subjectDN": "MEAxCzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlUcnVzdDEeMBwGA1UEAwwVU0hFQ0EgRFYgU2VydmVyIENBIEc1", - "whitelist": false, - "attachment": { - "hash": "4153674bf85308618da92043981bdd2d2f7ca5a4ed109d39c822cdd2408d1775", - "size": 2008, - "filename": "LSv8B00n0rDwNaioIz0qIgnC9J7YkSK9NS35qVjVZK4=.pem", - "location": "security-state-staging/intermediates/9e707aa9-a13b-417d-ac93-377ab92b3136.pem", - "mimetype": "application/x-pem-file" - }, - "pubKeyHash": "LSv8B00n0rDwNaioIz0qIgnC9J7YkSK9NS35qVjVZK4=", - "crlite_enrolled": true, - "id": "3884a378-6d9c-42ab-a578-6a20d7e609a8", - "last_modified": 1631714250636 - }, - { - "schema": 1631692196540, - "derHash": "irOgrPKJ5u91S+RJI2hD1n9FwZG93WZIS4Xm5gVWqa8=", - "subject": "CN=SHECA OV Server CA G5,O=UniTrust,C=CN", - "subjectDN": "MEAxCzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlUcnVzdDEeMBwGA1UEAwwVU0hFQ0EgT1YgU2VydmVyIENBIEc1", - "whitelist": false, - "attachment": { - "hash": "f80efdafc27d6bb8367919d6877b83658178c312d7ff4c7951be66667c2a033e", - "size": 2008, - "filename": "Ml9jtIo6CaZwLt7q6tlW9x4oQNlrHC-AQOHP17SXtCk=.pem", - "location": "security-state-staging/intermediates/9bd0ec19-9f61-45d1-a9f4-651e6aa64389.pem", - "mimetype": "application/x-pem-file" - }, - "pubKeyHash": "Ml9jtIo6CaZwLt7q6tlW9x4oQNlrHC+AQOHP17SXtCk=", - "crlite_enrolled": true, - "id": "61ad8f77-7825-42e3-a828-72ff88095f6e", - "last_modified": 1631692720459 - }, - { - "schema": 1631584648305, - "derHash": "irOgrPKJ5u91S+RJI2hD1n9FwZG93WZIS4Xm5gVWqa8=", - "subject": "CN=SHECA OV Server CA G5,O=UniTrust,C=CN", - "subjectDN": "MEAxCzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlUcnVzdDEeMBwGA1UEAwwVU0hFQ0EgT1YgU2VydmVyIENBIEc1", - "whitelist": false, - "attachment": { - "hash": "f80efdafc27d6bb8367919d6877b83658178c312d7ff4c7951be66667c2a033e", - "size": 2008, - "filename": "Ml9jtIo6CaZwLt7q6tlW9x4oQNlrHC-AQOHP17SXtCk=.pem", - "location": "security-state-staging/intermediates/ecb85ef3-60a6-486c-a07a-96efc94a8698.pem", - "mimetype": "application/x-pem-file" - }, - "pubKeyHash": "Ml9jtIo6CaZwLt7q6tlW9x4oQNlrHC+AQOHP17SXtCk=", - "crlite_enrolled": true, - "id": "317681eb-a6d1-4ecd-bf18-a7af01dde81d", - "last_modified": 1631692720437 - }, - { "schema": 1631545045582, "derHash": "+RYG0bxSxhATbKqFarUAxIw7mTusSAjNgrxLeKvyQVY=", "subject": "CN=NETLOCK DVSSL CA,OU=Tanúsítványkiadók (Certification Services),O=NETLOCK Kft.,L=Budapest,C=HU", @@ -7435,24 +7669,6 @@ "last_modified": 1624676239258 }, { - "schema": 1624673275803, - "derHash": "/mtvnkS2cHl9UuXxbvG7ELSes61mJSL8Yys335pQRM0=", - "subject": "CN=Staclar TLS Issuing CA R1,O=Staclar\\, Inc.,L=Claymont,ST=Delaware,C=US", - "subjectDN": "MG8xCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhEZWxhd2FyZTERMA8GA1UEBwwIQ2xheW1vbnQxFjAUBgNVBAoMDVN0YWNsYXIsIEluYy4xIjAgBgNVBAMMGVN0YWNsYXIgVExTIElzc3VpbmcgQ0EgUjE=", - "whitelist": false, - "attachment": { - "hash": "54038b6768167a2d4b10ab95d2f8e93b47ab7b8e6233e07343f621a977f51f34", - "size": 2495, - "filename": "2hrK22IRCeQc0huhCCGTSKY2R0LmyeFUaH4ArRR6YUc=.pem", - "location": "security-state-staging/intermediates/3efc8703-5c7e-45d9-b345-9857aec25425.pem", - "mimetype": "application/x-pem-file" - }, - "pubKeyHash": "2hrK22IRCeQc0huhCCGTSKY2R0LmyeFUaH4ArRR6YUc=", - "crlite_enrolled": true, - "id": "b3bfb57e-84e5-459a-93ea-751a8c706623", - "last_modified": 1624676239228 - }, - { "schema": 1624525053378, "derHash": "yeQPToM5bzSnyGGBe07as9wfi6xpn9UMsmH6kSPVXvQ=", "subject": "CN=SwissSign Personal Silver CA 2014 - G22,O=SwissSign AG,C=CH", @@ -7831,24 +8047,6 @@ "last_modified": 1622559454515 }, { - "schema": 1622515801648, - "derHash": "/M986IpkKHTRwQf00QxqfdM0eXY5upgTBFRWLDA68XA=", - "subject": "CN=Quantum Basic TLS DV RSA R1,O=Quantum CA Limited,C=GB", - "subjectDN": "MFAxCzAJBgNVBAYTAkdCMRswGQYDVQQKDBJRdWFudHVtIENBIExpbWl0ZWQxJDAiBgNVBAMMG1F1YW50dW0gQmFzaWMgVExTIERWIFJTQSBSMQ==", - "whitelist": false, - "attachment": { - "hash": "9a5b9cf6da2c98fb4d3adb057a8cb4c74161b112407d57a29dd0fc9cb2d24904", - "size": 2381, - "filename": "OB3HSFDxykZYkZCziJA-6l6iHYbsqNrhFIrMg_L45mU=.pem", - "location": "security-state-staging/intermediates/0f12f590-b4c0-47a3-a66e-78070f6bfe7f.pem", - "mimetype": "application/x-pem-file" - }, - "pubKeyHash": "OB3HSFDxykZYkZCziJA+6l6iHYbsqNrhFIrMg/L45mU=", - "crlite_enrolled": true, - "id": "10b10ba7-6c85-4583-9814-eda266ae2416", - "last_modified": 1622516241262 - }, - { "schema": 1621954724552, "derHash": "tBpIZPDU7E6mMtAbPn8jJ3XlXiKzv9hkLuEpIoDQ5Ho=", "subject": "CN=DigiCert Basic EV RSA CN CA G2,O=DigiCert Inc,C=US", @@ -8443,42 +8641,6 @@ "last_modified": 1620928650018 }, { - "schema": 1620917550568, - "derHash": "N/0pxwHWl3mY8gUVPqikwumWNU3wctSYTcXYsfdaK2E=", - "subject": "CN=SwissNS TLS Issuing RSA CA R1,O=swissns GmbH,L=Luzern,ST=Luzern,C=CH", - "subjectDN": "MG4xCzAJBgNVBAYTAkNIMQ8wDQYDVQQIDAZMdXplcm4xDzANBgNVBAcMBkx1emVybjEVMBMGA1UECgwMc3dpc3NucyBHbWJIMSYwJAYDVQQDDB1Td2lzc05TIFRMUyBJc3N1aW5nIFJTQSBDQSBSMQ==", - "whitelist": false, - "attachment": { - "hash": "712a44aa441a1572aa92968b2b418a8c979bfb72b6b17653fed5888d76210828", - "size": 2544, - "filename": "KAda-LJFeGtGthCqCejer2EUArFZNe-fmlkLdhkdcJc=.pem", - "location": "security-state-staging/intermediates/c7fb95b1-4e6e-44da-b797-1b64a448dbfb.pem", - "mimetype": "application/x-pem-file" - }, - "pubKeyHash": "KAda+LJFeGtGthCqCejer2EUArFZNe+fmlkLdhkdcJc=", - "crlite_enrolled": true, - "id": "1c713ec1-22b4-4bb9-a642-d8188603e3c9", - "last_modified": 1620928649990 - }, - { - "schema": 1620914372260, - "derHash": "N/0pxwHWl3mY8gUVPqikwumWNU3wctSYTcXYsfdaK2E=", - "subject": "CN=SwissNS TLS Issuing RSA CA R1,O=swissns GmbH,L=Luzern,ST=Luzern,C=CH", - "subjectDN": "MG4xCzAJBgNVBAYTAkNIMQ8wDQYDVQQIDAZMdXplcm4xDzANBgNVBAcMBkx1emVybjEVMBMGA1UECgwMc3dpc3NucyBHbWJIMSYwJAYDVQQDDB1Td2lzc05TIFRMUyBJc3N1aW5nIFJTQSBDQSBSMQ==", - "whitelist": false, - "attachment": { - "hash": "712a44aa441a1572aa92968b2b418a8c979bfb72b6b17653fed5888d76210828", - "size": 2544, - "filename": "KAda-LJFeGtGthCqCejer2EUArFZNe-fmlkLdhkdcJc=.pem", - "location": "security-state-staging/intermediates/2b761dc1-930d-4dc0-ba17-650c271bfb95.pem", - "mimetype": "application/x-pem-file" - }, - "pubKeyHash": "KAda+LJFeGtGthCqCejer2EUArFZNe+fmlkLdhkdcJc=", - "crlite_enrolled": true, - "id": "32fe68ce-7d4c-4d54-bab1-b2aab2d02223", - "last_modified": 1620928649971 - }, - { "schema": 1620917551037, "derHash": "Qi+dTmgTTjYrdWnlKYoXPOxAx8cnSyJjqbyK2h0aI/o=", "subject": "CN=Data Management Intermediate CA2,O=Fresenius Kabi AG,C=US", @@ -9307,24 +9469,6 @@ "last_modified": 1618102648358 }, { - "schema": 1617770984230, - "derHash": "NtGanN5gKdFNpNlRc1GQu0zG2yBtZD9kdNdzlHSRIpk=", - "subject": "CN=Quantum Secure Site OV Pro TLS CN RSA R1,O=Quantum CA Limited,C=GB", - "subjectDN": "MF0xCzAJBgNVBAYTAkdCMRswGQYDVQQKDBJRdWFudHVtIENBIExpbWl0ZWQxMTAvBgNVBAMMKFF1YW50dW0gU2VjdXJlIFNpdGUgT1YgUHJvIFRMUyBDTiBSU0EgUjE=", - "whitelist": false, - "attachment": { - "hash": "37e8e536a1b1e7f4996f53b272104ddf40c3497a8ed88f0be057d22bdedfb8ad", - "size": 2402, - "filename": "wmkhucJk0k_u-vh155sN1sgtdzW537s_eYqcbeYm4PU=.pem", - "location": "security-state-staging/intermediates/271cdd84-3255-4317-bf48-4451169b8dae.pem", - "mimetype": "application/x-pem-file" - }, - "pubKeyHash": "wmkhucJk0k/u+vh155sN1sgtdzW537s/eYqcbeYm4PU=", - "crlite_enrolled": true, - "id": "0dbf6d16-da28-4b79-98b6-478b71d6b156", - "last_modified": 1618102648339 - }, - { "schema": 1617256194859, "derHash": "KFBuM/VaYHIRXz4Eyb2/WvUxLKj8EyUIuXrJn+4FFug=", "subject": "CN=DigiCert High Assurance CA-3b,O=DigiCert Inc,C=US", @@ -12547,24 +12691,6 @@ "last_modified": 1614391116607 }, { - "schema": 1614390579264, - "derHash": "5Hft8R61T28u3VaYez2/ivprhVB655KsY81BzkGsE5c=", - "subject": "CN=Quantum Secure Site DV TLS CN RSA R1,O=Quantum CA Limited,C=GB", - "subjectDN": "MFkxCzAJBgNVBAYTAkdCMRswGQYDVQQKDBJRdWFudHVtIENBIExpbWl0ZWQxLTArBgNVBAMMJFF1YW50dW0gU2VjdXJlIFNpdGUgRFYgVExTIENOIFJTQSBSMQ==", - "whitelist": false, - "attachment": { - "hash": "4a8292642e07c7a31a3e646b5dee0077bb31ab11611979dd57528a38af3c0f4e", - "size": 2398, - "filename": "iZz0oapRjKvmsylcqDwbwcsBGfrjtSeUvrbqeWauFlo=.pem", - "location": "security-state-staging/intermediates/3884e68e-5ff3-4f81-8755-d774a966e23c.pem", - "mimetype": "application/x-pem-file" - }, - "pubKeyHash": "iZz0oapRjKvmsylcqDwbwcsBGfrjtSeUvrbqeWauFlo=", - "crlite_enrolled": true, - "id": "5730e704-8d00-4b00-acc9-6c9fb8d6a310", - "last_modified": 1614391116588 - }, - { "schema": 1614347385669, "derHash": "53V7sMSUKtwQjQkqF7gSsQQ+BqCU3V4hSSAyVVb8aSQ=", "subject": "CN=GlobalSign Atlas ECCR5 DV ACME CA H1 2021,O=GlobalSign nv-sa,C=BE", @@ -27577,24 +27703,6 @@ "last_modified": 1591199861175 }, { - "schema": 1591167097545, - "derHash": "VYRK03slu02z/+fc3DkCOkGzxIikepX60fz61Qi3AoU=", - "subject": "CN=Dodo Sign TLS ICA RSA R1,O=Dodo Sign Ltd,L=Ebene,ST=Plaines Wilhems,C=MU", - "subjectDN": "MHIxCzAJBgNVBAYTAk1VMRgwFgYDVQQIDA9QbGFpbmVzIFdpbGhlbXMxDjAMBgNVBAcMBUViZW5lMRYwFAYDVQQKDA1Eb2RvIFNpZ24gTHRkMSEwHwYDVQQDDBhEb2RvIFNpZ24gVExTIElDQSBSU0EgUjE=", - "whitelist": false, - "attachment": { - "hash": "9bbab79fdec346cdda828bc4486cedf2582bee9d80c23587c1c2f05407854b6e", - "size": 2438, - "filename": "QBni1na5RD10V0ehoagk-O8mlEaC8kPw1gH8_uoYYTY=.pem", - "location": "security-state-staging/intermediates/512c8ed9-74cb-4a34-a0b5-79f8a0f953b2.pem", - "mimetype": "application/x-pem-file" - }, - "pubKeyHash": "QBni1na5RD10V0ehoagk+O8mlEaC8kPw1gH8/uoYYTY=", - "crlite_enrolled": true, - "id": "d63929e7-804a-4e7f-9657-ccb49438e6ad", - "last_modified": 1591199861147 - }, - { "schema": 1591167131818, "derHash": "G/2HAtj5uzQPNTggMwwLun5SLGMWTJHylUFNrHl/CGM=", "subject": "CN=TI Trust Technologies OV CA,O=TI Trust Technologies S.R.L.,L=Pomezia,ST=Roma,C=IT", @@ -34741,24 +34849,6 @@ "last_modified": 1576536531856 }, { - "schema": 1576536094670, - "derHash": "NsfjKXUKQD2wuvBpyanzOWPlyYpmqKtXAW22+IzFYU0=", - "subject": "CN=MilleniumSign SSL Certificate CA RSA R2,O=MilleniumSign Limited,L=Ebene,ST=Plaines Wilhems,C=MU", - "subjectDN": "MIGJMQswCQYDVQQGEwJNVTEYMBYGA1UECAwPUGxhaW5lcyBXaWxoZW1zMQ4wDAYDVQQHDAVFYmVuZTEeMBwGA1UECgwVTWlsbGVuaXVtU2lnbiBMaW1pdGVkMTAwLgYDVQQDDCdNaWxsZW5pdW1TaWduIFNTTCBDZXJ0aWZpY2F0ZSBDQSBSU0EgUjI=", - "whitelist": false, - "attachment": { - "hash": "3c3182c3f974165726acb2e2b6c35f9306b120ba4684e22ac91eef26e5ef70a1", - "size": 2552, - "filename": "1jNByu9N5tc5kL0IfWW4-AIUQnp6ljVLB3ROX-cA6b4=.pem", - "location": "security-state-staging/intermediates/03421df1-3916-4c0a-87eb-0d83ea93a06f.pem", - "mimetype": "application/x-pem-file" - }, - "pubKeyHash": "1jNByu9N5tc5kL0IfWW4+AIUQnp6ljVLB3ROX+cA6b4=", - "crlite_enrolled": true, - "id": "17e6f300-d0c1-4ef6-ae0f-36ecd026b133", - "last_modified": 1576536531841 - }, - { "schema": 1576536072963, "derHash": "pBaiukkMRU4juFvwh9t7E39PR9l0fmD4aS/0yN8OBis=", "subject": "CN=TrustOcean Certification Authority,O=QiaoKr Corporation Limited,C=CN", @@ -39205,24 +39295,6 @@ "last_modified": 1562025697178 }, { - "schema": 1562025694846, - "derHash": "q97sUxSQmPigsH79lys0Wom+3o7eaXXmG+le4Cbafvo=", - "subject": "CN=Actalis Client Authentication CA G1,O=Actalis S.p.A./03358520967,L=Milano,ST=Milano,C=IT", - "subjectDN": "MIGCMQswCQYDVQQGEwJJVDEPMA0GA1UECAwGTWlsYW5vMQ8wDQYDVQQHDAZNaWxhbm8xIzAhBgNVBAoMGkFjdGFsaXMgUy5wLkEuLzAzMzU4NTIwOTY3MSwwKgYDVQQDDCNBY3RhbGlzIENsaWVudCBBdXRoZW50aWNhdGlvbiBDQSBHMQ==", - "whitelist": false, - "attachment": { - "hash": "737f0f0ce1590b65bb7efa52f0521e84ec1ac16313c8b4c6cd288064af2d127d", - "size": 2235, - "filename": "YsqG87ugr7iITRNFFaHeNX5oMmY80JlEVuqBCxAhZAM=.pem", - "location": "security-state-staging/intermediates/5df58a07-e383-4a1a-8af4-9471aa61c013.pem", - "mimetype": "application/x-pem-file" - }, - "pubKeyHash": "YsqG87ugr7iITRNFFaHeNX5oMmY80JlEVuqBCxAhZAM=", - "crlite_enrolled": true, - "id": "621a2382-280e-48de-b859-5293ba5379b7", - "last_modified": 1562025695624 - }, - { "schema": 1562025691799, "derHash": "Dc3Afsq7v+OKRga6ZjmHzbO9mBf8Z9HdDPCUrIyieH0=", "subject": "CN=RapidSSL CA - G2,O=GeoTrust Inc.,C=US", @@ -41095,24 +41167,6 @@ "last_modified": 1562025501666 }, { - "schema": 1562025500166, - "derHash": "6uWboswzKBpc3exQz7/Z3QylVl8872mMk2ahDOwIf5c=", - "subject": "CN=Domain The Net Technologies Ltd CA for SSL R2,O=Domain The Net Technologies Ltd,C=IL", - "subjectDN": "MG8xCzAJBgNVBAYTAklMMSgwJgYDVQQKDB9Eb21haW4gVGhlIE5ldCBUZWNobm9sb2dpZXMgTHRkMTYwNAYDVQQDDC1Eb21haW4gVGhlIE5ldCBUZWNobm9sb2dpZXMgTHRkIENBIGZvciBTU0wgUjI=", - "whitelist": false, - "attachment": { - "hash": "dc6270566751505a7936c7847e06bc81b9c0a9ca4b21fd6512fbeaa3d7d3bc7d", - "size": 2483, - "filename": "1FBqLyRsP8ibxXXsW64LYWGTeYMGSsUTMFEetUQakD8=.pem", - "location": "security-state-staging/intermediates/c052ede4-8c2e-4eed-97b4-5d3e127c78f8.pem", - "mimetype": "application/x-pem-file" - }, - "pubKeyHash": "1FBqLyRsP8ibxXXsW64LYWGTeYMGSsUTMFEetUQakD8=", - "crlite_enrolled": true, - "id": "461724c9-5ae7-4021-be3d-b5c081e3b8d7", - "last_modified": 1562025500918 - }, - { "schema": 1562025497127, "derHash": "/CJFvlncZGHUEZw6Bu2+5NKIVWvYjEeeMO1fPoFhZGk=", "subject": "CN=MPG CA - G02,O=Max-Planck-Gesellschaft,L=Muenchen,ST=Bayern,C=DE", @@ -42373,24 +42427,6 @@ "last_modified": 1562025378352 }, { - "schema": 1562025376125, - "derHash": "lItxEa9C9UbVec/1ziveyCE03ZkUhCvdsMUocutgTjk=", - "subject": "CN=SSL.com SSL Intermediate CA ECC R2,O=SSL Corp,L=Houston,ST=Texas,C=US", - "subjectDN": "MG8xCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjERMA8GA1UECgwIU1NMIENvcnAxKzApBgNVBAMMIlNTTC5jb20gU1NMIEludGVybWVkaWF0ZSBDQSBFQ0MgUjI=", - "whitelist": false, - "attachment": { - "hash": "29138ba36283c37133babfb45ad6da91b908056c4bc777229a9b6114c29bb9d7", - "size": 1264, - "filename": "zGgA4OU4DjJdvpRYUqbi5Vh2g9W5Oc_PgKihy9mkLsE=.pem", - "location": "security-state-staging/intermediates/583bc538-020a-40af-92a9-40c3653a90e8.pem", - "mimetype": "application/x-pem-file" - }, - "pubKeyHash": "zGgA4OU4DjJdvpRYUqbi5Vh2g9W5Oc/PgKihy9mkLsE=", - "crlite_enrolled": true, - "id": "a0acdb7c-e477-4e0c-8f54-b838e76497f5", - "last_modified": 1562025376857 - }, - { "schema": 1562025375368, "derHash": "O8UYVgQK1/9mg6qFoNNPnqaAzSPDfLigQjsPiaJEBbk=", "subject": "CN=ACCVCA-120,OU=PKIACCV,O=ACCV,C=ES", @@ -48169,24 +48205,6 @@ "last_modified": 1562024771677 }, { - "schema": 1562024767927, - "derHash": "UnpgsCq/OkpVGcT2L7vVYOMDQHTu7IuHmaqTaGk/420=", - "subject": "CN=SSL.com RSA SSL subCA,O=SSL Corporation,L=Houston,ST=Texas,C=US", - "subjectDN": "MGkxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMR4wHAYDVQQDDBVTU0wuY29tIFJTQSBTU0wgc3ViQ0E=", - "whitelist": false, - "attachment": { - "hash": "19883ec948ba65cabab87774d50633120945ef053a62f8362d2e09b43597fe1a", - "size": 2292, - "filename": "7LcB5Z8ATVz4rcQtIY5xJir8_-F3e_HPi8IDdCnjCaE=.pem", - "location": "security-state-staging/intermediates/23914bfd-6f12-4a9d-8c1a-a2c9e445a1bf.pem", - "mimetype": "application/x-pem-file" - }, - "pubKeyHash": "7LcB5Z8ATVz4rcQtIY5xJir8/+F3e/HPi8IDdCnjCaE=", - "crlite_enrolled": true, - "id": "b976a3a6-3c96-4aca-9c5f-fc654e3c4f6c", - "last_modified": 1562024768658 - }, - { "schema": 1562024767188, "derHash": "cx09nPqgYUh6HXFEWkL2ffCvyipsLS+Y/3s84RKx9Wg=", "subject": "CN=Let's Encrypt Authority X3,O=Let's Encrypt,C=US", @@ -50509,24 +50527,6 @@ "last_modified": 1562023920713 }, { - "schema": 1562023919206, - "derHash": "zJ9yVE/2F8FM6+goMZSnDx6Jgc8WPVo/2NzP2EbV3Bo=", - "subject": "CN=NetLock OnlineSSL (Class Online) Tanúsítványkiadó,OU=Tanúsítványkiadók (Certification Services),O=NetLock Kft.,L=Budapest,C=HU", - "subjectDN": "MIGwMQswCQYDVQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzKTE+MDwGA1UEAww1TmV0TG9jayBPbmxpbmVTU0wgKENsYXNzIE9ubGluZSkgVGFuw7pzw610dsOhbnlraWFkw7M=", - "whitelist": false, - "attachment": { - "hash": "72ae8f0a0087c7b13f79d2f02193b9bca142172f3053cad4b927af716cd0534a", - "size": 2190, - "filename": "He6eOdoX1CHCLhtg75AtdDdI18-x-VfWOYW5_LCt5kk=.pem", - "location": "security-state-staging/intermediates/6bab6451-8f63-49f3-a556-455c6b868bd1.pem", - "mimetype": "application/x-pem-file" - }, - "pubKeyHash": "He6eOdoX1CHCLhtg75AtdDdI18+x+VfWOYW5/LCt5kk=", - "crlite_enrolled": true, - "id": "ca8f61c9-fe67-45a0-8bc2-f0a8348e2cdb", - "last_modified": 1562023919950 - }, - { "schema": 1562023917707, "derHash": "2GoYtfdpUrc26Jots/D9+QcHEUYBFLagjWScpieTXJY=", "subject": "CN=COMODO SHA-256 Client Authentication and Secure Email CA 2,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB", diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/services/sync/modules/engines/bookmarks.js firefox-trunk-95.0~a1~hg20211020r596404/services/sync/modules/engines/bookmarks.js --- firefox-trunk-95.0~a1~hg20211017r596111/services/sync/modules/engines/bookmarks.js 2021-10-17 14:42:50.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/services/sync/modules/engines/bookmarks.js 2021-10-20 19:28:36.000000000 +0000 @@ -809,6 +809,7 @@ "bookmark-added", "bookmark-removed", "bookmark-moved", + "bookmark-tags-changed", "bookmark-time-changed", "bookmark-title-changed", "bookmark-url-changed", @@ -827,6 +828,7 @@ "bookmark-added", "bookmark-removed", "bookmark-moved", + "bookmark-tags-changed", "bookmark-time-changed", "bookmark-title-changed", "bookmark-url-changed", @@ -886,6 +888,7 @@ case "bookmark-removed": case "bookmark-moved": case "bookmark-guid-changed": + case "bookmark-tags-changed": case "bookmark-time-changed": case "bookmark-title-changed": case "bookmark-url-changed": diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/servo/components/style/custom_properties.rs firefox-trunk-95.0~a1~hg20211020r596404/servo/components/style/custom_properties.rs --- firefox-trunk-95.0~a1~hg20211017r596111/servo/components/style/custom_properties.rs 2021-10-17 14:42:50.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/servo/components/style/custom_properties.rs 2021-10-20 19:28:36.000000000 +0000 @@ -71,10 +71,24 @@ make_variable!(atom!("safe-area-inset-right"), get_safearea_inset_right), ]; +fn get_titlebar_radius(device: &Device) -> VariableValue { + VariableValue::pixel(device.titlebar_radius()) +} + +static CHROME_ENVIRONMENT_VARIABLES: [EnvironmentVariable; 1] = [ + make_variable!(atom!("-moz-gtk-csd-titlebar-radius"), get_titlebar_radius), +]; + impl CssEnvironment { #[inline] fn get(&self, name: &Atom, device: &Device) -> Option { - let var = ENVIRONMENT_VARIABLES.iter().find(|var| var.name == *name)?; + if let Some(var) = ENVIRONMENT_VARIABLES.iter().find(|var| var.name == *name) { + return Some((var.evaluator)(device)); + } + if !device.is_chrome_document() { + return None; + } + let var = CHROME_ENVIRONMENT_VARIABLES.iter().find(|var| var.name == *name)?; Some((var.evaluator)(device)) } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/servo/components/style/gecko/media_features.rs firefox-trunk-95.0~a1~hg20211020r596404/servo/components/style/gecko/media_features.rs --- firefox-trunk-95.0~a1~hg20211017r596111/servo/components/style/gecko/media_features.rs 2021-10-17 14:42:51.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/servo/components/style/gecko/media_features.rs 2021-10-20 19:28:35.000000000 +0000 @@ -651,7 +651,7 @@ /// to support new types in these entries and (2) ensuring that either /// nsPresContext::MediaFeatureValuesChanged is called when the value that /// would be returned by the evaluator function could change. -pub static MEDIA_FEATURES: [MediaFeatureDescription; 60] = [ +pub static MEDIA_FEATURES: [MediaFeatureDescription; 59] = [ feature!( atom!("width"), AllowsRanges::Yes, @@ -891,7 +891,6 @@ lnf_int_feature!(atom!("-moz-swipe-animation-enabled"), SwipeAnimationEnabled), lnf_int_feature!(atom!("-moz-gtk-csd-available"), GTKCSDAvailable), lnf_int_feature!(atom!("-moz-gtk-csd-hide-titlebar-by-default"), GTKCSDHideTitlebarByDefault), - lnf_int_feature!(atom!("-moz-gtk-csd-transparent-background"), GTKCSDTransparentBackground), lnf_int_feature!(atom!("-moz-gtk-csd-minimize-button"), GTKCSDMinimizeButton), lnf_int_feature!(atom!("-moz-gtk-csd-maximize-button"), GTKCSDMaximizeButton), lnf_int_feature!(atom!("-moz-gtk-csd-close-button"), GTKCSDCloseButton), diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/servo/components/style/gecko/media_queries.rs firefox-trunk-95.0~a1~hg20211020r596404/servo/components/style/gecko/media_queries.rs --- firefox-trunk-95.0~a1~hg20211017r596111/servo/components/style/gecko/media_queries.rs 2021-10-17 14:42:51.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/servo/components/style/gecko/media_queries.rs 2021-10-20 19:28:35.000000000 +0000 @@ -367,4 +367,17 @@ bindings::Gecko_IsSupportedImageMimeType(mime_type.as_ptr(), mime_type.len() as u32) } } + + /// Returns the gtk titlebar radius in CSS pixels. + pub fn titlebar_radius(&self) -> f32 { + unsafe { + bindings::Gecko_GetLookAndFeelFloat(bindings::LookAndFeel_FloatID::TitlebarRadius as i32) + } + } + + /// Return whether the document is a chrome document. + #[inline] + pub fn is_chrome_document(&self) -> bool { + self.pref_sheet_prefs().mIsChrome + } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/SOURCE_CHANGESET firefox-trunk-95.0~a1~hg20211020r596404/SOURCE_CHANGESET --- firefox-trunk-95.0~a1~hg20211017r596111/SOURCE_CHANGESET 2021-10-17 14:43:07.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/SOURCE_CHANGESET 2021-10-20 19:28:55.000000000 +0000 @@ -1 +1 @@ -650d84a601ba81d47e31d8fcb66a6d84642ae00d \ No newline at end of file +e45ba61007d1f8771179c0cc258166930acd75a5 \ No newline at end of file diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/taskcluster/ci/config.yml firefox-trunk-95.0~a1~hg20211020r596404/taskcluster/ci/config.yml --- firefox-trunk-95.0~a1~hg20211017r596111/taskcluster/ci/config.yml 2021-10-17 14:42:50.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/taskcluster/ci/config.yml 2021-10-20 19:28:36.000000000 +0000 @@ -65,6 +65,7 @@ 'M-f-spi-nw-1proc': 'Mochitest failures with networking on socket process without e10s' 'M-f-swr': 'Mochitests failures with software webrender enabled' 'M-f-swr-1proc': 'Mochitests failures with software webrender enabled without e10s' + 'M-f-wayland': 'Mochitests failures with Wayland backend enabled' 'MSI': 'Repack installers into MSIs' 'MSIs': 'Signing of Repacked installers of MSIs' 'MSIX': 'Repack into MSIX package' @@ -587,7 +588,7 @@ implementation: generic-worker os: windows worker-type: 'gecko-{alias}' - win10-64-2004(|-gpu): + win10-64-2004(|-gpu|-ssd): provisioner: 'gecko-t' implementation: generic-worker os: windows diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/taskcluster/ci/packages/debian.yml firefox-trunk-95.0~a1~hg20211020r596404/taskcluster/ci/packages/debian.yml --- firefox-trunk-95.0~a1~hg20211017r596111/taskcluster/ci/packages/debian.yml 2021-10-17 14:42:51.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/taskcluster/ci/packages/debian.yml 2021-10-20 19:28:35.000000000 +0000 @@ -55,9 +55,9 @@ run: dist: buster tarball: - url: https://github.com/indygreg/python-zstandard/releases/download/0.15.2/zstandard-0.15.2.tar.gz - sha256: 52de08355fd5cfb3ef4533891092bb96229d43c2069703d4aff04fdbedf9c92f - pre-build-command: debchange -v 0.15.2-1.deb10moz --distribution buster "Mozilla backport for buster" + url: https://github.com/indygreg/python-zstandard/releases/download/0.16.0/zstandard-0.16.0.tar.gz + sha256: eaae2d3e8fdf8bfe269628385087e4b648beef85bb0c187644e7df4fb0fe9046 + pre-build-command: debchange -v 0.16.0-1.deb10moz --distribution buster "Mozilla backport for buster" name: python-zstandard deb11-python-zstandard: @@ -68,9 +68,9 @@ using: debian-package dist: bullseye tarball: - url: https://github.com/indygreg/python-zstandard/releases/download/0.15.2/zstandard-0.15.2.tar.gz - sha256: 52de08355fd5cfb3ef4533891092bb96229d43c2069703d4aff04fdbedf9c92f - pre-build-command: debchange -v 0.15.2-1.deb11moz --distribution bullseye "Mozilla backport for buster" + url: https://github.com/indygreg/python-zstandard/releases/download/0.16.0/zstandard-0.16.0.tar.gz + sha256: eaae2d3e8fdf8bfe269628385087e4b648beef85bb0c187644e7df4fb0fe9046 + pre-build-command: debchange -v 0.16.0-1.deb11moz --distribution bullseye "Mozilla backport for buster" name: python-zstandard deb8-gcc-7: diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/taskcluster/ci/packages/ubuntu.yml firefox-trunk-95.0~a1~hg20211020r596404/taskcluster/ci/packages/ubuntu.yml --- firefox-trunk-95.0~a1~hg20211017r596111/taskcluster/ci/packages/ubuntu.yml 2021-10-17 14:42:50.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/taskcluster/ci/packages/ubuntu.yml 2021-10-20 19:28:36.000000000 +0000 @@ -34,10 +34,10 @@ symbol: Ub18(python-zstandard) run: tarball: - url: https://github.com/indygreg/python-zstandard/releases/download/0.15.2/zstandard-0.15.2.tar.gz - sha256: 52de08355fd5cfb3ef4533891092bb96229d43c2069703d4aff04fdbedf9c92f + url: https://github.com/indygreg/python-zstandard/releases/download/0.16.0/zstandard-0.16.0.tar.gz + sha256: eaae2d3e8fdf8bfe269628385087e4b648beef85bb0c187644e7df4fb0fe9046 name: python-zstandard - pre-build-command: debchange -v 0.15.2-1.ub18moz --distribution bionic "Mozilla backport for bionic" + pre-build-command: debchange -v 0.16.0-1.ub18moz --distribution bionic "Mozilla backport for bionic" ub18-mercurial: description: "Modern Mercurial for Ubuntu bionic" @@ -59,11 +59,11 @@ symbol: Ub20(python-zstandard) run: tarball: - url: https://github.com/indygreg/python-zstandard/releases/download/0.15.2/zstandard-0.15.2.tar.gz - sha256: 52de08355fd5cfb3ef4533891092bb96229d43c2069703d4aff04fdbedf9c92f + url: https://github.com/indygreg/python-zstandard/releases/download/0.16.0/zstandard-0.16.0.tar.gz + sha256: eaae2d3e8fdf8bfe269628385087e4b648beef85bb0c187644e7df4fb0fe9046 name: python-zstandard dist: focal - pre-build-command: debchange -v 0.15.2-1.ub20moz --distribution bionic "Mozilla backport for focal" + pre-build-command: debchange -v 0.16.0-1.ub20moz --distribution bionic "Mozilla backport for focal" ub20-mercurial: description: "Modern Mercurial for Ubuntu focal" diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/taskcluster/ci/test/mochitest.yml firefox-trunk-95.0~a1~hg20211020r596404/taskcluster/ci/test/mochitest.yml --- firefox-trunk-95.0~a1~hg20211017r596111/taskcluster/ci/test/mochitest.yml 2021-10-17 14:42:50.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/taskcluster/ci/test/mochitest.yml 2021-10-20 19:28:36.000000000 +0000 @@ -10,12 +10,6 @@ android-em-7.*: geckoview-androidTest.apk android-hw.*: geckoview-androidTest.apk default: null - variants: - by-test-platform: - linux.*64/debug: ['fission', 'wayland', 'socketprocess_networking'] - linux.*64-qr/debug: ['webrender-sw', 'fission', 'wayland', 'socketprocess_networking'] - android-em-7.0-x86_64-qr/debug: ['webrender-sw', 'fission'] - default: ['fission'] tier: by-variant: fission(-xorigin)?: @@ -55,12 +49,12 @@ schedules-component: mochitest-plain loopback-video: true virtualization: virtual - variants: - by-test-platform: - linux1804-64-qr/debug: ['webrender-sw', 'fission', 'fission-xorigin', 'wayland', 'socketprocess_networking'] - android-em-7.0-x86_64-qr/debug: ['webrender-sw', 'fission', 'fission-xorigin'] - default: ['fission', 'fission-xorigin'] + - fission + - fission-xorigin + - socketprocess_networking + - wayland + - webrender-sw run-on-projects: by-variant: fission: @@ -76,6 +70,19 @@ (linux.*64|macosx.*64|windows10-64-2004)(-shippable)?-qr/(opt|debug): ['trunk'] linux.*64-ccov-qr/opt: ['mozilla-central'] default: [] + socketprocess_networking: + by-test-platform: + linux1804-64-qr/debug: built-projects + default: [] + wayland: + by-test-platform: + linux1804-64-qr/debug: built-projects + default: [] + webrender-sw: + by-test-platform: + android-em-7.0-x86_64-qr/debug: built-projects + linux1804-64-qr/debug: built-projects + default: [] default: by-test-platform: .*-tsan-qr/opt: ['trunk'] @@ -118,10 +125,30 @@ test-manifest-loader: default # ensure we don't run with manifest-scheduling loopback-video: true e10s: false + variants: + - fission + - socketprocess_networking + - wayland + - webrender-sw run-on-projects: - by-test-platform: - .*-tsan-qr/opt: ['trunk'] - default: built-projects + by-variant: + socketprocess_networking: + by-test-platform: + linux.*64-qr/debug: built-projects + default: [] + wayland: + by-test-platform: + linux.*64-qr/debug: built-projects + default: [] + webrender-sw: + by-test-platform: + android-em-7.0-x86_64-qr/debug: built-projects + linux.*64-qr/debug: built-projects + default: [] + default: + by-test-platform: + .*-tsan-qr/opt: ['trunk'] + default: built-projects mozharness: mochitest-flavor: a11y @@ -133,12 +160,11 @@ schedules-component: mochitest-browser-chrome loopback-video: true variants: - by-test-platform: - linux.*64(-shippable)?-qr/opt: ['webrender-sw', 'webrender-sw-fission', 'webrender-sw-a11y-checks', 'webrender-sw-wayland'] - linux.*64(-asan|-ccov|-devedition|-tsan)-qr/opt: ['webrender-sw', 'webrender-sw-fission'] - linux.*64-qr/debug: ['webrender-sw', 'webrender-sw-fission', 'webrender-sw-wayland', 'socketprocess_networking'] - windows10-64-2004-qr/debug: ['webrender-sw', 'fission'] - default: ['fission'] + - fission + - webrender-sw + - webrender-sw-a11y-checks + - webrender-sw-fission + - webrender-sw-wayland run-on-projects: by-variant: fission: @@ -150,13 +176,17 @@ linux.*64(-asan)?-qr/(opt|debug): ['all'] linux.*64-tsan-qr/opt: ['trunk'] linux.*64-shippable-qr/opt: ['release'] - linux.*64-ccov-qr/opt: [] - default: built-projects + linux.*64(-shippable)?-qr/opt: built-projects + linux.*64-devedition-qr/opt: built-projects + windows10-64-2004-qr/debug: built-projects + default: [] + webrender-sw-a11y-checks: [] # run-on-projects defined in variant config webrender-sw-fission: by-test-platform: linux.*64(-asan|-shippable)?-qr/(opt|debug): ['trunk'] linux.*64-ccov-qr/opt: ['mozilla-central'] default: [] + webrender-sw-wayland: [] default: by-test-platform: linux.*-qr/.*: [] @@ -218,7 +248,27 @@ chunks: 2 max-run-time: 2700 e10s: true - run-on-projects: ['mozilla-central'] + variants: + - fission + - socketprocess_networking + - wayland + - webrender-sw + run-on-projects: + by-variant: + socketprocess_networking: + by-test-platform: + linux.*64-qr/debug: built-projects + default: [] + wayland: + by-test-platform: + linux.*64-qr/debug: built-projects + default: [] + webrender-sw: + by-test-platform: + android-em-7.0-x86_64-qr/debug: built-projects + linux.*64-qr/debug: built-projects + default: [] + default: ['mozilla-central'] mozharness: mochitest-flavor: browser chunked: true @@ -244,6 +294,12 @@ treeherder-symbol: M(ss) loopback-video: true test-manifest-loader: null # don't load tests in the taskgraph + variants: + by-test-platform: + linux.*64/debug: ['fission', 'wayland', 'socketprocess_networking'] + linux.*64-qr/debug: ['webrender-sw', 'fission', 'wayland', 'socketprocess_networking'] + android-em-7.0-x86_64-qr/debug: ['webrender-sw', 'fission'] + default: ['fission'] run-on-projects: by-variant: fission: [] @@ -265,10 +321,30 @@ treeherder-symbol: M(c) schedules-component: mochitest-chrome loopback-video: true + variants: + - fission + - socketprocess_networking + - wayland + - webrender-sw run-on-projects: - by-test-platform: - .*-tsan-qr/opt: ['trunk'] - default: built-projects + by-variant: + socketprocess_networking: + by-test-platform: + linux.*64-qr/debug: built-projects + default: [] + wayland: + by-test-platform: + linux.*64-qr/debug: built-projects + default: [] + webrender-sw: + by-test-platform: + android-em-7.0-x86_64-qr/debug: built-projects + linux.*64-qr/debug: built-projects + default: [] + default: + by-test-platform: + .*-tsan-qr/opt: ['trunk'] + default: built-projects chunks: by-test-platform: .*(-ccov|-qr).*/.*: 3 @@ -295,18 +371,36 @@ treeherder-symbol: M(dt) loopback-video: true variants: - by-test-platform: - linux.*64(-shippable)?-qr/opt: ['fission', 'a11y-checks', 'wayland'] - linux.*64-qr/debug: ['webrender-sw', 'fission', 'wayland', 'socketprocess_networking'] - default: ['fission'] + - a11y-checks + - fission + - socketprocess_networking + - wayland + - webrender-sw run-on-projects: by-variant: + a11y-checks: + by-test-platform: + linux.*64(-shippable)?-qr/opt: built-projects + default: [] fission: by-test-platform: linux.*64(-shippable)?-qr/(opt|debug): ['trunk'] windows10-64-2004(-shippable)?-qr/(opt|debug): ['trunk'] linux.*64-ccov-qr/opt: ["mozilla-central"] default: [] + socketprocess_networking: + by-test-platform: + linux.*64-qr/debug: built-projects + default: [] + wayland: + by-test-platform: + linux.*64-ccov-qr/opt: [] + linux.*64(-shippable)?-qr/(opt|debug): built-projects + default: [] + webrender-sw: + by-test-platform: + linux.*64-qr/debug: built-projects + default: [] default: by-test-platform: .*-tsan-qr/opt: ['trunk'] @@ -352,6 +446,12 @@ treeherder-symbol: M(gpu) schedules-component: mochitest-plain loopback-video: true + variants: + by-test-platform: + linux.*64/debug: ['fission', 'wayland', 'socketprocess_networking'] + linux.*64-qr/debug: ['webrender-sw', 'fission', 'wayland', 'socketprocess_networking'] + android-em-7.0-x86_64-qr/debug: ['webrender-sw', 'fission'] + default: ['fission'] run-on-projects: by-variant: fission: [] @@ -391,6 +491,12 @@ loopback-video: true virtualization: virtual-with-gpu e10s: true + variants: + by-test-platform: + linux.*64/debug: ['fission', 'wayland', 'socketprocess_networking'] + linux.*64-qr/debug: ['webrender-sw', 'fission', 'wayland', 'socketprocess_networking'] + android-em-7.0-x86_64-qr/debug: ['webrender-sw', 'fission'] + default: ['fission'] run-on-projects: by-variant: fission: [] @@ -427,6 +533,13 @@ macosx.*64-ccov.*/.*: 7200 linux.*64-tsan-qr/opt: 7200 default: 5400 + variants: + - fission + - fission-webgl-ipc + - socketprocess + - wayland + - webgl-ipc + - webrender-sw run-on-projects: by-variant: fission: @@ -446,21 +559,30 @@ default: [] socketprocess: by-test-platform: - .*-tsan-qr/opt: ['trunk'] - windows10-64-2004-asan-qr/opt: ['trunk'] - android-hw-.*(? for SwitchToWindowParameters { fn to_marionette(&self) -> WebDriverResult { Ok(Window { - name: self.handle.clone(), handle: self.handle.clone(), }) } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/testing/mochitest/dynamic/getMyDirectory.sjs firefox-trunk-95.0~a1~hg20211020r596404/testing/mochitest/dynamic/getMyDirectory.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/testing/mochitest/dynamic/getMyDirectory.sjs 2021-10-17 14:42:50.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/testing/mochitest/dynamic/getMyDirectory.sjs 2021-10-20 19:28:36.000000000 +0000 @@ -1,8 +1,6 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { var file; - getObjectState("SERVER_ROOT", function(serverRoot) - { + getObjectState("SERVER_ROOT", function(serverRoot) { var ref = request.getHeader("Referer").split("?")[0]; // 8 is "https://".length which is the longest string before the host. var pathStart = ref.indexOf("/", 8) + 1; @@ -11,5 +9,5 @@ }); response.setHeader("Content-Type", "text/plain", false); - response.write(file.path.substr(0, file.path.length-1)); + response.write(file.path.substr(0, file.path.length - 1)); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/testing/perfdocs/generated/raptor.rst firefox-trunk-95.0~a1~hg20211020r596404/testing/perfdocs/generated/raptor.rst --- firefox-trunk-95.0~a1~hg20211017r596111/testing/perfdocs/generated/raptor.rst 2021-10-17 14:42:51.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/testing/perfdocs/generated/raptor.rst 2021-10-20 19:28:35.000000000 +0000 @@ -1950,6 +1950,7 @@ * **playback**: mitmproxy * **playback pageset manifest**: mitm6-linux-firefox-cnn.manifest * **playback version**: 6.0.2 + * **preferences**: {"media.autoplay.default": 5, "media.autoplay.ask-permission": true, "media.autoplay.blocking_policy": 1, "media.autoplay.block-webaudio": true, "media.allowed-to-play.enabled": false, "media.block-autoplay-until-in-foreground": true} * **secondary url**: ``__ * **test url**: ``__ * **type**: pageload @@ -3311,9 +3312,10 @@ * **page cycles**: 25 * **page timeout**: 60000 * **playback**: mitmproxy - * **playback pageset manifest**: mitm5-linux-firefox-live-office.manifest - * **playback version**: 5.1.1 - * **test url**: ``__ + * **playback pageset manifest**: mitm6-linux-firefox-live-office.manifest + * **playback version**: 6.0.2 + * **secondary url**: ``__ + * **test url**: ``__ * **type**: pageload * **unit**: ms * **use live sites**: false diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/testing/profiles/reftest/user.js firefox-trunk-95.0~a1~hg20211020r596404/testing/profiles/reftest/user.js --- firefox-trunk-95.0~a1~hg20211017r596111/testing/profiles/reftest/user.js 2021-10-17 14:42:51.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/testing/profiles/reftest/user.js 2021-10-20 19:28:36.000000000 +0000 @@ -98,3 +98,5 @@ user_pref("toolkit.telemetry.initDelay", 99999999); // Setting this pref to true for usercss reftests, since it relies on userContent.css user_pref("toolkit.legacyUserProfileCustomizations.stylesheets", true); +// Use a light color-scheme unless explicitly overriden. +user_pref("layout.css.prefers-color-scheme.content-override", 1); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/testing/profiles/web-platform/user.js firefox-trunk-95.0~a1~hg20211020r596404/testing/profiles/web-platform/user.js --- firefox-trunk-95.0~a1~hg20211017r596111/testing/profiles/web-platform/user.js 2021-10-17 14:42:50.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/testing/profiles/web-platform/user.js 2021-10-20 19:28:37.000000000 +0000 @@ -67,3 +67,5 @@ // Enable blocking access to storage from tracking resources by default. // We don't want to run WPT using BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN (5 - aka Dynamic First Party Isolation) yet. user_pref("network.cookie.cookieBehavior", 4); +// Force a light color scheme unless explicitly overriden by pref. +user_pref("layout.css.prefers-color-scheme.content-override", 1); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/testing/raptor/raptor/tests/tp6/desktop/browsertime-tp6.ini firefox-trunk-95.0~a1~hg20211020r596404/testing/raptor/raptor/tests/tp6/desktop/browsertime-tp6.ini --- firefox-trunk-95.0~a1~hg20211017r596111/testing/raptor/raptor/tests/tp6/desktop/browsertime-tp6.ini 2021-10-17 14:42:51.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/testing/raptor/raptor/tests/tp6/desktop/browsertime-tp6.ini 2021-10-20 19:28:35.000000000 +0000 @@ -47,6 +47,12 @@ secondary_url = https://www.cnn.com/weather playback_version = 6.0.2 playback_pageset_manifest = mitm6-linux-firefox-cnn.manifest +preferences = {"media.autoplay.default": 5, + "media.autoplay.ask-permission": true, + "media.autoplay.blocking_policy": 1, + "media.autoplay.block-webaudio": true, + "media.allowed-to-play.enabled": false, + "media.block-autoplay-until-in-foreground": true} [ebay] test_url = https://www.ebay.com/ @@ -130,8 +136,10 @@ playback_pageset_manifest = mitm6-linux-firefox-nytimes.manifest [office] -test_url = https://office.live.com/start/Word.aspx?omkt=en-US -playback_pageset_manifest = mitm5-linux-firefox-live-office.manifest +test_url = https://www.office.com/launch/word +secondary_url = https://www.office.com/ +playback_version = 6.0.2 +playback_pageset_manifest = mitm6-linux-firefox-live-office.manifest [outlook] test_url = https://outlook.live.com/mail/inbox diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/testing/raptor/raptor/tooltool-manifests/playback/mitm5-linux-firefox-live-office.manifest firefox-trunk-95.0~a1~hg20211020r596404/testing/raptor/raptor/tooltool-manifests/playback/mitm5-linux-firefox-live-office.manifest --- firefox-trunk-95.0~a1~hg20211017r596111/testing/raptor/raptor/tooltool-manifests/playback/mitm5-linux-firefox-live-office.manifest 2021-10-17 14:42:51.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/testing/raptor/raptor/tooltool-manifests/playback/mitm5-linux-firefox-live-office.manifest 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -[ - { - "algorithm": "sha512", - "digest": "36cd7c235b116a39a11082fe14b89434aace368cc7304654852e2f077ac9b024f7c663ff6f81c5012aca4fcf04d4b0f96e5abd1d5bb3f3a1f7ae0113ddf75c01", - "filename": "mitm5-linux-firefox-live-office.zip", - "size": 11077445, - "visibility": "public" - } -] diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/testing/raptor/raptor/tooltool-manifests/playback/mitm6-linux-firefox-cnn.manifest firefox-trunk-95.0~a1~hg20211020r596404/testing/raptor/raptor/tooltool-manifests/playback/mitm6-linux-firefox-cnn.manifest --- firefox-trunk-95.0~a1~hg20211017r596111/testing/raptor/raptor/tooltool-manifests/playback/mitm6-linux-firefox-cnn.manifest 2021-10-17 14:42:51.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/testing/raptor/raptor/tooltool-manifests/playback/mitm6-linux-firefox-cnn.manifest 2021-10-20 19:28:35.000000000 +0000 @@ -1,9 +1,9 @@ [ { - "filename": "mitm6-linux-firefox-cnn.zip", - "size": 51994767, + "size": 52350354, + "visibility": "public", + "digest": "0992fb48eec8a53d4af213f5c5015af5de34dfbb057fecf223d7202f19599e1eb41a58257932f616493d100e6b75da2cdc7745eb673ed6b7579953ba3c4eb879", "algorithm": "sha512", - "digest": "0bfe6cbae34151fbfbf12185dedb15f9c00f7b026188e5797a94fb547c25b2cc5356367623368aac2442e956b8c131ee184b88a41365b34552737ee1ae6f980b", - "visibility": "public" + "filename": "mitm6-linux-firefox-cnn.zip" } ] diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/testing/raptor/raptor/tooltool-manifests/playback/mitm6-linux-firefox-live-office.manifest firefox-trunk-95.0~a1~hg20211020r596404/testing/raptor/raptor/tooltool-manifests/playback/mitm6-linux-firefox-live-office.manifest --- firefox-trunk-95.0~a1~hg20211017r596111/testing/raptor/raptor/tooltool-manifests/playback/mitm6-linux-firefox-live-office.manifest 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/testing/raptor/raptor/tooltool-manifests/playback/mitm6-linux-firefox-live-office.manifest 2021-10-20 19:28:35.000000000 +0000 @@ -0,0 +1,9 @@ +[ + { + "size": 12847002, + "visibility": "public", + "digest": "b73d097a1a8956ea22277d65565842e1a32f53a947f95ec09bf39cd80638bbeb66babdd8ff5a0f58a116c24b911e0f4e72472104a2eeafd42231515119d62a4f", + "algorithm": "sha512", + "filename": "mitm6-linux-firefox-office.zip" + } +] diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/testing/specialpowers/content/SpecialPowersParent.jsm firefox-trunk-95.0~a1~hg20211020r596404/testing/specialpowers/content/SpecialPowersParent.jsm --- firefox-trunk-95.0~a1~hg20211017r596111/testing/specialpowers/content/SpecialPowersParent.jsm 2021-10-17 14:42:50.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/testing/specialpowers/content/SpecialPowersParent.jsm 2021-10-20 19:28:37.000000000 +0000 @@ -412,6 +412,7 @@ for (let pref of actions) { requiresRefresh = requiresRefresh || + pref.name == "layout.css.prefers-color-scheme.content-override" || pref.name.startsWith("ui.") || pref.name.startsWith("browser.display.") || pref.name.startsWith("font."); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/css/css-text/i18n/css3-text-line-break-baspglwj-082.html.ini firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/css/css-text/i18n/css3-text-line-break-baspglwj-082.html.ini --- firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/css/css-text/i18n/css3-text-line-break-baspglwj-082.html.ini 2021-10-17 14:42:52.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/css/css-text/i18n/css3-text-line-break-baspglwj-082.html.ini 2021-10-20 19:28:36.000000000 +0000 @@ -2,19 +2,23 @@ [white-space:pre-wrap] expected: if os == "android": PASS + if os == "win": PASS FAIL [white-space:pre-line] expected: if os == "android": PASS + if os == "win": PASS FAIL [white-space:break-spaces] expected: if os == "android": PASS + if os == "win": PASS FAIL [white-space:normal] expected: if os == "android": PASS + if os == "win": PASS FAIL diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/css/css-text/i18n/css3-text-line-break-baspglwj-126.html.ini firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/css/css-text/i18n/css3-text-line-break-baspglwj-126.html.ini --- firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/css/css-text/i18n/css3-text-line-break-baspglwj-126.html.ini 2021-10-17 14:42:51.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/css/css-text/i18n/css3-text-line-break-baspglwj-126.html.ini 2021-10-20 19:28:37.000000000 +0000 @@ -2,20 +2,24 @@ [white-space:pre-wrap] expected: if os == "android": PASS + if os == "win": PASS FAIL [white-space:pre-line] expected: if os == "android": PASS + if os == "win": PASS FAIL [white-space:break-spaces] expected: if os == "android": PASS + if os == "win": PASS FAIL [white-space:normal] expected: if os == "android": PASS + if os == "win": PASS FAIL diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/css/css-text/i18n/css3-text-line-break-baspglwj-127.html.ini firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/css/css-text/i18n/css3-text-line-break-baspglwj-127.html.ini --- firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/css/css-text/i18n/css3-text-line-break-baspglwj-127.html.ini 2021-10-17 14:42:51.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/css/css-text/i18n/css3-text-line-break-baspglwj-127.html.ini 2021-10-20 19:28:37.000000000 +0000 @@ -2,20 +2,24 @@ [white-space:pre-wrap] expected: if os == "android": PASS + if os == "win": PASS FAIL [white-space:pre-line] expected: if os == "android": PASS + if os == "win": PASS FAIL [white-space:break-spaces] expected: if os == "android": PASS + if os == "win": PASS FAIL [white-space:normal] expected: if os == "android": PASS + if os == "win": PASS FAIL diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/css/css-text/i18n/css3-text-line-break-baspglwj-128.html.ini firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/css/css-text/i18n/css3-text-line-break-baspglwj-128.html.ini --- firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/css/css-text/i18n/css3-text-line-break-baspglwj-128.html.ini 2021-10-17 14:42:51.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/css/css-text/i18n/css3-text-line-break-baspglwj-128.html.ini 2021-10-20 19:28:37.000000000 +0000 @@ -2,20 +2,24 @@ [white-space:pre-wrap] expected: if os == "android": PASS + if os == "win": PASS FAIL [white-space:pre-line] expected: if os == "android": PASS + if os == "win": PASS FAIL [white-space:break-spaces] expected: if os == "android": PASS + if os == "win": PASS FAIL [white-space:normal] expected: if os == "android": PASS + if os == "win": PASS FAIL diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/css/css-text/word-break/word-break-normal-bo-000.html.ini firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/css/css-text/word-break/word-break-normal-bo-000.html.ini --- firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/css/css-text/word-break/word-break-normal-bo-000.html.ini 2021-10-17 14:42:51.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/css/css-text/word-break/word-break-normal-bo-000.html.ini 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -[word-break-normal-bo-000.html] - expected: - if (os == "win"): FAIL diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/mathml/relations/css-styling/color-001.html.ini firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/mathml/relations/css-styling/color-001.html.ini --- firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/mathml/relations/css-styling/color-001.html.ini 2021-10-17 14:42:52.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/mathml/relations/css-styling/color-001.html.ini 2021-10-20 19:28:37.000000000 +0000 @@ -1,4 +1,3 @@ [color-001.html] expected: if webrender and not swgl and (os == "win") and (bits == 64) and not ccov: FAIL - if win10_2004 and ccov: FAIL # Bug 1733368 diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/mathml/relations/css-styling/color-002.html.ini firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/mathml/relations/css-styling/color-002.html.ini --- firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/mathml/relations/css-styling/color-002.html.ini 2021-10-17 14:42:52.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/mathml/relations/css-styling/color-002.html.ini 2021-10-20 19:28:36.000000000 +0000 @@ -1,4 +1,3 @@ [color-002.html] expected: if webrender and not swgl and (os == "win") and (bits == 64) and not ccov: FAIL - if win10_2004 and ccov: FAIL # Bug 1733368 diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/mathml/relations/css-styling/color-003.html.ini firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/mathml/relations/css-styling/color-003.html.ini --- firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/mathml/relations/css-styling/color-003.html.ini 2021-10-17 14:42:52.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/mathml/relations/css-styling/color-003.html.ini 2021-10-20 19:28:37.000000000 +0000 @@ -1,4 +1,3 @@ [color-003.html] expected: if webrender and not swgl and (os == "win") and (bits == 64) and not ccov: FAIL - if win10_2004 and ccov: FAIL diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/web-locks/mode-exclusive.tentative.https.any.js.ini firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/web-locks/mode-exclusive.tentative.https.any.js.ini --- firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/web-locks/mode-exclusive.tentative.https.any.js.ini 2021-10-17 14:42:52.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/web-locks/mode-exclusive.tentative.https.any.js.ini 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -[mode-exclusive.tentative.https.any.html] - [Requests for distinct resources can be granted] - expected: FAIL - - -[mode-exclusive.tentative.https.any.worker.html] - [Requests for distinct resources can be granted] - expected: FAIL - - -[mode-exclusive.tentative.https.any.serviceworker.html] - [Requests for distinct resources can be granted] - expected: FAIL - - -[mode-exclusive.tentative.https.any.sharedworker.html] - [Requests for distinct resources can be granted] - expected: FAIL - diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/web-locks/query-ordering.tentative.https.html.ini firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/web-locks/query-ordering.tentative.https.html.ini --- firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/web-locks/query-ordering.tentative.https.html.ini 2021-10-17 14:42:52.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/web-locks/query-ordering.tentative.https.html.ini 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ -[query-ordering.tentative.https.html] - [Requests appear in state in order made.] - expected: FAIL - diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/web-locks/query.tentative.https.any.js.ini firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/web-locks/query.tentative.https.any.js.ini --- firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/web-locks/query.tentative.https.any.js.ini 2021-10-17 14:42:52.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/web-locks/query.tentative.https.any.js.ini 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -[query.tentative.https.any.html] - expected: - if swgl and (os == "linux"): [OK, ERROR, CRASH] - [query() can observe a deadlock] - expected: FAIL - - [query() reports different ids for held locks from different contexts] - expected: FAIL - - -[query.tentative.https.any.worker.html] - expected: - if not swgl and debug and (os == "linux") and not fission: [OK, ERROR] - if swgl and (os == "linux"): [OK, CRASH] - [query() can observe a deadlock] - expected: FAIL - - [query() reports different ids for held locks from different contexts] - expected: FAIL diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/web-locks/signal.tentative.https.any.js.ini firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/web-locks/signal.tentative.https.any.js.ini --- firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/web-locks/signal.tentative.https.any.js.ini 2021-10-17 14:42:52.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/web-locks/signal.tentative.https.any.js.ini 1970-01-01 00:00:00.000000000 +0000 @@ -1,83 +0,0 @@ -[signal.tentative.https.any.html] - expected: ERROR - [Abort after a timeout] - expected: FAIL - - [Synchronously signaled abort] - expected: FAIL - - [Abort signaled after lock released] - expected: NOTRUN - - [Signal that is not aborted] - expected: FAIL - - [Abort signaled after lock granted] - expected: TIMEOUT - - [An aborted request results in AbortError] - expected: FAIL - - -[signal.tentative.https.any.worker.html] - expected: TIMEOUT - [Abort after a timeout] - expected: FAIL - - [Synchronously signaled abort] - expected: FAIL - - [Abort signaled after lock released] - expected: NOTRUN - - [Signal that is not aborted] - expected: FAIL - - [Abort signaled after lock granted] - expected: TIMEOUT - - [An aborted request results in AbortError] - expected: FAIL - - -[signal.tentative.https.any.serviceworker.html] - expected: TIMEOUT - [Abort after a timeout] - expected: FAIL - - [Synchronously signaled abort] - expected: FAIL - - [Abort signaled after lock released] - expected: NOTRUN - - [Signal that is not aborted] - expected: FAIL - - [Abort signaled after lock granted] - expected: TIMEOUT - - [An aborted request results in AbortError] - expected: FAIL - - -[signal.tentative.https.any.sharedworker.html] - expected: TIMEOUT - [Abort after a timeout] - expected: FAIL - - [Synchronously signaled abort] - expected: FAIL - - [Abort signaled after lock released] - expected: NOTRUN - - [Signal that is not aborted] - expected: FAIL - - [Abort signaled after lock granted] - expected: TIMEOUT - - [An aborted request results in AbortError] - expected: FAIL - diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/web-locks/workers.tentative.https.html.ini firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/web-locks/workers.tentative.https.html.ini --- firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/web-locks/workers.tentative.https.html.ini 2021-10-17 14:42:53.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/web-locks/workers.tentative.https.html.ini 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -[workers.tentative.https.html] - expected: - if swgl and (os == "linux"): [OK, ERROR] diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/webrtc/RTCRtpReceiver-getSynchronizationSources.https.html.ini firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/webrtc/RTCRtpReceiver-getSynchronizationSources.https.html.ini --- firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/webrtc/RTCRtpReceiver-getSynchronizationSources.https.html.ini 2021-10-17 14:42:52.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/webrtc/RTCRtpReceiver-getSynchronizationSources.https.html.ini 2021-10-20 19:28:36.000000000 +0000 @@ -1,58 +1,4 @@ [RTCRtpReceiver-getSynchronizationSources.https.html] [[audio-only\] RTCRtpSynchronizationSource.voiceActivityFlag is a boolean] bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1525394 - expected: - if (processor == "x86") and not debug: [FAIL, NOTRUN] - FAIL - - [[audio\] getSynchronizationSources() eventually returns a non-empty list] - expected: - if (processor == "x86") and not debug: [PASS, TIMEOUT] - - [[audio\] RTCRtpSynchronizationSource.timestamp is a number] - expected: - if (processor == "x86") and not debug: [PASS, NOTRUN] - - [[audio\] RTCRtpSynchronizationSource.rtpTimestamp is a number [0, 2^32-1\]] - expected: - if (processor == "x86") and not debug: [PASS, NOTRUN] - - [[audio\] getSynchronizationSources() does not contain SSRCs older than 10 seconds] - expected: - if (processor == "x86") and not debug: [PASS, NOTRUN] - - [[audio\] RTCRtpSynchronizationSource.timestamp is comparable to performance.timeOrigin + performance.now()] - expected: - if (processor == "x86") and not debug: [PASS, NOTRUN] - - [[audio\] RTCRtpSynchronizationSource.source is a number] - expected: - if (processor == "x86") and not debug: [PASS, NOTRUN] - - [[video\] getSynchronizationSources() eventually returns a non-empty list] - expected: - if (processor == "x86") and not debug: [PASS, NOTRUN] - - [[video\] RTCRtpSynchronizationSource.timestamp is a number] - expected: - if (processor == "x86") and not debug: [PASS, NOTRUN] - - [[video\] RTCRtpSynchronizationSource.rtpTimestamp is a number [0, 2^32-1\]] - expected: - if (processor == "x86") and not debug: [PASS, NOTRUN] - - [[video\] getSynchronizationSources() does not contain SSRCs older than 10 seconds] - expected: - if (processor == "x86") and not debug: [PASS, NOTRUN] - - [[video\] RTCRtpSynchronizationSource.timestamp is comparable to performance.timeOrigin + performance.now()] - expected: - if (processor == "x86") and not debug: [PASS, NOTRUN] - - [[video\] RTCRtpSynchronizationSource.source is a number] - expected: - if (processor == "x86") and not debug: [PASS, NOTRUN] - - [[audio-only\] RTCRtpSynchronizationSource.audioLevel is a number [0, 1\]] - expected: - if (processor == "x86") and not debug: [PASS, NOTRUN] + expected: FAIL diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/webrtc/RTCRtpTransceiver-stop.html.ini firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/webrtc/RTCRtpTransceiver-stop.html.ini --- firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/webrtc/RTCRtpTransceiver-stop.html.ini 2021-10-17 14:42:52.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/webrtc/RTCRtpTransceiver-stop.html.ini 2021-10-20 19:28:37.000000000 +0000 @@ -1,7 +1,9 @@ [RTCRtpTransceiver-stop.html] [If a transceiver is stopped, transceivers should end up in state stopped] + bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1568296 expected: FAIL [If a transceiver is stopped, transceivers, senders and receivers should disappear after offer/answer] + bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1568296 expected: FAIL diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/webrtc/simplecall.https.html.ini firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/webrtc/simplecall.https.html.ini --- firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/webrtc/simplecall.https.html.ini 2021-10-17 14:42:53.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/webrtc/simplecall.https.html.ini 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -[simplecall.https.html] - expected: - if (os == "linux") and not fission and not debug and (processor == "x86"): [OK, ERROR, TIMEOUT, CRASH] - if (os == "linux") and not fission and not debug and (processor == "x86_64"): [OK, TIMEOUT, ERROR, CRASH] - if (os == "linux") and fission: [OK, ERROR, CRASH] - if (os == "mac") and not debug: [OK, TIMEOUT] - [Can set up a basic WebRTC call.] - expected: - if (os == "linux") and not fission and not debug and (processor == "x86"): [PASS, TIMEOUT] - if (os == "linux") and not fission and not debug and (processor == "x86_64"): [PASS, TIMEOUT] - if (os == "mac") and not debug: [PASS, TIMEOUT] diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/webrtc/simplecall-no-ssrcs.https.html.ini firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/webrtc/simplecall-no-ssrcs.https.html.ini --- firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/meta/webrtc/simplecall-no-ssrcs.https.html.ini 2021-10-17 14:42:52.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/meta/webrtc/simplecall-no-ssrcs.https.html.ini 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ -[simplecall-no-ssrcs.https.html] - expected: - if (os == "linux") and not debug and webrender and not fission: [OK, ERROR, CRASH] - if (os == "linux") and not debug and not webrender: [OK, ERROR, CRASH] diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/tests/shadow-dom/focus/focus-method-delegatesFocus.html firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/tests/shadow-dom/focus/focus-method-delegatesFocus.html --- firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/tests/shadow-dom/focus/focus-method-delegatesFocus.html 2021-10-17 14:43:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/tests/shadow-dom/focus/focus-method-delegatesFocus.html 2021-10-20 19:28:45.000000000 +0000 @@ -283,5 +283,28 @@ assert_equals(dom.outerShadow.activeElement, dom.innerHost); assert_equals(dom.innerShadow.activeElement, dom.innerShadowChild); }, 'focus() on host with delegatesFocus with another host with delegatesFocus and a focusable child'); + +// Note that we use flat tree here however it might not be the behavior we end up +// wanting, per https://github.com/whatwg/html/issues/7207 +test(() => { + // Structure: + //
host + // #shadowRoot root delegatesFocus=true + // + // (slotted)
+ // + const host = document.createElement("div"); + const slotted = document.createElement("div"); + const firstFocusable = document.createElement("input"); + slotted.appendChild(firstFocusable); + host.appendChild(slotted); + + const root = host.attachShadow({mode: "open", delegatesFocus: true}); + root.innerHTML = ""; + document.body.appendChild(host); + + host.focus(); + assert_equals(document.activeElement, firstFocusable); +}, "Focus() on host with delegatesFocus should focus the focusable child when it is a descendant of the slotted element"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/tests/web-locks/mode-mixed.tentative.https.any.js firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/tests/web-locks/mode-mixed.tentative.https.any.js --- firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/tests/web-locks/mode-mixed.tentative.https.any.js 2021-10-17 14:43:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/tests/web-locks/mode-mixed.tentative.https.any.js 2021-10-20 19:28:46.000000000 +0000 @@ -1,4 +1,5 @@ // META: title=Web Locks API: Mixed Modes +// META: script=resources/helpers.js // META: global=window,dedicatedworker,sharedworker,serviceworker 'use strict'; @@ -43,3 +44,55 @@ ['a-shared-1', 'a-shared-2', 'a-shared-3', 'b-exclusive', 'a-exclusive']); }, 'Lock requests are granted in order'); + +promise_test(async t => { + const res = uniqueName(t); + + let [promise, resolve] = makePromiseAndResolveFunc(); + + const exclusive = navigator.locks.request(res, () => promise); + for (let i = 0; i < 5; i++) { + requestLockAndHold(t, res, { mode: "shared" }); + } + + let answer = await navigator.locks.query(); + assert_equals(answer.held.length, 1, "An exclusive lock is held"); + assert_equals(answer.pending.length, 5, "Requests for shared locks are pending"); + resolve(); + await exclusive; + + answer = await navigator.locks.query(); + assert_equals(answer.held.length, 5, "Shared locks are held"); + assert_true(answer.held.every(l => l.mode === "shared"), "All held locks are shared ones"); +}, 'Releasing exclusive lock grants multiple shared locks'); + +promise_test(async t => { + const res = uniqueName(t); + + let [sharedPromise, sharedResolve] = makePromiseAndResolveFunc(); + let [exclusivePromise, exclusiveResolve] = makePromiseAndResolveFunc(); + + const sharedReleasedPromise = Promise.all(new Array(5).fill(0).map( + () => navigator.locks.request(res, { mode: "shared" }, () => sharedPromise)) + ); + const exclusiveReleasedPromise = navigator.locks.request(res, () => exclusivePromise); + for (let i = 0; i < 5; i++) { + requestLockAndHold(t, res, { mode: "shared" }); + } + + let answer = await navigator.locks.query(); + assert_equals(answer.held.length, 5, "Shared locks are held"); + assert_true(answer.held.every(l => l.mode === "shared"), "All held locks are shared ones"); + sharedResolve(); + await sharedReleasedPromise; + + answer = await navigator.locks.query(); + assert_equals(answer.held.length, 1, "An exclusive lock is held"); + assert_equals(answer.held[0].mode, "exclusive"); + exclusiveResolve(); + await exclusiveReleasedPromise; + + answer = await navigator.locks.query(); + assert_equals(answer.held.length, 5, "The next shared locks are held"); + assert_true(answer.held.every(l => l.mode === "shared"), "All held locks are shared ones"); +}, 'An exclusive lock between shared locks'); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/tests/web-locks/opaque-origin.tentative.https.html firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/tests/web-locks/opaque-origin.tentative.https.html --- firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/tests/web-locks/opaque-origin.tentative.https.html 2021-10-17 14:43:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/tests/web-locks/opaque-origin.tentative.https.html 2021-10-20 19:28:46.000000000 +0000 @@ -32,9 +32,19 @@ const script = ` diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/tests/web-locks/resources/helpers.js firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/tests/web-locks/resources/helpers.js --- firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/tests/web-locks/resources/helpers.js 2021-10-17 14:43:00.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/tests/web-locks/resources/helpers.js 2021-10-20 19:28:46.000000000 +0000 @@ -57,4 +57,23 @@ }); }; + /** + * Request a lock and hold it until the subtest ends. + * @param {*} t test runner object + * @param {string} name lock name + * @param {LockOptions=} options lock options + * @returns + */ + self.requestLockAndHold = (t, name, options = {}) => { + return navigator.locks.request(name, options, () => { + return new Promise(resolve => t.add_cleanup(resolve)); + }); + }; + + self.makePromiseAndResolveFunc = () => { + let resolve; + const promise = new Promise(r => { resolve = r; }); + return [promise, resolve]; + }; + })(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/tests/web-locks/signal.tentative.https.any.js firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/tests/web-locks/signal.tentative.https.any.js --- firefox-trunk-95.0~a1~hg20211017r596111/testing/web-platform/tests/web-locks/signal.tentative.https.any.js 2021-10-17 14:43:01.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/testing/web-platform/tests/web-locks/signal.tentative.https.any.js 2021-10-20 19:28:46.000000000 +0000 @@ -4,12 +4,6 @@ 'use strict'; -function makePromiseAndResolveFunc() { - let resolve; - const promise = new Promise(r => { resolve = r; }); - return [promise, resolve]; -} - promise_test(async t => { const res = uniqueName(t); @@ -39,8 +33,7 @@ const res = uniqueName(t); // Grab a lock and hold it until this subtest completes. - const never_settled = new Promise(resolve => t.add_cleanup(resolve)); - navigator.locks.request(res, lock => never_settled); + requestLockAndHold(t, res); const controller = new AbortController(); @@ -68,8 +61,7 @@ const res = uniqueName(t); // Grab a lock and hold it until this subtest completes. - const never_settled = new Promise(resolve => t.add_cleanup(resolve)); - navigator.locks.request(res, lock => never_settled); + requestLockAndHold(t, res); const controller = new AbortController(); @@ -198,3 +190,19 @@ 'Lock released promise should not reject'); }, 'Abort signaled after lock released'); + +promise_test(async t => { + const res = uniqueName(t); + + const controller = new AbortController(); + const first = requestLockAndHold(t, res, { signal: controller.signal }); + const next = navigator.locks.request(res, () => "resolved"); + controller.abort(); + + await promise_rejects_dom(t, "AbortError", first, "Request should abort"); + assert_equals( + await next, + "resolved", + "The next request is processed after abort" + ); +}, "Abort should process the next pending lock request"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/alerts/test/image_server.sjs firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/alerts/test/image_server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/alerts/test/image_server.sjs 2021-10-17 14:43:04.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/alerts/test/image_server.sjs 2021-10-20 19:28:49.000000000 +0000 @@ -1,15 +1,20 @@ const CC = Components.Constructor; -Cu.import("resource://gre/modules/Timer.jsm"); +let { setTimeout } = ChromeUtils.import("resource://gre/modules/Timer.jsm"); -const LocalFile = CC("@mozilla.org/file/local;1", "nsIFile", - "initWithPath"); +const LocalFile = CC("@mozilla.org/file/local;1", "nsIFile", "initWithPath"); -const FileInputStream = CC("@mozilla.org/network/file-input-stream;1", - "nsIFileInputStream", "init"); - -const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", "setInputStream"); +const FileInputStream = CC( + "@mozilla.org/network/file-input-stream;1", + "nsIFileInputStream", + "init" +); + +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); function handleRequest(request, response) { let params = parseQueryString(request.queryString); @@ -21,9 +26,12 @@ // loaded anonymously. if (params.has("c")) { let expectedValue = parseInt(params.get("c"), 10); - let actualValue = !request.hasHeader("Cookie") ? 0 : - parseInt(request.getHeader("Cookie") - .replace(/^counter=(\d+)/, "$1"), 10); + let actualValue = !request.hasHeader("Cookie") + ? 0 + : parseInt( + request.getHeader("Cookie").replace(/^counter=(\d+)/, "$1"), + 10 + ); if (actualValue != expectedValue) { response.setStatusLine(request.httpVersion, 400, "Wrong counter value"); return; @@ -68,8 +76,8 @@ file.append(name); let mimeType = Cc["@mozilla.org/uriloader/external-helper-app-service;1"] - .getService(Ci.nsIMIMEService) - .getTypeFromFile(file); + .getService(Ci.nsIMIMEService) + .getTypeFromFile(file); let fileStream = new FileInputStream(file, 1, 0, false); let binaryStream = new BinaryInputStream(fileStream); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/antitracking/test/browser/browser_staticPartition_CORS_preflight.sjs firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/antitracking/test/browser/browser_staticPartition_CORS_preflight.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/antitracking/test/browser/browser_staticPartition_CORS_preflight.sjs 2021-10-17 14:43:04.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/antitracking/test/browser/browser_staticPartition_CORS_preflight.sjs 2021-10-20 19:28:50.000000000 +0000 @@ -2,7 +2,7 @@ * 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/. */ -'use strict'; +"use strict"; Components.utils.importGlobalProperties(["URLSearchParams"]); @@ -18,7 +18,8 @@ response.setHeader( "Access-Control-Allow-Methods", request.getHeader("Access-Control-Request-Method"), - false); + false + ); response.setHeader("Access-Control-Max-Age", "20", false); setState(token, token); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/antitracking/test/browser/browser_staticPartition_HSTS.sjs firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/antitracking/test/browser/browser_staticPartition_HSTS.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/antitracking/test/browser/browser_staticPartition_HSTS.sjs 2021-10-17 14:43:04.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/antitracking/test/browser/browser_staticPartition_HSTS.sjs 2021-10-20 19:28:50.000000000 +0000 @@ -4,14 +4,18 @@ const IMG_BYTES = atob( "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAA" + - "DUlEQVQImWNgY2P7DwABOgESJhRQtgAAAABJRU5ErkJggg=="); + "DUlEQVQImWNgY2P7DwABOgESJhRQtgAAAABJRU5ErkJggg==" +); const PAGE = "

HSTS page

"; function handleRequest(request, response) { response.setStatusLine(request.httpVersion, "200", "OK"); if (request.queryString == "includeSub") { - response.setHeader("Strict-Transport-Security", "max-age=60; includeSubDomains"); + response.setHeader( + "Strict-Transport-Security", + "max-age=60; includeSubDomains" + ); } else { response.setHeader("Strict-Transport-Security", "max-age=60"); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/antitracking/test/browser/file_saveAsImage.sjs firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/antitracking/test/browser/file_saveAsImage.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/antitracking/test/browser/file_saveAsImage.sjs 2021-10-17 14:43:04.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/antitracking/test/browser/file_saveAsImage.sjs 2021-10-20 19:28:49.000000000 +0000 @@ -1,7 +1,8 @@ // small red image const IMAGE = atob( "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" + - "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="); + "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" +); function handleRequest(aRequest, aResponse) { aResponse.setStatusLine(aRequest.httpVersion, 200); @@ -15,5 +16,5 @@ aResponse.setHeader("Content-Type", "image/png", false); aResponse.write(IMAGE); - } + } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/antitracking/test/browser/file_saveAsVideo.sjs firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/antitracking/test/browser/file_saveAsVideo.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/antitracking/test/browser/file_saveAsVideo.sjs 2021-10-17 14:43:03.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/antitracking/test/browser/file_saveAsVideo.sjs 2021-10-20 19:28:50.000000000 +0000 @@ -1,20 +1,21 @@ const VIDEO = atob( "GkXfo49CgoR3ZWJtQoeBAkKFgQIYU4BnI0nOEU2bdKpNu" + - "4tTq4QVSalmU6yBL027i1OrhBZUrmtTrIGmTbuLU6uEHF" + - "O7a1OsgdEVSalm8k2ApWxpYmVibWwyIHYwLjkuNyArIGx" + - "pYm1hdHJvc2thMiB2MC45LjhXQZ5ta2NsZWFuIDAuMi41" + - "IGZyb20gTGF2ZjUyLjU3LjFzpJC/CoyJjSbckmOytS9Se" + - "y3cRImIQK9AAAAAAABEYYgEHiZDUAHwABZUrmumrqTXgQ" + - "FzxYEBnIEAIrWcg3VuZIaFVl9WUDiDgQHgh7CCAUC6gfA" + - "cU7trzbuMs4EAt4f3gQHxggEju42zggMgt4f3gQHxgrZd" + - "u46zggZAt4j3gQHxgwFba7uOs4IJYLeI94EB8YMCAR+7j" + - "rOCDIC3iPeBAfGDAqggH0O2dSBibueBAKNbiYEAAIDQdw" + - "CdASpAAfAAAAcIhYWIhYSIAIIb347n/c/Z0BPBfjv7f+I" + - "/6df275Wbh/XPuZ+qv9k58KrftA9tvkP+efiN/ovmd/if" + - "9L/ef2z+Xv+H/rv+39xD/N/zL+nfid71363e9X+pf6/+q" + - "+x7+W/0P/H/233Zf6n/Wv696APuDf0b+YdcL+2HsUfxr+" + - "gfP/91P+F/yH9K+H79Z/7j/Xvhj/VjVsvwHXt/n/qz9U/" + - "w749+c/g="); + "4tTq4QVSalmU6yBL027i1OrhBZUrmtTrIGmTbuLU6uEHF" + + "O7a1OsgdEVSalm8k2ApWxpYmVibWwyIHYwLjkuNyArIGx" + + "pYm1hdHJvc2thMiB2MC45LjhXQZ5ta2NsZWFuIDAuMi41" + + "IGZyb20gTGF2ZjUyLjU3LjFzpJC/CoyJjSbckmOytS9Se" + + "y3cRImIQK9AAAAAAABEYYgEHiZDUAHwABZUrmumrqTXgQ" + + "FzxYEBnIEAIrWcg3VuZIaFVl9WUDiDgQHgh7CCAUC6gfA" + + "cU7trzbuMs4EAt4f3gQHxggEju42zggMgt4f3gQHxgrZd" + + "u46zggZAt4j3gQHxgwFba7uOs4IJYLeI94EB8YMCAR+7j" + + "rOCDIC3iPeBAfGDAqggH0O2dSBibueBAKNbiYEAAIDQdw" + + "CdASpAAfAAAAcIhYWIhYSIAIIb347n/c/Z0BPBfjv7f+I" + + "/6df275Wbh/XPuZ+qv9k58KrftA9tvkP+efiN/ovmd/if" + + "9L/ef2z+Xv+H/rv+39xD/N/zL+nfid71363e9X+pf6/+q" + + "+x7+W/0P/H/233Zf6n/Wv696APuDf0b+YdcL+2HsUfxr+" + + "gfP/91P+F/yH9K+H79Z/7j/Xvhj/VjVsvwHXt/n/qz9U/" + + "w749+c/g=" +); function handleRequest(aRequest, aResponse) { aResponse.setStatusLine(aRequest.httpVersion, 200); @@ -30,7 +31,8 @@ aResponse.setHeader( "Cache-Control", "public, max-age=604800, immutable", - false); + false + ); aResponse.write(VIDEO); - } + } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/antitracking/test/browser/image.sjs firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/antitracking/test/browser/image.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/antitracking/test/browser/image.sjs 2021-10-17 14:43:04.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/antitracking/test/browser/image.sjs 2021-10-20 19:28:49.000000000 +0000 @@ -1,7 +1,9 @@ // A 1x1 PNG image. // Source: https://commons.wikimedia.org/wiki/File:1x1.png (Public Domain) -const IMAGE = atob("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAA" + - "ACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII="); +const IMAGE = atob( + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAA" + + "ACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=" +); function handleRequest(aRequest, aResponse) { aResponse.setStatusLine(aRequest.httpVersion, 200); @@ -16,5 +18,5 @@ aResponse.setHeader("Set-Cookie", "foopy=1"); aResponse.setHeader("Content-Type", "image/png", false); aResponse.write(IMAGE); - } + } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/antitracking/test/browser/redirect.sjs firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/antitracking/test/browser/redirect.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/antitracking/test/browser/redirect.sjs 2021-10-17 14:43:03.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/antitracking/test/browser/redirect.sjs 2021-10-20 19:28:50.000000000 +0000 @@ -2,7 +2,7 @@ aResponse.setStatusLine(aRequest.httpVersion, 302); let query = aRequest.queryString; - let locations = query.split("|"); + let locations = query.split("|"); let nextLocation = locations.shift(); if (locations.length) { nextLocation += "?" + locations.join("|"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/antitracking/test/browser/referrer.sjs firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/antitracking/test/browser/referrer.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/antitracking/test/browser/referrer.sjs 2021-10-17 14:43:03.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/antitracking/test/browser/referrer.sjs 2021-10-20 19:28:50.000000000 +0000 @@ -1,20 +1,26 @@ // A 1x1 PNG image. // Source: https://commons.wikimedia.org/wiki/File:1x1.png (Public Domain) -const IMAGE = atob("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAA" + - "ACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII="); - -const IFRAME = "\n" + - ""; +const IMAGE = atob( + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAA" + + "ACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=" +); + +const IFRAME = + "\n" + + ""; function handleRequest(aRequest, aResponse) { aResponse.setStatusLine(aRequest.httpVersion, 200); - let key = aRequest.queryString.includes("what=script") ? "script" : - (aRequest.queryString.includes("what=image") ? "image" : "iframe"); + let key = aRequest.queryString.includes("what=script") + ? "script" + : aRequest.queryString.includes("what=image") + ? "image" + : "iframe"; if (aRequest.queryString.includes("result")) { aResponse.write(getState(key)); @@ -22,8 +28,8 @@ return; } - if (aRequest.hasHeader('Referer')) { - let referrer = aRequest.getHeader('Referer'); + if (aRequest.hasHeader("Referer")) { + let referrer = aRequest.getHeader("Referer"); setState(key, referrer); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/antitracking/test/browser/server.sjs firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/antitracking/test/browser/server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/antitracking/test/browser/server.sjs 2021-10-17 14:43:03.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/antitracking/test/browser/server.sjs 2021-10-20 19:28:50.000000000 +0000 @@ -1,20 +1,20 @@ function handleRequest(aRequest, aResponse) { - if (aRequest.queryString.includes("redirect")) { - aResponse.setStatusLine(aRequest.httpVersion, 302); - if (aRequest.queryString.includes("redirect-checkonly")) { - aResponse.setHeader("Location", "server.sjs?checkonly"); - } else { - aResponse.setHeader("Location", "server.sjs"); - } - return; - } - aResponse.setStatusLine(aRequest.httpVersion, 200); - if (aRequest.hasHeader('Cookie')) { - aResponse.write("cookie-present"); - } else { - if (!aRequest.queryString.includes("checkonly")) { - aResponse.setHeader("Set-Cookie", "foopy=1"); - } - aResponse.write("cookie-not-present"); - } + if (aRequest.queryString.includes("redirect")) { + aResponse.setStatusLine(aRequest.httpVersion, 302); + if (aRequest.queryString.includes("redirect-checkonly")) { + aResponse.setHeader("Location", "server.sjs?checkonly"); + } else { + aResponse.setHeader("Location", "server.sjs"); + } + return; + } + aResponse.setStatusLine(aRequest.httpVersion, 200); + if (aRequest.hasHeader("Cookie")) { + aResponse.write("cookie-present"); + } else { + if (!aRequest.queryString.includes("checkonly")) { + aResponse.setHeader("Set-Cookie", "foopy=1"); + } + aResponse.write("cookie-not-present"); + } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/antitracking/test/browser/subResources.sjs firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/antitracking/test/browser/subResources.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/antitracking/test/browser/subResources.sjs 2021-10-17 14:43:04.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/antitracking/test/browser/subResources.sjs 2021-10-20 19:28:50.000000000 +0000 @@ -1,7 +1,9 @@ // A 1x1 PNG image. // Source: https://commons.wikimedia.org/wiki/File:1x1.png (Public Domain) -const IMAGE = atob("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAA" + - "ACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII="); +const IMAGE = atob( + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAA" + + "ACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=" +); function handleRequest(aRequest, aResponse) { aResponse.setStatusLine(aRequest.httpVersion, 200); @@ -14,7 +16,7 @@ return; } - if (aRequest.hasHeader('Cookie')) { + if (aRequest.hasHeader("Cookie")) { let hints = parseInt(getState(key) || 0) + 1; setState(key, hints.toString()); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.cpp firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.cpp 2021-10-17 14:43:03.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.cpp 2021-10-20 19:28:50.000000000 +0000 @@ -541,7 +541,7 @@ // If the profiler is enabled, add a marker. #ifdef MOZ_GECKO_PROFILER - if (profiler_can_accept_markers()) { + if (profiler_thread_is_being_profiled(mStackHelper.GetThreadId())) { struct HangMarker { static constexpr Span MarkerTypeName() { return MakeStringSpan("BHR-detected hang"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/browser/nsWebBrowser.cpp firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/browser/nsWebBrowser.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/browser/nsWebBrowser.cpp 2021-10-17 14:43:04.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/browser/nsWebBrowser.cpp 2021-10-20 19:28:50.000000000 +0000 @@ -31,6 +31,7 @@ #include "nsComponentManagerUtils.h" #include "nsDocShell.h" #include "nsServiceManagerUtils.h" +#include "WindowRenderer.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/BrowsingContext.h" diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/cleardata/tests/browser/file_cors_preflight.sjs firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/cleardata/tests/browser/file_cors_preflight.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/cleardata/tests/browser/file_cors_preflight.sjs 2021-10-17 14:43:04.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/cleardata/tests/browser/file_cors_preflight.sjs 2021-10-20 19:28:50.000000000 +0000 @@ -2,7 +2,7 @@ * 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/. */ -'use strict'; +"use strict"; Components.utils.importGlobalProperties(["URLSearchParams"]); @@ -18,7 +18,8 @@ response.setHeader( "Access-Control-Allow-Methods", request.getHeader("Access-Control-Request-Method"), - false); + false + ); response.setHeader("Access-Control-Max-Age", "20", false); setState(token, token); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/extensions/ConduitsParent.jsm firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/extensions/ConduitsParent.jsm --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/extensions/ConduitsParent.jsm 2021-10-17 14:43:03.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/extensions/ConduitsParent.jsm 2021-10-20 19:28:50.000000000 +0000 @@ -184,7 +184,7 @@ address.verified = this.verifyEnv(address); if (actor instanceof JSWindowActorParent) { address.frameId = WebNavigationFrames.getFrameId(actor.browsingContext); - address.url = actor.browsingContext.currentURI.spec; + address.url = actor.manager.documentURI?.spec; } else { // Background service worker contexts do not have an associated frame // and there is no browsingContext to retrieve the expected url from. diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/extensions/test/mochitest/redirect_auto.sjs firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/extensions/test/mochitest/redirect_auto.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/extensions/test/mochitest/redirect_auto.sjs 2021-10-17 14:43:04.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/extensions/test/mochitest/redirect_auto.sjs 2021-10-20 19:28:50.000000000 +0000 @@ -14,7 +14,9 @@ } else { response.setStatusLine(request.httpVersion, 302, "Moved Temporarily"); } - let url = new URL(params.get("redirect_uri") || params.get("default_redirect")); + let url = new URL( + params.get("redirect_uri") || params.get("default_redirect") + ); url.searchParams.set("access_token", "here ya go"); response.setHeader("Location", url.href); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/extensions/test/mochitest/return_headers.sjs firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/extensions/test/mochitest/return_headers.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/extensions/test/mochitest/return_headers.sjs 2021-10-17 14:43:04.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/extensions/test/mochitest/return_headers.sjs 2021-10-20 19:28:50.000000000 +0000 @@ -17,4 +17,3 @@ response.write(JSON.stringify(headers)); } - diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/extensions/test/mochitest/slow_response.sjs firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/extensions/test/mochitest/slow_response.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/extensions/test/mochitest/slow_response.sjs 2021-10-17 14:43:04.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/extensions/test/mochitest/slow_response.sjs 2021-10-20 19:28:50.000000000 +0000 @@ -4,11 +4,17 @@ /* eslint-disable no-unused-vars */ -Cu.import("resource://gre/modules/AppConstants.jsm"); +let { AppConstants } = ChromeUtils.import( + "resource://gre/modules/AppConstants.jsm" +); const DELAY = AppConstants.DEBUG ? 4000 : 800; -let nsTimer = Components.Constructor("@mozilla.org/timer;1", "nsITimer", "initWithCallback"); +let nsTimer = Components.Constructor( + "@mozilla.org/timer;1", + "nsITimer", + "initWithCallback" +); let timer; function delay() { @@ -52,4 +58,3 @@ response.finish(); } - diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/extensions/webidl-api/ExtensionEventListener.h firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/extensions/webidl-api/ExtensionEventListener.h --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/extensions/webidl-api/ExtensionEventListener.h 2021-10-17 14:43:04.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/extensions/webidl-api/ExtensionEventListener.h 2021-10-20 19:28:50.000000000 +0000 @@ -155,7 +155,8 @@ private: ~ExtensionListenerCallWorkerRunnable() { - NS_ReleaseOnMainThread(mPromiseResult.forget()); + NS_ReleaseOnMainThread("~ExtensionListenerCallWorkerRunnable", + mPromiseResult.forget()); ReleaseArgsHolder(); mListener = nullptr; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/formautofill/FormAutofillHandler.jsm firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/formautofill/FormAutofillHandler.jsm --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/formautofill/FormAutofillHandler.jsm 2021-10-17 14:43:04.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/formautofill/FormAutofillHandler.jsm 2021-10-20 19:28:50.000000000 +0000 @@ -627,10 +627,7 @@ data.flowId = this.flowId; } let condensedDetails = this.fieldDetails; - // This NIGHTLY_BUILD statement should be removed as part of Bug 1735562 once the feature is stable - if (AppConstants.NIGHTLY_BUILD) { - this._condenseMultipleCCNumberFields(condensedDetails); - } + this._condenseMultipleCCNumberFields(condensedDetails); condensedDetails.forEach(detail => { let element = detail.elementWeakRef.get(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/formautofill/FormAutofillHeuristics.jsm firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/formautofill/FormAutofillHeuristics.jsm --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/formautofill/FormAutofillHeuristics.jsm 2021-10-17 14:43:03.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/formautofill/FormAutofillHeuristics.jsm 2021-10-20 19:28:50.000000000 +0000 @@ -17,10 +17,7 @@ const { FormAutofill } = ChromeUtils.import( "resource://autofill/FormAutofill.jsm" ); -// This AppConstants import can be removed as part of Bug 1735562 -const { AppConstants } = ChromeUtils.import( - "resource://gre/modules/AppConstants.jsm" -); + ChromeUtils.defineModuleGetter( this, "FormAutofillUtils", @@ -226,9 +223,7 @@ ); } // This NIGHTLY_BUILD statement should be removed as part of Bug 1735562 once the feature is stable - if (AppConstants.NIGHTLY_BUILD) { - this._classifyMultipleCCNumberFields(); - } + this._classifyMultipleCCNumberFields(); } /** diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/httpsonlyerror/tests/browser/file_errorpage_timeout_server.sjs firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/httpsonlyerror/tests/browser/file_errorpage_timeout_server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/httpsonlyerror/tests/browser/file_errorpage_timeout_server.sjs 2021-10-17 14:43:04.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/httpsonlyerror/tests/browser/file_errorpage_timeout_server.sjs 2021-10-20 19:28:50.000000000 +0000 @@ -1,7 +1,6 @@ // Custom *.sjs file specifically for the needs of Bug 1657348 -function handleRequest(request, response) -{ +function handleRequest(request, response) { // avoid confusing cache behaviors response.setHeader("Cache-Control", "no-cache", false); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/nimbus/FeatureManifest.js firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/nimbus/FeatureManifest.js --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/nimbus/FeatureManifest.js 2021-10-17 14:43:03.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/nimbus/FeatureManifest.js 2021-10-20 19:28:50.000000000 +0000 @@ -19,6 +19,11 @@ description: "Used to activate only matching configurations that contain the value in `experiment`", }, + extraParams: { + type: "json", + description: + "Query parameters values for search engine configurations.", + }, }, }, urlbar: { diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/normandy/test/unit/query_server.sjs firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/normandy/test/unit/query_server.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/normandy/test/unit/query_server.sjs 2021-10-17 14:43:04.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/normandy/test/unit/query_server.sjs 2021-10-20 19:28:50.000000000 +0000 @@ -1,5 +1,9 @@ const CC = Components.Constructor; -const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", "nsIBinaryInputStream", "setInputStream"); +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); // Returns a JSON string containing the query string arguments and the // request body parsed as JSON. @@ -20,7 +24,7 @@ const body = String.fromCharCode.apply(null, bytes); // Write response body - const data = {queryString: {}, body: body ? JSON.parse(body) : {}}; + const data = { queryString: {}, body: body ? JSON.parse(body) : {} }; const params = request.queryString.split("&"); for (const param of params) { const [key, value] = param.split("="); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/passwordmgr/PasswordGenerator.jsm firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/passwordmgr/PasswordGenerator.jsm --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/passwordmgr/PasswordGenerator.jsm 2021-10-17 14:43:05.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/passwordmgr/PasswordGenerator.jsm 2021-10-20 19:28:50.000000000 +0000 @@ -81,7 +81,13 @@ for (const charClassString of requiredClasses) { password += charClassString[this._randomUInt8Index(charClassString.length)]; - allRequiredCharacters += charClassString; + if (Array.isArray(charClassString)) { + // Convert array into single string so that commas aren't + // concatenated with each character in the arbitrary character array. + allRequiredCharacters += charClassString.join(""); + } else { + allRequiredCharacters += charClassString; + } } // Now fill the rest of the password with random characters. diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/passwordmgr/test/authenticate.sjs firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/passwordmgr/test/authenticate.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/passwordmgr/test/authenticate.sjs 2021-10-17 14:43:04.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/passwordmgr/test/authenticate.sjs 2021-10-20 19:28:50.000000000 +0000 @@ -1,5 +1,4 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { try { reallyHandleRequest(request, response); } catch (e) { @@ -8,152 +7,200 @@ } } - function reallyHandleRequest(request, response) { - var match; - var requestAuth = true, requestProxyAuth = true; + let match; + let requestAuth = true, + requestProxyAuth = true; // Allow the caller to drive how authentication is processed via the query. // Eg, http://localhost:8888/authenticate.sjs?user=foo&realm=bar // The extra ? allows the user/pass/realm checks to succeed if the name is // at the beginning of the query string. - var query = "?" + request.queryString; + let query = "?" + request.queryString; - var expected_user = "", expected_pass = "", realm = "mochitest"; - var proxy_expected_user = "", proxy_expected_pass = "", proxy_realm = "mochi-proxy"; - var huge = false, plugin = false, anonymous = false, formauth = false; - var authHeaderCount = 1; + let expected_user = "", + expected_pass = "", + realm = "mochitest"; + let proxy_expected_user = "", + proxy_expected_pass = "", + proxy_realm = "mochi-proxy"; + let huge = false, + plugin = false, + anonymous = false, + formauth = false; + let authHeaderCount = 1; // user=xxx match = /[^_]user=([^&]*)/.exec(query); - if (match) + if (match) { expected_user = match[1]; + } // pass=xxx match = /[^_]pass=([^&]*)/.exec(query); - if (match) + if (match) { expected_pass = match[1]; + } // realm=xxx match = /[^_]realm=([^&]*)/.exec(query); - if (match) + if (match) { realm = match[1]; + } // proxy_user=xxx match = /proxy_user=([^&]*)/.exec(query); - if (match) + if (match) { proxy_expected_user = match[1]; + } // proxy_pass=xxx match = /proxy_pass=([^&]*)/.exec(query); - if (match) + if (match) { proxy_expected_pass = match[1]; + } // proxy_realm=xxx match = /proxy_realm=([^&]*)/.exec(query); - if (match) + if (match) { proxy_realm = match[1]; + } // huge=1 match = /huge=1/.exec(query); - if (match) + if (match) { huge = true; + } // plugin=1 match = /plugin=1/.exec(query); - if (match) + if (match) { plugin = true; + } // multiple=1 match = /multiple=([^&]*)/.exec(query); - if (match) - authHeaderCount = match[1]+0; + if (match) { + authHeaderCount = match[1] + 0; + } // anonymous=1 match = /anonymous=1/.exec(query); - if (match) + if (match) { anonymous = true; + } // formauth=1 match = /formauth=1/.exec(query); - if (match) + if (match) { formauth = true; + } // Look for an authentication header, if any, in the request. // // EG: Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== - // + // // This test only supports Basic auth. The value sent by the client is // "username:password", obscured with base64 encoding. - var actual_user = "", actual_pass = "", authHeader, authPresent = false; + let actual_user = "", + actual_pass = "", + authHeader, + authPresent = false; if (request.hasHeader("Authorization")) { authPresent = true; authHeader = request.getHeader("Authorization"); match = /Basic (.+)/.exec(authHeader); - if (match.length != 2) - throw new Error("Couldn't parse auth header: " + authHeader); + if (match.length != 2) { + throw new Error("Couldn't parse auth header: " + authHeader); + } var userpass = base64ToString(match[1]); // no atob() :-( match = /(.*):(.*)/.exec(userpass); - if (match.length != 3) - throw new Error("Couldn't decode auth header: " + userpass); + if (match.length != 3) { + throw new Error("Couldn't decode auth header: " + userpass); + } actual_user = match[1]; actual_pass = match[2]; - } + } - var proxy_actual_user = "", proxy_actual_pass = ""; + let proxy_actual_user = "", + proxy_actual_pass = ""; if (request.hasHeader("Proxy-Authorization")) { authHeader = request.getHeader("Proxy-Authorization"); match = /Basic (.+)/.exec(authHeader); - if (match.length != 2) - throw new Error("Couldn't parse auth header: " + authHeader); + if (match.length != 2) { + throw new Error("Couldn't parse auth header: " + authHeader); + } var userpass = base64ToString(match[1]); // no atob() :-( match = /(.*):(.*)/.exec(userpass); - if (match.length != 3) - throw new Error("Couldn't decode auth header: " + userpass); + if (match.length != 3) { + throw new Error("Couldn't decode auth header: " + userpass); + } proxy_actual_user = match[1]; proxy_actual_pass = match[2]; } // Don't request authentication if the credentials we got were what we // expected. - if (expected_user == actual_user && - expected_pass == actual_pass) { + if (expected_user == actual_user && expected_pass == actual_pass) { requestAuth = false; } - if (proxy_expected_user == proxy_actual_user && - proxy_expected_pass == proxy_actual_pass) { + if ( + proxy_expected_user == proxy_actual_user && + proxy_expected_pass == proxy_actual_pass + ) { requestProxyAuth = false; } if (anonymous) { if (authPresent) { - response.setStatusLine("1.0", 400, "Unexpected authorization header found"); + response.setStatusLine( + "1.0", + 400, + "Unexpected authorization header found" + ); } else { response.setStatusLine("1.0", 200, "Authorization header not found"); } - } else { - if (requestProxyAuth) { - response.setStatusLine("1.0", 407, "Proxy authentication required"); - for (i = 0; i < authHeaderCount; ++i) - response.setHeader("Proxy-Authenticate", "basic realm=\"" + proxy_realm + "\"", true); - } else if (requestAuth) { - if (formauth && authPresent) - response.setStatusLine("1.0", 403, "Form authentication required"); - else - response.setStatusLine("1.0", 401, "Authentication required"); - for (i = 0; i < authHeaderCount; ++i) - response.setHeader("WWW-Authenticate", "basic realm=\"" + realm + "\"", true); + } else if (requestProxyAuth) { + response.setStatusLine("1.0", 407, "Proxy authentication required"); + for (i = 0; i < authHeaderCount; ++i) { + response.setHeader( + "Proxy-Authenticate", + 'basic realm="' + proxy_realm + '"', + true + ); + } + } else if (requestAuth) { + if (formauth && authPresent) { + response.setStatusLine("1.0", 403, "Form authentication required"); } else { - response.setStatusLine("1.0", 200, "OK"); + response.setStatusLine("1.0", 401, "Authentication required"); + } + for (i = 0; i < authHeaderCount; ++i) { + response.setHeader( + "WWW-Authenticate", + 'basic realm="' + realm + '"', + true + ); } + } else { + response.setStatusLine("1.0", 200, "OK"); } response.setHeader("Content-Type", "application/xhtml+xml", false); response.write(""); - response.write("

Login: " + (requestAuth ? "FAIL" : "PASS") + "

\n"); - response.write("

Proxy: " + (requestProxyAuth ? "FAIL" : "PASS") + "

\n"); + response.write( + "

Login: " + + (requestAuth ? "FAIL" : "PASS") + + "

\n" + ); + response.write( + "

Proxy: " + + (requestProxyAuth ? "FAIL" : "PASS") + + "

\n" + ); response.write("

Auth: " + authHeader + "

\n"); response.write("

User: " + actual_user + "

\n"); response.write("

Pass: " + actual_pass + "

\n"); @@ -164,23 +211,27 @@ response.write("123456789\n"); } response.write("
"); - response.write("This is a footnote after the huge content fill"); + response.write( + "This is a footnote after the huge content fill" + ); } if (plugin) { - response.write("\n"); + response.write( + "\n" + ); } response.write(""); } - // base64 decoder // // Yoinked from extensions/xml-rpc/src/nsXmlRpcClient.js because btoa() // doesn't seem to exist. :-( /* Convert Base64 data to a string */ +/* eslint-disable prettier/prettier */ const toBinaryTable = [ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, @@ -191,38 +242,43 @@ -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 ]; -const base64Pad = '='; + +/* eslint-enable prettier/prettier */ +const base64Pad = "="; function base64ToString(data) { + let result = ""; + let leftbits = 0; // number of bits decoded, but yet to be appended + let leftdata = 0; // bits decoded, but yet to be appended + + // Convert one by one. + for (let i = 0; i < data.length; i++) { + let c = toBinaryTable[data.charCodeAt(i) & 0x7f]; + let padding = data[i] == base64Pad; + // Skip illegal characters and whitespace + if (c == -1) { + continue; + } - var result = ''; - var leftbits = 0; // number of bits decoded, but yet to be appended - var leftdata = 0; // bits decoded, but yet to be appended - - // Convert one by one. - for (var i = 0; i < data.length; i++) { - var c = toBinaryTable[data.charCodeAt(i) & 0x7f]; - var padding = (data[i] == base64Pad); - // Skip illegal characters and whitespace - if (c == -1) continue; - - // Collect data into leftdata, update bitcount - leftdata = (leftdata << 6) | c; - leftbits += 6; - - // If we have 8 or more bits, append 8 bits to the result - if (leftbits >= 8) { - leftbits -= 8; - // Append if not padding. - if (!padding) - result += String.fromCharCode((leftdata >> leftbits) & 0xff); - leftdata &= (1 << leftbits) - 1; - } - } - - // If there are any bits left, the base64 string was corrupted - if (leftbits) - throw Components.Exception('Corrupted base64 string'); + // Collect data into leftdata, update bitcount + leftdata = (leftdata << 6) | c; + leftbits += 6; + + // If we have 8 or more bits, append 8 bits to the result + if (leftbits >= 8) { + leftbits -= 8; + // Append if not padding. + if (!padding) { + result += String.fromCharCode((leftdata >> leftbits) & 0xff); + } + leftdata &= (1 << leftbits) - 1; + } + } + + // If there are any bits left, the base64 string was corrupted + if (leftbits) { + throw Components.Exception("Corrupted base64 string"); + } - return result; + return result; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/passwordmgr/test/browser/authenticate.sjs firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/passwordmgr/test/browser/authenticate.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/passwordmgr/test/browser/authenticate.sjs 2021-10-17 14:43:04.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/passwordmgr/test/browser/authenticate.sjs 2021-10-20 19:28:50.000000000 +0000 @@ -1,5 +1,4 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { var match; var requestAuth = true; @@ -9,23 +8,27 @@ // at the beginning of the query string. var query = "?" + request.queryString; - var expected_user = "test", expected_pass = "testpass", realm = "mochitest"; + var expected_user = "test", + expected_pass = "testpass", + realm = "mochitest"; // user=xxx match = /[^_]user=([^&]*)/.exec(query); - if (match) + if (match) { expected_user = match[1]; + } // pass=xxx match = /[^_]pass=([^&]*)/.exec(query); - if (match) + if (match) { expected_pass = match[1]; + } // realm=xxx match = /[^_]realm=([^&]*)/.exec(query); - if (match) + if (match) { realm = match[1]; - + } // Look for an authentication header, if any, in the request. // @@ -34,51 +37,59 @@ // This test only supports Basic auth. The value sent by the client is // "username:password", obscured with base64 encoding. - var actual_user = "", actual_pass = "", authHeader, authPresent = false; + var actual_user = "", + actual_pass = "", + authHeader, + authPresent = false; if (request.hasHeader("Authorization")) { authPresent = true; authHeader = request.getHeader("Authorization"); match = /Basic (.+)/.exec(authHeader); - if (match.length != 2) - throw new Error("Couldn't parse auth header: " + authHeader); + if (match.length != 2) { + throw new Error("Couldn't parse auth header: " + authHeader); + } var userpass = base64ToString(match[1]); // no atob() :-( match = /(.*):(.*)/.exec(userpass); - if (match.length != 3) - throw new Error("Couldn't decode auth header: " + userpass); + if (match.length != 3) { + throw new Error("Couldn't decode auth header: " + userpass); + } actual_user = match[1]; actual_pass = match[2]; } // Don't request authentication if the credentials we got were what we // expected. - if (expected_user == actual_user && - expected_pass == actual_pass) { + if (expected_user == actual_user && expected_pass == actual_pass) { requestAuth = false; } if (requestAuth) { response.setStatusLine("1.0", 401, "Authentication required"); - response.setHeader("WWW-Authenticate", "basic realm=\"" + realm + "\"", true); + response.setHeader("WWW-Authenticate", 'basic realm="' + realm + '"', true); } else { response.setStatusLine("1.0", 200, "OK"); } response.setHeader("Content-Type", "application/xhtml+xml", false); response.write(""); - response.write("

Login: " + (requestAuth ? "FAIL" : "PASS") + "

\n"); + response.write( + "

Login: " + + (requestAuth ? "FAIL" : "PASS") + + "

\n" + ); response.write("

Auth: " + authHeader + "

\n"); response.write("

User: " + actual_user + "

\n"); response.write("

Pass: " + actual_pass + "

\n"); response.write(""); } - // base64 decoder // // Yoinked from extensions/xml-rpc/src/nsXmlRpcClient.js because btoa() // doesn't seem to exist. :-( /* Convert Base64 data to a string */ +/* eslint-disable prettier/prettier */ const toBinaryTable = [ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, @@ -89,38 +100,42 @@ -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 ]; -const base64Pad = '='; +/* eslint-enable prettier/prettier */ +const base64Pad = "="; function base64ToString(data) { + var result = ""; + var leftbits = 0; // number of bits decoded, but yet to be appended + var leftdata = 0; // bits decoded, but yet to be appended + + // Convert one by one. + for (var i = 0; i < data.length; i++) { + var c = toBinaryTable[data.charCodeAt(i) & 0x7f]; + var padding = data[i] == base64Pad; + // Skip illegal characters and whitespace + if (c == -1) { + continue; + } - var result = ''; - var leftbits = 0; // number of bits decoded, but yet to be appended - var leftdata = 0; // bits decoded, but yet to be appended - - // Convert one by one. - for (var i = 0; i < data.length; i++) { - var c = toBinaryTable[data.charCodeAt(i) & 0x7f]; - var padding = (data[i] == base64Pad); - // Skip illegal characters and whitespace - if (c == -1) continue; - - // Collect data into leftdata, update bitcount - leftdata = (leftdata << 6) | c; - leftbits += 6; - - // If we have 8 or more bits, append 8 bits to the result - if (leftbits >= 8) { - leftbits -= 8; - // Append if not padding. - if (!padding) - result += String.fromCharCode((leftdata >> leftbits) & 0xff); - leftdata &= (1 << leftbits) - 1; - } + // Collect data into leftdata, update bitcount + leftdata = (leftdata << 6) | c; + leftbits += 6; + + // If we have 8 or more bits, append 8 bits to the result + if (leftbits >= 8) { + leftbits -= 8; + // Append if not padding. + if (!padding) { + result += String.fromCharCode((leftdata >> leftbits) & 0xff); + } + leftdata &= (1 << leftbits) - 1; } + } - // If there are any bits left, the base64 string was corrupted - if (leftbits) - throw Components.Exception('Corrupted base64 string'); + // If there are any bits left, the base64 string was corrupted + if (leftbits) { + throw Components.Exception("Corrupted base64 string"); + } - return result; + return result; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/passwordmgr/test/browser/file_focus_before_DOMContentLoaded.sjs firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/passwordmgr/test/browser/file_focus_before_DOMContentLoaded.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/passwordmgr/test/browser/file_focus_before_DOMContentLoaded.sjs 2021-10-17 14:43:04.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/passwordmgr/test/browser/file_focus_before_DOMContentLoaded.sjs 2021-10-20 19:28:50.000000000 +0000 @@ -7,7 +7,7 @@ const DELAY = 2 * 1000; // Delay two seconds before completing the request. /* eslint-disable-next-line mozilla/use-chromeutils-import */ -let {setTimeout} = Cu.import("resource://gre/modules/Timer.jsm", {}); +let { setTimeout } = Cu.import("resource://gre/modules/Timer.jsm", {}); function handleRequest(request, response) { response.processAsync(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/passwordmgr/test/formsubmit.sjs firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/passwordmgr/test/formsubmit.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/passwordmgr/test/formsubmit.sjs 2021-10-17 14:43:04.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/passwordmgr/test/formsubmit.sjs 2021-10-20 19:28:50.000000000 +0000 @@ -1,5 +1,4 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { try { reallyHandleRequest(request, response); } catch (e) { @@ -8,24 +7,26 @@ } } - function reallyHandleRequest(request, response) { - var match; - var requestAuth = true; + let match; + let requestAuth = true; // XXX I bet this doesn't work for POST requests. - var query = request.queryString; + let query = request.queryString; - var user = null, pass = null; + let user = null, + pass = null; // user=xxx match = /user(?:name)?=([^&]*)/.exec(query); - if (match) + if (match) { user = match[1]; + } // pass=xxx match = /pass(?:word)?=([^&]*)/.exec(query); - if (match) + if (match) { pass = match[1]; + } response.setStatusLine("1.0", 200, "OK"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/passwordmgr/test/mochitest/auth2/authenticate.sjs firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/passwordmgr/test/mochitest/auth2/authenticate.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/passwordmgr/test/mochitest/auth2/authenticate.sjs 2021-10-17 14:43:04.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/passwordmgr/test/mochitest/auth2/authenticate.sjs 2021-10-20 19:28:50.000000000 +0000 @@ -1,5 +1,4 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { try { reallyHandleRequest(request, response); } catch (e) { @@ -8,10 +7,10 @@ } } - function reallyHandleRequest(request, response) { var match; - var requestAuth = true, requestProxyAuth = true; + var requestAuth = true, + requestProxyAuth = true; // Allow the caller to drive how authentication is processed via the query. // Eg, http://localhost:8888/authenticate.sjs?user=foo&realm=bar @@ -19,133 +18,178 @@ // at the beginning of the query string. var query = "?" + request.queryString; - var expected_user = "", expected_pass = "", realm = "mochitest"; - var proxy_expected_user = "", proxy_expected_pass = "", proxy_realm = "mochi-proxy"; - var huge = false, plugin = false, anonymous = false; + var expected_user = "", + expected_pass = "", + realm = "mochitest"; + var proxy_expected_user = "", + proxy_expected_pass = "", + proxy_realm = "mochi-proxy"; + var huge = false, + plugin = false, + anonymous = false; var authHeaderCount = 1; // user=xxx match = /[^_]user=([^&]*)/.exec(query); - if (match) + if (match) { expected_user = match[1]; + } // pass=xxx match = /[^_]pass=([^&]*)/.exec(query); - if (match) + if (match) { expected_pass = match[1]; + } // realm=xxx match = /[^_]realm=([^&]*)/.exec(query); - if (match) + if (match) { realm = match[1]; + } // proxy_user=xxx match = /proxy_user=([^&]*)/.exec(query); - if (match) + if (match) { proxy_expected_user = match[1]; + } // proxy_pass=xxx match = /proxy_pass=([^&]*)/.exec(query); - if (match) + if (match) { proxy_expected_pass = match[1]; + } // proxy_realm=xxx match = /proxy_realm=([^&]*)/.exec(query); - if (match) + if (match) { proxy_realm = match[1]; + } // huge=1 match = /huge=1/.exec(query); - if (match) + if (match) { huge = true; + } // plugin=1 match = /plugin=1/.exec(query); - if (match) + if (match) { plugin = true; + } // multiple=1 match = /multiple=([^&]*)/.exec(query); - if (match) - authHeaderCount = match[1]+0; + if (match) { + authHeaderCount = match[1] + 0; + } // anonymous=1 match = /anonymous=1/.exec(query); - if (match) + if (match) { anonymous = true; + } // Look for an authentication header, if any, in the request. // // EG: Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== - // + // // This test only supports Basic auth. The value sent by the client is // "username:password", obscured with base64 encoding. - var actual_user = "", actual_pass = "", authHeader, authPresent = false; + var actual_user = "", + actual_pass = "", + authHeader, + authPresent = false; if (request.hasHeader("Authorization")) { authPresent = true; authHeader = request.getHeader("Authorization"); match = /Basic (.+)/.exec(authHeader); - if (match.length != 2) - throw new Error("Couldn't parse auth header: " + authHeader); + if (match.length != 2) { + throw new Error("Couldn't parse auth header: " + authHeader); + } var userpass = base64ToString(match[1]); // no atob() :-( match = /(.*):(.*)/.exec(userpass); - if (match.length != 3) - throw new Error("Couldn't decode auth header: " + userpass); + if (match.length != 3) { + throw new Error("Couldn't decode auth header: " + userpass); + } actual_user = match[1]; actual_pass = match[2]; - } + } - var proxy_actual_user = "", proxy_actual_pass = ""; + var proxy_actual_user = "", + proxy_actual_pass = ""; if (request.hasHeader("Proxy-Authorization")) { authHeader = request.getHeader("Proxy-Authorization"); match = /Basic (.+)/.exec(authHeader); - if (match.length != 2) - throw new Error("Couldn't parse auth header: " + authHeader); + if (match.length != 2) { + throw new Error("Couldn't parse auth header: " + authHeader); + } var userpass = base64ToString(match[1]); // no atob() :-( match = /(.*):(.*)/.exec(userpass); - if (match.length != 3) - throw new Error("Couldn't decode auth header: " + userpass); + if (match.length != 3) { + throw new Error("Couldn't decode auth header: " + userpass); + } proxy_actual_user = match[1]; proxy_actual_pass = match[2]; } // Don't request authentication if the credentials we got were what we // expected. - if (expected_user == actual_user && - expected_pass == actual_pass) { + if (expected_user == actual_user && expected_pass == actual_pass) { requestAuth = false; } - if (proxy_expected_user == proxy_actual_user && - proxy_expected_pass == proxy_actual_pass) { + if ( + proxy_expected_user == proxy_actual_user && + proxy_expected_pass == proxy_actual_pass + ) { requestProxyAuth = false; } if (anonymous) { if (authPresent) { - response.setStatusLine("1.0", 400, "Unexpected authorization header found"); + response.setStatusLine( + "1.0", + 400, + "Unexpected authorization header found" + ); } else { response.setStatusLine("1.0", 200, "Authorization header not found"); } - } else { - if (requestProxyAuth) { - response.setStatusLine("1.0", 407, "Proxy authentication required"); - for (i = 0; i < authHeaderCount; ++i) - response.setHeader("Proxy-Authenticate", "basic realm=\"" + proxy_realm + "\"", true); - } else if (requestAuth) { - response.setStatusLine("1.0", 401, "Authentication required"); - for (i = 0; i < authHeaderCount; ++i) - response.setHeader("WWW-Authenticate", "basic realm=\"" + realm + "\"", true); - } else { - response.setStatusLine("1.0", 200, "OK"); + } else if (requestProxyAuth) { + response.setStatusLine("1.0", 407, "Proxy authentication required"); + for (i = 0; i < authHeaderCount; ++i) { + response.setHeader( + "Proxy-Authenticate", + 'basic realm="' + proxy_realm + '"', + true + ); + } + } else if (requestAuth) { + response.setStatusLine("1.0", 401, "Authentication required"); + for (i = 0; i < authHeaderCount; ++i) { + response.setHeader( + "WWW-Authenticate", + 'basic realm="' + realm + '"', + true + ); } + } else { + response.setStatusLine("1.0", 200, "OK"); } response.setHeader("Content-Type", "application/xhtml+xml", false); response.write(""); - response.write("

Login: " + (requestAuth ? "FAIL" : "PASS") + "

\n"); - response.write("

Proxy: " + (requestProxyAuth ? "FAIL" : "PASS") + "

\n"); + response.write( + "

Login: " + + (requestAuth ? "FAIL" : "PASS") + + "

\n" + ); + response.write( + "

Proxy: " + + (requestProxyAuth ? "FAIL" : "PASS") + + "

\n" + ); response.write("

Auth: " + authHeader + "

\n"); response.write("

User: " + actual_user + "

\n"); response.write("

Pass: " + actual_pass + "

\n"); @@ -156,23 +200,27 @@ response.write("123456789\n"); } response.write("
"); - response.write("This is a footnote after the huge content fill"); + response.write( + "This is a footnote after the huge content fill" + ); } if (plugin) { - response.write("\n"); + response.write( + "\n" + ); } response.write(""); } - // base64 decoder // // Yoinked from extensions/xml-rpc/src/nsXmlRpcClient.js because btoa() // doesn't seem to exist. :-( /* Convert Base64 data to a string */ +/* eslint-disable prettier/prettier */ const toBinaryTable = [ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, @@ -183,38 +231,42 @@ -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 ]; -const base64Pad = '='; +/* eslint-enable prettier/prettier */ +const base64Pad = "="; function base64ToString(data) { + var result = ""; + var leftbits = 0; // number of bits decoded, but yet to be appended + var leftdata = 0; // bits decoded, but yet to be appended + + // Convert one by one. + for (var i = 0; i < data.length; i++) { + var c = toBinaryTable[data.charCodeAt(i) & 0x7f]; + var padding = data[i] == base64Pad; + // Skip illegal characters and whitespace + if (c == -1) { + continue; + } + + // Collect data into leftdata, update bitcount + leftdata = (leftdata << 6) | c; + leftbits += 6; + + // If we have 8 or more bits, append 8 bits to the result + if (leftbits >= 8) { + leftbits -= 8; + // Append if not padding. + if (!padding) { + result += String.fromCharCode((leftdata >> leftbits) & 0xff); + } + leftdata &= (1 << leftbits) - 1; + } + } - var result = ''; - var leftbits = 0; // number of bits decoded, but yet to be appended - var leftdata = 0; // bits decoded, but yet to be appended - - // Convert one by one. - for (var i = 0; i < data.length; i++) { - var c = toBinaryTable[data.charCodeAt(i) & 0x7f]; - var padding = (data[i] == base64Pad); - // Skip illegal characters and whitespace - if (c == -1) continue; - - // Collect data into leftdata, update bitcount - leftdata = (leftdata << 6) | c; - leftbits += 6; - - // If we have 8 or more bits, append 8 bits to the result - if (leftbits >= 8) { - leftbits -= 8; - // Append if not padding. - if (!padding) - result += String.fromCharCode((leftdata >> leftbits) & 0xff); - leftdata &= (1 << leftbits) - 1; - } - } - - // If there are any bits left, the base64 string was corrupted - if (leftbits) - throw Components.Exception('Corrupted base64 string'); + // If there are any bits left, the base64 string was corrupted + if (leftbits) { + throw Components.Exception("Corrupted base64 string"); + } - return result; + return result; } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/passwordmgr/test/unit/test_PasswordRulesManager_generatePassword.js firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/passwordmgr/test/unit/test_PasswordRulesManager_generatePassword.js --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/passwordmgr/test/unit/test_PasswordRulesManager_generatePassword.js 2021-10-17 14:43:04.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/passwordmgr/test/unit/test_PasswordRulesManager_generatePassword.js 2021-10-20 19:28:49.000000000 +0000 @@ -225,6 +225,71 @@ verifyPassword(TEST_RULES, generatedPassword); }); +add_task( + async function test_generatePassword_with_arbitrary_required_characters() { + // Force password generation to be enabled. + Services.prefs.setBoolPref("signon.generation.available", true); + Services.prefs.setBoolPref("signon.generation.enabled", true); + Services.prefs.setBoolPref("signon.improvedPasswordRules.enabled", true); + // TEST_ORIGIN emulates the browsingContext.currentWindowGlobal.documentURI variable in LoginManagerParent + // and so it should always be a correctly formed URI when working with + // the PasswordRulesParser and PasswordRulesManager modules + const TEST_ORIGIN = Services.io.newURI("https://example.com"); + + // TEST_BASE_ORIGIN is how each domain is stored in RemoteSettings, and so + // we need this in order to parse out the particular password rules we're verifying + const TEST_BASE_ORIGIN = "example.com"; + const REQUIRED_ARBITRARY_CHARACTERS = "!#$@*()_+="; + // We use an extremely long password to ensure there are no invalid characters generated in the password. + // This ensures we exhaust all of "allRequiredCharacters" in PasswordGenerator.jsm. + // Otherwise, there's a small chance a "," may have been added to "allRequiredCharacters" + // which will generate an invalid password in this case. + const TEST_RULES = `required: [${REQUIRED_ARBITRARY_CHARACTERS}], upper, lower; maxlength: 255; minlength: 255;`; + await LoginTestUtils.remoteSettings.setupImprovedPasswordRules( + TEST_BASE_ORIGIN, + TEST_RULES + ); + const records = await RemoteSettings(IMPROVED_RULES_COLLECTION).get(); + + let rules = getRulesForRecord(records, TEST_BASE_ORIGIN); + + rules = PasswordRulesParser.parsePasswordRules(rules); + ok(rules.length, "Rules should exist after parsing"); + + let PRMP = new PasswordRulesManagerParent(); + ok(PRMP.generatePassword, "PRMP.generatePassword exists"); + + let generatedPassword = await PRMP.generatePassword(TEST_ORIGIN); + generatedPassword = await PRMP.generatePassword(TEST_ORIGIN); + ok(generatedPassword, "A password was generated"); + + verifyPassword(TEST_RULES, generatedPassword); + + let specialCharacters = PasswordGenerator._getSpecialCharacters(); + let digits = PasswordGenerator._getDigits(); + // Additional verification for this password case since + // we want to ensure no extra special characters and no digits are generated. + let disallowedSpecialCharacters = ""; + for (let char of specialCharacters) { + if (!REQUIRED_ARBITRARY_CHARACTERS.includes(char)) { + disallowedSpecialCharacters += char; + } + } + for (let char of disallowedSpecialCharacters) { + ok( + !generatedPassword.includes(char), + "Password must not contain any disallowed special characters: " + char + ); + } + for (let char of digits) { + ok( + !generatedPassword.includes(char), + "Password must not contain any digits: " + char + ); + } + } +); + // Checks the "www4.prepaid.bankofamerica.com" case to ensure the rules are found add_task(async function test_generatePassword_subdomain_rule() { const testCases = [ diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/places/Bookmarks.jsm firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/places/Bookmarks.jsm --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/places/Bookmarks.jsm 2021-10-17 14:43:04.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/places/Bookmarks.jsm 2021-10-20 19:28:50.000000000 +0000 @@ -320,43 +320,47 @@ url = item.url.href; } - let notification = new PlacesBookmarkAddition({ - id: itemId, - url, - itemType: item.type, - parentId: parent._id, - index: item.index, - title: item.title, - dateAdded: item.dateAdded, - guid: item.guid, - parentGuid: item.parentGuid, - source: item.source, - isTagging: isTagging || isTagsFolder, - }); - PlacesObservers.notifyListeners([notification]); + const notifications = [ + new PlacesBookmarkAddition({ + id: itemId, + url, + itemType: item.type, + parentId: parent._id, + index: item.index, + title: item.title, + dateAdded: item.dateAdded, + guid: item.guid, + parentGuid: item.parentGuid, + source: item.source, + isTagging: isTagging || isTagsFolder, + }), + ]; - // If it's a tag, notify OnItemChanged to all bookmarks for this URL. + // If it's a tag, notify bookmark-tags-changed event to all bookmarks for this URL. if (isTagging) { - let observers = PlacesUtils.bookmarks.getObservers(); + const tags = PlacesUtils.tagging.getTagsForURI(NetUtil.newURI(url)); + for (let entry of await fetchBookmarksByURL(item, { concurrent: true, })) { - notify(observers, "onItemChanged", [ - entry._id, - "tags", - false, - "", - PlacesUtils.toPRTime(entry.lastModified), - entry.type, - entry._parentId, - entry.guid, - entry.parentGuid, - "", - item.source, - ]); + notifications.push( + new PlacesBookmarkTags({ + id: entry._id, + itemType: entry.type, + url, + guid: entry.guid, + parentGuid: entry.parentGuid, + tags, + lastModified: entry.lastModified, + source: item.source, + isTagging: false, + }) + ); } } + PlacesObservers.notifyListeners(notifications); + // Remove non-enumerable properties. delete item.source; return Object.assign({}, item); @@ -852,9 +856,6 @@ const notifications = []; - // Notify onItemChanged to listeners. - let observers = PlacesUtils.bookmarks.getObservers(); - // For lastModified, we only care about the original input, since we // should not notify implicit lastModified changes. if ( @@ -918,19 +919,22 @@ { tags: [updatedItem.title] }, { concurrent: true } )) { - notify(observers, "onItemChanged", [ - entry._id, - "tags", - false, - "", - PlacesUtils.toPRTime(entry.lastModified), - entry.type, - entry._parentId, - entry.guid, - entry.parentGuid, - "", - updatedItem.source, - ]); + const tags = PlacesUtils.tagging.getTagsForURI( + NetUtil.newURI(entry.url) + ); + notifications.push( + new PlacesBookmarkTags({ + id: entry._id, + itemType: entry.type, + url: entry.url, + guid: entry.guid, + parentGuid: entry.parentGuid, + tags, + lastModified: entry.lastModified, + source: updatedItem.source, + isTagging: false, + }) + ); } } } @@ -1313,7 +1317,6 @@ let notifications = []; for (let item of removeItems) { - let observers = PlacesUtils.bookmarks.getObservers(); let isUntagging = item._grandParentId == PlacesUtils.tagsFolderId; let url = ""; if (item.type == Bookmarks.TYPE_BOOKMARK) { @@ -1336,22 +1339,23 @@ ); if (isUntagging) { + const tags = PlacesUtils.tagging.getTagsForURI(NetUtil.newURI(url)); for (let entry of await fetchBookmarksByURL(item, { concurrent: true, })) { - notify(observers, "onItemChanged", [ - entry._id, - "tags", - false, - "", - PlacesUtils.toPRTime(entry.lastModified), - entry.type, - entry._parentId, - entry.guid, - entry.parentGuid, - "", - options.source, - ]); + notifications.push( + new PlacesBookmarkTags({ + id: entry._id, + itemType: entry.type, + url, + guid: entry.guid, + parentGuid: entry.parentGuid, + tags, + lastModified: entry.lastModified, + source: options.source, + isTagging: false, + }) + ); } } } @@ -1855,38 +1859,6 @@ // Globals. -/** - * Sends a bookmarks notification through the given observers. - * - * @param {Array} observers - * array of nsINavBookmarkObserver objects. - * @param {String} notification - * the notification name. - * @param {Array} [args] - * array of arguments to pass to the notification. - * @param {Object} [information] - * Information about the notification, so we can filter based - * based on the observer's preferences. - */ -function notify(observers, notification, args = [], information = {}) { - for (let observer of observers) { - if (information.isTagging && observer.skipTags) { - continue; - } - - if ( - information.isDescendantRemoval && - !PlacesUtils.bookmarks.userContentRoots.includes(information.parentGuid) - ) { - continue; - } - - try { - observer[notification](...args); - } catch (ex) {} - } -} - // Update implementation. /** @@ -3288,7 +3260,6 @@ // Notify listeners in reverse order to serve children before parents. let { source = Bookmarks.SOURCES.DEFAULT } = options; - let observers = PlacesUtils.bookmarks.getObservers(); let notifications = []; for (let item of itemsRemoved.reverse()) { let isUntagging = item._grandParentId == PlacesUtils.tagsFolderId; @@ -3314,20 +3285,21 @@ ); if (isUntagging) { + const tags = PlacesUtils.tagging.getTagsForURI(NetUtil.newURI(url)); for (let entry of await fetchBookmarksByURL(item, true)) { - notify(observers, "onItemChanged", [ - entry._id, - "tags", - false, - "", - PlacesUtils.toPRTime(entry.lastModified), - entry.type, - entry._parentId, - entry.guid, - entry.parentGuid, - "", - source, - ]); + notifications.push( + new PlacesBookmarkTags({ + id: entry._id, + itemType: entry.type, + url, + guid: entry.guid, + parentGuid: entry.parentGuid, + tags, + lastModified: entry.lastModified, + source, + isTagging: false, + }) + ); } } } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/places/nsNavBookmarks.cpp firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/places/nsNavBookmarks.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/places/nsNavBookmarks.cpp 2021-10-17 14:43:04.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/places/nsNavBookmarks.cpp 2021-10-20 19:28:49.000000000 +0000 @@ -10,6 +10,7 @@ #include "Helpers.h" #include "nsAppDirectoryServiceDefs.h" +#include "nsITaggingService.h" #include "nsNetUtil.h" #include "nsUnicharUtils.h" #include "nsPrintfCString.h" @@ -19,6 +20,7 @@ #include "mozilla/storage.h" #include "mozilla/dom/PlacesBookmarkAddition.h" #include "mozilla/dom/PlacesBookmarkRemoved.h" +#include "mozilla/dom/PlacesBookmarkTags.h" #include "mozilla/dom/PlacesBookmarkTime.h" #include "mozilla/dom/PlacesBookmarkTitle.h" #include "mozilla/dom/PlacesObservers.h" @@ -74,6 +76,18 @@ return aBookmark.syncStatus == nsINavBookmarksService::SYNC_STATUS_NORMAL; } +inline nsresult GetTags(nsIURI* aURI, nsTArray& aResult) { + nsresult rv; + nsCOMPtr taggingService = + do_GetService("@mozilla.org/browser/tagging-service;1", &rv); + + if (NS_FAILED(rv)) { + return rv; + } + + return taggingService->GetTagsForURI(aURI, aResult); +} + } // namespace nsNavBookmarks::nsNavBookmarks() : mCanNotify(false) { @@ -421,28 +435,28 @@ rv = transaction.Commit(); NS_ENSURE_SUCCESS(rv, rv); - if (mCanNotify) { - Sequence> events; - nsAutoCString utf8spec; - aURI->GetSpec(utf8spec); + if (!mCanNotify) { + return NS_OK; + } - RefPtr bookmark = new PlacesBookmarkAddition(); - bookmark->mItemType = TYPE_BOOKMARK; - bookmark->mId = *aNewBookmarkId; - bookmark->mParentId = aFolder; - bookmark->mIndex = index; - bookmark->mUrl.Assign(NS_ConvertUTF8toUTF16(utf8spec)); - bookmark->mTitle.Assign(NS_ConvertUTF8toUTF16(title)); - bookmark->mDateAdded = dateAdded / 1000; - bookmark->mGuid.Assign(guid); - bookmark->mParentGuid.Assign(folderGuid); - bookmark->mSource = aSource; - bookmark->mIsTagging = grandParentId == mDB->GetTagsFolderId(); - bool success = !!events.AppendElement(bookmark.forget(), fallible); - MOZ_RELEASE_ASSERT(success); + Sequence> notifications; + nsAutoCString utf8spec; + aURI->GetSpec(utf8spec); - PlacesObservers::NotifyListeners(events); - } + RefPtr bookmark = new PlacesBookmarkAddition(); + bookmark->mItemType = TYPE_BOOKMARK; + bookmark->mId = *aNewBookmarkId; + bookmark->mParentId = aFolder; + bookmark->mIndex = index; + bookmark->mUrl.Assign(NS_ConvertUTF8toUTF16(utf8spec)); + bookmark->mTitle.Assign(NS_ConvertUTF8toUTF16(title)); + bookmark->mDateAdded = dateAdded / 1000; + bookmark->mGuid.Assign(guid); + bookmark->mParentGuid.Assign(folderGuid); + bookmark->mSource = aSource; + bookmark->mIsTagging = grandParentId == mDB->GetTagsFolderId(); + bool success = !!notifications.AppendElement(bookmark.forget(), fallible); + MOZ_RELEASE_ASSERT(success); // If the bookmark has been added to a tag container, notify all // bookmark-folder result nodes which contain a bookmark for the new @@ -453,19 +467,30 @@ rv = GetBookmarksForURI(aURI, bookmarks); NS_ENSURE_SUCCESS(rv, rv); + nsTArray tags; + rv = GetTags(aURI, tags); + NS_ENSURE_SUCCESS(rv, rv); + for (uint32_t i = 0; i < bookmarks.Length(); ++i) { // Check that bookmarks doesn't include the current tag itemId. MOZ_ASSERT(bookmarks[i].id != *aNewBookmarkId); - - NOTIFY_BOOKMARKS_OBSERVERS( - mCanNotify, mObservers, - OnItemChanged(bookmarks[i].id, "tags"_ns, false, ""_ns, - bookmarks[i].lastModified, TYPE_BOOKMARK, - bookmarks[i].parentId, bookmarks[i].guid, - bookmarks[i].parentGuid, ""_ns, aSource)); + RefPtr tagsChanged = new PlacesBookmarkTags(); + tagsChanged->mId = bookmarks[i].id; + tagsChanged->mItemType = TYPE_BOOKMARK; + tagsChanged->mUrl.Assign(NS_ConvertUTF8toUTF16(utf8spec)); + tagsChanged->mGuid = bookmarks[i].guid; + tagsChanged->mParentGuid = bookmarks[i].parentGuid; + tagsChanged->mTags.Assign(tags); + tagsChanged->mLastModified = bookmarks[i].lastModified / 1000; + tagsChanged->mSource = aSource; + tagsChanged->mIsTagging = false; + success = !!notifications.AppendElement(tagsChanged.forget(), fallible); + MOZ_RELEASE_ASSERT(success); } } + PlacesObservers::NotifyListeners(notifications); + return NS_OK; } @@ -550,27 +575,28 @@ NS_WARNING_ASSERTION(uri, "Invalid URI in RemoveItem"); } - if (mCanNotify) { - Sequence> events; - RefPtr bookmarkRef = new PlacesBookmarkRemoved(); - bookmarkRef->mItemType = bookmark.type; - bookmarkRef->mId = bookmark.id; - bookmarkRef->mParentId = bookmark.parentId; - bookmarkRef->mIndex = bookmark.position; - if (bookmark.type == TYPE_BOOKMARK) { - bookmarkRef->mUrl.Assign(NS_ConvertUTF8toUTF16(bookmark.url)); - } - bookmarkRef->mGuid.Assign(bookmark.guid); - bookmarkRef->mParentGuid.Assign(bookmark.parentGuid); - bookmarkRef->mSource = aSource; - bookmarkRef->mIsTagging = - bookmark.parentId == tagsRootId || bookmark.grandParentId == tagsRootId; - bookmarkRef->mIsDescendantRemoval = false; - bool success = !!events.AppendElement(bookmarkRef.forget(), fallible); - MOZ_RELEASE_ASSERT(success); + if (!mCanNotify) { + return NS_OK; + } - PlacesObservers::NotifyListeners(events); + Sequence> notifications; + RefPtr bookmarkRef = new PlacesBookmarkRemoved(); + bookmarkRef->mItemType = bookmark.type; + bookmarkRef->mId = bookmark.id; + bookmarkRef->mParentId = bookmark.parentId; + bookmarkRef->mIndex = bookmark.position; + if (bookmark.type == TYPE_BOOKMARK) { + bookmarkRef->mUrl.Assign(NS_ConvertUTF8toUTF16(bookmark.url)); } + bookmarkRef->mGuid.Assign(bookmark.guid); + bookmarkRef->mParentGuid.Assign(bookmark.parentGuid); + bookmarkRef->mSource = aSource; + bookmarkRef->mIsTagging = + bookmark.parentId == tagsRootId || bookmark.grandParentId == tagsRootId; + bookmarkRef->mIsDescendantRemoval = false; + bool success = !!notifications.AppendElement(bookmarkRef.forget(), fallible); + MOZ_RELEASE_ASSERT(success); + if (bookmark.type == TYPE_BOOKMARK && bookmark.grandParentId == tagsRootId && uri) { // If the removed bookmark was child of a tag container, notify a tags @@ -579,16 +605,31 @@ rv = GetBookmarksForURI(uri, bookmarks); NS_ENSURE_SUCCESS(rv, rv); + nsAutoCString utf8spec; + uri->GetSpec(utf8spec); + + nsTArray tags; + rv = GetTags(uri, tags); + NS_ENSURE_SUCCESS(rv, rv); + for (uint32_t i = 0; i < bookmarks.Length(); ++i) { - NOTIFY_BOOKMARKS_OBSERVERS( - mCanNotify, mObservers, - OnItemChanged(bookmarks[i].id, "tags"_ns, false, ""_ns, - bookmarks[i].lastModified, TYPE_BOOKMARK, - bookmarks[i].parentId, bookmarks[i].guid, - bookmarks[i].parentGuid, ""_ns, aSource)); + RefPtr tagsChanged = new PlacesBookmarkTags(); + tagsChanged->mId = bookmarks[i].id; + tagsChanged->mItemType = TYPE_BOOKMARK; + tagsChanged->mUrl.Assign(NS_ConvertUTF8toUTF16(utf8spec)); + tagsChanged->mGuid = bookmarks[i].guid; + tagsChanged->mParentGuid = bookmarks[i].parentGuid; + tagsChanged->mTags.Assign(tags); + tagsChanged->mLastModified = bookmarks[i].lastModified / 1000; + tagsChanged->mSource = aSource; + tagsChanged->mIsTagging = false; + success = !!notifications.AppendElement(tagsChanged.forget(), fallible); + MOZ_RELEASE_ASSERT(success); } } + PlacesObservers::NotifyListeners(notifications); + return NS_OK; } @@ -856,21 +897,24 @@ NS_WARNING_ASSERTION(uri, "Invalid URI in RemoveFolderChildren"); } - if (mCanNotify) { - RefPtr bookmark = new PlacesBookmarkRemoved(); - bookmark->mItemType = TYPE_BOOKMARK; - bookmark->mId = child.id; - bookmark->mParentId = child.parentId; - bookmark->mIndex = child.position; - bookmark->mUrl.Assign(NS_ConvertUTF8toUTF16(child.url)); - bookmark->mGuid.Assign(child.guid); - bookmark->mParentGuid.Assign(child.parentGuid); - bookmark->mSource = aSource; - bookmark->mIsTagging = (child.grandParentId == tagsRootId); - bookmark->mIsDescendantRemoval = (child.grandParentId != tagsRootId); - bool success = !!notifications.AppendElement(bookmark.forget(), fallible); - MOZ_RELEASE_ASSERT(success); + if (!mCanNotify) { + return NS_OK; } + + RefPtr bookmark = new PlacesBookmarkRemoved(); + bookmark->mItemType = TYPE_BOOKMARK; + bookmark->mId = child.id; + bookmark->mParentId = child.parentId; + bookmark->mIndex = child.position; + bookmark->mUrl.Assign(NS_ConvertUTF8toUTF16(child.url)); + bookmark->mGuid.Assign(child.guid); + bookmark->mParentGuid.Assign(child.parentGuid); + bookmark->mSource = aSource; + bookmark->mIsTagging = (child.grandParentId == tagsRootId); + bookmark->mIsDescendantRemoval = (child.grandParentId != tagsRootId); + bool success = !!notifications.AppendElement(bookmark.forget(), fallible); + MOZ_RELEASE_ASSERT(success); + if (child.type == TYPE_BOOKMARK && child.grandParentId == tagsRootId && uri) { // If the removed bookmark was a child of a tag container, notify all @@ -880,18 +924,31 @@ rv = GetBookmarksForURI(uri, bookmarks); NS_ENSURE_SUCCESS(rv, rv); + nsAutoCString utf8spec; + uri->GetSpec(utf8spec); + + nsTArray tags; + rv = GetTags(uri, tags); + NS_ENSURE_SUCCESS(rv, rv); + for (uint32_t i = 0; i < bookmarks.Length(); ++i) { - NOTIFY_BOOKMARKS_OBSERVERS( - mCanNotify, mObservers, - OnItemChanged(bookmarks[i].id, "tags"_ns, false, ""_ns, - bookmarks[i].lastModified, TYPE_BOOKMARK, - bookmarks[i].parentId, bookmarks[i].guid, - bookmarks[i].parentGuid, ""_ns, aSource)); + RefPtr tagsChanged = new PlacesBookmarkTags(); + tagsChanged->mId = bookmarks[i].id; + tagsChanged->mItemType = TYPE_BOOKMARK; + tagsChanged->mUrl.Assign(NS_ConvertUTF8toUTF16(utf8spec)); + tagsChanged->mGuid = bookmarks[i].guid; + tagsChanged->mParentGuid = bookmarks[i].parentGuid; + tagsChanged->mTags.Assign(tags); + tagsChanged->mLastModified = bookmarks[i].lastModified / 1000; + tagsChanged->mSource = aSource; + tagsChanged->mIsTagging = false; + success = !!notifications.AppendElement(tagsChanged.forget(), fallible); + MOZ_RELEASE_ASSERT(success); } } } - if (notifications.Length() > 0) { + if (notifications.Length()) { PlacesObservers::NotifyListeners(notifications); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/places/nsNavHistoryResult.cpp firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/places/nsNavHistoryResult.cpp --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/places/nsNavHistoryResult.cpp 2021-10-17 14:43:03.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/places/nsNavHistoryResult.cpp 2021-10-20 19:28:50.000000000 +0000 @@ -22,6 +22,7 @@ #include "mozilla/dom/PlacesVisitRemoved.h" #include "mozilla/dom/PlacesVisitTitle.h" #include "mozilla/dom/PlacesBookmarkMoved.h" +#include "mozilla/dom/PlacesBookmarkTags.h" #include "mozilla/dom/PlacesBookmarkTime.h" #include "mozilla/dom/PlacesBookmarkTitle.h" #include "mozilla/dom/PlacesBookmarkUrl.h" @@ -2483,25 +2484,6 @@ return Refresh(); } - // Some node could observe both bookmarks and history. But a node observing - // only history should never get a bookmark notification. - NS_WARNING_ASSERTION( - mResult && mResult->mIsBookmarksObserver, - "history observers should not get OnItemChanged, but should get the " - "corresponding history notifications instead"); - - // Tags in history queries are a special case since tags are per uri and - // we filter tags based on searchterms. - if (aItemType == nsINavBookmarksService::TYPE_BOOKMARK && - aProperty.EqualsLiteral("tags")) { - nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); - NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY); - nsCOMPtr uri; - nsresult rv = bookmarks->GetBookmarkURI(aItemId, getter_AddRefs(uri)); - NS_ENSURE_SUCCESS(rv, rv); - rv = NotifyIfTagsChanged(uri); - NS_ENSURE_SUCCESS(rv, rv); - } return NS_OK; } @@ -2522,6 +2504,19 @@ return NS_OK; } +nsresult nsNavHistoryQueryResultNode::OnItemTagsChanged(int64_t aItemId, + const nsAString& aURL) { + nsresult rv = nsNavHistoryResultNode::OnItemTagsChanged(aItemId, aURL); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr uri; + rv = NS_NewURI(getter_AddRefs(uri), aURL); + NS_ENSURE_SUCCESS(rv, rv); + rv = NotifyIfTagsChanged(uri); + NS_ENSURE_SUCCESS(rv, rv); + return NS_OK; +} + nsresult nsNavHistoryQueryResultNode::OnItemUrlChanged(int64_t aItemId, const nsACString& aGUID, const nsACString& aURL, @@ -3193,6 +3188,24 @@ return RemoveChildAt(index); } +nsresult nsNavHistoryResultNode::OnItemTagsChanged(int64_t aItemId, + const nsAString& aURL) { + if (aItemId != mItemId) { + return NS_OK; + } + + mTags.SetIsVoid(true); + + bool shouldNotify = !mParent || mParent->AreChildrenVisible(); + if (shouldNotify) { + nsNavHistoryResult* result = GetResult(); + NS_ENSURE_STATE(result); + NOTIFY_RESULT_OBSERVERS(result, NodeTagsChanged(this)); + } + + return NS_OK; +} + nsresult nsNavHistoryResultNode::OnItemTimeChanged(int64_t aItemId, const nsACString& aGUID, PRTime aDateAdded, @@ -3305,9 +3318,6 @@ NOTIFY_RESULT_OBSERVERS( result, NodeHistoryDetailsChanged(this, oldTime, mAccessCount)); } - } else if (aProperty.EqualsLiteral("tags")) { - mTags.SetIsVoid(true); - if (shouldNotify) NOTIFY_RESULT_OBSERVERS(result, NodeTagsChanged(this)); } else if (aProperty.EqualsLiteral("keyword")) { if (shouldNotify) NOTIFY_RESULT_OBSERVERS(result, NodeKeywordChanged(this, aNewValue)); @@ -3583,7 +3593,7 @@ } void nsNavHistoryResult::StopObserving() { - AutoTArray events; + AutoTArray events; events.AppendElement(PlacesEventType::Favicon_changed); if (mIsBookmarksObserver) { nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); @@ -3594,6 +3604,7 @@ events.AppendElement(PlacesEventType::Bookmark_added); events.AppendElement(PlacesEventType::Bookmark_removed); events.AppendElement(PlacesEventType::Bookmark_moved); + events.AppendElement(PlacesEventType::Bookmark_tags_changed); events.AppendElement(PlacesEventType::Bookmark_time_changed); events.AppendElement(PlacesEventType::Bookmark_title_changed); events.AppendElement(PlacesEventType::Bookmark_url_changed); @@ -3717,10 +3728,11 @@ return; } bookmarks->AddObserver(this, true); - AutoTArray events; + AutoTArray events; events.AppendElement(PlacesEventType::Bookmark_added); events.AppendElement(PlacesEventType::Bookmark_removed); events.AppendElement(PlacesEventType::Bookmark_moved); + events.AppendElement(PlacesEventType::Bookmark_tags_changed); events.AppendElement(PlacesEventType::Bookmark_time_changed); events.AppendElement(PlacesEventType::Bookmark_title_changed); events.AppendElement(PlacesEventType::Bookmark_url_changed); @@ -4291,6 +4303,18 @@ item->mParentGuid, item->mSource, url)); break; } + case PlacesEventType::Bookmark_tags_changed: { + const dom::PlacesBookmarkTags* tagsEvent = + event->AsPlacesBookmarkTags(); + if (NS_WARN_IF(!tagsEvent)) { + continue; + } + + ENUMERATE_BOOKMARK_CHANGED_OBSERVERS( + tagsEvent->mParentGuid, tagsEvent->mId, + OnItemTagsChanged(tagsEvent->mId, tagsEvent->mUrl)); + break; + } case PlacesEventType::Bookmark_time_changed: { const dom::PlacesBookmarkTime* timeEvent = event->AsPlacesBookmarkTime(); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/places/nsNavHistoryResult.h firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/places/nsNavHistoryResult.h --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/places/nsNavHistoryResult.h 2021-10-17 14:43:05.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/places/nsNavHistoryResult.h 2021-10-20 19:28:50.000000000 +0000 @@ -314,6 +314,7 @@ virtual void OnRemoving(); + nsresult OnItemTagsChanged(int64_t aItemId, const nsAString& aURL); nsresult OnItemTimeChanged(int64_t aItemId, const nsACString& aGUID, PRTime aDateAdded, PRTime aLastModified); nsresult OnItemTitleChanged(int64_t aItemId, const nsACString& aGUID, @@ -700,6 +701,7 @@ const nsACString& aOldParentGUID, const nsACString& aNewParentGUID, uint16_t aSource, const nsACString& aURI); + nsresult OnItemTagsChanged(int64_t aItemId, const nsAString& aURL); nsresult OnItemUrlChanged(int64_t aItemId, const nsACString& aGUID, const nsACString& aURL, PRTime aLastModified); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/places/tests/bookmarks/head_bookmarks.js firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/places/tests/bookmarks/head_bookmarks.js --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/places/tests/bookmarks/head_bookmarks.js 2021-10-17 14:43:04.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/places/tests/bookmarks/head_bookmarks.js 2021-10-20 19:28:49.000000000 +0000 @@ -123,6 +123,20 @@ isTagging: event.isTagging, }); break; + case "bookmark-tags-changed": + notifications.push({ + type: event.type, + id: event.id, + itemType: event.itemType, + url: event.url, + guid: event.guid, + parentGuid: event.parentGuid, + tags: event.tags, + lastModified: new Date(event.lastModified), + source: event.source, + isTagging: event.isTagging, + }); + break; case "bookmark-time-changed": notifications.push({ type: event.type, diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/places/tests/bookmarks/test_bookmarks_notifications.js firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/places/tests/bookmarks/test_bookmarks_notifications.js --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/places/tests/bookmarks/test_bookmarks_notifications.js 2021-10-17 14:43:03.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/places/tests/bookmarks/test_bookmarks_notifications.js 2021-10-20 19:28:51.000000000 +0000 @@ -150,15 +150,16 @@ url: new URL("http://tag.example.com/"), }); let itemId = await PlacesUtils.promiseItemId(bm.guid); - let parentId = await PlacesUtils.promiseItemId(bm.parentGuid); let tagFolder = await PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_FOLDER, parentGuid: PlacesUtils.bookmarks.tagsGuid, title: "tag", }); - let placesObserver = expectPlacesObserverNotifications(["bookmark-added"]); - let bookmarksObserver = expectNotifications(); + const observer = expectPlacesObserverNotifications([ + "bookmark-added", + "bookmark-tags-changed", + ]); let tag = await PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_BOOKMARK, parentGuid: tagFolder.guid, @@ -167,7 +168,7 @@ let tagId = await PlacesUtils.promiseItemId(tag.guid); let tagParentId = await PlacesUtils.promiseItemId(tag.parentGuid); - placesObserver.check([ + observer.check([ { type: "bookmark-added", id: tagId, @@ -182,24 +183,17 @@ source: Ci.nsINavBookmarksService.SOURCE_DEFAULT, isTagging: true, }, - ]); - - bookmarksObserver.check([ { - name: "onItemChanged", - arguments: [ - itemId, - "tags", - false, - "", - PlacesUtils.toPRTime(bm.lastModified), - bm.type, - parentId, - bm.guid, - bm.parentGuid, - "", - Ci.nsINavBookmarksService.SOURCE_DEFAULT, - ], + type: "bookmark-tags-changed", + id: itemId, + itemType: bm.type, + url: bm.url, + guid: bm.guid, + parentGuid: bm.parentGuid, + tags: ["tag"], + lastModified: bm.lastModified, + source: Ci.nsINavBookmarksService.SOURCE_DEFAULT, + isTagging: false, }, ]); }); @@ -545,7 +539,6 @@ url: new URL("http://untag.example.com/"), }); let itemId = await PlacesUtils.promiseItemId(bm.guid); - let parentId = await PlacesUtils.promiseItemId(bm.parentGuid); let tagFolder = await PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_FOLDER, @@ -560,11 +553,13 @@ let tagId = await PlacesUtils.promiseItemId(tag.guid); let tagParentId = await PlacesUtils.promiseItemId(tag.parentGuid); - let placesObserver = expectPlacesObserverNotifications(["bookmark-removed"]); - let observer = expectNotifications(); + const observer = expectPlacesObserverNotifications([ + "bookmark-removed", + "bookmark-tags-changed", + ]); await PlacesUtils.bookmarks.remove(tag.guid); - placesObserver.check([ + observer.check([ { type: "bookmark-removed", id: tagId, @@ -577,23 +572,17 @@ itemType: PlacesUtils.bookmarks.TYPE_BOOKMARK, isTagging: true, }, - ]); - observer.check([ { - name: "onItemChanged", - arguments: [ - itemId, - "tags", - false, - "", - PlacesUtils.toPRTime(bm.lastModified), - bm.type, - parentId, - bm.guid, - bm.parentGuid, - "", - Ci.nsINavBookmarksService.SOURCE_DEFAULT, - ], + type: "bookmark-tags-changed", + id: itemId, + itemType: bm.type, + url: bm.url, + guid: bm.guid, + parentGuid: bm.parentGuid, + tags: [], + lastModified: bm.lastModified, + source: Ci.nsINavBookmarksService.SOURCE_DEFAULT, + isTagging: false, }, ]); }); @@ -681,6 +670,94 @@ ]); }); +add_task(async function multiple_tags() { + const BOOKMARK_URL = "http://multipletags.example.com/"; + const TAG_NAMES = ["tag1", "tag2", "tag3", "tag4", "tag5", "tag6"]; + + const bm = await PlacesUtils.bookmarks.insert({ + type: PlacesUtils.bookmarks.TYPE_BOOKMARK, + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + url: new URL(BOOKMARK_URL), + }); + const itemId = await PlacesUtils.promiseItemId(bm.guid); + + info("Register all tags"); + const tagFolders = await Promise.all( + TAG_NAMES.map(tagName => + PlacesUtils.bookmarks.insert({ + type: PlacesUtils.bookmarks.TYPE_FOLDER, + parentGuid: PlacesUtils.bookmarks.tagsGuid, + title: tagName, + }) + ) + ); + + info("Test adding tags to bookmark"); + for (let i = 0; i < tagFolders.length; i++) { + const tagFolder = tagFolders[i]; + const expectedTagNames = TAG_NAMES.slice(0, i + 1); + + const observer = expectPlacesObserverNotifications([ + "bookmark-tags-changed", + ]); + + await PlacesUtils.bookmarks.insert({ + type: PlacesUtils.bookmarks.TYPE_BOOKMARK, + parentGuid: tagFolder.guid, + url: new URL(BOOKMARK_URL), + }); + + observer.check([ + { + type: "bookmark-tags-changed", + id: itemId, + itemType: bm.type, + url: bm.url, + guid: bm.guid, + parentGuid: bm.parentGuid, + tags: expectedTagNames, + lastModified: bm.lastModified, + source: Ci.nsINavBookmarksService.SOURCE_DEFAULT, + isTagging: false, + }, + ]); + } + + info("Test removing tags from bookmark"); + for (const removedLength of [1, 2, 3]) { + const removedTags = tagFolders.splice(0, removedLength); + + const observer = expectPlacesObserverNotifications([ + "bookmark-tags-changed", + ]); + + // We can remove multiple tags at one time. + await PlacesUtils.bookmarks.remove(removedTags); + + const expectedResults = []; + + for (let i = 0; i < removedLength; i++) { + TAG_NAMES.splice(0, 1); + const expectedTagNames = [...TAG_NAMES]; + + expectedResults.push({ + type: "bookmark-tags-changed", + id: itemId, + itemType: bm.type, + url: bm.url, + guid: bm.guid, + parentGuid: bm.parentGuid, + tags: expectedTagNames, + lastModified: bm.lastModified, + source: Ci.nsINavBookmarksService.SOURCE_DEFAULT, + isTagging: false, + }); + } + + observer.check(expectedResults); + } +}); + add_task(async function eraseEverything_notification() { // Let's start from a clean situation. await PlacesUtils.bookmarks.eraseEverything(); @@ -993,35 +1070,3 @@ }, ]); }); - -function expectNotifications() { - let notifications = []; - let observer = new Proxy(NavBookmarkObserver, { - get(target, name) { - if (name == "check") { - PlacesUtils.bookmarks.removeObserver(observer); - return expectedNotifications => - Assert.deepEqual(notifications, expectedNotifications); - } - - if (name.startsWith("onItem")) { - return (...origArgs) => { - let args = Array.from(origArgs, arg => { - if (arg && arg instanceof Ci.nsIURI) { - return new URL(arg.spec); - } - return arg; - }); - notifications.push({ name, arguments: args }); - }; - } - - if (name in target) { - return target[name]; - } - return undefined; - }, - }); - PlacesUtils.bookmarks.addObserver(observer); - return observer; -} diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/places/tests/bookmarks/test_nsINavBookmarkObserver.js firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/places/tests/bookmarks/test_nsINavBookmarkObserver.js --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/places/tests/bookmarks/test_nsINavBookmarkObserver.js 2021-10-17 14:43:04.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/places/tests/bookmarks/test_nsINavBookmarkObserver.js 2021-10-20 19:28:50.000000000 +0000 @@ -1,7 +1,7 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -// Tests that each nsINavBookmarksObserver method gets the correct input. +// Tests that each bookmark event gets the correct input. var gUnfiledFolderId; @@ -35,34 +35,9 @@ } }, - validate(aMethodName, aArguments) { - Assert.equal(this.expected[0].name, aMethodName); - - let args = this.expected.shift().args; - Assert.equal(aArguments.length, args.length); - for (let i = 0; i < aArguments.length; i++) { - Assert.ok( - args[i].check(aArguments[i]), - aMethodName + "(args[" + i + "]: " + args[i].name + ")" - ); - } - - if (this.expected.length === 0) { - this.deferred.resolve(); - } - }, - handlePlacesEvents(events) { this.validateEvents(events); }, - - // nsINavBookmarkObserver - onItemChanged() { - return this.validate("onItemChanged", arguments); - }, - - // nsISupports - QueryInterface: ChromeUtils.generateQI(["nsINavBookmarkObserver"]), }; var gBookmarkSkipObserver = { @@ -88,29 +63,12 @@ } }, - validate(aMethodName) { - Assert.equal(this.expected.shift(), aMethodName); - if (this.expected.length === 0) { - this.deferred.resolve(); - } - }, - handlePlacesEvents(events) { this.validateEvents(events); }, - - // nsINavBookmarkObserver - onItemChanged() { - return this.validate("onItemChanged", arguments); - }, - - // nsISupports - QueryInterface: ChromeUtils.generateQI(["nsINavBookmarkObserver"]), }; add_task(async function setup() { - PlacesUtils.bookmarks.addObserver(gBookmarksObserver); - PlacesUtils.bookmarks.addObserver(gBookmarkSkipObserver); gUnfiledFolderId = await PlacesUtils.promiseItemId( PlacesUtils.bookmarks.unfiledGuid ); @@ -125,6 +83,7 @@ "bookmark-added", "bookmark-removed", "bookmark-moved", + "bookmark-tags-changed", "bookmark-title-changed", ], gBookmarksObserver.handlePlacesEvents @@ -134,6 +93,7 @@ "bookmark-added", "bookmark-removed", "bookmark-moved", + "bookmark-tags-changed", "bookmark-title-changed", ], gBookmarkSkipObserver.handlePlacesEvents @@ -268,7 +228,7 @@ await promise; }); -add_task(async function onItemChanged_title_bookmark() { +add_task(async function bookmarkTitleChanged() { let bm = await PlacesUtils.bookmarks.fetch({ parentGuid: PlacesUtils.bookmarks.unfiledGuid, index: 0, @@ -304,7 +264,7 @@ await promise; }); -add_task(async function onItemChanged_tags_bookmark() { +add_task(async function bookmarkTagsChanged() { let bm = await PlacesUtils.bookmarks.fetch({ parentGuid: PlacesUtils.bookmarks.unfiledGuid, index: 0, @@ -312,7 +272,10 @@ let uri = Services.io.newURI(bm.url.href); const TAG = "tag"; let promise = Promise.all([ - gBookmarkSkipObserver.setup(["onItemChanged", "onItemChanged"]), + gBookmarkSkipObserver.setup([ + "bookmark-tags-changed", + "bookmark-tags-changed", + ]), gBookmarksObserver.setup([ { eventType: "bookmark-added", // This is the tag folder. @@ -371,18 +334,13 @@ ], }, { - name: "onItemChanged", + eventType: "bookmark-tags-changed", args: [ - { name: "itemId", check: v => typeof v == "number" && v > 0 }, - { name: "property", check: v => v === "tags" }, - { name: "isAnno", check: v => v === false }, - { name: "newValue", check: v => v === "" }, - { name: "lastModified", check: v => typeof v == "number" && v > 0 }, + { name: "id", check: v => typeof v == "number" && v > 0 }, { name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_BOOKMARK, }, - { name: "parentId", check: v => v === gUnfiledFolderId }, { name: "guid", check: v => typeof v == "string" && PlacesUtils.isValidGuid(v), @@ -391,12 +349,16 @@ name: "parentGuid", check: v => typeof v == "string" && PlacesUtils.isValidGuid(v), }, - { name: "oldValue", check: v => typeof v == "string" }, + { name: "lastModified", check: v => typeof v == "number" && v > 0 }, { name: "source", check: v => Object.values(PlacesUtils.bookmarks.SOURCES).includes(v), }, + { + name: "isTagging", + check: v => v === false, + }, ], }, { @@ -425,20 +387,14 @@ }, ], }, - { - name: "onItemChanged", + eventType: "bookmark-tags-changed", args: [ - { name: "itemId", check: v => typeof v == "number" && v > 0 }, - { name: "property", check: v => v === "tags" }, - { name: "isAnno", check: v => v === false }, - { name: "newValue", check: v => v === "" }, - { name: "lastModified", check: v => typeof v == "number" && v > 0 }, + { name: "id", check: v => typeof v == "number" && v > 0 }, { name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_BOOKMARK, }, - { name: "parentId", check: v => v === gUnfiledFolderId }, { name: "guid", check: v => typeof v == "string" && PlacesUtils.isValidGuid(v), @@ -447,12 +403,16 @@ name: "parentGuid", check: v => typeof v == "string" && PlacesUtils.isValidGuid(v), }, - { name: "oldValue", check: v => typeof v == "string" }, + { name: "lastModified", check: v => typeof v == "number" && v > 0 }, { name: "source", check: v => Object.values(PlacesUtils.bookmarks.SOURCES).includes(v), }, + { + name: "isTagging", + check: v => v === false, + }, ], }, { @@ -952,14 +912,24 @@ }); add_task(function cleanup() { - PlacesUtils.bookmarks.removeObserver(gBookmarksObserver); - PlacesUtils.bookmarks.removeObserver(gBookmarkSkipObserver); PlacesUtils.observers.removeListener( - ["bookmark-added"], + [ + "bookmark-added", + "bookmark-removed", + "bookmark-moved", + "bookmark-tags-changed", + "bookmark-title-changed", + ], gBookmarksObserver.handlePlacesEvents ); PlacesUtils.observers.removeListener( - ["bookmark-added"], + [ + "bookmark-added", + "bookmark-removed", + "bookmark-moved", + "bookmark-tags-changed", + "bookmark-title-changed", + ], gBookmarkSkipObserver.handlePlacesEvents ); }); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/places/tests/browser/history_post.sjs firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/places/tests/browser/history_post.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/places/tests/browser/history_post.sjs 2021-10-17 14:43:04.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/places/tests/browser/history_post.sjs 2021-10-20 19:28:50.000000000 +0000 @@ -1,5 +1,4 @@ -function handleRequest(request, response) -{ +function handleRequest(request, response) { response.setStatusLine("1.0", 200, "OK"); response.setHeader("Content-Type", "text/plain; charset=utf-8", false); response.write("Ciao"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/places/tests/browser/redirect_once.sjs firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/places/tests/browser/redirect_once.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/places/tests/browser/redirect_once.sjs 2021-10-17 14:43:04.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/places/tests/browser/redirect_once.sjs 2021-10-20 19:28:51.000000000 +0000 @@ -5,5 +5,9 @@ function handleRequest(request, response) { response.setStatusLine("1.1", 301, "Found"); - response.setHeader("Location", "http://test1.example.com/tests/toolkit/components/places/tests/browser/final.html", false); + response.setHeader( + "Location", + "http://test1.example.com/tests/toolkit/components/places/tests/browser/final.html", + false + ); } diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/places/tests/browser/redirect.sjs firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/places/tests/browser/redirect.sjs --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/places/tests/browser/redirect.sjs 2021-10-17 14:43:04.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/places/tests/browser/redirect.sjs 2021-10-20 19:28:50.000000000 +0000 @@ -2,8 +2,7 @@ * 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/. */ -function handleRequest(request, response) -{ +function handleRequest(request, response) { let page = "

Redirecting...

"; response.setStatusLine(request.httpVersion, "301", "Moved Permanently"); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/places/tests/chrome/head.js firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/places/tests/chrome/head.js --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/places/tests/chrome/head.js 2021-10-17 14:43:05.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/places/tests/chrome/head.js 2021-10-20 19:28:50.000000000 +0000 @@ -2,18 +2,8 @@ "resource://gre/modules/XPCOMUtils.jsm" ); -ChromeUtils.defineModuleGetter( - this, - "PlacesTestUtils", - "resource://testing-common/PlacesTestUtils.jsm" -); -ChromeUtils.defineModuleGetter( - this, - "PlacesUtils", - "resource://gre/modules/PlacesUtils.jsm" -); -ChromeUtils.defineModuleGetter( - this, - "NetUtil", - "resource://gre/modules/NetUtil.jsm" -); +XPCOMUtils.defineLazyModuleGetters(this, { + PlacesTestUtils: "resource://testing-common/PlacesTestUtils.jsm", + PlacesUtils: "resource://gre/modules/PlacesUtils.jsm", + Services: "resource://gre/modules/Services.jsm", +}); diff -Nru firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/places/tests/chrome/test_371798.xhtml firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/places/tests/chrome/test_371798.xhtml --- firefox-trunk-95.0~a1~hg20211017r596111/toolkit/components/places/tests/chrome/test_371798.xhtml 2021-10-17 14:43:04.000000000 +0000 +++ firefox-trunk-95.0~a1~hg20211020r596404/toolkit/components/places/tests/chrome/test_371798.xhtml 2021-10-20 19:28:51.000000000 +0000 @@ -5,22 +5,17 @@ - + +