diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:13:33 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:13:33 +0000 |
commit | 086c044dc34dfc0f74fbe41f4ecb402b2cd34884 (patch) | |
tree | a4f824bd33cb075dd5aa3eb5a0a94af221bbe83a /dom/base | |
parent | Adding debian version 124.0.1-1. (diff) | |
download | firefox-086c044dc34dfc0f74fbe41f4ecb402b2cd34884.tar.xz firefox-086c044dc34dfc0f74fbe41f4ecb402b2cd34884.zip |
Merging upstream version 125.0.1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/base')
120 files changed, 1722 insertions, 3375 deletions
diff --git a/dom/base/BodyUtil.cpp b/dom/base/BodyUtil.cpp index e5d86a3c36..e8de3d18ec 100644 --- a/dom/base/BodyUtil.cpp +++ b/dom/base/BodyUtil.cpp @@ -267,7 +267,7 @@ class MOZ_STACK_CLASS FormDataParser { } // Determine boundary from mimetype. - UniquePtr<CMimeType> parsed = CMimeType::Parse(mMixedCaseMimeType); + RefPtr<CMimeType> parsed = CMimeType::Parse(mMixedCaseMimeType); if (!parsed) { return false; } @@ -422,7 +422,7 @@ already_AddRefed<FormData> BodyUtil::ConsumeFormData( if (isValidUrlEncodedMimeType) { RefPtr<FormData> fd = new FormData(aParent); DebugOnly<bool> status = URLParams::Parse( - aStr, [&fd](const nsAString& aName, const nsAString& aValue) { + aStr, true, [&fd](const nsAString& aName, const nsAString& aValue) { ErrorResult rv; fd->Append(aName, aValue, rv); MOZ_ASSERT(!rv.Failed()); diff --git a/dom/base/CharacterData.cpp b/dom/base/CharacterData.cpp index b4809a0293..eccf9fe4d9 100644 --- a/dom/base/CharacterData.cpp +++ b/dom/base/CharacterData.cpp @@ -11,32 +11,22 @@ #include "mozilla/dom/CharacterData.h" -#include "mozilla/DebugOnly.h" - #include "mozilla/AsyncEventDispatcher.h" -#include "mozilla/MemoryReporting.h" #include "mozilla/dom/BindContext.h" #include "mozilla/dom/Element.h" -#include "mozilla/dom/HTMLSlotElement.h" #include "mozilla/dom/MutationObservers.h" #include "mozilla/dom/ShadowRoot.h" #include "mozilla/dom/Document.h" +#include "mozilla/dom/UnbindContext.h" #include "nsReadableUtils.h" #include "mozilla/InternalMutationEvent.h" -#include "nsCOMPtr.h" -#include "nsDOMString.h" -#include "nsChangeHint.h" -#include "nsCOMArray.h" #include "mozilla/dom/DirectionalityUtils.h" -#include "nsCCUncollectableMarker.h" #include "mozAutoDocUpdate.h" #include "nsIContentInlines.h" #include "nsTextNode.h" #include "nsBidiUtils.h" -#include "PLDHashTable.h" #include "mozilla/Sprintf.h" #include "nsWindowSizes.h" -#include "nsWrapperCacheInlines.h" #if defined(ACCESSIBILITY) && defined(DEBUG) # include "nsAccessibilityService.h" @@ -478,13 +468,14 @@ nsresult CharacterData::BindToTree(BindContext& aContext, nsINode& aParent) { return NS_OK; } -void CharacterData::UnbindFromTree(bool aNullParent) { +void CharacterData::UnbindFromTree(UnbindContext& aContext) { // Unset frame flags; if we need them again later, they'll get set again. UnsetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE | NS_REFRAME_IF_WHITESPACE); - HandleShadowDOMRelatedRemovalSteps(aNullParent); + const bool nullParent = aContext.IsUnbindRoot(this); + HandleShadowDOMRelatedRemovalSteps(nullParent); - if (aNullParent) { + if (nullParent) { if (GetParent()) { NS_RELEASE(mParent); } else { @@ -495,15 +486,13 @@ void CharacterData::UnbindFromTree(bool aNullParent) { ClearInDocument(); SetIsConnected(false); - if (aNullParent || !mParent->IsInShadowTree()) { + if (nullParent || !mParent->IsInShadowTree()) { UnsetFlags(NODE_IS_IN_SHADOW_TREE); // Begin keeping track of our subtree root. - SetSubtreeRootPointer(aNullParent ? this : mParent->SubtreeRoot()); - } + SetSubtreeRootPointer(nullParent ? this : mParent->SubtreeRoot()); - if (nsExtendedContentSlots* slots = GetExistingExtendedContentSlots()) { - if (aNullParent || !mParent->IsInShadowTree()) { + if (nsExtendedContentSlots* slots = GetExistingExtendedContentSlots()) { slots->mContainingShadow = nullptr; } } diff --git a/dom/base/CharacterData.h b/dom/base/CharacterData.h index 8e008b134b..50ff159eef 100644 --- a/dom/base/CharacterData.h +++ b/dom/base/CharacterData.h @@ -106,7 +106,7 @@ class CharacterData : public nsIContent { // Implementation for nsIContent nsresult BindToTree(BindContext&, nsINode& aParent) override; - void UnbindFromTree(bool aNullParent = true) override; + void UnbindFromTree(UnbindContext&) override; const nsTextFragment* GetText() override { return &mText; } uint32_t TextLength() const final { return TextDataLength(); } diff --git a/dom/base/ChromeUtils.cpp b/dom/base/ChromeUtils.cpp index be06bb083a..0df1cd3c9b 100644 --- a/dom/base/ChromeUtils.cpp +++ b/dom/base/ChromeUtils.cpp @@ -602,23 +602,6 @@ void ChromeUtils::Import(const GlobalObject& aGlobal, aRetval.set(exports); } -static mozJSModuleLoader* GetContextualESLoader( - const Optional<bool>& aLoadInDevToolsLoader, JSObject* aGlobal) { - RefPtr devToolsModuleloader = mozJSModuleLoader::GetDevToolsLoader(); - // We should load the module in the DevTools loader if: - // - ChromeUtils.importESModule's `loadInDevToolsLoader` option is true, or, - // - if the callsite is from a module loaded in the DevTools loader and - // `loadInDevToolsLoader` isn't an explicit false. - bool shouldUseDevToolsLoader = - (aLoadInDevToolsLoader.WasPassed() && aLoadInDevToolsLoader.Value()) || - (devToolsModuleloader && !aLoadInDevToolsLoader.WasPassed() && - devToolsModuleloader->IsLoaderGlobal(aGlobal)); - if (shouldUseDevToolsLoader) { - return mozJSModuleLoader::GetOrCreateDevToolsLoader(); - } - return mozJSModuleLoader::Get(); -} - static mozJSModuleLoader* GetModuleLoaderForCurrentGlobal( JSContext* aCx, const GlobalObject& aGlobal, Maybe<loader::NonSharedGlobalSyncModuleLoaderScope>& @@ -629,7 +612,7 @@ static mozJSModuleLoader* GetModuleLoaderForCurrentGlobal( return mozJSModuleLoader::Get(); } if (mozJSModuleLoader::IsDevToolsLoaderGlobal(global)) { - return mozJSModuleLoader::GetOrCreateDevToolsLoader(); + return mozJSModuleLoader::GetOrCreateDevToolsLoader(aCx); } if (loader::NonSharedGlobalSyncModuleLoaderScope::IsActive()) { @@ -681,7 +664,7 @@ static mozJSModuleLoader* GetModuleLoaderForOptions( Maybe<loader::NonSharedGlobalSyncModuleLoaderScope>& aMaybeSyncLoaderScope) { if (!aOptions.mGlobal.WasPassed()) { - return GetContextualESLoader(aOptions.mLoadInDevToolsLoader, aGlobal.Get()); + return mozJSModuleLoader::Get(); } switch (aOptions.mGlobal.Value()) { @@ -689,7 +672,7 @@ static mozJSModuleLoader* GetModuleLoaderForOptions( return mozJSModuleLoader::Get(); case ImportESModuleTargetGlobal::Devtools: - return mozJSModuleLoader::GetOrCreateDevToolsLoader(); + return mozJSModuleLoader::GetOrCreateDevToolsLoader(aCx); case ImportESModuleTargetGlobal::Contextual: { if (!NS_IsMainThread()) { @@ -700,7 +683,7 @@ static mozJSModuleLoader* GetModuleLoaderForOptions( RefPtr devToolsModuleloader = mozJSModuleLoader::GetDevToolsLoader(); if (devToolsModuleloader && devToolsModuleloader->IsLoaderGlobal(aGlobal.Get())) { - return mozJSModuleLoader::GetOrCreateDevToolsLoader(); + return mozJSModuleLoader::GetOrCreateDevToolsLoader(aCx); } return mozJSModuleLoader::Get(); } @@ -715,7 +698,8 @@ static mozJSModuleLoader* GetModuleLoaderForOptions( } static bool ValidateImportOptions( - JSContext* aCx, const ImportESModuleOptionsDictionary& aOptions) { + JSContext* aCx, const GlobalObject& aGlobal, + const ImportESModuleOptionsDictionary& aOptions) { if (!NS_IsMainThread() && (!aOptions.mGlobal.WasPassed() || (aOptions.mGlobal.Value() != ImportESModuleTargetGlobal::Current && @@ -727,12 +711,17 @@ static bool ValidateImportOptions( return false; } - if (aOptions.mGlobal.WasPassed() && - aOptions.mLoadInDevToolsLoader.WasPassed()) { - JS_ReportErrorASCII(aCx, - "global option and loadInDevToolsLoader option " - "cannot be used at the same time"); - return false; + if (NS_IsMainThread()) { + nsCOMPtr<nsIGlobalObject> global = + do_QueryInterface(aGlobal.GetAsSupports()); + + if (mozJSModuleLoader::IsDevToolsLoaderGlobal(global) && + !aOptions.mGlobal.WasPassed()) { + JS_ReportErrorASCII(aCx, + "ChromeUtils.importESModule: global option is " + "required in DevTools distinct global"); + return false; + } } return true; @@ -745,7 +734,7 @@ void ChromeUtils::ImportESModule( JS::MutableHandle<JSObject*> aRetval, ErrorResult& aRv) { JSContext* cx = aGlobal.Context(); - if (!ValidateImportOptions(cx, aOptions)) { + if (!ValidateImportOptions(cx, aGlobal, aOptions)) { aRv.Throw(NS_ERROR_FAILURE); return; } @@ -789,21 +778,11 @@ void ChromeUtils::ImportESModule( class EncodedOptions { public: explicit EncodedOptions(const ImportESModuleOptionsDictionary& aOptions) { - uint32_t globalFlag = 0; if (aOptions.mGlobal.WasPassed()) { - globalFlag = uint32_t(aOptions.mGlobal.Value()) + 1; - } - - uint32_t devtoolsFlag = 0; - if (aOptions.mLoadInDevToolsLoader.WasPassed()) { - if (aOptions.mLoadInDevToolsLoader.Value()) { - devtoolsFlag = DevToolsFlag_True; - } else { - devtoolsFlag = DevToolsFlag_False; - } + mValue = uint32_t(aOptions.mGlobal.Value()) + 1; + } else { + mValue = 0; } - - mValue = globalFlag | devtoolsFlag; } explicit EncodedOptions(uint32_t aValue) : mValue(aValue) {} @@ -811,37 +790,14 @@ class EncodedOptions { int32_t toInt32() const { return int32_t(mValue); } void DecodeInto(ImportESModuleOptionsDictionary& aOptions) { - uint32_t globalFlag = mValue & GlobalFlag_Mask; - if (globalFlag == 0) { + if (mValue == 0) { aOptions.mGlobal.Reset(); } else { - aOptions.mGlobal.Construct(ImportESModuleTargetGlobal(globalFlag - 1)); - } - - uint32_t devtoolsFlag = mValue & DevToolsFlag_Mask; - switch (devtoolsFlag) { - case DevToolsFlag_NotPassed: - aOptions.mLoadInDevToolsLoader.Reset(); - break; - case DevToolsFlag_False: - aOptions.mLoadInDevToolsLoader.Construct(false); - break; - case DevToolsFlag_True: - aOptions.mLoadInDevToolsLoader.Construct(true); - break; - default: - MOZ_CRASH("Unknown DevToolsFlag"); + aOptions.mGlobal.Construct(ImportESModuleTargetGlobal(mValue - 1)); } } private: - static constexpr uint32_t GlobalFlag_Mask = 0xF; - - static constexpr uint32_t DevToolsFlag_NotPassed = 0x00; - static constexpr uint32_t DevToolsFlag_False = 0x10; - static constexpr uint32_t DevToolsFlag_True = 0x20; - static constexpr uint32_t DevToolsFlag_Mask = 0x0F0; - uint32_t mValue = 0; }; @@ -1017,10 +973,6 @@ static bool ModuleGetterImpl(JSContext* aCx, unsigned aArgc, JS::Value* aVp, ImportESModuleOptionsDictionary options; encodedOptions.DecodeInto(options); - if (!ValidateImportOptions(aCx, options)) { - return false; - } - GlobalObject global(aCx, callee); Maybe<loader::NonSharedGlobalSyncModuleLoaderScope> maybeSyncLoaderScope; @@ -1198,6 +1150,11 @@ void ChromeUtils::DefineESModuleGetters( return; } + if (!ValidateImportOptions(cx, global, aOptions)) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + EncodedOptions encodedOptions(aOptions); JS::Rooted<JS::PropertyKey> prop(cx); @@ -1411,7 +1368,8 @@ void ChromeUtils::ClearStyleSheetCache(GlobalObject&) { static WebIDLProcType ProcTypeToWebIDL(mozilla::ProcType aType) { // Max is the value of the last enum, not the length, so add one. static_assert( - WebIDLProcTypeValues::Count == static_cast<size_t>(ProcType::Max) + 1, + static_cast<size_t>(MaxContiguousEnumValue<WebIDLProcType>::value) == + static_cast<size_t>(ProcType::Max), "In order for this static cast to be okay, " "WebIDLProcType must match ProcType exactly"); @@ -2118,9 +2076,9 @@ unsigned ChromeUtils::AliveUtilityProcesses(const GlobalObject&) { void ChromeUtils::GetAllPossibleUtilityActorNames(GlobalObject& aGlobal, nsTArray<nsCString>& aNames) { aNames.Clear(); - for (size_t i = 0; i < WebIDLUtilityActorNameValues::Count; ++i) { - auto idlName = static_cast<UtilityActorName>(i); - aNames.AppendElement(WebIDLUtilityActorNameValues::GetString(idlName)); + for (UtilityActorName idlName : + MakeWebIDLEnumeratedRange<WebIDLUtilityActorName>()) { + aNames.AppendElement(GetEnumString(idlName)); } } diff --git a/dom/base/ChromeUtils.h b/dom/base/ChromeUtils.h index 804fc44e4a..4c2d043ecb 100644 --- a/dom/base/ChromeUtils.h +++ b/dom/base/ChromeUtils.h @@ -129,8 +129,7 @@ class ChromeUtils { static bool IsOriginAttributesEqualIgnoringFPD( const dom::OriginAttributesDictionary& aA, const dom::OriginAttributesDictionary& aB) { - return aA.mInIsolatedMozBrowser == aB.mInIsolatedMozBrowser && - aA.mUserContextId == aB.mUserContextId && + return aA.mUserContextId == aB.mUserContextId && aA.mPrivateBrowsingId == aB.mPrivateBrowsingId; } diff --git a/dom/base/ContentProcessMessageManager.cpp b/dom/base/ContentProcessMessageManager.cpp index 7fe9f4c2c7..7661d1036f 100644 --- a/dom/base/ContentProcessMessageManager.cpp +++ b/dom/base/ContentProcessMessageManager.cpp @@ -6,7 +6,6 @@ #include "ContentProcessMessageManager.h" -#include "nsContentCID.h" #include "mozilla/dom/ContentChild.h" #include "mozilla/dom/MessageManagerBinding.h" #include "mozilla/dom/ParentProcessMessageManager.h" @@ -31,7 +30,7 @@ ContentProcessMessageManager::~ContentProcessMessageManager() { ContentProcessMessageManager* ContentProcessMessageManager::Get() { nsCOMPtr<nsIMessageSender> service = - do_GetService(NS_CHILDPROCESSMESSAGEMANAGER_CONTRACTID); + do_GetService("@mozilla.org/childprocessmessagemanager;1"); if (!service) { return nullptr; } diff --git a/dom/base/DOMParser.cpp b/dom/base/DOMParser.cpp index 87bac20093..b3cf6ba04b 100644 --- a/dom/base/DOMParser.cpp +++ b/dom/base/DOMParser.cpp @@ -181,12 +181,10 @@ already_AddRefed<Document> DOMParser::ParseFromStream(nsIInputStream* aStream, // Create a fake channel nsCOMPtr<nsIChannel> parserChannel; - NS_NewInputStreamChannel( - getter_AddRefs(parserChannel), mDocumentURI, - nullptr, // aStream - mPrincipal, nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL, - nsIContentPolicy::TYPE_OTHER, - nsDependentCSubstring(SupportedTypeValues::GetString(aType))); + NS_NewInputStreamChannel(getter_AddRefs(parserChannel), mDocumentURI, + nullptr, // aStream + mPrincipal, nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL, + nsIContentPolicy::TYPE_OTHER, GetEnumString(aType)); if (NS_WARN_IF(!parserChannel)) { aRv.Throw(NS_ERROR_UNEXPECTED); return nullptr; diff --git a/dom/base/DOMRequest.cpp b/dom/base/DOMRequest.cpp deleted file mode 100644 index 93c1d75d89..0000000000 --- a/dom/base/DOMRequest.cpp +++ /dev/null @@ -1,256 +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/. */ - -#include "DOMRequest.h" - -#include "DOMException.h" -#include "nsThreadUtils.h" -#include "mozilla/HoldDropJSObjects.h" -#include "mozilla/ErrorResult.h" -#include "mozilla/dom/Event.h" -#include "mozilla/dom/Promise.h" -#include "mozilla/dom/ScriptSettings.h" -#include "jsfriendapi.h" -#include "nsContentUtils.h" - -using mozilla::dom::AnyCallback; -using mozilla::dom::AutoJSAPI; -using mozilla::dom::DOMException; -using mozilla::dom::DOMRequest; -using mozilla::dom::DOMRequestService; -using mozilla::dom::Promise; -using mozilla::dom::RootingCx; - -DOMRequest::DOMRequest(nsPIDOMWindowInner* aWindow) - : DOMEventTargetHelper(aWindow), - mResult(JS::UndefinedValue()), - mDone(false) {} - -DOMRequest::DOMRequest(nsIGlobalObject* aGlobal) - : DOMEventTargetHelper(aGlobal), - mResult(JS::UndefinedValue()), - mDone(false) {} - -DOMRequest::~DOMRequest() { mozilla::DropJSObjects(this); } - -NS_IMPL_CYCLE_COLLECTION_INHERITED_WITH_JS_MEMBERS(DOMRequest, - DOMEventTargetHelper, - (mError, mPromise), - (mResult)) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMRequest) -NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) - -NS_IMPL_ADDREF_INHERITED(DOMRequest, DOMEventTargetHelper) -NS_IMPL_RELEASE_INHERITED(DOMRequest, DOMEventTargetHelper) - -/* virtual */ -JSObject* DOMRequest::WrapObject(JSContext* aCx, - JS::Handle<JSObject*> aGivenProto) { - return DOMRequest_Binding::Wrap(aCx, this, aGivenProto); -} - -void DOMRequest::FireSuccess(JS::Handle<JS::Value> aResult) { - NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!"); - NS_ASSERTION(!mError, "mError shouldn't have been set!"); - NS_ASSERTION(mResult.isUndefined(), "mResult shouldn't have been set!"); - - mDone = true; - if (aResult.isGCThing()) { - RootResultVal(); - } - mResult = aResult; - - FireEvent(u"success"_ns, false, false); - - if (mPromise) { - mPromise->MaybeResolve(mResult); - } -} - -void DOMRequest::FireError(const nsAString& aError) { - NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!"); - NS_ASSERTION(!mError, "mError shouldn't have been set!"); - NS_ASSERTION(mResult.isUndefined(), "mResult shouldn't have been set!"); - - mDone = true; - // XXX Error code chosen arbitrarily - mError = DOMException::Create(NS_ERROR_DOM_UNKNOWN_ERR, - NS_ConvertUTF16toUTF8(aError)); - - FireEvent(u"error"_ns, true, true); - - if (mPromise) { - mPromise->MaybeRejectBrokenly(mError); - } -} - -void DOMRequest::FireError(nsresult aError) { - NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!"); - NS_ASSERTION(!mError, "mError shouldn't have been set!"); - NS_ASSERTION(mResult.isUndefined(), "mResult shouldn't have been set!"); - - mDone = true; - mError = DOMException::Create(aError); - - FireEvent(u"error"_ns, true, true); - - if (mPromise) { - mPromise->MaybeRejectBrokenly(mError); - } -} - -void DOMRequest::FireDetailedError(DOMException& aError) { - NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!"); - NS_ASSERTION(!mError, "mError shouldn't have been set!"); - NS_ASSERTION(mResult.isUndefined(), "mResult shouldn't have been set!"); - - mDone = true; - mError = &aError; - - FireEvent(u"error"_ns, true, true); - - if (mPromise) { - mPromise->MaybeRejectBrokenly(mError); - } -} - -void DOMRequest::FireEvent(const nsAString& aType, bool aBubble, - bool aCancelable) { - if (NS_FAILED(CheckCurrentGlobalCorrectness())) { - return; - } - - RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr); - event->InitEvent(aType, aBubble, aCancelable); - event->SetTrusted(true); - - DispatchEvent(*event); -} - -void DOMRequest::RootResultVal() { mozilla::HoldJSObjects(this); } - -void DOMRequest::Then(JSContext* aCx, AnyCallback* aResolveCallback, - AnyCallback* aRejectCallback, - JS::MutableHandle<JS::Value> aRetval, - mozilla::ErrorResult& aRv) { - if (!mPromise) { - mPromise = Promise::Create(DOMEventTargetHelper::GetParentObject(), aRv); - if (aRv.Failed()) { - return; - } - if (mDone) { - // Since we create mPromise lazily, it's possible that the DOMRequest - // object has already fired its success/error event. In that case we - // should manually resolve/reject mPromise here. mPromise will take care - // of calling the callbacks on |promise| as needed. - if (mError) { - mPromise->MaybeRejectBrokenly(mError); - } else { - mPromise->MaybeResolve(mResult); - } - } - } - - // Just use the global of the Promise itself as the callee global. - JS::Rooted<JSObject*> global(aCx, mPromise->PromiseObj()); - global = JS::GetNonCCWObjectGlobal(global); - mPromise->Then(aCx, global, aResolveCallback, aRejectCallback, aRetval, aRv); -} - -NS_IMPL_ISUPPORTS(DOMRequestService, nsIDOMRequestService) - -NS_IMETHODIMP -DOMRequestService::CreateRequest(mozIDOMWindow* aWindow, - DOMRequest** aRequest) { - MOZ_ASSERT(NS_IsMainThread()); - NS_ENSURE_STATE(aWindow); - auto* win = nsPIDOMWindowInner::From(aWindow); - RefPtr<DOMRequest> req = new DOMRequest(win); - req.forget(aRequest); - - return NS_OK; -} - -NS_IMETHODIMP -DOMRequestService::FireSuccess(DOMRequest* aRequest, - JS::Handle<JS::Value> aResult) { - NS_ENSURE_STATE(aRequest); - aRequest->FireSuccess(aResult); - - return NS_OK; -} - -NS_IMETHODIMP -DOMRequestService::FireError(DOMRequest* aRequest, const nsAString& aError) { - NS_ENSURE_STATE(aRequest); - aRequest->FireError(aError); - - return NS_OK; -} - -class FireSuccessAsyncTask : public mozilla::Runnable { - FireSuccessAsyncTask(DOMRequest* aRequest, const JS::Value& aResult) - : mozilla::Runnable("FireSuccessAsyncTask"), - mReq(aRequest), - mResult(RootingCx(), aResult) {} - - public: - // Due to the fact that initialization can fail during shutdown (since we - // can't fetch a js context), set up an initiatization function to make sure - // we can return the failure appropriately - static nsresult Dispatch(DOMRequest* aRequest, const JS::Value& aResult) { - RefPtr<FireSuccessAsyncTask> asyncTask = - new FireSuccessAsyncTask(aRequest, aResult); - MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(asyncTask)); - return NS_OK; - } - - NS_IMETHOD - Run() override { - mReq->FireSuccess( - JS::Handle<JS::Value>::fromMarkedLocation(mResult.address())); - return NS_OK; - } - - private: - RefPtr<DOMRequest> mReq; - JS::PersistentRooted<JS::Value> mResult; -}; - -class FireErrorAsyncTask : public mozilla::Runnable { - public: - FireErrorAsyncTask(DOMRequest* aRequest, const nsAString& aError) - : mozilla::Runnable("FireErrorAsyncTask"), - mReq(aRequest), - mError(aError) {} - - NS_IMETHOD - Run() override { - mReq->FireError(mError); - return NS_OK; - } - - private: - RefPtr<DOMRequest> mReq; - nsString mError; -}; - -NS_IMETHODIMP -DOMRequestService::FireSuccessAsync(DOMRequest* aRequest, - JS::Handle<JS::Value> aResult) { - NS_ENSURE_STATE(aRequest); - return FireSuccessAsyncTask::Dispatch(aRequest, aResult); -} - -NS_IMETHODIMP -DOMRequestService::FireErrorAsync(DOMRequest* aRequest, - const nsAString& aError) { - NS_ENSURE_STATE(aRequest); - nsCOMPtr<nsIRunnable> asyncTask = new FireErrorAsyncTask(aRequest, aError); - MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(asyncTask)); - return NS_OK; -} diff --git a/dom/base/DOMRequest.h b/dom/base/DOMRequest.h deleted file mode 100644 index b0e7c23112..0000000000 --- a/dom/base/DOMRequest.h +++ /dev/null @@ -1,103 +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 mozilla_dom_domrequest_h__ -#define mozilla_dom_domrequest_h__ - -#include "nsIDOMRequestService.h" -#include "mozilla/Attributes.h" -#include "mozilla/DOMEventTargetHelper.h" -#include "mozilla/dom/DOMException.h" -#include "mozilla/dom/DOMRequestBinding.h" - -#include "nsCOMPtr.h" - -namespace mozilla { - -class ErrorResult; - -namespace dom { - -class AnyCallback; -class Promise; - -class DOMRequest : public DOMEventTargetHelper { - protected: - JS::Heap<JS::Value> mResult; - RefPtr<DOMException> mError; - RefPtr<Promise> mPromise; - bool mDone; - - public: - NS_DECL_ISUPPORTS_INHERITED - - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(DOMRequest, - DOMEventTargetHelper) - - // WrapperCache - nsPIDOMWindowInner* GetParentObject() const { return GetOwner(); } - - virtual JSObject* WrapObject(JSContext* aCx, - JS::Handle<JSObject*> aGivenProto) override; - - // WebIDL Interface - DOMRequestReadyState ReadyState() const { - return mDone ? DOMRequestReadyState::Done : DOMRequestReadyState::Pending; - } - - void GetResult(JSContext*, JS::MutableHandle<JS::Value> aRetval) const { - NS_ASSERTION(mDone || mResult.isUndefined(), - "Result should be undefined when pending"); - aRetval.set(mResult); - } - - DOMException* GetError() const { - NS_ASSERTION(mDone || !mError, "Error should be null when pending"); - return mError; - } - - IMPL_EVENT_HANDLER(success) - IMPL_EVENT_HANDLER(error) - - void Then(JSContext* aCx, AnyCallback* aResolveCallback, - AnyCallback* aRejectCallback, JS::MutableHandle<JS::Value> aRetval, - mozilla::ErrorResult& aRv); - - void FireSuccess(JS::Handle<JS::Value> aResult); - void FireError(const nsAString& aError); - void FireError(nsresult aError); - void FireDetailedError(DOMException& aError); - - explicit DOMRequest(nsPIDOMWindowInner* aWindow); - explicit DOMRequest(nsIGlobalObject* aGlobal); - - protected: - virtual ~DOMRequest(); - - void FireEvent(const nsAString& aType, bool aBubble, bool aCancelable); - - void RootResultVal(); -}; - -class DOMRequestService final : public nsIDOMRequestService { - ~DOMRequestService() = default; - - public: - NS_DECL_ISUPPORTS - NS_DECL_NSIDOMREQUESTSERVICE - - // No one should call this but the factory. - static already_AddRefed<DOMRequestService> FactoryCreate() { - return MakeAndAddRef<DOMRequestService>(); - } -}; - -} // namespace dom -} // namespace mozilla - -#define DOMREQUEST_SERVICE_CONTRACTID "@mozilla.org/dom/dom-request-service;1" - -#endif // mozilla_dom_domrequest_h__ diff --git a/dom/base/DOMRequestHelper.sys.mjs b/dom/base/DOMRequestHelper.sys.mjs deleted file mode 100644 index 832c06c4de..0000000000 --- a/dom/base/DOMRequestHelper.sys.mjs +++ /dev/null @@ -1,335 +0,0 @@ -/* 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/. */ - -/** - * Helper object for APIs that deal with DOMRequests and Promises. - * It allows objects inheriting from it to create and keep track of DOMRequests - * and Promises objects in the common scenario where requests are created in - * the child, handed out to content and delivered to the parent within an async - * message (containing the identifiers of these requests). The parent may send - * messages back as answers to different requests and the child will use this - * helper to get the right request object. This helper also takes care of - * releasing the requests objects when the window goes out of scope. - * - * DOMRequestIPCHelper also deals with message listeners, allowing to add them - * to the child side of frame and process message manager and removing them - * when needed. - */ -export function DOMRequestIpcHelper() { - // _listeners keeps a list of messages for which we added a listener and the - // kind of listener that we added (strong or weak). It's an object of this - // form: - // { - // "message1": true, - // "messagen": false - // } - // - // where each property is the name of the message and its value is a boolean - // that indicates if the listener is weak or not. - this._listeners = null; - this._requests = null; - this._window = null; -} - -DOMRequestIpcHelper.prototype = { - /** - * An object which "inherits" from DOMRequestIpcHelper and declares its own - * queryInterface method MUST implement Ci.nsISupportsWeakReference. - */ - QueryInterface: ChromeUtils.generateQI([ - "nsISupportsWeakReference", - "nsIObserver", - ]), - - /** - * 'aMessages' is expected to be an array of either: - * - objects of this form: - * { - * name: "messageName", - * weakRef: false - * } - * where 'name' is the message identifier and 'weakRef' a boolean - * indicating if the listener should be a weak referred one or not. - * - * - or only strings containing the message name, in which case the listener - * will be added as a strong reference by default. - */ - addMessageListeners(aMessages) { - if (!aMessages) { - return; - } - - if (!this._listeners) { - this._listeners = {}; - } - - if (!Array.isArray(aMessages)) { - aMessages = [aMessages]; - } - - aMessages.forEach(aMsg => { - let name = aMsg.name || aMsg; - // If the listener is already set and it is of the same type we just - // increase the count and bail out. If it is not of the same type, - // we throw an exception. - if (this._listeners[name] != undefined) { - if (!!aMsg.weakRef == this._listeners[name].weakRef) { - this._listeners[name].count++; - return; - } - throw Components.Exception("", Cr.NS_ERROR_FAILURE); - } - - aMsg.weakRef - ? Services.cpmm.addWeakMessageListener(name, this) - : Services.cpmm.addMessageListener(name, this); - this._listeners[name] = { - weakRef: !!aMsg.weakRef, - count: 1, - }; - }); - }, - - /** - * 'aMessages' is expected to be a string or an array of strings containing - * the message names of the listeners to be removed. - */ - removeMessageListeners(aMessages) { - if (!this._listeners || !aMessages) { - return; - } - - if (!Array.isArray(aMessages)) { - aMessages = [aMessages]; - } - - aMessages.forEach(aName => { - if (this._listeners[aName] == undefined) { - return; - } - - // Only remove the listener really when we don't have anybody that could - // be waiting on a message. - if (!--this._listeners[aName].count) { - this._listeners[aName].weakRef - ? Services.cpmm.removeWeakMessageListener(aName, this) - : Services.cpmm.removeMessageListener(aName, this); - delete this._listeners[aName]; - } - }); - }, - - /** - * Initialize the helper adding the corresponding listeners to the messages - * provided as the second parameter. - * - * 'aMessages' is expected to be an array of either: - * - * - objects of this form: - * { - * name: 'messageName', - * weakRef: false - * } - * where 'name' is the message identifier and 'weakRef' a boolean - * indicating if the listener should be a weak referred one or not. - * - * - or only strings containing the message name, in which case the listener - * will be added as a strong referred one by default. - */ - initDOMRequestHelper(aWindow, aMessages) { - // Query our required interfaces to force a fast fail if they are not - // provided. These calls will throw if the interface is not available. - this.QueryInterface(Ci.nsISupportsWeakReference); - this.QueryInterface(Ci.nsIObserver); - - if (aMessages) { - this.addMessageListeners(aMessages); - } - - this._id = this._getRandomId(); - - this._window = aWindow; - if (this._window) { - // We don't use this.innerWindowID, but other classes rely on it. - this.innerWindowID = this._window.windowGlobalChild.innerWindowId; - } - - this._destroyed = false; - - Services.obs.addObserver( - this, - "inner-window-destroyed", - /* weak-ref */ true - ); - }, - - destroyDOMRequestHelper() { - if (this._destroyed) { - return; - } - - this._destroyed = true; - - Services.obs.removeObserver(this, "inner-window-destroyed"); - - if (this._listeners) { - Object.keys(this._listeners).forEach(aName => { - this._listeners[aName].weakRef - ? Services.cpmm.removeWeakMessageListener(aName, this) - : Services.cpmm.removeMessageListener(aName, this); - }); - } - - this._listeners = null; - this._requests = null; - - // Objects inheriting from DOMRequestIPCHelper may have an uninit function. - if (this.uninit) { - this.uninit(); - } - - this._window = null; - }, - - observe(aSubject, aTopic, aData) { - if (aTopic !== "inner-window-destroyed") { - return; - } - - let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data; - if (wId != this.innerWindowID) { - return; - } - - this.destroyDOMRequestHelper(); - }, - - getRequestId(aRequest) { - if (!this._requests) { - this._requests = {}; - } - - let id = "id" + this._getRandomId(); - this._requests[id] = aRequest; - return id; - }, - - getPromiseResolverId(aPromiseResolver) { - // Delegates to getRequest() since the lookup table is agnostic about - // storage. - return this.getRequestId(aPromiseResolver); - }, - - getRequest(aId) { - if (this._requests && this._requests[aId]) { - return this._requests[aId]; - } - return undefined; - }, - - getPromiseResolver(aId) { - // Delegates to getRequest() since the lookup table is agnostic about - // storage. - return this.getRequest(aId); - }, - - removeRequest(aId) { - if (this._requests && this._requests[aId]) { - delete this._requests[aId]; - } - }, - - removePromiseResolver(aId) { - // Delegates to getRequest() since the lookup table is agnostic about - // storage. - this.removeRequest(aId); - }, - - takeRequest(aId) { - if (!this._requests || !this._requests[aId]) { - return null; - } - let request = this._requests[aId]; - delete this._requests[aId]; - return request; - }, - - takePromiseResolver(aId) { - // Delegates to getRequest() since the lookup table is agnostic about - // storage. - return this.takeRequest(aId); - }, - - _getRandomId() { - return Services.uuid.generateUUID().toString(); - }, - - createRequest() { - // If we don't have a valid window object, throw. - if (!this._window) { - console.error( - "DOMRequestHelper trying to create a DOMRequest without a valid window, failing." - ); - throw Components.Exception("", Cr.NS_ERROR_FAILURE); - } - return Services.DOMRequest.createRequest(this._window); - }, - - /** - * createPromise() creates a new Promise, with `aPromiseInit` as the - * PromiseInit callback. The promise constructor is obtained from the - * reference to window owned by this DOMRequestIPCHelper. - */ - createPromise(aPromiseInit) { - // If we don't have a valid window object, throw. - if (!this._window) { - console.error( - "DOMRequestHelper trying to create a Promise without a valid window, failing." - ); - throw Components.Exception("", Cr.NS_ERROR_FAILURE); - } - return new this._window.Promise(aPromiseInit); - }, - - /** - * createPromiseWithId() creates a new Promise, accepting a callback - * which is immediately called with the generated resolverId. - */ - createPromiseWithId(aCallback) { - return this.createPromise((aResolve, aReject) => { - let resolverId = this.getPromiseResolverId({ - resolve: aResolve, - reject: aReject, - }); - aCallback(resolverId); - }); - }, - - forEachRequest(aCallback) { - if (!this._requests) { - return; - } - - Object.keys(this._requests).forEach(aKey => { - if (this._window.DOMRequest.isInstance(this.getRequest(aKey))) { - aCallback(aKey); - } - }); - }, - - forEachPromiseResolver(aCallback) { - if (!this._requests) { - return; - } - - Object.keys(this._requests).forEach(aKey => { - if ( - "resolve" in this.getPromiseResolver(aKey) && - "reject" in this.getPromiseResolver(aKey) - ) { - aCallback(aKey); - } - }); - }, -}; diff --git a/dom/base/DirectionalityUtils.cpp b/dom/base/DirectionalityUtils.cpp index 46229cfbd3..dd427c61b1 100644 --- a/dom/base/DirectionalityUtils.cpp +++ b/dom/base/DirectionalityUtils.cpp @@ -5,8 +5,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* - Implementation description from https://etherpad.mozilla.org/dir-auto - Static case =========== When we see a new content node with @dir=auto from the parser, we set the @@ -45,23 +43,8 @@ I will call this algorithm "upward propagation". - Each text node should maintain a list of elements which have their - directionality determined by the first strong character of that text node. - This is useful to make dynamic changes more efficient. One way to implement - this is to have a per-document hash table mapping a text node to a set of - elements. I'll call this data structure TextNodeDirectionalityMap. The - algorithm for appending a new text node above needs to update this data - structure. - - *IMPLEMENTATION NOTE* - In practice, the implementation uses two per-node properties: - - dirAutoSetBy, which is set on a node with auto-directionality, and points to - the textnode that contains the strong character which determines the - directionality of the node. - - textNodeDirectionalityMap, which is set on a text node and points to a hash - table listing the nodes whose directionality is determined by the text node. + Each text node keeps a flag if it might determine the directionality of any + ancestor. This is useful to make dynamic changes more efficient. Handling dynamic changes ======================== @@ -90,16 +73,15 @@ (I'll call this the "downward propagation algorithm".) by walking the child subtree in tree order. Note that an element with @dir=auto should not affect other elements in its document with @dir=auto. So there is no need to walk up - the parent chain in this case. TextNodeDirectionalityMap needs to be updated - as appropriate. + the parent chain in this case. 3a. When the dir attribute is set to any valid value on an element that didn't have a valid dir attribute before, this means that any descendant of that element will not affect the directionality of any of its ancestors. So we need - to check whether any text node descendants of the element are listed in - TextNodeDirectionalityMap, and whether the elements whose direction they set - are ancestors of the element. If so, we need to rerun the downward propagation - algorithm for those ancestors. + to check whether any text node descendants of the element can set the dir of + any ancestor, and whether the elements whose direction they set are ancestors + of the element. If so, we need to rerun the downward propagation algorithm for + those ancestors. That's done by OnSetDirAttr. 4. When the dir attribute is changed from auto to something else (including the case where it gets removed) on a textarea or an input element with @@ -118,80 +100,29 @@ should still retain the same flag.) * We resolve the directionality of the element based on the value of the @dir attribute on the element itself or its parent element. - TextNodeDirectionalityMap needs to be updated as appropriate. 5a. When the dir attribute is removed or set to an invalid value on any element (except a bdi element) with the NodeAncestorHasDirAuto flag which previously had a valid dir attribute, it might have a text node descendant that did not previously affect the directionality of any of its ancestors but - should now begin to affect them. We run the following algorithm: - * Walk up the parent chain from the element. - * For any element that appears in the TextNodeDirectionalityMap, remove the - element from the map and rerun the downward propagation algorithm - (see section 3). - * If we reach an element without either of the NodeHasDirAuto or - NodeAncestorHasDirAuto flags, abort the parent chain walk. + should now begin to affect them. We run OnSetDirAttr. 6. When an element with @dir=auto is added to the document, we should handle it similar to the case 2/3 above. 7. When an element with NodeHasDirAuto or NodeAncestorHasDirAuto is removed from the document, we should handle it similar to the case 4/5 above, - except that we don't need to handle anything in the child subtree. We should - also remove all of the occurrences of that node and its descendants from - TextNodeDirectionalityMap. (This is the conceptual description of what needs - to happen but in the implementation UnbindFromTree is going to be called on - all of the descendants so we don't need to descend into the child subtree). + except that we don't need to handle anything in the child subtree. 8. When the contents of a text node is changed either from script or by the - user, we need to run the following algorithm: - * If the change has happened after the first character with strong - directionality in the text node, do nothing. - * If the text node is a child of a bdi, script or style element, do nothing. - * If the text node belongs to a textarea with NodeHasDirAuto, we need to - update the directionality of the textarea. - * Grab a list of elements affected by this text node from - TextNodeDirectionalityMap and re-resolve the directionality of each one of - them based on the new contents of the text node. - * If the text node does not exist in TextNodeDirectionalityMap, and it has the - NodeAncestorHasDirAuto flag set, this could potentially be a text node - which is going to start affecting the directionality of its parent @dir=auto - elements. In this case, we need to fall back to the (potentially expensive) - "upward propagation algorithm". The TextNodeDirectionalityMap data structure - needs to be update during this algorithm. - * If the new contents of the text node do not have any strong characters, and - the old contents used to, and the text node used to exist in - TextNodeDirectionalityMap and it has the NodeAncestorHasDirAuto flag set, - the elements associated with this text node inside TextNodeDirectionalityMap - will now get their directionality from another text node. In this case, for - each element in the list retrieved from TextNodeDirectionalityMap, run the - downward propagation algorithm (section 3), and remove the text node from - TextNodeDirectionalityMap. - - 9. When a new text node is injected into a document, we need to run the - following algorithm: - * If the contents of the text node do not have any characters with strong - direction, do nothing. - * If the text node is a child of a bdi, script or style element, do nothing. - * If the text node is appended to a textarea element with NodeHasDirAuto, we - need to update the directionality of the textarea. - * If the text node has NodeAncestorHasDirAuto, we need to run the "upward - propagation algorithm". The TextNodeDirectionalityMap data structure needs to - be update during this algorithm. - - 10. When a text node is removed from a document, we need to run the following - algorithm: - * If the contents of the text node do not have any characters with strong - direction, do nothing. - * If the text node is a child of a bdi, script or style element, do nothing. - * If the text node is removed from a textarea element with NodeHasDirAuto, - set the directionality to "ltr". (This is what the spec currently says, but - I'm filing a spec bug to get it fixed -- the directionality should depend on - the parent element here.) - * If the text node has NodeAncestorHasDirAuto, we need to look at the list - of elements being affected by this text node from TextNodeDirectionalityMap, - run the "downward propagation algorithm" (section 3) for each one of them, - while updating TextNodeDirectionalityMap along the way. + user, we need to run TextNode{WillChange,Changed}Direction, see inline docs + for details. + + 9. When a new text node is injected into a document, we need to run + SetDirectionFromNewTextNode. + + 10. When a text node is removed from a document, we need to run + ResetDirectionSetByTextNode. 11. If the value of the @dir attribute on a bdi element is changed to an invalid value (or if it's removed), determine the new directionality similar @@ -210,18 +141,16 @@ #include "nsIContent.h" #include "nsIContentInlines.h" #include "mozilla/dom/Document.h" -#include "mozilla/AutoRestore.h" -#include "mozilla/DebugOnly.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/HTMLInputElement.h" #include "mozilla/dom/HTMLSlotElement.h" #include "mozilla/dom/ShadowRoot.h" +#include "mozilla/dom/UnbindContext.h" #include "mozilla/intl/UnicodeProperties.h" #include "nsUnicodeProperties.h" #include "nsTextFragment.h" #include "nsAttrValue.h" #include "nsTextNode.h" -#include "nsCheapSets.h" namespace mozilla { @@ -230,75 +159,53 @@ using mozilla::dom::HTMLInputElement; using mozilla::dom::HTMLSlotElement; using mozilla::dom::ShadowRoot; -static nsIContent* GetParentOrHostOrSlot( - const nsIContent* aContent, bool* aCrossedShadowBoundary = nullptr) { +static nsIContent* GetParentOrHostOrSlot(const nsIContent* aContent) { if (HTMLSlotElement* slot = aContent->GetAssignedSlot()) { - if (aCrossedShadowBoundary) { - *aCrossedShadowBoundary = true; - } return slot; } - - nsIContent* parent = aContent->GetParent(); - if (parent) { + if (nsIContent* parent = aContent->GetParent()) { return parent; } - - const ShadowRoot* sr = ShadowRoot::FromNode(aContent); - if (sr) { - if (aCrossedShadowBoundary) { - *aCrossedShadowBoundary = true; - } - return sr->Host(); + if (const ShadowRoot* sr = ShadowRoot::FromNode(aContent)) { + return sr->GetHost(); } - return nullptr; } -static bool AncestorChainCrossesShadowBoundary(nsIContent* aDescendant, - nsIContent* aAncestor) { - bool crossedShadowBoundary = false; - nsIContent* content = aDescendant; - while (content && content != aAncestor) { - content = GetParentOrHostOrSlot(content, &crossedShadowBoundary); - if (crossedShadowBoundary) { - return true; - } - } - - return false; -} - /** - * Returns true if aElement is one of the elements whose text content should not - * affect its own direction, nor the direction of ancestors with dir=auto. + * Returns true if aElement is one of the elements whose text content should + * affect its own direction, or the direction of ancestors with dir=auto. * * Note that this does not include <bdi>, whose content does affect its own * direction when it has dir=auto (which it has by default), so one needs to - * test for it separately, e.g. with DoesNotAffectDirectionOfAncestors. + * test for it separately, e.g. with AffectsDirectionOfAncestors. * It *does* include textarea, because even if a textarea has dir=auto, it has * unicode-bidi: plaintext and is handled automatically in bidi resolution. * It also includes `input`, because it takes the `dir` value from its value * attribute, instead of the child nodes. */ -static bool DoesNotParticipateInAutoDirection(const nsIContent* aContent) { - mozilla::dom::NodeInfo* nodeInfo = aContent->NodeInfo(); - return ((!aContent->IsHTMLElement() || nodeInfo->Equals(nsGkAtoms::script) || - nodeInfo->Equals(nsGkAtoms::style) || - nodeInfo->Equals(nsGkAtoms::input) || - nodeInfo->Equals(nsGkAtoms::textarea) || - aContent->IsInNativeAnonymousSubtree())) && - !aContent->IsShadowRoot(); +static bool ParticipatesInAutoDirection(const nsIContent* aContent) { + if (aContent->IsInNativeAnonymousSubtree()) { + return false; + } + if (aContent->IsShadowRoot()) { + return true; + } + dom::NodeInfo* ni = aContent->NodeInfo(); + return ni->NamespaceID() == kNameSpaceID_XHTML && + !ni->Equals(nsGkAtoms::script) && !ni->Equals(nsGkAtoms::style) && + !ni->Equals(nsGkAtoms::input) && !ni->Equals(nsGkAtoms::textarea); } /** - * Returns true if aElement is one of the element whose text content should not - * affect the direction of ancestors with dir=auto (though it may affect its own - * direction, e.g. <bdi>) + * Returns true if aElement is one of the element whose text should affect the + * direction of ancestors with dir=auto (though note that even if it returns + * false it may affect its own direction, e.g. <bdi> or dir=auto itself) */ -static bool DoesNotAffectDirectionOfAncestors(const Element* aElement) { - return (DoesNotParticipateInAutoDirection(aElement) || - aElement->IsHTMLElement(nsGkAtoms::bdi) || aElement->HasFixedDir()); +static bool AffectsDirectionOfAncestors(const Element* aElement) { + return ParticipatesInAutoDirection(aElement) && + !aElement->IsHTMLElement(nsGkAtoms::bdi) && !aElement->HasFixedDir() && + !aElement->HasDirAuto(); } /** @@ -318,11 +225,15 @@ static Directionality GetDirectionFromChar(uint32_t ch) { } } -inline static bool NodeAffectsDirAutoAncestor(nsIContent* aTextNode) { +inline static bool TextChildrenAffectDirAutoAncestor(nsIContent* aContent) { + return ParticipatesInAutoDirection(aContent) && + aContent->NodeOrAncestorHasDirAuto(); +} + +inline static bool NodeAffectsDirAutoAncestor(nsTextNode* aTextNode) { nsIContent* parent = GetParentOrHostOrSlot(aTextNode); - return (parent && !DoesNotParticipateInAutoDirection(parent) && - parent->NodeOrAncestorHasDirAuto() && - !aTextNode->IsInNativeAnonymousSubtree()); + return parent && TextChildrenAffectDirAutoAncestor(parent) && + !aTextNode->IsInNativeAnonymousSubtree(); } Directionality GetDirectionFromText(const char16_t* aText, @@ -394,11 +305,11 @@ static Directionality GetDirectionFromText(const mozilla::dom::Text* aTextNode, } static nsTextNode* WalkDescendantsAndGetDirectionFromText( - nsINode* aRoot, nsINode* aSkip, Directionality* aDirectionality) { + nsINode* aRoot, Directionality* aDirectionality) { nsIContent* child = aRoot->GetFirstChild(); while (child) { if ((child->IsElement() && - DoesNotAffectDirectionOfAncestors(child->AsElement())) || + !AffectsDirectionOfAncestors(child->AsElement())) || child->GetAssignedSlot()) { child = child->GetNextNonChildNode(aRoot); continue; @@ -409,19 +320,16 @@ static nsTextNode* WalkDescendantsAndGetDirectionFromText( for (uint32_t i = 0; i < assignedNodes.Length(); ++i) { nsIContent* assignedNode = assignedNodes[i]->AsContent(); if (assignedNode->NodeType() == nsINode::TEXT_NODE) { - auto text = static_cast<nsTextNode*>(assignedNode); - if (assignedNode != aSkip) { - Directionality textNodeDir = GetDirectionFromText(text); - if (textNodeDir != Directionality::Unset) { - *aDirectionality = textNodeDir; - return text; - } + auto* text = static_cast<nsTextNode*>(assignedNode); + Directionality textNodeDir = GetDirectionFromText(text); + if (textNodeDir != Directionality::Unset) { + *aDirectionality = textNodeDir; + return text; } } else if (assignedNode->IsElement() && - !DoesNotAffectDirectionOfAncestors( - assignedNode->AsElement())) { + AffectsDirectionOfAncestors(assignedNode->AsElement())) { nsTextNode* text = WalkDescendantsAndGetDirectionFromText( - assignedNode, aSkip, aDirectionality); + assignedNode, aDirectionality); if (text) { return text; } @@ -429,8 +337,8 @@ static nsTextNode* WalkDescendantsAndGetDirectionFromText( } } - if (child->NodeType() == nsINode::TEXT_NODE && child != aSkip) { - auto text = static_cast<nsTextNode*>(child); + if (child->NodeType() == nsINode::TEXT_NODE) { + auto* text = static_cast<nsTextNode*>(child); Directionality textNodeDir = GetDirectionFromText(text); if (textNodeDir != Directionality::Unset) { *aDirectionality = textNodeDir; @@ -447,17 +355,14 @@ static nsTextNode* WalkDescendantsAndGetDirectionFromText( * Set the directionality of a node with dir=auto as defined in * http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#the-directionality * - * @param[in] changedNode If we call this method because the content of a text - * node is about to change, pass in the changed node, so that we - * know not to return it * @return the text node containing the character that determined the direction */ -static nsTextNode* WalkDescendantsSetDirectionFromText( - Element* aElement, bool aNotify, nsINode* aChangedNode = nullptr) { +static nsTextNode* WalkDescendantsSetDirectionFromText(Element* aElement, + bool aNotify) { MOZ_ASSERT(aElement, "Must have an element"); MOZ_ASSERT(aElement->HasDirAuto(), "Element must have dir=auto"); - if (DoesNotParticipateInAutoDirection(aElement)) { + if (!ParticipatesInAutoDirection(aElement)) { return nullptr; } @@ -465,8 +370,8 @@ static nsTextNode* WalkDescendantsSetDirectionFromText( // Check the text in Shadow DOM. if (ShadowRoot* shadowRoot = aElement->GetShadowRoot()) { - nsTextNode* text = WalkDescendantsAndGetDirectionFromText( - shadowRoot, aChangedNode, &textNodeDir); + nsTextNode* text = + WalkDescendantsAndGetDirectionFromText(shadowRoot, &textNodeDir); if (text) { aElement->SetDirectionality(textNodeDir, aNotify); return text; @@ -474,8 +379,8 @@ static nsTextNode* WalkDescendantsSetDirectionFromText( } // Check the text in light DOM. - nsTextNode* text = WalkDescendantsAndGetDirectionFromText( - aElement, aChangedNode, &textNodeDir); + nsTextNode* text = + WalkDescendantsAndGetDirectionFromText(aElement, &textNodeDir); if (text) { aElement->SetDirectionality(textNodeDir, aNotify); return text; @@ -487,194 +392,6 @@ static nsTextNode* WalkDescendantsSetDirectionFromText( return nullptr; } -class nsTextNodeDirectionalityMap { - static void nsTextNodeDirectionalityMapDtor(void* aObject, - nsAtom* aPropertyName, - void* aPropertyValue, - void* aData) { - nsINode* textNode = static_cast<nsINode*>(aObject); - textNode->ClearHasTextNodeDirectionalityMap(); - - nsTextNodeDirectionalityMap* map = - reinterpret_cast<nsTextNodeDirectionalityMap*>(aPropertyValue); - map->EnsureMapIsClear(); - delete map; - } - - public: - explicit nsTextNodeDirectionalityMap(nsINode* aTextNode) - : mElementToBeRemoved(nullptr) { - MOZ_ASSERT(aTextNode, "Null text node"); - MOZ_COUNT_CTOR(nsTextNodeDirectionalityMap); - aTextNode->SetProperty(nsGkAtoms::textNodeDirectionalityMap, this, - nsTextNodeDirectionalityMapDtor); - aTextNode->SetHasTextNodeDirectionalityMap(); - } - - MOZ_COUNTED_DTOR(nsTextNodeDirectionalityMap) - - static void nsTextNodeDirectionalityMapPropertyDestructor( - void* aObject, nsAtom* aProperty, void* aPropertyValue, void* aData) { - nsTextNode* textNode = static_cast<nsTextNode*>(aPropertyValue); - nsTextNodeDirectionalityMap* map = GetDirectionalityMap(textNode); - if (map) { - map->RemoveEntryForProperty(static_cast<Element*>(aObject)); - } - NS_RELEASE(textNode); - } - - void AddEntry(nsTextNode* aTextNode, Element* aElement) { - if (!mElements.Contains(aElement)) { - mElements.Put(aElement); - NS_ADDREF(aTextNode); - aElement->SetProperty(nsGkAtoms::dirAutoSetBy, aTextNode, - nsTextNodeDirectionalityMapPropertyDestructor); - aElement->SetHasDirAutoSet(); - } - } - - void RemoveEntry(nsTextNode* aTextNode, Element* aElement) { - NS_ASSERTION(mElements.Contains(aElement), - "element already removed from map"); - - mElements.Remove(aElement); - aElement->ClearHasDirAutoSet(); - aElement->RemoveProperty(nsGkAtoms::dirAutoSetBy); - } - - void RemoveEntryForProperty(Element* aElement) { - if (mElementToBeRemoved != aElement) { - mElements.Remove(aElement); - } - aElement->ClearHasDirAutoSet(); - } - - private: - nsCheapSet<nsPtrHashKey<Element>> mElements; - // Only used for comparison. - Element* mElementToBeRemoved; - - static nsTextNodeDirectionalityMap* GetDirectionalityMap(nsINode* aTextNode) { - MOZ_ASSERT(aTextNode->NodeType() == nsINode::TEXT_NODE, - "Must be a text node"); - nsTextNodeDirectionalityMap* map = nullptr; - - if (aTextNode->HasTextNodeDirectionalityMap()) { - map = static_cast<nsTextNodeDirectionalityMap*>( - aTextNode->GetProperty(nsGkAtoms::textNodeDirectionalityMap)); - } - - return map; - } - - static nsCheapSetOperator SetNodeDirection(nsPtrHashKey<Element>* aEntry, - void* aDir) { - aEntry->GetKey()->SetDirectionality( - *reinterpret_cast<Directionality*>(aDir), true); - return OpNext; - } - - struct nsTextNodeDirectionalityMapAndElement { - nsTextNodeDirectionalityMap* mMap; - nsCOMPtr<nsINode> mNode; - }; - - static nsCheapSetOperator ResetNodeDirection(nsPtrHashKey<Element>* aEntry, - void* aData) { - // run the downward propagation algorithm - // and remove the text node from the map - nsTextNodeDirectionalityMapAndElement* data = - static_cast<nsTextNodeDirectionalityMapAndElement*>(aData); - nsINode* oldTextNode = data->mNode; - Element* rootNode = aEntry->GetKey(); - nsTextNode* newTextNode = nullptr; - if (rootNode->GetParentNode() && rootNode->HasDirAuto()) { - newTextNode = - WalkDescendantsSetDirectionFromText(rootNode, true, oldTextNode); - } - - AutoRestore<Element*> restore(data->mMap->mElementToBeRemoved); - data->mMap->mElementToBeRemoved = rootNode; - if (newTextNode) { - nsINode* oldDirAutoSetBy = static_cast<nsTextNode*>( - rootNode->GetProperty(nsGkAtoms::dirAutoSetBy)); - if (oldDirAutoSetBy == newTextNode) { - // We're already registered. - return OpNext; - } - nsTextNodeDirectionalityMap::AddEntryToMap(newTextNode, rootNode); - } else { - rootNode->ClearHasDirAutoSet(); - rootNode->RemoveProperty(nsGkAtoms::dirAutoSetBy); - } - return OpRemove; - } - - static nsCheapSetOperator TakeEntries(nsPtrHashKey<Element>* aEntry, - void* aData) { - AutoTArray<Element*, 8>* entries = - static_cast<AutoTArray<Element*, 8>*>(aData); - entries->AppendElement(aEntry->GetKey()); - return OpRemove; - } - - public: - uint32_t UpdateAutoDirection(Directionality aDir) { - return mElements.EnumerateEntries(SetNodeDirection, &aDir); - } - - void ResetAutoDirection(nsINode* aTextNode) { - nsTextNodeDirectionalityMapAndElement data = {this, aTextNode}; - mElements.EnumerateEntries(ResetNodeDirection, &data); - } - - void EnsureMapIsClear() { - AutoRestore<Element*> restore(mElementToBeRemoved); - AutoTArray<Element*, 8> entries; - mElements.EnumerateEntries(TakeEntries, &entries); - for (Element* el : entries) { - el->ClearHasDirAutoSet(); - el->RemoveProperty(nsGkAtoms::dirAutoSetBy); - } - } - - static void RemoveElementFromMap(nsTextNode* aTextNode, Element* aElement) { - if (aTextNode->HasTextNodeDirectionalityMap()) { - GetDirectionalityMap(aTextNode)->RemoveEntry(aTextNode, aElement); - } - } - - static void AddEntryToMap(nsTextNode* aTextNode, Element* aElement) { - nsTextNodeDirectionalityMap* map = GetDirectionalityMap(aTextNode); - if (!map) { - map = new nsTextNodeDirectionalityMap(aTextNode); - } - - map->AddEntry(aTextNode, aElement); - } - - static uint32_t UpdateTextNodeDirection(nsINode* aTextNode, - Directionality aDir) { - MOZ_ASSERT(aTextNode->HasTextNodeDirectionalityMap(), - "Map missing in UpdateTextNodeDirection"); - return GetDirectionalityMap(aTextNode)->UpdateAutoDirection(aDir); - } - - static void ResetTextNodeDirection(nsTextNode* aTextNode, - nsTextNode* aChangedTextNode) { - MOZ_ASSERT(aTextNode->HasTextNodeDirectionalityMap(), - "Map missing in ResetTextNodeDirection"); - RefPtr<nsTextNode> textNode = aTextNode; - GetDirectionalityMap(textNode)->ResetAutoDirection(aChangedTextNode); - } - - static void EnsureMapIsClearFor(nsINode* aTextNode) { - if (aTextNode->HasTextNodeDirectionalityMap()) { - GetDirectionalityMap(aTextNode)->EnsureMapIsClear(); - } - } -}; - Directionality GetParentDirectionality(const Element* aElement) { if (nsIContent* parent = GetParentOrHostOrSlot(aElement)) { if (ShadowRoot* shadow = ShadowRoot::FromNode(parent)) { @@ -729,7 +446,7 @@ static inline bool IsBoundary(const Element& aElement) { static void SetDirectionalityOnDescendantsInternal(nsINode* aNode, Directionality aDir, bool aNotify) { - if (Element* element = Element::FromNode(aNode)) { + if (auto* element = Element::FromNode(aNode)) { if (ShadowRoot* shadow = element->GetShadowRoot()) { SetDirectionalityOnDescendantsInternal(shadow, aDir, aNotify); } @@ -778,66 +495,37 @@ void SetDirectionalityOnDescendants(Element* aElement, Directionality aDir, } static void ResetAutoDirection(Element* aElement, bool aNotify) { - if (aElement->HasDirAutoSet()) { - // If the parent has the DirAutoSet flag, its direction is determined by - // some text node descendant. - // Remove it from the map and reset its direction by the downward - // propagation algorithm - nsTextNode* setByNode = static_cast<nsTextNode*>( - aElement->GetProperty(nsGkAtoms::dirAutoSetBy)); - if (setByNode) { - nsTextNodeDirectionalityMap::RemoveElementFromMap(setByNode, aElement); - } - } - - if (aElement->HasDirAuto()) { - nsTextNode* setByNode = - WalkDescendantsSetDirectionFromText(aElement, aNotify); - if (setByNode) { - nsTextNodeDirectionalityMap::AddEntryToMap(setByNode, aElement); - } - SetDirectionalityOnDescendants(aElement, aElement->GetDirectionality(), - aNotify); + MOZ_ASSERT(aElement->HasDirAuto()); + nsTextNode* setByNode = + WalkDescendantsSetDirectionFromText(aElement, aNotify); + if (setByNode) { + setByNode->SetMaySetDirAuto(); } + SetDirectionalityOnDescendants(aElement, aElement->GetDirectionality(), + aNotify); } /** - * Walk the parent chain of a text node whose dir attribute has been removed and - * reset the direction of any of its ancestors which have dir=auto and whose - * directionality is determined by a text node descendant. + * Walk the parent chain of a text node whose dir attribute has been removed or + * added and reset the direction of any of its ancestors which have dir=auto and + * whose directionality is determined by a text node descendant. */ void WalkAncestorsResetAutoDirection(Element* aElement, bool aNotify) { - nsTextNode* setByNode; - nsIContent* parent = GetParentOrHostOrSlot(aElement); - while (parent && parent->NodeOrAncestorHasDirAuto()) { - if (!parent->IsElement()) { - parent = GetParentOrHostOrSlot(parent); + for (nsIContent* parent = GetParentOrHostOrSlot(aElement); + parent && parent->NodeOrAncestorHasDirAuto(); + parent = GetParentOrHostOrSlot(parent)) { + auto* parentElement = Element::FromNode(*parent); + if (!parentElement || !parentElement->HasDirAuto()) { continue; } - - Element* parentElement = parent->AsElement(); - if (parent->HasDirAutoSet()) { - // If the parent has the DirAutoSet flag, its direction is determined by - // some text node descendant. - // Remove it from the map and reset its direction by the downward - // propagation algorithm - setByNode = static_cast<nsTextNode*>( - parent->GetProperty(nsGkAtoms::dirAutoSetBy)); - if (setByNode) { - nsTextNodeDirectionalityMap::RemoveElementFromMap(setByNode, - parentElement); - } - } - if (parentElement->HasDirAuto()) { - setByNode = WalkDescendantsSetDirectionFromText(parentElement, aNotify); - if (setByNode) { - nsTextNodeDirectionalityMap::AddEntryToMap(setByNode, parentElement); - } - SetDirectionalityOnDescendants( - parentElement, parentElement->GetDirectionality(), aNotify); - break; + nsTextNode* setByNode = + WalkDescendantsSetDirectionFromText(parentElement, aNotify); + if (setByNode) { + setByNode->SetMaySetDirAuto(); } - parent = GetParentOrHostOrSlot(parent); + SetDirectionalityOnDescendants(parentElement, + parentElement->GetDirectionality(), aNotify); + break; } } @@ -901,26 +589,6 @@ void SlotStateChanged(HTMLSlotElement* aSlot, bool aAllAssignedNodesChanged) { } } -void WalkDescendantsResetAutoDirection(Element* aElement) { - nsIContent* child = aElement->GetFirstChild(); - while (child) { - if (child->IsElement() && child->AsElement()->HasDirAuto()) { - child = child->GetNextNonChildNode(aElement); - continue; - } - - if (child->NodeType() == nsINode::TEXT_NODE && - child->HasTextNodeDirectionalityMap()) { - nsTextNodeDirectionalityMap::ResetTextNodeDirection( - static_cast<nsTextNode*>(child), nullptr); - // Don't call nsTextNodeDirectionalityMap::EnsureMapIsClearFor(child) - // since ResetTextNodeDirection may have kept elements in child's - // DirectionalityMap. - } - child = child->GetNextNode(aElement); - } -} - static void SetAncestorHasDirAutoOnDescendants(nsINode* aRoot); static void MaybeSetAncestorHasDirAutoOnShadowDOM(nsINode* aNode) { @@ -938,7 +606,7 @@ static void SetAncestorHasDirAutoOnDescendants(nsINode* aRoot) { nsIContent* child = aRoot->GetFirstChild(); while (child) { if (child->IsElement() && - DoesNotAffectDirectionOfAncestors(child->AsElement())) { + !AffectsDirectionOfAncestors(child->AsElement())) { child = child->GetNextNonChildNode(aRoot); continue; } @@ -961,20 +629,20 @@ static void SetAncestorHasDirAutoOnDescendants(nsINode* aRoot) { } void WalkDescendantsSetDirAuto(Element* aElement, bool aNotify) { - // Only test for DoesNotParticipateInAutoDirection -- in other words, if - // aElement is a <bdi> which is having its dir attribute set to auto (or + // Only test for ParticipatesInAutoDirection -- in other words, if aElement is + // a <bdi> which is having its dir attribute set to auto (or // removed or set to an invalid value, which are equivalent to dir=auto for // <bdi>, we *do* want to set AncestorHasDirAuto on its descendants, unlike // in SetDirOnBind where we don't propagate AncestorHasDirAuto to a <bdi> // being bound to an existing node with dir=auto. - if (!DoesNotParticipateInAutoDirection(aElement) && + if (ParticipatesInAutoDirection(aElement) && !aElement->AncestorHasDirAuto()) { SetAncestorHasDirAutoOnDescendants(aElement); } nsTextNode* textNode = WalkDescendantsSetDirectionFromText(aElement, aNotify); if (textNode) { - nsTextNodeDirectionalityMap::AddEntryToMap(textNode, aElement); + textNode->SetMaySetDirAuto(); } } @@ -1022,98 +690,67 @@ void WalkDescendantsClearAncestorDirAuto(nsIContent* aContent) { } } -void SetAncestorDirectionIfAuto(nsTextNode* aTextNode, Directionality aDir, - bool aNotify = true) { - MOZ_ASSERT(aTextNode->NodeType() == nsINode::TEXT_NODE, - "Must be a text node"); +struct DirAutoElementResult { + Element* mElement = nullptr; + // This is false when we hit the top of the ancestor chain without finding a + // dir=auto element or an element with a fixed direction. This is useful when + // processing node removals, since we might need to look at the subtree we're + // removing from. + bool mAnswerIsDefinitive = false; +}; - bool crossedShadowBoundary = false; - nsIContent* parent = GetParentOrHostOrSlot(aTextNode, &crossedShadowBoundary); - while (parent && parent->NodeOrAncestorHasDirAuto()) { - if (!parent->IsElement()) { - parent = GetParentOrHostOrSlot(parent, &crossedShadowBoundary); +static DirAutoElementResult FindDirAutoElementFrom(nsIContent* aContent) { + for (nsIContent* parent = aContent; + parent && parent->NodeOrAncestorHasDirAuto(); + parent = GetParentOrHostOrSlot(parent)) { + auto* parentElement = Element::FromNode(*parent); + if (!parentElement) { continue; } - - Element* parentElement = parent->AsElement(); - if (DoesNotParticipateInAutoDirection(parentElement) || + if (!ParticipatesInAutoDirection(parentElement) || parentElement->HasFixedDir()) { - break; + return {nullptr, true}; } - if (parentElement->HasDirAuto()) { - bool resetDirection = false; - nsTextNode* directionWasSetByTextNode = static_cast<nsTextNode*>( - parent->GetProperty(nsGkAtoms::dirAutoSetBy)); - - if (!parent->HasDirAutoSet()) { - // Fast path if parent's direction is not yet set by any descendant - MOZ_ASSERT(!directionWasSetByTextNode, - "dirAutoSetBy property should be null"); - resetDirection = true; - } else { - // If parent's direction is already set, we need to know if - // aTextNode is before or after the text node that had set it. - // We will walk parent's descendants in tree order starting from - // aTextNode to optimize for the most common case where text nodes are - // being appended to tree. - if (!directionWasSetByTextNode) { - resetDirection = true; - } else if (directionWasSetByTextNode != aTextNode) { - if (crossedShadowBoundary || AncestorChainCrossesShadowBoundary( - directionWasSetByTextNode, parent)) { - // Need to take the slow path when the path from either the old or - // new text node to the dir=auto element crosses shadow boundary. - ResetAutoDirection(parentElement, aNotify); - return; - } - - nsIContent* child = aTextNode->GetNextNode(parent); - while (child) { - if (child->IsElement() && - DoesNotAffectDirectionOfAncestors(child->AsElement())) { - child = child->GetNextNonChildNode(parent); - continue; - } - - if (child == directionWasSetByTextNode) { - // we found the node that set the element's direction after our - // text node, so we need to reset the direction - resetDirection = true; - break; - } - - child = child->GetNextNode(parent); - } - } - } - - if (resetDirection) { - if (directionWasSetByTextNode) { - nsTextNodeDirectionalityMap::RemoveElementFromMap( - directionWasSetByTextNode, parentElement); - } - parentElement->SetDirectionality(aDir, aNotify); - nsTextNodeDirectionalityMap::AddEntryToMap(aTextNode, parentElement); - SetDirectionalityOnDescendants(parentElement, aDir, aNotify); - } + return {parentElement, true}; + } + } + return {nullptr, false}; +} - // Since we found an element with dir=auto, we can stop walking the - // parent chain: none of its ancestors will have their direction set by - // any of its descendants. - return; +static DirAutoElementResult FindDirAutoElementForText(nsTextNode* aTextNode) { + MOZ_ASSERT(aTextNode->NodeType() == nsINode::TEXT_NODE, + "Must be a text node"); + return FindDirAutoElementFrom(GetParentOrHostOrSlot(aTextNode)); +} + +static DirAutoElementResult SetAncestorDirectionIfAuto(nsTextNode* aTextNode, + Directionality aDir, + bool aNotify = true) { + auto result = FindDirAutoElementForText(aTextNode); + if (Element* parentElement = result.mElement) { + if (parentElement->GetDirectionality() == aDir) { + // If we know that the directionality is already correct, we don't need to + // reset it. But we might be responsible for the directionality of + // parentElement. + MOZ_ASSERT(aDir != Directionality::Unset); + aTextNode->SetMaySetDirAuto(); + } else { + // Otherwise recompute the directionality of parentElement. + ResetAutoDirection(parentElement, aNotify); } - parent = GetParentOrHostOrSlot(parent, &crossedShadowBoundary); } + return result; } bool TextNodeWillChangeDirection(nsTextNode* aTextNode, Directionality* aOldDir, uint32_t aOffset) { if (!NodeAffectsDirAutoAncestor(aTextNode)) { - nsTextNodeDirectionalityMap::EnsureMapIsClearFor(aTextNode); return false; } + // If the change has happened after the first character with strong + // directionality in the text node, do nothing. uint32_t firstStrong; *aOldDir = GetDirectionFromText(aTextNode, &firstStrong); return (aOffset <= firstStrong); @@ -1121,29 +758,17 @@ bool TextNodeWillChangeDirection(nsTextNode* aTextNode, Directionality* aOldDir, void TextNodeChangedDirection(nsTextNode* aTextNode, Directionality aOldDir, bool aNotify) { + MOZ_ASSERT(NodeAffectsDirAutoAncestor(aTextNode), "Caller should check"); Directionality newDir = GetDirectionFromText(aTextNode); - if (newDir == Directionality::Unset) { - if (aOldDir != Directionality::Unset && - aTextNode->HasTextNodeDirectionalityMap()) { - // This node used to have a strong directional character but no - // longer does. ResetTextNodeDirection() will re-resolve the - // directionality of any elements whose directionality was - // determined by this node. - nsTextNodeDirectionalityMap::ResetTextNodeDirection(aTextNode, aTextNode); - } - } else { - // This node has a strong directional character. If it has a - // TextNodeDirectionalityMap property, it already determines the - // directionality of some element(s), so call UpdateTextNodeDirection to - // reresolve their directionality. If it has no map, or if - // UpdateTextNodeDirection returns zero, indicating that the map is - // empty, call SetAncestorDirectionIfAuto to find ancestor elements - // which should have their directionality determined by this node. - if (aTextNode->HasTextNodeDirectionalityMap() && - nsTextNodeDirectionalityMap::UpdateTextNodeDirection(aTextNode, - newDir)) { - return; - } + if (newDir == aOldDir) { + return; + } + // If the old directionality is Unset, we might determine now dir=auto + // ancestor direction now, even if we don't have the MaySetDirAuto flag. + // + // Otherwise we used to have a strong directionality and either no longer + // does, or it changed. We might need to reset the direction. + if (aOldDir == Directionality::Unset || aTextNode->MaySetDirAuto()) { SetAncestorDirectionIfAuto(aTextNode, newDir, aNotify); } } @@ -1164,17 +789,39 @@ void SetDirectionFromNewTextNode(nsTextNode* aTextNode) { } } -void ResetDirectionSetByTextNode(nsTextNode* aTextNode) { - if (!NodeAffectsDirAutoAncestor(aTextNode)) { - nsTextNodeDirectionalityMap::EnsureMapIsClearFor(aTextNode); +void ResetDirectionSetByTextNode(nsTextNode* aTextNode, + dom::UnbindContext& aContext) { + MOZ_ASSERT(!aTextNode->IsInComposedDoc(), "Should be disconnected already"); + if (!aTextNode->MaySetDirAuto()) { + return; + } + auto result = FindDirAutoElementForText(aTextNode); + if (result.mAnswerIsDefinitive) { + // The dir=auto element is in our (now detached) subtree. We're done, as + // nothing really changed for our purposes. + return; + } + MOZ_ASSERT(!result.mElement); + // The dir=auto element might have been on the element we're unbinding from. + // In any case, this text node is clearly no longer what determines its + // directionality. + aTextNode->ClearMaySetDirAuto(); + auto* unboundFrom = + nsIContent::FromNodeOrNull(aContext.GetOriginalSubtreeParent()); + if (!unboundFrom || !TextChildrenAffectDirAutoAncestor(unboundFrom)) { return; } Directionality dir = GetDirectionFromText(aTextNode); - if (dir != Directionality::Unset && - aTextNode->HasTextNodeDirectionalityMap()) { - nsTextNodeDirectionalityMap::ResetTextNodeDirection(aTextNode, aTextNode); + if (dir == Directionality::Unset) { + return; + } + + result = FindDirAutoElementFrom(unboundFrom); + if (!result.mElement || result.mElement->GetDirectionality() != dir) { + return; } + ResetAutoDirection(result.mElement, /* aNotify = */ true); } void SetDirectionalityFromValue(Element* aElement, const nsAString& value, @@ -1192,26 +839,15 @@ void SetDirectionalityFromValue(Element* aElement, const nsAString& value, void OnSetDirAttr(Element* aElement, const nsAttrValue* aNewValue, bool hadValidDir, bool hadDirAuto, bool aNotify) { - if (aElement->IsHTMLElement(nsGkAtoms::input) || - aElement->IsHTMLElement(nsGkAtoms::textarea)) { + if (aElement->IsAnyOfHTMLElements(nsGkAtoms::input, nsGkAtoms::textarea)) { return; } if (aElement->AncestorHasDirAuto()) { - if (!hadValidDir) { - // The element is a descendant of an element with dir = auto, is - // having its dir attribute set, and previously didn't have a valid dir - // attribute. - // Check whether any of its text node descendants determine the - // direction of any of its ancestors, and redetermine their direction - WalkDescendantsResetAutoDirection(aElement); - } else if (!aElement->HasValidDir()) { - // The element is a descendant of an element with dir = auto and is - // having its dir attribute removed or set to an invalid value. - // Reset the direction of any of its ancestors whose direction is - // determined by a text node descendant - WalkAncestorsResetAutoDirection(aElement, aNotify); - } + // The element is a descendant of an element with dir = auto, is having its + // dir attribute changed. Reset the direction of any of its ancestors whose + // direction might be determined by a text node descendant + WalkAncestorsResetAutoDirection(aElement, aNotify); } else if (hadDirAuto && !aElement->HasDirAuto()) { // The element isn't a descendant of an element with dir = auto, and is // having its dir attribute set to something other than auto. @@ -1231,11 +867,6 @@ void OnSetDirAttr(Element* aElement, const nsAttrValue* aNewValue, if (aElement->HasDirAuto()) { WalkDescendantsSetDirAuto(aElement, aNotify); } else { - if (aElement->HasDirAutoSet()) { - nsTextNode* setByNode = static_cast<nsTextNode*>( - aElement->GetProperty(nsGkAtoms::dirAutoSetBy)); - nsTextNodeDirectionalityMap::RemoveElementFromMap(setByNode, aElement); - } SetDirectionalityOnDescendants( aElement, RecomputeDirectionality(aElement, aNotify), aNotify); } @@ -1244,7 +875,7 @@ void OnSetDirAttr(Element* aElement, const nsAttrValue* aNewValue, void SetDirOnBind(Element* aElement, nsIContent* aParent) { // Set the AncestorHasDirAuto flag, unless this element shouldn't affect // ancestors that have dir=auto - if (!DoesNotParticipateInAutoDirection(aElement) && + if (ParticipatesInAutoDirection(aElement) && !aElement->IsHTMLElement(nsGkAtoms::bdi) && aParent && aParent->NodeOrAncestorHasDirAuto()) { aElement->SetAncestorHasDirAuto(); @@ -1265,12 +896,6 @@ void SetDirOnBind(Element* aElement, nsIContent* aParent) { } void ResetDir(Element* aElement) { - if (aElement->HasDirAutoSet()) { - nsTextNode* setByNode = static_cast<nsTextNode*>( - aElement->GetProperty(nsGkAtoms::dirAutoSetBy)); - nsTextNodeDirectionalityMap::RemoveElementFromMap(setByNode, aElement); - } - if (!aElement->HasDirAuto()) { RecomputeDirectionality(aElement, false); } diff --git a/dom/base/DirectionalityUtils.h b/dom/base/DirectionalityUtils.h index 17cec80485..0012a1d4be 100644 --- a/dom/base/DirectionalityUtils.h +++ b/dom/base/DirectionalityUtils.h @@ -18,6 +18,7 @@ class nsTextNode; namespace mozilla::dom { class Element; class HTMLSlotElement; +struct UnbindContext; } // namespace mozilla::dom namespace mozilla { @@ -133,10 +134,8 @@ void SetDirectionFromNewTextNode(nsTextNode* aTextNode); /** * When a text node is removed from a document, find any ancestors whose * directionality it determined and redetermine their directionality - * - * @param aTextNode the text node */ -void ResetDirectionSetByTextNode(nsTextNode* aTextNode); +void ResetDirectionSetByTextNode(nsTextNode*, dom::UnbindContext&); /** * Set the directionality of an element according to the directionality of the diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp index 819cd8c11d..4e9286a91e 100644 --- a/dom/base/Document.cpp +++ b/dom/base/Document.cpp @@ -104,6 +104,7 @@ #include "mozilla/SMILTimeContainer.h" #include "mozilla/ScopeExit.h" #include "mozilla/Components.h" +#include "mozilla/SVGUtils.h" #include "mozilla/ServoStyleConsts.h" #include "mozilla/ServoTypes.h" #include "mozilla/SizeOfState.h" @@ -1399,6 +1400,7 @@ Document::Document(const char* aContentType) mShouldResistFingerprinting(false), mCloningForSVGUse(false), mAllowDeclarativeShadowRoots(false), + mSuspendDOMNotifications(false), mXMLDeclarationBits(0), mOnloadBlockCount(0), mWriteLevel(0), @@ -1430,7 +1432,6 @@ Document::Document(const char* aContentType) mHttpsOnlyStatus(nsILoadInfo::HTTPS_ONLY_UNINITIALIZED), mViewportType(Unknown), mViewportFit(ViewportFitType::Auto), - mSubDocuments(nullptr), mHeaderData(nullptr), mServoRestyleRootDirtyBits(0), mThrowOnDynamicMarkupInsertionCounter(0), @@ -2333,7 +2334,6 @@ Document::~Document() { // Kill the subdocument map, doing this will release its strong // references, if any. - delete mSubDocuments; mSubDocuments = nullptr; nsAutoScriptBlocker scriptBlocker; @@ -2505,7 +2505,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(Document) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnloadBlocker) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLazyLoadObserver) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLastRememberedSizeObserver) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElementsObservedForLastRememberedSize) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMImplementation) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImageMaps) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOrientationPendingPromise) @@ -2627,7 +2627,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Document) NS_IMPL_CYCLE_COLLECTION_UNLINK(mSecurityInfo) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDisplayDocument) NS_IMPL_CYCLE_COLLECTION_UNLINK(mLazyLoadObserver) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mLastRememberedSizeObserver) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mElementsObservedForLastRememberedSize); NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet) NS_IMPL_CYCLE_COLLECTION_UNLINK(mReadyForIdle) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentL10n) @@ -2687,7 +2687,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Document) tmp->mStyleSheetSetList = nullptr; } - delete tmp->mSubDocuments; tmp->mSubDocuments = nullptr; NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameRequestManager) @@ -2848,7 +2847,6 @@ void Document::DisconnectNodeTree() { // Delete references to sub-documents and kill the subdocument map, // if any. This is not strictly needed, but makes the node tree // teardown a bit faster. - delete mSubDocuments; mSubDocuments = nullptr; bool oldVal = mInUnlinkOrDeletion; @@ -6319,9 +6317,11 @@ void Document::DeferredContentEditableCountChange(Element* aElement) { if (aElement) { if (RefPtr<HTMLEditor> htmlEditor = GetHTMLEditor()) { nsCOMPtr<nsIInlineSpellChecker> spellChecker; - rv = htmlEditor->GetInlineSpellChecker(false, - getter_AddRefs(spellChecker)); - NS_ENSURE_SUCCESS_VOID(rv); + DebugOnly<nsresult> rvIgnored = htmlEditor->GetInlineSpellChecker( + false, getter_AddRefs(spellChecker)); + NS_WARNING_ASSERTION( + NS_SUCCEEDED(rvIgnored), + "EditorBase::GetInlineSpellChecker() failed, but ignored"); if (spellChecker && aElement->InclusiveDescendantMayNeedSpellchecking(htmlEditor)) { @@ -7205,7 +7205,8 @@ nsresult Document::SetSubDocumentFor(Element* aElement, Document* aSubDoc) { PLDHashTable::HashVoidPtrKeyStub, PLDHashTable::MatchEntryStub, PLDHashTable::MoveEntryStub, SubDocClearEntry, SubDocInitEntry}; - mSubDocuments = new PLDHashTable(&hash_table_ops, sizeof(SubDocMapEntry)); + mSubDocuments = + MakeUnique<PLDHashTable>(&hash_table_ops, sizeof(SubDocMapEntry)); } // Add a mapping to the hash table @@ -12394,7 +12395,8 @@ already_AddRefed<nsIURI> Document::ResolvePreloadImage( void Document::PreLoadImage(nsIURI* aUri, const nsAString& aCrossOriginAttr, ReferrerPolicyEnum aReferrerPolicy, bool aIsImgSet, - bool aLinkPreload, uint64_t aEarlyHintPreloaderId) { + bool aLinkPreload, uint64_t aEarlyHintPreloaderId, + const nsAString& aFetchPriority) { nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL | nsContentUtils::CORSModeToLoadImageFlags( Element::StringToCORSMode(aCrossOriginAttr)); @@ -12415,7 +12417,8 @@ void Document::PreLoadImage(nsIURI* aUri, const nsAString& aCrossOriginAttr, nsresult rv = nsContentUtils::LoadImage( aUri, static_cast<nsINode*>(this), this, NodePrincipal(), 0, referrerInfo, nullptr /* no observer */, loadFlags, initiator, getter_AddRefs(request), - policyType, false /* urgent */, aLinkPreload, aEarlyHintPreloaderId); + policyType, false /* urgent */, aLinkPreload, aEarlyHintPreloaderId, + nsGenericHTMLElement::ToFetchPriority(aFetchPriority)); // Pin image-reference to avoid evicting it from the img-cache before // the "real" load occurs. Unpinned in DispatchContentLoadedEvents and @@ -12428,7 +12431,8 @@ void Document::PreLoadImage(nsIURI* aUri, const nsAString& aCrossOriginAttr, void Document::MaybePreLoadImage(nsIURI* aUri, const nsAString& aCrossOriginAttr, ReferrerPolicyEnum aReferrerPolicy, - bool aIsImgSet, bool aLinkPreload) { + bool aIsImgSet, bool aLinkPreload, + const nsAString& aFetchPriority) { const CORSMode corsMode = dom::Element::StringToCORSMode(aCrossOriginAttr); if (aLinkPreload) { // Check if the image was already preloaded in this document to avoid @@ -12437,7 +12441,7 @@ void Document::MaybePreLoadImage(nsIURI* aUri, PreloadHashKey::CreateAsImage(aUri, NodePrincipal(), corsMode); if (!mPreloadService.PreloadExists(key)) { PreLoadImage(aUri, aCrossOriginAttr, aReferrerPolicy, aIsImgSet, - aLinkPreload, 0); + aLinkPreload, 0, aFetchPriority); } return; } @@ -12451,7 +12455,7 @@ void Document::MaybePreLoadImage(nsIURI* aUri, // Image not in cache - trigger preload PreLoadImage(aUri, aCrossOriginAttr, aReferrerPolicy, aIsImgSet, aLinkPreload, - 0); + 0, aFetchPriority); } void Document::MaybePreconnect(nsIURI* aOrigURI, mozilla::CORSMode aCORSMode) { @@ -14751,6 +14755,10 @@ void Document::TopLayerPush(Element& aElement) { const bool modal = aElement.State().HasState(ElementState::MODAL); TopLayerPop(aElement); + if (nsIFrame* f = aElement.GetPrimaryFrame()) { + f->MarkNeedsDisplayItemRebuild(); + } + mTopLayer.AppendElement(do_GetWeakReference(&aElement)); NS_ASSERTION(GetTopLayerTop() == &aElement, "Should match"); @@ -14804,6 +14812,9 @@ Element* Document::TopLayerPop(FunctionRef<bool(Element*)> aPredicate) { nsCOMPtr<Element> element(do_QueryReferent(mTopLayer[i])); if (element && aPredicate(element)) { removedElement = element; + if (nsIFrame* f = element->GetPrimaryFrame()) { + f->MarkNeedsDisplayItemRebuild(); + } mTopLayer.RemoveElementAt(i); break; } @@ -14818,6 +14829,12 @@ Element* Document::TopLayerPop(FunctionRef<bool(Element*)> aPredicate) { while (!mTopLayer.IsEmpty()) { Element* element = GetTopLayerTop(); if (!element || element->GetComposedDoc() != this) { + if (element) { + if (nsIFrame* f = element->GetPrimaryFrame()) { + f->MarkNeedsDisplayItemRebuild(); + } + } + mTopLayer.RemoveLastElement(); } else { // The top element of the stack is now an in-doc element. Return here. @@ -16432,27 +16449,79 @@ DOMIntersectionObserver& Document::EnsureLazyLoadObserver() { return *mLazyLoadObserver; } -ResizeObserver& Document::EnsureLastRememberedSizeObserver() { - if (!mLastRememberedSizeObserver) { - mLastRememberedSizeObserver = - ResizeObserver::CreateLastRememberedSizeObserver(*this); - } - return *mLastRememberedSizeObserver; -} - void Document::ObserveForLastRememberedSize(Element& aElement) { if (NS_WARN_IF(!IsActive())) { return; } - // Options are initialized with ResizeObserverBoxOptions::Content_box by - // default, which is what we want. - static ResizeObserverOptions options; - EnsureLastRememberedSizeObserver().Observe(aElement, options); + mElementsObservedForLastRememberedSize.Insert(&aElement); } void Document::UnobserveForLastRememberedSize(Element& aElement) { - if (mLastRememberedSizeObserver) { - mLastRememberedSizeObserver->Unobserve(aElement); + mElementsObservedForLastRememberedSize.Remove(&aElement); +} + +void Document::UpdateLastRememberedSizes() { + auto shouldRemoveElement = [&](auto* element) { + if (element->GetComposedDoc() != this) { + element->RemoveLastRememberedBSize(); + element->RemoveLastRememberedISize(); + return true; + } + return !element->GetPrimaryFrame(); + }; + + for (auto it = mElementsObservedForLastRememberedSize.begin(), + end = mElementsObservedForLastRememberedSize.end(); + it != end; ++it) { + if (shouldRemoveElement(*it)) { + mElementsObservedForLastRememberedSize.Remove(it); + continue; + } + const auto element = *it; + MOZ_ASSERT(element->GetComposedDoc() == this); + nsIFrame* frame = element->GetPrimaryFrame(); + MOZ_ASSERT(frame); + + // As for ResizeObserver, skip nodes hidden `content-visibility`. + if (frame->IsHiddenByContentVisibilityOnAnyAncestor()) { + continue; + } + + MOZ_ASSERT(!frame->IsLineParticipant() || frame->IsReplaced(), + "Should have unobserved non-replaced inline."); + MOZ_ASSERT(!frame->HidesContent(), + "Should have unobserved element skipping its contents."); + const nsStylePosition* stylePos = frame->StylePosition(); + const WritingMode wm = frame->GetWritingMode(); + bool canUpdateBSize = stylePos->ContainIntrinsicBSize(wm).HasAuto(); + bool canUpdateISize = stylePos->ContainIntrinsicISize(wm).HasAuto(); + MOZ_ASSERT(canUpdateBSize || !element->HasLastRememberedBSize(), + "Should have removed the last remembered block size."); + MOZ_ASSERT(canUpdateISize || !element->HasLastRememberedISize(), + "Should have removed the last remembered inline size."); + MOZ_ASSERT(canUpdateBSize || canUpdateISize, + "Should have unobserved if we can't update any size."); + + AutoTArray<LogicalPixelSize, 1> contentSizeList = + ResizeObserver::CalculateBoxSize(element, + ResizeObserverBoxOptions::Content_box, + /* aForceFragmentHandling */ true); + MOZ_ASSERT(!contentSizeList.IsEmpty()); + + if (canUpdateBSize) { + float bSize = 0; + for (const auto& current : contentSizeList) { + bSize += current.BSize(); + } + element->SetLastRememberedBSize(bSize); + } + if (canUpdateISize) { + float iSize = 0; + for (const auto& current : contentSizeList) { + iSize = std::max(iSize, current.ISize()); + } + element->SetLastRememberedISize(iSize); + } } } @@ -17103,13 +17172,7 @@ bool Document::IsExtensionPage() const { void Document::AddResizeObserver(ResizeObserver& aObserver) { MOZ_ASSERT(!mResizeObservers.Contains(&aObserver)); - // Insert internal ResizeObservers before scripted ones, since they may have - // observable side-effects and we don't want to expose the insertion time. - if (aObserver.HasNativeCallback()) { - mResizeObservers.InsertElementAt(0, &aObserver); - } else { - mResizeObservers.AppendElement(&aObserver); - } + mResizeObservers.AppendElement(&aObserver); } void Document::RemoveResizeObserver(ResizeObserver& aObserver) { @@ -17164,6 +17227,18 @@ void Document::DetermineProximityToViewportAndNotifyResizeObservers() { // sub-documents or ancestors, so flushing layout for the whole browsing // context tree makes sure we don't miss anyone. FlushLayoutForWholeBrowsingContextTree(*this); + + // Last remembered sizes are recorded "at the time that ResizeObserver + // events are determined and delivered". + // https://drafts.csswg.org/css-sizing-4/#last-remembered + // + // We do it right after layout to make sure sizes are up-to-date. If we do + // it after determining the proximities to viewport of + // 'content-visibility: auto' nodes, and if one of such node ever becomes + // relevant to the user, then we would be incorrectly recording the size + // of its rendering when it was skipping its content. + UpdateLastRememberedSizes(); + if (PresShell* presShell = GetPresShell()) { auto result = presShell->DetermineProximityToViewport(); if (result.mHadInitialDetermination) { @@ -18627,7 +18702,7 @@ nsIPrincipal* Document::EffectiveStoragePrincipal() const { } // Calling StorageAllowedForDocument will notify the ContentBlockLog. This - // loads TrackingDBService.jsm, which in turn pulls in osfile.jsm, making us + // loads TrackingDBService.sys.mjs, making us potentially // fail // browser/base/content/test/performance/browser_startup.js. To avoid // that, we short-circuit the check here by allowing storage access to system // and addon principles, avoiding the test-failure. diff --git a/dom/base/Document.h b/dom/base/Document.h index 6a2bd55a9c..a52c61addf 100644 --- a/dom/base/Document.h +++ b/dom/base/Document.h @@ -57,7 +57,6 @@ #include "mozilla/dom/LargestContentfulPaint.h" #include "mozilla/dom/UserActivation.h" #include "mozilla/dom/WakeLockBinding.h" -#include "mozilla/glean/GleanMetrics.h" #include "nsAtom.h" #include "nsCOMArray.h" #include "nsCOMPtr.h" @@ -320,6 +319,9 @@ enum BFCacheStatus { }; } // namespace dom +namespace glean::perf { +struct PageLoadExtra; +} } // namespace mozilla namespace mozilla::net { @@ -2930,10 +2932,11 @@ class Document : public nsINode, */ void MaybePreLoadImage(nsIURI* uri, const nsAString& aCrossOriginAttr, ReferrerPolicyEnum aReferrerPolicy, bool aIsImgSet, - bool aLinkPreload); + bool aLinkPreload, const nsAString& aFetchPriority); void PreLoadImage(nsIURI* uri, const nsAString& aCrossOriginAttr, ReferrerPolicyEnum aReferrerPolicy, bool aIsImgSet, - bool aLinkPreload, uint64_t aEarlyHintPreloaderId); + bool aLinkPreload, uint64_t aEarlyHintPreloaderId, + const nsAString& aFetchPriority); /** * Called by images to forget an image preload when they start doing @@ -3722,12 +3725,12 @@ class Document : public nsINode, DOMIntersectionObserver* GetLazyLoadObserver() { return mLazyLoadObserver; } DOMIntersectionObserver& EnsureLazyLoadObserver(); - ResizeObserver* GetLastRememberedSizeObserver() { - return mLastRememberedSizeObserver; + bool HasElementsWithLastRememberedSize() const { + return !mElementsObservedForLastRememberedSize.IsEmpty(); } - ResizeObserver& EnsureLastRememberedSizeObserver(); void ObserveForLastRememberedSize(Element&); void UnobserveForLastRememberedSize(Element&); + void UpdateLastRememberedSizes(); // Dispatch a runnable related to the document. nsresult Dispatch(already_AddRefed<nsIRunnable>&& aRunnable) const; @@ -3875,6 +3878,17 @@ class Document : public nsINode, void SetAllowDeclarativeShadowRoots(bool aAllowDeclarativeShadowRoots); bool AllowsDeclarativeShadowRoots() const; + void SuspendDOMNotifications() { + MOZ_ASSERT(IsHTMLDocument(), + "Currently suspending DOM notifications is supported only on " + "HTML documents."); + mSuspendDOMNotifications = true; + } + + void ResumeDOMNotifications() { mSuspendDOMNotifications = false; } + + bool DOMNotificationsSuspended() const { return mSuspendDOMNotifications; } + protected: RefPtr<DocumentL10n> mDocumentL10n; @@ -4866,6 +4880,8 @@ class Document : public nsINode, bool mAllowDeclarativeShadowRoots : 1; + bool mSuspendDOMNotifications : 1; + // The fingerprinting protections overrides for this document. The value will // override the default enabled fingerprinting protections for this document. // This will only get populated if these is one that comes from the local @@ -5125,7 +5141,11 @@ class Document : public nsINode, // https://drafts.csswg.org/css-round-display/#viewport-fit-descriptor ViewportFitType mViewportFit; - PLDHashTable* mSubDocuments; + // XXXdholbert This should really be modernized to a nsTHashMap or similar, + // though note that the modernization will need to take care to also convert + // the special hash_table_ops logic (e.g. how SubDocClearEntry clears the + // parent document as part of cleaning up an entry in this table). + UniquePtr<PLDHashTable> mSubDocuments; class HeaderData; UniquePtr<HeaderData> mHeaderData; @@ -5162,9 +5182,9 @@ class Document : public nsINode, RefPtr<DOMIntersectionObserver> mLazyLoadObserver; - // ResizeObserver for storing and removing the last remembered size. + // Elements observed for a last remembered size. // @see {@link https://drafts.csswg.org/css-sizing-4/#last-remembered} - RefPtr<ResizeObserver> mLastRememberedSizeObserver; + nsTHashSet<RefPtr<Element>> mElementsObservedForLastRememberedSize; // Stack of top layer elements. nsTArray<nsWeakPtr> mTopLayer; diff --git a/dom/base/DocumentFragment.h b/dom/base/DocumentFragment.h index 4b1c11c480..9b8cc69673 100644 --- a/dom/base/DocumentFragment.h +++ b/dom/base/DocumentFragment.h @@ -66,7 +66,7 @@ class DocumentFragment : public FragmentOrElement { return NS_ERROR_NOT_IMPLEMENTED; } - virtual void UnbindFromTree(bool aNullParent) override { + virtual void UnbindFromTree(UnbindContext&) override { NS_ASSERTION(false, "Trying to unbind a fragment from a tree"); } diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index f653363f48..be31000278 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -54,7 +54,6 @@ #include "mozilla/PresShellForwards.h" #include "mozilla/ReflowOutput.h" #include "mozilla/RelativeTo.h" -#include "mozilla/ScrollOrigin.h" #include "mozilla/ScrollTypes.h" #include "mozilla/ServoStyleConsts.h" #include "mozilla/ServoStyleConstsInlines.h" @@ -106,6 +105,7 @@ #include "mozilla/dom/ScriptLoader.h" #include "mozilla/dom/ShadowRoot.h" #include "mozilla/dom/Text.h" +#include "mozilla/dom/UnbindContext.h" #include "mozilla/dom/WindowBinding.h" #include "mozilla/dom/XULCommandEvent.h" #include "mozilla/dom/nsCSPContext.h" @@ -113,6 +113,7 @@ #include "mozilla/gfx/BaseRect.h" #include "mozilla/gfx/BaseSize.h" #include "mozilla/gfx/Matrix.h" +#include "mozilla/widget/Screen.h" #include "nsAtom.h" #include "nsAttrName.h" #include "nsAttrValueInlines.h" @@ -161,7 +162,6 @@ #include "nsIInterfaceRequestor.h" #include "nsIMemoryReporter.h" #include "nsIPrincipal.h" -#include "nsIScreenManager.h" #include "nsIScriptError.h" #include "nsIScrollableFrame.h" #include "nsISpeculativeConnect.h" @@ -780,8 +780,6 @@ void Element::ScrollIntoView(const ScrollIntoViewOptions& aOptions) { return WhereToScroll::Center; case ScrollLogicalPosition::End: return WhereToScroll::End; - case ScrollLogicalPosition::EndGuard_: - MOZ_FALLTHROUGH_ASSERT("Unexpected block direction value"); case ScrollLogicalPosition::Nearest: break; } @@ -1048,20 +1046,13 @@ int32_t Element::ScreenY() { } already_AddRefed<nsIScreen> Element::GetScreen() { - nsIFrame* frame = GetPrimaryFrame(FlushType::Layout); - if (!frame) { - return nullptr; + // Flush layout to guarantee that frames are created if needed, and preserve + // behavior. + Unused << GetPrimaryFrame(FlushType::Frames); + if (nsIWidget* widget = nsContentUtils::WidgetForContent(this)) { + return widget->GetWidgetScreen(); } - nsCOMPtr<nsIScreenManager> screenMgr = - do_GetService("@mozilla.org/gfx/screenmanager;1"); - if (!screenMgr) { - return nullptr; - } - nsPresContext* pc = frame->PresContext(); - const CSSIntRect rect = frame->GetScreenRect(); - DesktopRect desktopRect = rect * pc->CSSToDevPixelScale() / - pc->DeviceContext()->GetDesktopToDeviceScale(); - return screenMgr->ScreenForRect(DesktopIntRect::Round(desktopRect)); + return nullptr; } already_AddRefed<DOMRect> Element::GetBoundingClientRect() { @@ -1722,8 +1713,7 @@ already_AddRefed<nsIHTMLCollection> Element::GetElementsByClassName( } Element* Element::GetAttrAssociatedElement(nsAtom* aAttr) const { - const nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots(); - if (slots) { + if (const nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots()) { nsWeakPtr weakAttrEl = slots->mExplicitlySetAttrElements.Get(aAttr); if (nsCOMPtr<Element> attrEl = do_QueryReferent(weakAttrEl)) { // If reflectedTarget's explicitly set attr-element |attrEl| is @@ -1774,16 +1764,56 @@ void Element::ClearExplicitlySetAttrElement(nsAtom* aAttr) { } void Element::ExplicitlySetAttrElement(nsAtom* aAttr, Element* aElement) { +#ifdef ACCESSIBILITY + nsAccessibilityService* accService = GetAccService(); +#endif + // Accessibility requires that no other attribute changes occur between + // AttrElementWillChange and AttrElementChanged. Scripts could cause + // this, so don't let them run here. We do this even if accessibility isn't + // running so that the JS behavior is consistent regardless of accessibility. + // Otherwise, JS might be able to use this difference to determine whether + // accessibility is running, which would be a privacy concern. + nsAutoScriptBlocker scriptBlocker; if (aElement) { +#ifdef ACCESSIBILITY + if (accService) { + accService->NotifyAttrElementWillChange(this, aAttr); + } +#endif SetAttr(aAttr, EmptyString(), IgnoreErrors()); nsExtendedDOMSlots* slots = ExtendedDOMSlots(); slots->mExplicitlySetAttrElements.InsertOrUpdate( aAttr, do_GetWeakReference(aElement)); +#ifdef ACCESSIBILITY + if (accService) { + accService->NotifyAttrElementChanged(this, aAttr); + } +#endif return; } +#ifdef ACCESSIBILITY + if (accService) { + accService->NotifyAttrElementWillChange(this, aAttr); + } +#endif ClearExplicitlySetAttrElement(aAttr); UnsetAttr(aAttr, IgnoreErrors()); +#ifdef ACCESSIBILITY + if (accService) { + accService->NotifyAttrElementChanged(this, aAttr); + } +#endif +} + +Element* Element::GetExplicitlySetAttrElement(nsAtom* aAttr) const { + if (const nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots()) { + nsWeakPtr weakAttrEl = slots->mExplicitlySetAttrElements.Get(aAttr); + if (nsCOMPtr<Element> attrEl = do_QueryReferent(weakAttrEl)) { + return attrEl; + } + } + return nullptr; } void Element::GetElementsWithGrid(nsTArray<RefPtr<Element>>& aElements) { @@ -1958,7 +1988,8 @@ nsresult Element::BindToTree(BindContext& aContext, nsINode& aParent) { return NS_OK; } -bool WillDetachFromShadowOnUnbind(const Element& aElement, bool aNullParent) { +static bool WillDetachFromShadowOnUnbind(const Element& aElement, + bool aNullParent) { // If our parent still is in a shadow tree by now, and we're not removing // ourselves from it, then we're still going to be in a shadow tree after // this. @@ -1966,12 +1997,14 @@ bool WillDetachFromShadowOnUnbind(const Element& aElement, bool aNullParent) { (aNullParent || !aElement.GetParent()->IsInShadowTree()); } -void Element::UnbindFromTree(bool aNullParent) { - HandleShadowDOMRelatedRemovalSteps(aNullParent); +void Element::UnbindFromTree(UnbindContext& aContext) { + const bool nullParent = aContext.IsUnbindRoot(this); + + HandleShadowDOMRelatedRemovalSteps(nullParent); if (HasFlag(ELEMENT_IS_DATALIST_OR_HAS_DATALIST_ANCESTOR) && !IsHTMLElement(nsGkAtoms::datalist)) { - if (aNullParent) { + if (nullParent) { UnsetFlags(ELEMENT_IS_DATALIST_OR_HAS_DATALIST_ANCESTOR); } else { nsIContent* parent = GetParent(); @@ -1983,7 +2016,7 @@ void Element::UnbindFromTree(bool aNullParent) { } const bool detachingFromShadow = - WillDetachFromShadowOnUnbind(*this, aNullParent); + WillDetachFromShadowOnUnbind(*this, nullParent); // Make sure to only remove from the ID table if our subtree root is actually // changing. if (IsInUncomposedDoc() || detachingFromShadow) { @@ -2033,7 +2066,7 @@ void Element::UnbindFromTree(bool aNullParent) { data->ClearAllAnimationCollections(); } - if (aNullParent) { + if (nullParent) { if (GetParent()) { RefPtr<nsINode> p; p.swap(mParent); @@ -2071,15 +2104,13 @@ void Element::UnbindFromTree(bool aNullParent) { ClearElementCreatedFromPrototypeAndHasUnmodifiedL10n(); } - if (aNullParent || !mParent->IsInShadowTree()) { + if (nullParent || !mParent->IsInShadowTree()) { UnsetFlags(NODE_IS_IN_SHADOW_TREE); // Begin keeping track of our subtree root. - SetSubtreeRootPointer(aNullParent ? this : mParent->SubtreeRoot()); - } + SetSubtreeRootPointer(nullParent ? this : mParent->SubtreeRoot()); - if (nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots()) { - if (aNullParent || !mParent->IsInShadowTree()) { + if (nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots()) { slots->mContainingShadow = nullptr; } } @@ -2103,11 +2134,10 @@ void Element::UnbindFromTree(bool aNullParent) { } if (HasLastRememberedBSize() || HasLastRememberedISize()) { - // Need to remove the last remembered size at the next ResizeObserver - // opportunity, so observe the element. But if already observed, we still - // want the callback to be invoked even if the size was already 0x0, so - // unobserve it first. - document->UnobserveForLastRememberedSize(*this); + // Make sure the element is observed so that remembered sizes are kept + // until the next time "ResizeObserver events are determined and + // delivered". See "Disconnected element" tests from + // css/css-sizing/contain-intrinsic-size/auto-006.html document->ObserveForLastRememberedSize(*this); } } @@ -2121,9 +2151,7 @@ void Element::UnbindFromTree(bool aNullParent) { for (nsIContent* child = GetFirstChild(); child; child = child->GetNextSibling()) { - // Note that we pass false for aNullParent here, since we don't want - // the kids to forget us. - child->UnbindFromTree(false); + child->UnbindFromTree(aContext); } MutationObservers::NotifyParentChainChanged(this); @@ -2751,6 +2779,12 @@ bool Element::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute, return true; } + if (aAttribute == nsGkAtoms::aria_activedescendant) { + // String in aria-activedescendant is an id, so store as an atom. + aResult.ParseAtom(aValue); + return true; + } + if (aAttribute == nsGkAtoms::id) { // Store id as an atom. id="" means that the element has no id, // not that it has an emptystring as the id. @@ -2805,6 +2839,8 @@ void Element::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName, if (ShadowRoot* shadow = GetParent()->GetShadowRoot()) { shadow->MaybeReassignContent(*this); } + } else if (aName == nsGkAtoms::aria_activedescendant) { + ClearExplicitlySetAttrElement(aName); } } } @@ -2856,6 +2892,11 @@ void Element::OnAttrSetButNotChanged(int32_t aNamespaceID, nsAtom* aName, ElementCallbackType::eAttributeChanged, this, args, definition); } } + + if (aNamespaceID == kNameSpaceID_None && + aName == nsGkAtoms::aria_activedescendant) { + ClearExplicitlySetAttrElement(aName); + } } EventListenerManager* Element::GetEventListenerManagerForAttr(nsAtom* aAttrName, @@ -3397,14 +3438,6 @@ nsresult Element::PostHandleEventForLinks(EventChainPostVisitor& aVisitor) { void Element::GetLinkTarget(nsAString& aTarget) { aTarget.Truncate(); } -static nsStaticAtom* const sPropertiesToTraverseAndUnlink[] = { - nsGkAtoms::dirAutoSetBy, nullptr}; - -// static -nsStaticAtom* const* Element::HTMLSVGPropertiesToTraverseAndUnlink() { - return sPropertiesToTraverseAndUnlink; -} - nsresult Element::CopyInnerTo(Element* aDst, ReparseAttributes aReparse) { nsresult rv = aDst->mAttrs.EnsureCapacityToClone(mAttrs); NS_ENSURE_SUCCESS(rv, rv); @@ -5048,4 +5081,14 @@ void Element::SetHTMLUnsafe(const nsAString& aHTML) { nsContentUtils::SetHTMLUnsafe(this, this, aHTML); } +bool Element::BlockingContainsRender() const { + const nsAttrValue* attrValue = GetParsedAttr(nsGkAtoms::blocking); + if (!attrValue || !StaticPrefs::dom_element_blocking_enabled()) { + return false; + } + MOZ_ASSERT(attrValue->Type() == nsAttrValue::eAtomArray, + "Checking blocking attribute on element that doesn't parse it?"); + return attrValue->Contains(nsGkAtoms::render, eIgnoreCase); +} + } // namespace mozilla::dom diff --git a/dom/base/Element.h b/dom/base/Element.h index 6c717bfc88..40a8052aef 100644 --- a/dom/base/Element.h +++ b/dom/base/Element.h @@ -90,7 +90,6 @@ class nsIDOMXULSelectControlElement; class nsIDOMXULSelectControlItemElement; class nsIFrame; class nsIHTMLCollection; -class nsIMozBrowserFrame; class nsIPrincipal; class nsIScreen; class nsIScrollableFrame; @@ -244,6 +243,15 @@ class Grid; SetOrRemoveNullableStringAttr(nsGkAtoms::attr, aValue, aRv); \ } +#define REFLECT_NULLABLE_ELEMENT_ATTR(method, attr) \ + Element* Get##method() const { \ + return GetAttrAssociatedElement(nsGkAtoms::attr); \ + } \ + \ + void Set##method(Element* aElement) { \ + ExplicitlySetAttrElement(nsGkAtoms::attr, aElement); \ + } + class Element : public FragmentOrElement { public: #ifdef MOZILLA_INTERNAL_API @@ -459,22 +467,13 @@ class Element : public FragmentOrElement { virtual bool IsInteractiveHTMLContent() const; /** - * Returns |this| as an nsIMozBrowserFrame* if the element is a frame or - * iframe element. - * - * We have this method, rather than using QI, so that we can use it during - * the servo traversal, where we can't QI DOM nodes because of non-thread-safe - * refcounts. - */ - virtual nsIMozBrowserFrame* GetAsMozBrowserFrame() { return nullptr; } - - /** * Is the attribute named aAttribute a mapped attribute? */ NS_IMETHOD_(bool) IsAttributeMapped(const nsAtom* aAttribute) const; nsresult BindToTree(BindContext&, nsINode& aParent) override; - void UnbindFromTree(bool aNullParent = true) override; + void UnbindFromTree(UnbindContext&) override; + using nsIContent::UnbindFromTree; virtual nsMapRuleToAttributesFunc GetAttributeMappingFunction() const; static void MapNoAttributesInto(mozilla::MappedDeclarationsBuilder&); @@ -658,8 +657,13 @@ class Element : public FragmentOrElement { REFLECT_NULLABLE_DOMSTRING_ATTR(Role, role) // AriaAttributes + REFLECT_NULLABLE_ELEMENT_ATTR(AriaActiveDescendantElement, + aria_activedescendant) REFLECT_NULLABLE_DOMSTRING_ATTR(AriaAtomic, aria_atomic) REFLECT_NULLABLE_DOMSTRING_ATTR(AriaAutoComplete, aria_autocomplete) + REFLECT_NULLABLE_DOMSTRING_ATTR(AriaBrailleLabel, aria_braillelabel) + REFLECT_NULLABLE_DOMSTRING_ATTR(AriaBrailleRoleDescription, + aria_brailleroledescription) REFLECT_NULLABLE_DOMSTRING_ATTR(AriaBusy, aria_busy) REFLECT_NULLABLE_DOMSTRING_ATTR(AriaChecked, aria_checked) REFLECT_NULLABLE_DOMSTRING_ATTR(AriaColCount, aria_colcount) @@ -1107,8 +1111,6 @@ class Element : public FragmentOrElement { return FindAttributeDependence(aAttribute, aMaps, N); } - static nsStaticAtom* const* HTMLSVGPropertiesToTraverseAndUnlink(); - MOZ_CAN_RUN_SCRIPT virtual void HandleInvokeInternal(nsAtom* aAction, ErrorResult& aRv) {} @@ -1245,6 +1247,16 @@ class Element : public FragmentOrElement { void ClearExplicitlySetAttrElement(nsAtom*); + /** + * Gets the attribute element for the given attribute. + * https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#explicitly-set-attr-element + * Unlike GetAttrAssociatedElement, this returns the target even if it isn't + * a descendant of any of this element's shadow-including ancestors. It also + * doesn't attempt to retrieve an element using a string id set in the content + * attribute. + */ + Element* GetExplicitlySetAttrElement(nsAtom* aAttr) const; + PseudoStyleType GetPseudoElementType() const { nsresult rv = NS_OK; auto raw = GetProperty(nsGkAtoms::pseudoProperty, &rv); @@ -1332,6 +1344,10 @@ class Element : public FragmentOrElement { void GetLoading(nsAString& aValue) const; bool ParseLoadingAttribute(const nsAString& aValue, nsAttrValue& aResult); + // https://html.spec.whatwg.org/#potentially-render-blocking + virtual bool IsPotentiallyRenderBlocking() { return false; } + bool BlockingContainsRender() const; + // Shadow DOM v1 enum class ShadowRootDeclarative : bool { No, Yes }; diff --git a/dom/base/EventSource.cpp b/dom/base/EventSource.cpp index 52cb13f097..f70db487dd 100644 --- a/dom/base/EventSource.cpp +++ b/dom/base/EventSource.cpp @@ -849,7 +849,8 @@ EventSourceImpl::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) { aStatusCode != NS_ERROR_NET_PARTIAL_TRANSFER && aStatusCode != NS_ERROR_NET_TIMEOUT_EXTERNAL && aStatusCode != NS_ERROR_PROXY_CONNECTION_REFUSED && - aStatusCode != NS_ERROR_DNS_LOOKUP_QUEUE_FULL) { + aStatusCode != NS_ERROR_DNS_LOOKUP_QUEUE_FULL && + aStatusCode != NS_ERROR_INVALID_CONTENT_ENCODING) { DispatchFailConnection(); return NS_ERROR_ABORT; } diff --git a/dom/base/FlushType.h b/dom/base/FlushType.h index 04a253c534..c90cbbd16f 100644 --- a/dom/base/FlushType.h +++ b/dom/base/FlushType.h @@ -40,7 +40,7 @@ enum class FlushType : uint8_t { // Flush type strings that will be displayed in the profiler // clang-format off -const EnumeratedArray<FlushType, FlushType::Count, const char*> +const EnumeratedArray<FlushType, const char*, size_t(FlushType::Count)> kFlushTypeNames = { "", "Event", diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp index 9811ce5ace..87fd81bfa3 100644 --- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -16,11 +16,11 @@ #include "mozilla/dom/FragmentOrElement.h" #include "DOMIntersectionObserver.h" #include "mozilla/AsyncEventDispatcher.h" -#include "mozilla/DeclarationBlock.h" #include "mozilla/EffectSet.h" #include "mozilla/EventDispatcher.h" #include "mozilla/EventListenerManager.h" #include "mozilla/ElementAnimationData.h" +#include "mozilla/DeclarationBlock.h" #include "mozilla/HTMLEditor.h" #include "mozilla/mozInlineSpellChecker.h" #include "mozilla/PresShell.h" @@ -30,39 +30,31 @@ #include "mozilla/URLExtraData.h" #include "mozilla/dom/Attr.h" #include "mozilla/dom/RadioGroupContainer.h" +#include "mozilla/dom/UnbindContext.h" #include "nsDOMAttributeMap.h" #include "nsAtom.h" #include "mozilla/dom/NodeInfo.h" #include "mozilla/dom/Event.h" #include "mozilla/dom/ScriptLoader.h" -#include "mozilla/dom/TouchEvent.h" #include "mozilla/dom/CustomElementRegistry.h" #include "mozilla/dom/Document.h" #include "mozilla/dom/DocumentInlines.h" #include "nsIControllers.h" #include "nsIDocumentEncoder.h" #include "nsFocusManager.h" -#include "nsIScriptGlobalObject.h" #include "nsNetUtil.h" #include "nsIFrame.h" #include "nsIAnonymousContentCreator.h" #include "nsPresContext.h" -#include "nsStyleConsts.h" #include "nsString.h" -#include "nsUnicharUtils.h" -#include "nsDOMCID.h" #include "nsDOMCSSAttrDeclaration.h" #include "nsNameSpaceManager.h" #include "nsContentList.h" #include "nsDOMTokenList.h" #include "nsError.h" -#include "nsDOMString.h" #include "nsXULElement.h" #include "mozilla/InternalMutationEvent.h" #include "mozilla/MouseEvents.h" -#include "nsAttrValueOrString.h" -#include "nsQueryObject.h" -#include "nsFrameSelection.h" #ifdef DEBUG # include "nsRange.h" #endif @@ -73,7 +65,6 @@ #include "nsGkAtoms.h" #include "nsContentUtils.h" #include "nsTextFragment.h" -#include "nsContentCID.h" #include "nsWindowSizes.h" #include "nsIWidget.h" @@ -82,12 +73,10 @@ #include "nsGenericHTMLElement.h" #include "nsContentCreatorFunctions.h" #include "nsView.h" -#include "nsViewManager.h" #include "nsIScrollableFrame.h" #include "ChildIterator.h" -#include "nsTextNode.h" #include "mozilla/dom/NodeListBinding.h" - +#include "mozilla/dom/MutationObservers.h" #include "nsCCUncollectableMarker.h" #include "mozAutoDocUpdate.h" @@ -97,16 +86,12 @@ #include "nsWrapperCacheInlines.h" #include "nsCycleCollector.h" #include "xpcpublic.h" -#include "mozilla/Telemetry.h" - -#include "mozilla/CORSMode.h" #include "mozilla/dom/ShadowRoot.h" #include "mozilla/dom/HTMLSlotElement.h" #include "mozilla/dom/HTMLTemplateElement.h" #include "mozilla/dom/SVGUseElement.h" -#include "nsStyledElement.h" #include "nsIContentInlines.h" #include "nsChildContentList.h" #include "mozilla/BloomFilter.h" @@ -169,6 +154,11 @@ nsIContent* nsIContent::FindFirstNonChromeOnlyAccessContent() const { return nullptr; } +void nsIContent::UnbindFromTree() { + UnbindContext context(*this); + UnbindFromTree(context); +} + // https://dom.spec.whatwg.org/#dom-slotable-assignedslot HTMLSlotElement* nsIContent::GetAssignedSlotByMode() const { /** @@ -1337,14 +1327,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FragmentOrElement) Element* elem = tmp->AsElement(); elem->UnlinkIntersectionObservers(); } - - if (tmp->IsHTMLElement() || tmp->IsSVGElement()) { - nsStaticAtom* const* props = - Element::HTMLSVGPropertiesToTraverseAndUnlink(); - for (uint32_t i = 0; props[i]; ++i) { - tmp->RemoveProperty(props[i]); - } - } } // Unlink child content (and unbind our subtree). @@ -1815,15 +1797,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(FragmentOrElement) } } } - if (tmp->IsHTMLElement() || tmp->IsSVGElement()) { - nsStaticAtom* const* props = - Element::HTMLSVGPropertiesToTraverseAndUnlink(); - for (uint32_t i = 0; props[i]; ++i) { - nsISupports* property = - static_cast<nsISupports*>(tmp->GetProperty(props[i])); - cb.NoteXPCOMChild(property); - } - } } if (tmp->IsElement()) { Element* element = tmp->AsElement(); @@ -2029,6 +2002,7 @@ void FragmentOrElement::SetInnerHTMLInternal(const nsAString& aInnerHTML, } if (doc->IsHTMLDocument()) { + doc->SuspendDOMNotifications(); nsAtom* contextLocalName = parseContext->NodeInfo()->NameAtom(); int32_t contextNameSpaceID = parseContext->GetNameSpaceID(); @@ -2036,6 +2010,10 @@ void FragmentOrElement::SetInnerHTMLInternal(const nsAString& aInnerHTML, aError = nsContentUtils::ParseFragmentHTML( aInnerHTML, target, contextLocalName, contextNameSpaceID, doc->GetCompatibilityMode() == eCompatibility_NavQuirks, true); + doc->ResumeDOMNotifications(); + if (target->GetFirstChild()) { + MutationObservers::NotifyContentAppended(target, target->GetFirstChild()); + } mb.NodesAdded(); // HTML5 parser has notified, but not fired mutation events. nsContentUtils::FireMutationEventsForDirectParsing(doc, target, diff --git a/dom/base/IDTracker.cpp b/dom/base/IDTracker.cpp index 4db592534d..813aa44e11 100644 --- a/dom/base/IDTracker.cpp +++ b/dom/base/IDTracker.cpp @@ -132,13 +132,30 @@ void IDTracker::ResetWithLocalRef(Element& aFrom, const nsAString& aLocalRef, bool aWatch) { MOZ_ASSERT(nsContentUtils::IsLocalRefURL(aLocalRef)); - nsAutoCString ref; - if (!AppendUTF16toUTF8(Substring(aLocalRef, 1), ref, mozilla::fallible)) { + auto ref = Substring(aLocalRef, 1); + if (ref.IsEmpty()) { Unlink(); return; } - NS_UnescapeURL(ref); - RefPtr<nsAtom> idAtom = NS_Atomize(ref); + + nsAutoCString utf8Ref; + if (!AppendUTF16toUTF8(ref, utf8Ref, mozilla::fallible)) { + Unlink(); + return; + } + + // Only unescape ASCII characters; if we were to unescape arbitrary bytes, + // we'd potentially end up with invalid UTF-8. + nsAutoCString unescaped; + bool appended; + if (NS_FAILED(NS_UnescapeURL(utf8Ref.BeginReading(), utf8Ref.Length(), + esc_OnlyASCII | esc_AlwaysCopy, unescaped, + appended, mozilla::fallible))) { + Unlink(); + return; + } + + RefPtr<nsAtom> idAtom = NS_Atomize(unescaped); ResetWithID(aFrom, idAtom, aWatch); } diff --git a/dom/base/InProcessBrowserChildMessageManager.cpp b/dom/base/InProcessBrowserChildMessageManager.cpp index 191ec9f7b5..830c57591b 100644 --- a/dom/base/InProcessBrowserChildMessageManager.cpp +++ b/dom/base/InProcessBrowserChildMessageManager.cpp @@ -13,7 +13,6 @@ #include "nsFrameLoaderOwner.h" #include "nsQueryObject.h" #include "xpcpublic.h" -#include "nsIMozBrowserFrame.h" #include "mozilla/EventDispatcher.h" #include "mozilla/dom/ChromeMessageSender.h" #include "mozilla/dom/Document.h" @@ -100,15 +99,6 @@ InProcessBrowserChildMessageManager::InProcessBrowserChildMessageManager( mOwner(aOwner), mChromeMessageManager(aChrome) { mozilla::HoldJSObjects(this); - - // If owner corresponds to an <iframe mozbrowser>, we'll have to tweak our - // GetEventTargetParent implementation. - nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwner); - if (browserFrame) { - mIsBrowserFrame = browserFrame->GetReallyIsBrowser(); - } else { - mIsBrowserFrame = false; - } } InProcessBrowserChildMessageManager::~InProcessBrowserChildMessageManager() { @@ -236,19 +226,7 @@ void InProcessBrowserChildMessageManager::GetEventTargetParent( return; } - if (mIsBrowserFrame && - (!mOwner || !nsContentUtils::IsInChromeDocshell(mOwner->OwnerDoc()))) { - if (mOwner) { - if (nsPIDOMWindowInner* innerWindow = - mOwner->OwnerDoc()->GetInnerWindow()) { - // 'this' is already a "chrome handler", so we consider window's - // parent target to be part of that same part of the event path. - aVisitor.SetParentTarget(innerWindow->GetParentTarget(), false); - } - } - } else { - aVisitor.SetParentTarget(mOwner, false); - } + aVisitor.SetParentTarget(mOwner, false); } class nsAsyncScriptLoad : public Runnable { diff --git a/dom/base/InProcessBrowserChildMessageManager.h b/dom/base/InProcessBrowserChildMessageManager.h index b12e84290f..4064a6d65b 100644 --- a/dom/base/InProcessBrowserChildMessageManager.h +++ b/dom/base/InProcessBrowserChildMessageManager.h @@ -108,9 +108,6 @@ class InProcessBrowserChildMessageManager final RefPtr<nsDocShell> mDocShell; bool mLoadingScript; - // Is this the message manager for an in-process <iframe mozbrowser>? This - // affects where events get sent, so it affects GetEventTargetParent. - bool mIsBrowserFrame; bool mPreventEventsEscaping; // We keep a strong reference to the frameloader after we've started diff --git a/dom/base/IndexedDBHelper.sys.mjs b/dom/base/IndexedDBHelper.sys.mjs index a36b211c0a..a9a6b39786 100644 --- a/dom/base/IndexedDBHelper.sys.mjs +++ b/dom/base/IndexedDBHelper.sys.mjs @@ -9,7 +9,7 @@ if (DEBUG) { dump("-*- IndexedDBHelper: " + s + "\n"); }; } else { - debug = function (s) {}; + debug = function () {}; } function getErrorName(err) { @@ -70,7 +70,7 @@ IndexedDBHelper.prototype = { debug("Opened database:" + self.dbName + " " + self.dbVersion); } self._db = event.target.result; - self._db.onversionchange = function (event) { + self._db.onversionchange = function () { if (DEBUG) { debug("WARNING: DB modified from a different window."); } @@ -106,7 +106,7 @@ IndexedDBHelper.prototype = { } invokeCallbacks(getErrorName(aEvent.target.error)); }; - req.onblocked = function (aEvent) { + req.onblocked = function () { if (DEBUG) { debug("Opening database request is blocked."); } diff --git a/dom/base/MimeType.cpp b/dom/base/MimeType.cpp index c5869b1d8a..da61489c1f 100644 --- a/dom/base/MimeType.cpp +++ b/dom/base/MimeType.cpp @@ -9,8 +9,8 @@ #include "nsUnicharUtils.h" template <typename char_type> -/* static */ mozilla::UniquePtr<TMimeType<char_type>> -TMimeType<char_type>::Parse(const nsTSubstring<char_type>& aMimeType) { +/* static */ RefPtr<TMimeType<char_type>> TMimeType<char_type>::Parse( + const nsTSubstring<char_type>& aMimeType) { // See https://mimesniff.spec.whatwg.org/#parsing-a-mime-type // Steps 1-2 @@ -85,8 +85,8 @@ TMimeType<char_type>::Parse(const nsTSubstring<char_type>& aMimeType) { for (const char_type* c = subtypeStart; c < subtypeEnd; ++c) { subtype.Append(ToLowerCaseASCII(*c)); } - mozilla::UniquePtr<TMimeType<char_type>> mimeType( - mozilla::MakeUnique<TMimeType<char_type>>(type, subtype)); + RefPtr<TMimeType<char_type>> mimeType = + new TMimeType<char_type>(type, subtype); // Step 11 while (pos < end) { @@ -274,7 +274,7 @@ template <typename char_type> static char_type kCHARSET[] = {'c', 'h', 'a', 'r', 's', 'e', 't'}; static nsTDependentSubstring<char_type> kCharset(kCHARSET, 7); - mozilla::UniquePtr<TMimeType<char_type>> parsed; + RefPtr<TMimeType<char_type>> parsed; nsTAutoString<char_type> prevContentType; nsTAutoString<char_type> prevCharset; @@ -398,9 +398,9 @@ void TMimeType<char_type>::SetParameterValue( }); } -template mozilla::UniquePtr<TMimeType<char16_t>> TMimeType<char16_t>::Parse( +template RefPtr<TMimeType<char16_t>> TMimeType<char16_t>::Parse( const nsTSubstring<char16_t>& aMimeType); -template mozilla::UniquePtr<TMimeType<char>> TMimeType<char>::Parse( +template RefPtr<TMimeType<char>> TMimeType<char>::Parse( const nsTSubstring<char>& aMimeType); template bool TMimeType<char16_t>::Parse( const nsTSubstring<char16_t>& aMimeType, diff --git a/dom/base/MimeType.h b/dom/base/MimeType.h index 8c980c7f63..a8b23dfa9b 100644 --- a/dom/base/MimeType.h +++ b/dom/base/MimeType.h @@ -8,7 +8,6 @@ #define mozilla_dom_MimeType_h #include "mozilla/TextUtils.h" -#include "mozilla/UniquePtr.h" #include "nsTHashMap.h" #include "nsTArray.h" @@ -26,6 +25,8 @@ struct HashKeyType<char> { template <typename char_type> class TMimeType final { private: + ~TMimeType() = default; + class ParameterValue : public nsTString<char_type> { public: bool mRequiresQuoting; @@ -48,8 +49,8 @@ class TMimeType final { const nsTSubstring<char_type>& aSubtype) : mType(aType), mSubtype(aSubtype) {} - static mozilla::UniquePtr<TMimeType<char_type>> Parse( - const nsTSubstring<char_type>& aStr); + static RefPtr<TMimeType<char_type>> Parse( + const nsTSubstring<char_type>& aMimeType); // @param aMimeType - the mimetype string // @param aOutEssence - will hold the value of the content-type @@ -84,6 +85,8 @@ class TMimeType final { // @param aValue - the value of the parameter void SetParameterValue(const nsTSubstring<char_type>& aName, const nsTSubstring<char_type>& aValue); + + NS_INLINE_DECL_REFCOUNTING(TMimeType) }; using MimeType = TMimeType<char16_t>; diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp index d4b04f8092..14a00b8ed8 100644 --- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp @@ -171,6 +171,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Navigator) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRServiceTest) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSharePromise) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXRSystem) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mClipboard) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END void Navigator::Invalidate() { @@ -254,6 +255,8 @@ void Navigator::Invalidate() { mSharePromise = nullptr; mWakeLock = nullptr; + + mClipboard = nullptr; } void Navigator::GetUserAgent(nsAString& aUserAgent, CallerType aCallerType, diff --git a/dom/base/Navigator.h b/dom/base/Navigator.h index 998328cfc6..e559dc4d6a 100644 --- a/dom/base/Navigator.h +++ b/dom/base/Navigator.h @@ -39,7 +39,6 @@ class MediaDevices; struct MediaStreamConstraints; class ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams; class ServiceWorkerContainer; -class DOMRequest; class CredentialsContainer; class Clipboard; class LockManager; diff --git a/dom/base/RadioGroupContainer.cpp b/dom/base/RadioGroupContainer.cpp index 1d5abb78c6..3bb4d7da20 100644 --- a/dom/base/RadioGroupContainer.cpp +++ b/dom/base/RadioGroupContainer.cpp @@ -131,6 +131,17 @@ nsresult RadioGroupContainer::GetNextRadioButton( return NS_OK; } +HTMLInputElement* RadioGroupContainer::GetFirstRadioButton( + const nsAString& aName) { + nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName); + for (HTMLInputElement* radio : radioGroup->mRadioButtons.AsList()) { + if (!radio->Disabled()) { + return radio; + } + } + return nullptr; +} + void RadioGroupContainer::AddToRadioGroup(const nsAString& aName, HTMLInputElement* aRadio, nsIContent* aAncestor) { diff --git a/dom/base/RadioGroupContainer.h b/dom/base/RadioGroupContainer.h index 0c920c1c09..6331c71dd9 100644 --- a/dom/base/RadioGroupContainer.h +++ b/dom/base/RadioGroupContainer.h @@ -32,6 +32,7 @@ class RadioGroupContainer final { nsresult GetNextRadioButton(const nsAString& aName, const bool aPrevious, HTMLInputElement* aFocusedRadio, HTMLInputElement** aRadioOut); + HTMLInputElement* GetFirstRadioButton(const nsAString& aName); void AddToRadioGroup(const nsAString& aName, HTMLInputElement* aRadio, nsIContent* aAncestor); void RemoveFromRadioGroup(const nsAString& aName, HTMLInputElement* aRadio); diff --git a/dom/base/ResizeObserver.cpp b/dom/base/ResizeObserver.cpp index 77472179c8..4a8ffd4c76 100644 --- a/dom/base/ResizeObserver.cpp +++ b/dom/base/ResizeObserver.cpp @@ -66,16 +66,9 @@ static nsSize GetContentRectSize(const nsIFrame& aFrame) { return aFrame.GetContentRectRelativeToSelf().Size(); } -/** - * Returns |aTarget|'s size in the form of gfx::Size (in pixels). - * If the target is an SVG that does not participate in CSS layout, - * its width and height are determined from bounding box. - * - * https://www.w3.org/TR/resize-observer-1/#calculate-box-size - */ -static AutoTArray<LogicalPixelSize, 1> CalculateBoxSize( +AutoTArray<LogicalPixelSize, 1> ResizeObserver::CalculateBoxSize( Element* aTarget, ResizeObserverBoxOptions aBox, - const ResizeObserver& aObserver) { + bool aForceFragmentHandling) { nsIFrame* frame = aTarget->GetPrimaryFrame(); if (!frame) { @@ -158,7 +151,7 @@ static AutoTArray<LogicalPixelSize, 1> CalculateBoxSize( return CSSPixel::FromAppUnits(GetContentRectSize(*aFrame)).ToUnknownSize(); }; if (!StaticPrefs::dom_resize_observer_support_fragments() && - !aObserver.HasNativeCallback()) { + !aForceFragmentHandling) { return {LogicalPixelSize(frame->GetWritingMode(), GetFrameSize(frame))}; } AutoTArray<LogicalPixelSize, 1> size; @@ -209,7 +202,7 @@ bool ResizeObservation::IsActive() const { } return mLastReportedSize != - CalculateBoxSize(mTarget, mObservedBox, *mObserver); + ResizeObserver::CalculateBoxSize(mTarget, mObservedBox); } void ResizeObservation::UpdateLastReportedSize( @@ -221,23 +214,14 @@ void ResizeObservation::UpdateLastReportedSize( NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(ResizeObserver) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ResizeObserver) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner, mDocument, mActiveTargets, - mObservationMap); - if (tmp->mCallback.is<RefPtr<ResizeObserverCallback>>()) { - ImplCycleCollectionTraverse( - cb, tmp->mCallback.as<RefPtr<ResizeObserverCallback>>(), "mCallback", - 0); - } + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner, mDocument, mCallback, + mActiveTargets, mObservationMap); NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ResizeObserver) tmp->Disconnect(); - NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner, mDocument, mActiveTargets, + NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner, mDocument, mCallback, mActiveTargets, mObservationMap); - if (tmp->mCallback.is<RefPtr<ResizeObserverCallback>>()) { - ImplCycleCollectionUnlink( - tmp->mCallback.as<RefPtr<ResizeObserverCallback>>()); - } NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER NS_IMPL_CYCLE_COLLECTION_UNLINK_END @@ -248,14 +232,6 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ResizeObserver) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END -ResizeObserver::ResizeObserver(Document& aDocument, NativeCallback aCallback) - : mOwner(aDocument.GetInnerWindow()), - mDocument(&aDocument), - mCallback(aCallback) { - MOZ_ASSERT(mOwner, "Need a non-null owner window"); - MOZ_ASSERT(mDocument == mOwner->GetExtantDoc()); -} - already_AddRefed<ResizeObserver> ResizeObserver::Constructor( const GlobalObject& aGlobal, ResizeObserverCallback& aCb, ErrorResult& aRv) { @@ -361,12 +337,7 @@ void ResizeObserver::GatherActiveObservations(uint32_t aDepth) { if (targetDepth > aDepth) { mActiveTargets.AppendElement(observation); } else { - // This boolean is only used to indicate we will deliver resize loop error - // notification later on. However, we don't want to do that for our - // internal observers. - if (!HasNativeCallback()) { - mHasSkippedTargets = true; - } + mHasSkippedTargets = true; } } } @@ -383,12 +354,12 @@ uint32_t ResizeObserver::BroadcastActiveObservations() { for (auto& observation : mActiveTargets) { Element* target = observation->Target(); - auto borderBoxSize = - CalculateBoxSize(target, ResizeObserverBoxOptions::Border_box, *this); - auto contentBoxSize = - CalculateBoxSize(target, ResizeObserverBoxOptions::Content_box, *this); - auto devicePixelContentBoxSize = CalculateBoxSize( - target, ResizeObserverBoxOptions::Device_pixel_content_box, *this); + auto borderBoxSize = ResizeObserver::CalculateBoxSize( + target, ResizeObserverBoxOptions::Border_box); + auto contentBoxSize = ResizeObserver::CalculateBoxSize( + target, ResizeObserverBoxOptions::Content_box); + auto devicePixelContentBoxSize = ResizeObserver::CalculateBoxSize( + target, ResizeObserverBoxOptions::Device_pixel_content_box); RefPtr<ResizeObserverEntry> entry = new ResizeObserverEntry(mOwner, *target, borderBoxSize, contentBoxSize, devicePixelContentBoxSize); @@ -419,13 +390,8 @@ uint32_t ResizeObserver::BroadcastActiveObservations() { } } - if (mCallback.is<RefPtr<ResizeObserverCallback>>()) { - RefPtr<ResizeObserverCallback> callback( - mCallback.as<RefPtr<ResizeObserverCallback>>()); - callback->Call(this, entries, *this); - } else { - mCallback.as<NativeCallback>()(entries, *this); - } + RefPtr<ResizeObserverCallback> callback(mCallback); + callback->Call(this, entries, *this); mActiveTargets.Clear(); mHasSkippedTargets = false; @@ -524,61 +490,6 @@ void ResizeObserverEntry::SetDevicePixelContentSize( } } -static void LastRememberedSizeCallback( - const Sequence<OwningNonNull<ResizeObserverEntry>>& aEntries, - ResizeObserver& aObserver) { - for (const auto& entry : aEntries) { - Element* target = entry->Target(); - if (!target->IsInComposedDoc()) { - aObserver.Unobserve(*target); - target->RemoveLastRememberedBSize(); - target->RemoveLastRememberedISize(); - continue; - } - nsIFrame* frame = target->GetPrimaryFrame(); - if (!frame) { - aObserver.Unobserve(*target); - continue; - } - MOZ_ASSERT(!frame->IsLineParticipant() || frame->IsReplaced(), - "Should have unobserved non-replaced inline."); - MOZ_ASSERT(!frame->HidesContent(), - "Should have unobserved element skipping its contents."); - const nsStylePosition* stylePos = frame->StylePosition(); - const WritingMode wm = frame->GetWritingMode(); - bool canUpdateBSize = stylePos->ContainIntrinsicBSize(wm).HasAuto(); - bool canUpdateISize = stylePos->ContainIntrinsicISize(wm).HasAuto(); - MOZ_ASSERT(canUpdateBSize || !target->HasLastRememberedBSize(), - "Should have removed the last remembered block size."); - MOZ_ASSERT(canUpdateISize || !target->HasLastRememberedISize(), - "Should have removed the last remembered inline size."); - MOZ_ASSERT(canUpdateBSize || canUpdateISize, - "Should have unobserved if we can't update any size."); - AutoTArray<RefPtr<ResizeObserverSize>, 1> contentSizeList; - entry->GetContentBoxSize(contentSizeList); - MOZ_ASSERT(!contentSizeList.IsEmpty()); - if (canUpdateBSize) { - float bSize = 0; - for (const auto& current : contentSizeList) { - bSize += current->BlockSize(); - } - target->SetLastRememberedBSize(bSize); - } - if (canUpdateISize) { - float iSize = 0; - for (const auto& current : contentSizeList) { - iSize = std::max(iSize, current->InlineSize()); - } - target->SetLastRememberedISize(iSize); - } - } -} - -/* static */ already_AddRefed<ResizeObserver> -ResizeObserver::CreateLastRememberedSizeObserver(Document& aDocument) { - return do_AddRef(new ResizeObserver(aDocument, LastRememberedSizeCallback)); -} - NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ResizeObserverSize, mOwner) NS_IMPL_CYCLE_COLLECTING_ADDREF(ResizeObserverSize) NS_IMPL_CYCLE_COLLECTING_RELEASE(ResizeObserverSize) diff --git a/dom/base/ResizeObserver.h b/dom/base/ResizeObserver.h index 6f1fc5b6cd..eaa0e1726d 100644 --- a/dom/base/ResizeObserver.h +++ b/dom/base/ResizeObserver.h @@ -121,9 +121,6 @@ class ResizeObservation final : public LinkedListElement<ResizeObservation> { * https://drafts.csswg.org/resize-observer/#api */ class ResizeObserver final : public nsISupports, public nsWrapperCache { - using NativeCallback = void (*)( - const Sequence<OwningNonNull<ResizeObserverEntry>>&, ResizeObserver&); - ResizeObserver(Document& aDocument, NativeCallback aCallback); public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS @@ -131,9 +128,7 @@ class ResizeObserver final : public nsISupports, public nsWrapperCache { ResizeObserver(nsCOMPtr<nsPIDOMWindowInner>&& aOwner, Document* aDocument, ResizeObserverCallback& aCb) - : mOwner(std::move(aOwner)), - mDocument(aDocument), - mCallback(RefPtr<ResizeObserverCallback>(&aCb)) { + : mOwner(std::move(aOwner)), mDocument(aDocument), mCallback(&aCb) { MOZ_ASSERT(mOwner, "Need a non-null owner window"); MOZ_ASSERT(mDocument, "Need a non-null doc"); MOZ_ASSERT(mDocument == mOwner->GetExtantDoc()); @@ -177,11 +172,6 @@ class ResizeObserver final : public nsISupports, public nsWrapperCache { bool HasSkippedObservations() const { return mHasSkippedTargets; } /** - * Returns whether this is an internal ResizeObserver with a native callback. - */ - bool HasNativeCallback() const { return mCallback.is<NativeCallback>(); } - - /** * Invoke the callback function in JavaScript for all active observations * and pass the sequence of ResizeObserverEntry so JavaScript can access them. * The active observations' mLastReportedSize fields will be updated, and @@ -191,8 +181,21 @@ class ResizeObserver final : public nsISupports, public nsWrapperCache { */ MOZ_CAN_RUN_SCRIPT uint32_t BroadcastActiveObservations(); - static already_AddRefed<ResizeObserver> CreateLastRememberedSizeObserver( - Document&); + /** + * Returns |aTarget|'s size in the form of gfx::Size (in pixels). + * If the target is an SVG that does not participate in CSS layout, + * its width and height are determined from bounding box. Otherwise, the + * relevant box is determined according to the |aBox| parameter. + * + * If dom.resize_observer.support_fragments is enabled, or if + * |aForceFragmentHandling| is true then the function reports the size of all + * fragments, and not just the first one. + * + * https://www.w3.org/TR/resize-observer-1/#calculate-box-size + */ + static AutoTArray<LogicalPixelSize, 1> CalculateBoxSize( + Element* aTarget, ResizeObserverBoxOptions aBox, + bool aForceFragmentHandling = false); protected: ~ResizeObserver() { Disconnect(); } @@ -200,7 +203,7 @@ class ResizeObserver final : public nsISupports, public nsWrapperCache { nsCOMPtr<nsPIDOMWindowInner> mOwner; // The window's document at the time of ResizeObserver creation. RefPtr<Document> mDocument; - Variant<RefPtr<ResizeObserverCallback>, NativeCallback> mCallback; + RefPtr<ResizeObserverCallback> mCallback; nsTArray<RefPtr<ResizeObservation>> mActiveTargets; // The spec uses a list to store the skipped targets. However, it seems what // we want is to check if there are any skipped targets (i.e. existence). diff --git a/dom/base/Selection.cpp b/dom/base/Selection.cpp index a56e460cbf..69986e6b78 100644 --- a/dom/base/Selection.cpp +++ b/dom/base/Selection.cpp @@ -46,7 +46,6 @@ #include "nsString.h" #include "nsFrameSelection.h" #include "nsISelectionListener.h" -#include "nsContentCID.h" #include "nsDeviceContext.h" #include "nsIContent.h" #include "nsIContentInlines.h" @@ -817,7 +816,8 @@ void Selection::SetAnchorFocusRange(size_t aIndex) { static int32_t CompareToRangeStart(const nsINode& aCompareNode, uint32_t aCompareOffset, - const AbstractRange& aRange) { + const AbstractRange& aRange, + nsContentUtils::NodeIndexCache* aCache) { MOZ_ASSERT(aRange.GetStartContainer()); nsINode* start = aRange.GetStartContainer(); // If the nodes that we're comparing are not in the same document, assume that @@ -831,7 +831,13 @@ static int32_t CompareToRangeStart(const nsINode& aCompareNode, // The points are in the same subtree, hence there has to be an order. return *nsContentUtils::ComparePoints(&aCompareNode, aCompareOffset, start, - aRange.StartOffset()); + aRange.StartOffset(), aCache); +} + +static int32_t CompareToRangeStart(const nsINode& aCompareNode, + uint32_t aCompareOffset, + const AbstractRange& aRange) { + return CompareToRangeStart(aCompareNode, aCompareOffset, aRange, nullptr); } static int32_t CompareToRangeEnd(const nsINode& aCompareNode, @@ -1463,10 +1469,49 @@ void Selection::StyledRanges::ReorderRangesIfNecessary() { mInvalidStaticRanges.AppendElements(std::move(invalidStaticRanges)); } if (domMutationHasHappened || mRangesMightHaveChanged) { - mRanges.Sort([](const StyledRange& a, const StyledRange& b) -> int { - return CompareToRangeStart(*a.mRange->GetStartContainer(), - a.mRange->StartOffset(), *b.mRange); - }); + // This is hot code. Proceed with caution. + // This path uses a cache that keep the last 100 node/index combinations + // in a stack-allocated array to save up on expensive calls to + // nsINode::ComputeIndexOf() (which happen in + // nsContentUtils::ComparePoints()). + // The second expensive call here is the sort() below, which should be + // avoided if possible. Sorting can be avoided if the ranges are still in + // order. Checking the order is cheap compared to sorting (also, it fills up + // the cache, which is reused by the sort call). + nsContentUtils::NodeIndexCache cache; + bool rangeOrderHasChanged = false; + const nsINode* prevStartContainer = nullptr; + uint32_t prevStartOffset = 0; + for (const StyledRange& range : mRanges) { + const nsINode* startContainer = range.mRange->GetStartContainer(); + uint32_t startOffset = range.mRange->StartOffset(); + if (!prevStartContainer) { + prevStartContainer = startContainer; + prevStartOffset = startOffset; + continue; + } + // Calling ComparePoints here saves one call of + // AbstractRange::StartOffset() per iteration (which is surprisingly + // expensive). + const Maybe<int32_t> compareResult = nsContentUtils::ComparePoints( + startContainer, startOffset, prevStartContainer, prevStartOffset, + &cache); + // If the nodes are in different subtrees, the Maybe is empty. + // Since CompareToRangeStart pretends ranges to be ordered, this aligns + // to that behavior. + if (compareResult.valueOr(1) != 1) { + rangeOrderHasChanged = true; + break; + } + prevStartContainer = startContainer; + prevStartOffset = startOffset; + } + if (rangeOrderHasChanged) { + mRanges.Sort([&cache](const StyledRange& a, const StyledRange& b) -> int { + return CompareToRangeStart(*a.mRange->GetStartContainer(), + a.mRange->StartOffset(), *b.mRange, &cache); + }); + } mDocumentGeneration = currentDocumentGeneration; mRangesMightHaveChanged = false; } diff --git a/dom/base/ShadowRoot.cpp b/dom/base/ShadowRoot.cpp index 64a1614aee..94db7bad85 100644 --- a/dom/base/ShadowRoot.cpp +++ b/dom/base/ShadowRoot.cpp @@ -4,7 +4,6 @@ * 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/Preferences.h" #include "mozilla/dom/BindContext.h" #include "mozilla/dom/ShadowRoot.h" #include "mozilla/dom/DocumentFragment.h" @@ -17,8 +16,10 @@ #include "mozilla/dom/HTMLDetailsElement.h" #include "mozilla/dom/HTMLSlotElement.h" #include "mozilla/dom/HTMLSummaryElement.h" +#include "mozilla/dom/MutationObservers.h" #include "mozilla/dom/Text.h" #include "mozilla/dom/TreeOrderedArrayInlines.h" +#include "mozilla/dom/UnbindContext.h" #include "mozilla/EventDispatcher.h" #include "mozilla/IdentifierMapEntry.h" #include "mozilla/PresShell.h" @@ -26,7 +27,6 @@ #include "mozilla/ScopeExit.h" #include "mozilla/ServoStyleRuleMap.h" #include "mozilla/StyleSheet.h" -#include "mozilla/StyleSheetInlines.h" #include "mozilla/dom/StyleSheetList.h" using namespace mozilla; @@ -184,10 +184,13 @@ void ShadowRoot::Unbind() { OwnerDoc()->RemoveComposedDocShadowRoot(*this); } + UnbindContext context(*this); for (nsIContent* child = GetFirstChild(); child; child = child->GetNextSibling()) { - child->UnbindFromTree(false); + child->UnbindFromTree(context); } + + MutationObservers::NotifyParentChainChanged(this); } void ShadowRoot::Unattach() { diff --git a/dom/base/StructuredCloneHolder.cpp b/dom/base/StructuredCloneHolder.cpp index bf3945b1d7..f3e2bea46b 100644 --- a/dom/base/StructuredCloneHolder.cpp +++ b/dom/base/StructuredCloneHolder.cpp @@ -34,6 +34,8 @@ #include "mozilla/dom/DOMTypes.h" #include "mozilla/dom/Directory.h" #include "mozilla/dom/DocGroup.h" +#include "mozilla/dom/EncodedAudioChunk.h" +#include "mozilla/dom/EncodedAudioChunkBinding.h" #include "mozilla/dom/EncodedVideoChunk.h" #include "mozilla/dom/EncodedVideoChunkBinding.h" #include "mozilla/dom/File.h" @@ -58,6 +60,7 @@ #include "mozilla/dom/TransformStream.h" #include "mozilla/dom/TransformStreamBinding.h" #include "mozilla/dom/VideoFrame.h" +#include "mozilla/dom/AudioData.h" #include "mozilla/dom/VideoFrameBinding.h" #include "mozilla/dom/WebIDLSerializable.h" #include "mozilla/dom/WritableStream.h" @@ -399,6 +402,7 @@ void StructuredCloneHolder::Read(nsIGlobalObject* aGlobal, JSContext* aCx, mClonedSurfaces.Clear(); mInputStreamArray.Clear(); mVideoFrames.Clear(); + mEncodedAudioChunks.Clear(); mEncodedVideoChunks.Clear(); Clear(); } @@ -1127,6 +1131,28 @@ JSObject* StructuredCloneHolder::CustomReadHandler( } } + if (StaticPrefs::dom_media_webcodecs_enabled() && + aTag == SCTAG_DOM_AUDIODATA && + CloneScope() == StructuredCloneScope::SameProcess && + aCloneDataPolicy.areIntraClusterClonableSharedObjectsAllowed()) { + JS::Rooted<JSObject*> global(aCx, mGlobal->GetGlobalJSObject()); + if (AudioData_Binding::ConstructorEnabled(aCx, global)) { + return AudioData::ReadStructuredClone(aCx, mGlobal, aReader, + AudioData()[aIndex]); + } + } + + if (StaticPrefs::dom_media_webcodecs_enabled() && + aTag == SCTAG_DOM_ENCODEDAUDIOCHUNK && + CloneScope() == StructuredCloneScope::SameProcess && + aCloneDataPolicy.areIntraClusterClonableSharedObjectsAllowed()) { + JS::Rooted<JSObject*> global(aCx, mGlobal->GetGlobalJSObject()); + if (EncodedAudioChunk_Binding::ConstructorEnabled(aCx, global)) { + return EncodedAudioChunk::ReadStructuredClone( + aCx, mGlobal, aReader, EncodedAudioChunks()[aIndex]); + } + } + return ReadFullySerializableObjects(aCx, aReader, aTag, false); } @@ -1246,6 +1272,29 @@ bool StructuredCloneHolder::CustomWriteHandler( } } + // See if this is an AudioData object. + if (StaticPrefs::dom_media_webcodecs_enabled()) { + mozilla::dom::AudioData* audioData = nullptr; + if (NS_SUCCEEDED(UNWRAP_OBJECT(AudioData, &obj, audioData))) { + SameProcessScopeRequired(aSameProcessScopeRequired); + return CloneScope() == StructuredCloneScope::SameProcess + ? audioData->WriteStructuredClone(aWriter, this) + : false; + } + } + + // See if this is a EncodedAudioChunk object. + if (StaticPrefs::dom_media_webcodecs_enabled()) { + EncodedAudioChunk* encodedAudioChunk = nullptr; + if (NS_SUCCEEDED( + UNWRAP_OBJECT(EncodedAudioChunk, &obj, encodedAudioChunk))) { + SameProcessScopeRequired(aSameProcessScopeRequired); + return CloneScope() == StructuredCloneScope::SameProcess + ? encodedAudioChunk->WriteStructuredClone(aWriter, this) + : false; + } + } + { // We only care about streams, so ReflectorToISupportsStatic is fine. nsCOMPtr<nsISupports> base = xpc::ReflectorToISupportsStatic(aObj); @@ -1429,6 +1478,39 @@ StructuredCloneHolder::CustomReadTransferHandler( return true; } + if (StaticPrefs::dom_media_webcodecs_enabled() && + aTag == SCTAG_DOM_AUDIODATA && + CloneScope() == StructuredCloneScope::SameProcess && + aCloneDataPolicy.areIntraClusterClonableSharedObjectsAllowed()) { + MOZ_ASSERT(aContent); + + JS::Rooted<JSObject*> globalObj(aCx, mGlobal->GetGlobalJSObject()); + // aContent will be released in CustomFreeTransferHandler. + if (!AudioData_Binding::ConstructorEnabled(aCx, globalObj)) { + return false; + } + + AudioData::TransferredData* data = + static_cast<AudioData::TransferredData*>(aContent); + nsCOMPtr<nsIGlobalObject> global = mGlobal; + RefPtr<mozilla::dom::AudioData> audioData = + AudioData::FromTransferred(global.get(), data); + // aContent will be released in CustomFreeTransferHandler if frame is null. + if (!audioData) { + return false; + } + delete data; + aContent = nullptr; + + JS::Rooted<JS::Value> value(aCx); + if (!GetOrCreateDOMReflector(aCx, audioData, &value)) { + JS_ClearPendingException(aCx); + return false; + } + aReturnObject.set(&value.toObject()); + return true; + } + return false; } @@ -1530,6 +1612,26 @@ StructuredCloneHolder::CustomWriteTransferHandler( return true; } } + if (StaticPrefs::dom_media_webcodecs_enabled()) { + mozilla::dom::AudioData* audioData = nullptr; + rv = UNWRAP_OBJECT(AudioData, &obj, audioData); + if (NS_SUCCEEDED(rv)) { + MOZ_ASSERT(audioData); + + *aExtraData = 0; + *aTag = SCTAG_DOM_AUDIODATA; + *aContent = nullptr; + + UniquePtr<AudioData::TransferredData> data = audioData->Transfer(); + if (!data) { + return false; + } + *aContent = data.release(); + MOZ_ASSERT(*aContent); + *aOwnership = JS::SCTAG_TMO_CUSTOM; + return true; + } + } } { @@ -1667,6 +1769,16 @@ void StructuredCloneHolder::CustomFreeTransferHandler( } return; } + if (StaticPrefs::dom_media_webcodecs_enabled() && + aTag == SCTAG_DOM_AUDIODATA && + CloneScope() == StructuredCloneScope::SameProcess) { + if (aContent) { + AudioData::TransferredData* data = + static_cast<AudioData::TransferredData*>(aContent); + delete data; + } + return; + } } bool StructuredCloneHolder::CustomCanTransferHandler( @@ -1750,6 +1862,15 @@ bool StructuredCloneHolder::CustomCanTransferHandler( } } + if (StaticPrefs::dom_media_webcodecs_enabled()) { + mozilla::dom::AudioData* audioData = nullptr; + nsresult rv = UNWRAP_OBJECT(AudioData, &obj, audioData); + if (NS_SUCCEEDED(rv)) { + SameProcessScopeRequired(aSameProcessScopeRequired); + return CloneScope() == StructuredCloneScope::SameProcess; + } + } + return false; } diff --git a/dom/base/StructuredCloneHolder.h b/dom/base/StructuredCloneHolder.h index 206c3d3a25..d1d7cf7806 100644 --- a/dom/base/StructuredCloneHolder.h +++ b/dom/base/StructuredCloneHolder.h @@ -165,10 +165,12 @@ class StructuredCloneHolderBase { }; class BlobImpl; +class EncodedAudioChunkData; class EncodedVideoChunkData; class MessagePort; class MessagePortIdentifier; struct VideoFrameSerializedData; +struct AudioDataSerializedData; class StructuredCloneHolder : public StructuredCloneHolderBase { public: @@ -270,10 +272,16 @@ class StructuredCloneHolder : public StructuredCloneHolderBase { nsTArray<VideoFrameSerializedData>& VideoFrames() { return mVideoFrames; } + nsTArray<AudioDataSerializedData>& AudioData() { return mAudioData; } + nsTArray<EncodedVideoChunkData>& EncodedVideoChunks() { return mEncodedVideoChunks; } + nsTArray<EncodedAudioChunkData>& EncodedAudioChunks() { + return mEncodedAudioChunks; + } + // Implementations of the virtual methods to allow cloning of objects which // JS engine itself doesn't clone. @@ -379,9 +387,15 @@ class StructuredCloneHolder : public StructuredCloneHolderBase { // Used for cloning VideoFrame in the structured cloning algorithm. nsTArray<VideoFrameSerializedData> mVideoFrames; + // Used for cloning AudioData in the structured cloning algorithm. + nsTArray<AudioDataSerializedData> mAudioData; + // Used for cloning EncodedVideoChunk in the structured cloning algorithm. nsTArray<EncodedVideoChunkData> mEncodedVideoChunks; + // Used for cloning EncodedAudioChunk in the structured cloning algorithm. + nsTArray<EncodedAudioChunkData> mEncodedAudioChunks; + // This raw pointer is only set within ::Read() and is unset by the end. nsIGlobalObject* MOZ_NON_OWNING_REF mGlobal; diff --git a/dom/base/StructuredCloneTags.h b/dom/base/StructuredCloneTags.h index 95233c62d7..bbc50d9f9d 100644 --- a/dom/base/StructuredCloneTags.h +++ b/dom/base/StructuredCloneTags.h @@ -157,6 +157,10 @@ enum StructuredCloneTags : uint32_t { SCTAG_DOM_ENCODEDVIDEOCHUNK, + SCTAG_DOM_AUDIODATA, + + SCTAG_DOM_ENCODEDAUDIOCHUNK, + // IMPORTANT: If you plan to add an new IDB tag, it _must_ be add before the // "less stable" tags! }; diff --git a/dom/base/UnbindContext.h b/dom/base/UnbindContext.h new file mode 100644 index 0000000000..77505ba582 --- /dev/null +++ b/dom/base/UnbindContext.h @@ -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/. */ + +/* State that is passed down to UnbindToTree. */ + +#ifndef mozilla_dom_UnbindContext_h__ +#define mozilla_dom_UnbindContext_h__ + +#include "mozilla/Attributes.h" +#include "nsINode.h" + +namespace mozilla::dom { + +struct MOZ_STACK_CLASS UnbindContext final { + // The root of the subtree being unbound. + nsINode& Root() const { return mRoot; } + // Whether we're the root of the subtree being unbound. + bool IsUnbindRoot(const nsINode* aNode) const { return &mRoot == aNode; } + // The parent node of the subtree we're unbinding from. + nsINode* GetOriginalSubtreeParent() const { return mOriginalParent; } + + explicit UnbindContext(nsINode& aRoot) + : mRoot(aRoot), mOriginalParent(aRoot.GetParentNode()) {} + + private: + nsINode& mRoot; + nsINode* const mOriginalParent; +}; + +} // namespace mozilla::dom + +#endif diff --git a/dom/base/UseCounters.conf b/dom/base/UseCounters.conf index 67aa988176..86d782b476 100644 --- a/dom/base/UseCounters.conf +++ b/dom/base/UseCounters.conf @@ -74,7 +74,6 @@ custom onunderflow sets an element onunderflow event listener // JavaScript feature usage custom JS_asmjs uses asm.js custom JS_wasm uses WebAssembly -custom JS_late_weekday parses a Date with day of week in an unexpected position custom JS_wasm_legacy_exceptions uses WebAssembly legacy exception-handling // Console API diff --git a/dom/base/crashtests/crashtests.list b/dom/base/crashtests/crashtests.list index 7260a18878..864538ddf5 100644 --- a/dom/base/crashtests/crashtests.list +++ b/dom/base/crashtests/crashtests.list @@ -220,7 +220,7 @@ load 1406109-1.html load 1411473.html load 1413815.html load 1419799.html -skip-if(!browserIsRemote) skip-if(geckoview) skip-if(geckoview&&isDebugBuild) skip-if(AddressSanitizer) skip-if(ThreadSanitizer) pref(dom.disable_open_during_load,false) load 1419902.html # skip on non e10s loads, Bug 1419902. Bug 1563013 for GV+WR. Bug 1524493 GV+debug. Bug 1573281 asan +skip-if(geckoview) skip-if(geckoview&&isDebugBuild) skip-if(AddressSanitizer) skip-if(ThreadSanitizer) pref(dom.disable_open_during_load,false) load 1419902.html # skip Bug 1419902. Bug 1563013 for GV+WR. Bug 1524493 GV+debug. Bug 1573281 asan load 1422883.html load 1428053.html load 1441029.html @@ -250,7 +250,7 @@ load 1555786.html load 1577191.html load eventSource_invalid_scheme_worker_shutdown.html load 1291535.html -skip-if(!isDebugBuild||xulRuntime.OS!="Linux") load 1611853.html +skip-if(!isDebugBuild||!gtkWidget) load 1611853.html load 1619322.html asserts(0-2) load 1623918.html # May hit an assertion if the <input> element's anonymous tree hasn't been flushed when IMEContentObserver handles focus load 1656925.html diff --git a/dom/base/moz.build b/dom/base/moz.build index 14c9f9dd96..ef1780f161 100644 --- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -22,7 +22,6 @@ include("/tools/fuzzing/libfuzzer-config.mozbuild") XPIDL_SOURCES += [ "mozIDOMWindow.idl", "nsIContentPolicy.idl", - "nsIDOMRequestService.idl", "nsIDroppedLinkHandler.idl", "nsIEventSourceEventService.idl", "nsIImageLoadingContent.idl", @@ -51,7 +50,6 @@ EXPORTS += [ "nsAttrValueInlines.h", "nsCaseTreatment.h", "nsChildContentList.h", - "nsContentCID.h", "nsContentCreatorFunctions.h", "nsContentList.h", "nsContentListDeclarations.h", @@ -187,7 +185,6 @@ EXPORTS.mozilla.dom += [ "DOMPoint.h", "DOMQuad.h", "DOMRect.h", - "DOMRequest.h", "DOMStringList.h", "DOMTokenListSupportedTokens.h", "Element.h", @@ -283,6 +280,7 @@ EXPORTS.mozilla.dom += [ "TreeOrderedArrayInlines.h", "TreeWalker.h", "UIDirectionManager.h", + "UnbindContext.h", "UseCounterMetrics.h", "UserActivation.h", "ViewportMetaData.h", @@ -345,7 +343,6 @@ UNIFIED_SOURCES += [ "DOMPoint.cpp", "DOMQuad.cpp", "DOMRect.cpp", - "DOMRequest.cpp", "DOMStringList.cpp", "Element.cpp", "EventSource.cpp", @@ -545,7 +542,6 @@ if CONFIG["TARGET_CPU"].startswith("ppc"): EXTRA_JS_MODULES += [ "ContentAreaDropListener.sys.mjs", - "DOMRequestHelper.sys.mjs", "IndexedDBHelper.sys.mjs", "LocationHelper.sys.mjs", "ProcessSelector.sys.mjs", diff --git a/dom/base/nsAttrValue.cpp b/dom/base/nsAttrValue.cpp index ea7923fa0f..01efef2eb9 100644 --- a/dom/base/nsAttrValue.cpp +++ b/dom/base/nsAttrValue.cpp @@ -15,11 +15,10 @@ #include "nsAttrValue.h" #include "nsAttrValueInlines.h" -#include "nsAtomHashKeys.h" #include "nsUnicharUtils.h" #include "mozilla/AttributeStyles.h" +#include "mozilla/ClearOnShutdown.h" #include "mozilla/BloomFilter.h" -#include "mozilla/CORSMode.h" #include "mozilla/DeclarationBlock.h" #include "mozilla/MemoryReporting.h" #include "mozilla/ServoBindingTypes.h" @@ -27,7 +26,6 @@ #include "mozilla/ShadowParts.h" #include "mozilla/SVGAttrValueWrapper.h" #include "mozilla/URLExtraData.h" -#include "mozilla/dom/CSSRuleBinding.h" #include "mozilla/dom/Document.h" #include "nsContentUtils.h" #include "nsReadableUtils.h" diff --git a/dom/base/nsContentCID.h b/dom/base/nsContentCID.h deleted file mode 100644 index cda1cb4075..0000000000 --- a/dom/base/nsContentCID.h +++ /dev/null @@ -1,195 +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 nsContentCID_h__ -#define nsContentCID_h__ - -// {972D8D8F-F0DA-11d4-9885-00C04FA0CF4B} -#define NS_CONTENT_VIEWER_CID \ - { \ - 0x972d8d8f, 0xf0da, 0x11d4, { \ - 0x98, 0x85, 0x0, 0xc0, 0x4f, 0xa0, 0xcf, 0x4b \ - } \ - } - -// {FC886801-E768-11d4-9885-00C04FA0CF4B} -#define NS_CONTENT_DOCUMENT_LOADER_FACTORY_CID \ - { \ - 0xfc886801, 0xe768, 0x11d4, { \ - 0x98, 0x85, 0x0, 0xc0, 0x4f, 0xa0, 0xcf, 0x4b \ - } \ - } - -#define NS_NAMESPACEMANAGER_CID \ - { /* d9783472-8fe9-11d2-9d3c-0060088f9ff7 */ \ - 0xd9783472, 0x8fe9, 0x11d2, { \ - 0x9d, 0x3c, 0x00, 0x60, 0x08, 0x8f, 0x9f, 0xf7 \ - } \ - } - -// {09F689E0-B4DA-11d2-A68B-00104BDE6048} -#define NS_EVENTLISTENERMANAGER_CID \ - { \ - 0x9f689e0, 0xb4da, 0x11d2, { \ - 0xa6, 0x8b, 0x0, 0x10, 0x4b, 0xde, 0x60, 0x48 \ - } \ - } - -// {64F300A1-C88C-11d3-97FB-00400553EEF0} -#define NS_XBLSERVICE_CID \ - { \ - 0x64f300a1, 0xc88c, 0x11d3, { \ - 0x97, 0xfb, 0x0, 0x40, 0x5, 0x53, 0xee, 0xf0 \ - } \ - } - -// {4aef38b7-6364-4e23-a5e7-12f837fbbd9c} -#define NS_XMLCONTENTSERIALIZER_CID \ - { \ - 0x4aef38b7, 0x6364, 0x4e23, { \ - 0xa5, 0xe7, 0x12, 0xf8, 0x37, 0xfb, 0xbd, 0x9c \ - } \ - } - -// {e7c2aaf5-c11a-4954-9dbf-e28edec1fd91} -#define NS_XHTMLCONTENTSERIALIZER_CID \ - { \ - 0xe7c2aaf5, 0xc11a, 0x4954, { \ - 0x9d, 0xbf, 0xe2, 0x8e, 0xde, 0xc1, 0xfd, 0x91 \ - } \ - } - -// {9d3f70da-86e9-11d4-95ec-00b0d03e37b7} -#define NS_HTMLCONTENTSERIALIZER_CID \ - { \ - 0x9d3f70da, 0x86e9, 0x11d4, { \ - 0x95, 0xec, 0x00, 0xb0, 0xd0, 0x3e, 0x37, 0xb7 \ - } \ - } - -// {feca3c34-205e-4ae5-bd1c-03c686ff012b} -#define MOZ_SANITIZINGHTMLSERIALIZER_CID \ - { \ - 0xfeca3c34, 0x205e, 0x4ae5, { \ - 0xbd, 0x1c, 0x03, 0xc6, 0x86, 0xff, 0x01, 0x2b \ - } \ - } - -// {6030f7ef-32ed-46a7-9a63-6a5d3f90445f} -#define NS_PLAINTEXTSERIALIZER_CID \ - { \ - 0x6030f7ef, 0x32ed, 0x46a7, { \ - 0x9a, 0x63, 0x6a, 0x5d, 0x3f, 0x90, 0x44, 0x5f \ - } \ - } - -// {d4f2b600-b5c1-11d6-b483-cc97c63e567c} -#define NS_HTMLFRAGMENTSINK_CID \ - { \ - 0xd4f2b600, 0xb5c1, 0x11d6, { \ - 0xb4, 0x83, 0xcc, 0x97, 0xc6, 0x3e, 0x56, 0x7c \ - } \ - } - -// {13111d00-ce81-11d6-8082-ecf3665af67c} -#define NS_HTMLFRAGMENTSINK2_CID \ - { \ - 0x13111d00, 0xce81, 0x11d6, { \ - 0x80, 0x82, 0xec, 0xf3, 0x66, 0x5a, 0xf6, 0x7c \ - } \ - } - -// {4B664E54-72A2-4bbf-A5C2-66D4DC3066A0} -#define NS_XMLFRAGMENTSINK_CID \ - { \ - 0x4b664e54, 0x72a2, 0x4bbf, { \ - 0xa5, 0xc2, 0x66, 0xd4, 0xdc, 0x30, 0x66, 0xa0 \ - } \ - } - -// {4DC30689-929D-425e-A709-082C6294E542} -#define NS_XMLFRAGMENTSINK2_CID \ - { \ - 0x4dc30689, 0x929d, 0x425e, { \ - 0xa7, 0x9, 0x8, 0x2c, 0x62, 0x94, 0xe5, 0x42 \ - } \ - } - -// {3986B301-097C-11d3-BF87-00105A1B0627} -#define NS_XULPOPUPLISTENER_CID \ - { \ - 0x3986b301, 0x97c, 0x11d3, { \ - 0xbf, 0x87, 0x0, 0x10, 0x5a, 0x1b, 0x6, 0x27 \ - } \ - } - -// {3D262D00-8B5A-11d2-8EB0-00805F29F370} -#define NS_XULTEMPLATEBUILDER_CID \ - { \ - 0x3d262d00, 0x8b5a, 0x11d2, { \ - 0x8e, 0xb0, 0x0, 0x80, 0x5f, 0x29, 0xf3, 0x70 \ - } \ - } - -// {1abdcc96-1dd2-11b2-b520-f8f59cdd67bc} -#define NS_XULTREEBUILDER_CID \ - { \ - 0x1abdcc96, 0x1dd2, 0x11b2, { \ - 0xb5, 0x20, 0xf8, 0xf5, 0x9c, 0xdd, 0x67, 0xbc \ - } \ - } - -#define NS_EVENTLISTENERSERVICE_CID \ - { /* baa34652-f1f1-4185-b224-244ee82a413a */ \ - 0xbaa34652, 0xf1f1, 0x4185, { \ - 0xb2, 0x24, 0x24, 0x4e, 0xe8, 0x2a, 0x41, 0x3a \ - } \ - } -#define NS_EVENTLISTENERSERVICE_CONTRACTID "@mozilla.org/eventlistenerservice;1" - -#define NS_GLOBALMESSAGEMANAGER_CID \ - { /* 130b016f-fad7-4526-bc7f-827dabf79265 */ \ - 0x130b016f, 0xfad7, 0x4526, { \ - 0xbc, 0x7f, 0x82, 0x7d, 0xab, 0xf7, 0x92, 0x65 \ - } \ - } -#define NS_GLOBALMESSAGEMANAGER_CONTRACTID "@mozilla.org/globalmessagemanager;1" - -#define NS_PARENTPROCESSMESSAGEMANAGER_CID \ - { /* 2a058404-fb85-44ec-8cfd-e8cbdc988dc1 */ \ - 0x2a058404, 0xfb85, 0x44ec, { \ - 0x8c, 0xfd, 0xe8, 0xcb, 0xdc, 0x98, 0x8d, 0xc1 \ - } \ - } -#define NS_PARENTPROCESSMESSAGEMANAGER_CONTRACTID \ - "@mozilla.org/parentprocessmessagemanager;1" - -#define NS_CHILDPROCESSMESSAGEMANAGER_CID \ - { /* fe0ff7c3-8e97-448b-9a8a-86afdb9fbbb6 */ \ - 0xfe0ff7c3, 0x8e97, 0x448b, { \ - 0x9a, 0x8a, 0x86, 0xaf, 0xdb, 0x9f, 0xbb, 0xb6 \ - } \ - } -#define NS_CHILDPROCESSMESSAGEMANAGER_CONTRACTID \ - "@mozilla.org/childprocessmessagemanager;1" - -// {08c6cc8b-cfb0-421d-b1f7-683ff2989681} -#define THIRDPARTYUTIL_CID \ - { \ - 0x08c6cc8b, 0xcfb0, 0x421d, { \ - 0xb1, 0xf7, 0x68, 0x3f, 0xf2, 0x98, 0x96, 0x81 \ - } \ - } - -// {7B121F7E-EBE4-43AB-9410-DC9087A1DBA6} -#define GECKO_MEDIA_PLUGIN_SERVICE_CID \ - { \ - 0x7B121F7E, 0xEBE4, 0x43AB, { \ - 0x94, 0x10, 0xDC, 0x90, 0x87, 0xA1, 0xDB, 0xA6 \ - } \ - } - -#endif /* nsContentCID_h__ */ diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index d849b13927..c6f1687f73 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -3027,13 +3027,15 @@ bool nsContentUtils::PositionIsBefore(nsINode* aNode1, nsINode* aNode2, } /* static */ -Maybe<int32_t> nsContentUtils::ComparePoints( - const nsINode* aParent1, uint32_t aOffset1, const nsINode* aParent2, - uint32_t aOffset2, ComparePointsCache* aParent1Cache) { +Maybe<int32_t> nsContentUtils::ComparePoints(const nsINode* aParent1, + uint32_t aOffset1, + const nsINode* aParent2, + uint32_t aOffset2, + NodeIndexCache* aIndexCache) { bool disconnected{false}; const int32_t order = ComparePoints_Deprecated( - aParent1, aOffset1, aParent2, aOffset2, &disconnected, aParent1Cache); + aParent1, aOffset1, aParent2, aOffset2, &disconnected, aIndexCache); if (disconnected) { return Nothing(); } @@ -3044,7 +3046,7 @@ Maybe<int32_t> nsContentUtils::ComparePoints( /* static */ int32_t nsContentUtils::ComparePoints_Deprecated( const nsINode* aParent1, uint32_t aOffset1, const nsINode* aParent2, - uint32_t aOffset2, bool* aDisconnected, ComparePointsCache* aParent1Cache) { + uint32_t aOffset2, bool* aDisconnected, NodeIndexCache* aIndexCache) { if (aParent1 == aParent2) { return aOffset1 < aOffset2 ? -1 : aOffset1 > aOffset2 ? 1 : 0; } @@ -3089,10 +3091,15 @@ int32_t nsContentUtils::ComparePoints_Deprecated( if (MOZ_UNLIKELY(child2->IsShadowRoot())) { return 1; } - const Maybe<uint32_t> child1Index = - aParent1Cache ? aParent1Cache->ComputeIndexOf(parent, child1) - : parent->ComputeIndexOf(child1); - const Maybe<uint32_t> child2Index = parent->ComputeIndexOf(child2); + Maybe<uint32_t> child1Index; + Maybe<uint32_t> child2Index; + if (aIndexCache) { + aIndexCache->ComputeIndicesOf(parent, child1, child2, child1Index, + child2Index); + } else { + child1Index = parent->ComputeIndexOf(child1); + child2Index = parent->ComputeIndexOf(child2); + } if (MOZ_LIKELY(child1Index.isSome() && child2Index.isSome())) { return *child1Index < *child2Index ? -1 : 1; } @@ -3110,7 +3117,9 @@ int32_t nsContentUtils::ComparePoints_Deprecated( if (!pos1) { const nsINode* child2 = parents2.ElementAt(--pos2); - const Maybe<uint32_t> child2Index = parent->ComputeIndexOf(child2); + const Maybe<uint32_t> child2Index = + aIndexCache ? aIndexCache->ComputeIndexOf(parent, child2) + : parent->ComputeIndexOf(child2); if (MOZ_UNLIKELY(NS_WARN_IF(child2Index.isNothing()))) { return 1; } @@ -3119,8 +3128,8 @@ int32_t nsContentUtils::ComparePoints_Deprecated( const nsINode* child1 = parents1.ElementAt(--pos1); const Maybe<uint32_t> child1Index = - aParent1Cache ? aParent1Cache->ComputeIndexOf(parent, child1) - : parent->ComputeIndexOf(child1); + aIndexCache ? aIndexCache->ComputeIndexOf(parent, child1) + : parent->ComputeIndexOf(child1); if (MOZ_UNLIKELY(NS_WARN_IF(child1Index.isNothing()))) { return -1; } @@ -4004,7 +4013,8 @@ nsresult nsContentUtils::LoadImage( int32_t aLoadFlags, const nsAString& initiatorType, imgRequestProxy** aRequest, nsContentPolicyType aContentPolicyType, bool aUseUrgentStartForChannel, bool aLinkPreload, - uint64_t aEarlyHintPreloaderId) { + uint64_t aEarlyHintPreloaderId, + mozilla::dom::FetchPriority aFetchPriority) { MOZ_ASSERT(aURI, "Must have a URI"); MOZ_ASSERT(aContext, "Must have a context"); MOZ_ASSERT(aLoadingDocument, "Must have a document"); @@ -4041,7 +4051,7 @@ nsresult nsContentUtils::LoadImage( initiatorType, /* the load initiator */ aUseUrgentStartForChannel, /* urgent-start flag */ aLinkPreload, /* <link preload> initiator */ - aEarlyHintPreloaderId, aRequest); + aEarlyHintPreloaderId, aFetchPriority, aRequest); } // static @@ -11426,6 +11436,7 @@ int32_t nsContentUtils::CompareTreePosition(const nsINode* aNode1, nsIContent* nsContentUtils::AttachDeclarativeShadowRoot(nsIContent* aHost, ShadowRootMode aMode, + bool aIsClonable, bool aDelegatesFocus) { RefPtr<Element> host = mozilla::dom::Element::FromNodeOrNull(aHost); if (!host) { @@ -11436,7 +11447,7 @@ nsIContent* nsContentUtils::AttachDeclarativeShadowRoot(nsIContent* aHost, init.mMode = aMode; init.mDelegatesFocus = aDelegatesFocus; init.mSlotAssignment = SlotAssignmentMode::Named; - init.mClonable = true; + init.mClonable = aIsClonable; RefPtr shadowRoot = host->AttachShadow(init, IgnoreErrors(), Element::ShadowRootDeclarative::Yes); diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index 95744fe831..338fc097de 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -39,6 +39,7 @@ #include "mozilla/UniquePtr.h" #include "mozilla/dom/BindingDeclarations.h" #include "mozilla/dom/FromParser.h" +#include "mozilla/dom/FetchPriority.h" #include "mozilla/fallible.h" #include "mozilla/gfx/Point.h" #include "nsCOMPtr.h" @@ -545,26 +546,115 @@ class nsContentUtils { mozilla::Maybe<uint32_t>* aNode1Index = nullptr, mozilla::Maybe<uint32_t>* aNode2Index = nullptr); - struct ComparePointsCache { + /** + * Cache implementation for ComparePoints(). + * + * This cache keeps the last cache_size child/index combinations + * in a stack-allocated array for fast lookup. + * If the cache is full, the entries are overridden, + * starting from the oldest entry. + * + * Note: This cache does not observe invalidation. As soon as script has + * run, this cache must not be used anymore. + * Also, this cache uses raw pointers. Beware! + */ + template <size_t cache_size> + struct ResizableNodeIndexCache { + /** + * Looks up or computes two indices in one loop. + */ + void ComputeIndicesOf(const nsINode* aParent, const nsINode* aChild1, + const nsINode* aChild2, + mozilla::Maybe<uint32_t>& aChild1Index, + mozilla::Maybe<uint32_t>& aChild2Index) { + bool foundChild1 = false; + bool foundChild2 = false; + for (size_t cacheIndex = 0; cacheIndex < cache_size; ++cacheIndex) { + if (foundChild1 && foundChild2) { + return; + } + const nsINode* node = mNodes[cacheIndex]; + if (!node) { + // reached the end of not-fully-populated cache. + break; + } + if (!foundChild1 && node == aChild1) { + aChild1Index = mIndices[cacheIndex]; + foundChild1 = true; + continue; + } + if (!foundChild2 && node == aChild2) { + aChild2Index = mIndices[cacheIndex]; + foundChild2 = true; + continue; + } + } + if (!foundChild1) { + aChild1Index = ComputeAndInsertIndexIntoCache(aParent, aChild1); + } + if (!foundChild2) { + aChild2Index = ComputeAndInsertIndexIntoCache(aParent, aChild2); + } + } + /** + * Looks up or computes child index. + */ mozilla::Maybe<uint32_t> ComputeIndexOf(const nsINode* aParent, const nsINode* aChild) { - if (aParent == mParent && aChild == mChild) { - return mIndex; + for (size_t cacheIndex = 0; cacheIndex < cache_size; ++cacheIndex) { + const nsINode* node = mNodes[cacheIndex]; + if (!node) { + break; + } + if (node == aChild) { + return mIndices[cacheIndex]; + } } - - mIndex = aParent->ComputeIndexOf(aChild); - mParent = aParent; - mChild = aChild; - return mIndex; + return ComputeAndInsertIndexIntoCache(aParent, aChild); } private: - const nsINode* mParent = nullptr; - const nsINode* mChild = nullptr; - mozilla::Maybe<uint32_t> mIndex; + /** + * Computes the index of aChild in aParent, inserts the index into the + * cache, and returns the index. + */ + mozilla::Maybe<uint32_t> ComputeAndInsertIndexIntoCache( + const nsINode* aParent, const nsINode* aChild) { + mozilla::Maybe<uint32_t> childIndex = aParent->ComputeIndexOf(aChild); + + mNodes[mNext] = aChild; + mIndices[mNext] = childIndex; + + ++mNext; + if (mNext == cache_size) { + // the last element of the cache has been reached. + // set mNext to 0 to start overriding the oldest cache entries. + mNext = 0; + } + return childIndex; + } + + /// Node storage. The array is initialized to null + /// by the empty initializer list. + const nsINode* mNodes[cache_size]{}; + + mozilla::Maybe<uint32_t> mIndices[cache_size]; + + /// The next element in the cache that will be written to. + /// If the cache is full (mNext == cache_size), + /// the oldest entries in the cache will be overridden, + /// ie. mNext will be set to 0. + size_t mNext{0}; }; /** + * Typedef with a reasonable default cache size. + * If Caches of different sizes are needed, + * ComparePoints would need to become templated. + */ + using NodeIndexCache = ResizableNodeIndexCache<100>; + + /** * Utility routine to compare two "points", where a point is a node/offset * pair. * Pass a cache object as aParent1Cache if you expect to repeatedly @@ -577,7 +667,7 @@ class nsContentUtils { */ static mozilla::Maybe<int32_t> ComparePoints( const nsINode* aParent1, uint32_t aOffset1, const nsINode* aParent2, - uint32_t aOffset2, ComparePointsCache* aParent1Cache = nullptr); + uint32_t aOffset2, NodeIndexCache* aIndexCache = nullptr); template <typename FPT, typename FRT, typename SPT, typename SRT> static mozilla::Maybe<int32_t> ComparePoints( const mozilla::RangeBoundaryBase<FPT, FRT>& aFirstBoundary, @@ -592,13 +682,16 @@ class nsContentUtils { * the result is 1, and the optional aDisconnected parameter * is set to true. * - * Pass a cache object as aParent1Cache if you expect to repeatedly - * call this function with the same value as aParent1. + * Pass a cache object as aIndexCache if you expect to repeatedly + * call this function. + * ComparePointsCache will store the last X (currently 100) node/index + * combinations in a stack-allocated array and does a lookup there + * before going into the expensive ComputeIndexOf() method. */ static int32_t ComparePoints_Deprecated( const nsINode* aParent1, uint32_t aOffset1, const nsINode* aParent2, uint32_t aOffset2, bool* aDisconnected = nullptr, - ComparePointsCache* aParent1Cache = nullptr); + NodeIndexCache* aIndexCache = nullptr); template <typename FPT, typename FRT, typename SPT, typename SRT> static int32_t ComparePoints_Deprecated( const mozilla::RangeBoundaryBase<FPT, FRT>& aFirstBoundary, @@ -1026,7 +1119,9 @@ class nsContentUtils { nsContentPolicyType aContentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE, bool aUseUrgentStartForChannel = false, bool aLinkPreload = false, - uint64_t aEarlyHintPreloaderId = 0); + uint64_t aEarlyHintPreloaderId = 0, + mozilla::dom::FetchPriority aFetchPriority = + mozilla::dom::FetchPriority::Auto); /** * Obtain an image loader that respects the given document/channel's privacy @@ -3438,7 +3533,7 @@ class nsContentUtils { MOZ_CAN_RUN_SCRIPT_BOUNDARY static nsIContent* AttachDeclarativeShadowRoot( - nsIContent* aHost, mozilla::dom::ShadowRootMode aMode, + nsIContent* aHost, mozilla::dom::ShadowRootMode aMode, bool aIsClonable, bool aDelegatesFocus); private: diff --git a/dom/base/nsCopySupport.cpp b/dom/base/nsCopySupport.cpp index 4dc183d664..15c0cf4cf0 100644 --- a/dom/base/nsCopySupport.cpp +++ b/dom/base/nsCopySupport.cpp @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsCopySupport.h" +#include "nsGlobalWindowInner.h" #include "nsIDocumentEncoder.h" #include "nsISupports.h" #include "nsIContent.h" @@ -38,7 +39,6 @@ #include "nsIImageLoadingContent.h" #include "nsIInterfaceRequestorUtils.h" #include "nsContentUtils.h" -#include "nsContentCID.h" #ifdef XP_WIN # include "mozilla/StaticPrefs_clipboard.h" @@ -714,6 +714,33 @@ static Element* GetElementOrNearestFlattenedTreeParentElement(nsINode* aNode) { return nullptr; } +/** + * This class is used while processing clipboard paste event. + */ +class MOZ_RAII AutoHandlingPasteEvent final { + public: + explicit AutoHandlingPasteEvent(nsGlobalWindowInner* aWindow, + DataTransfer* aDataTransfer, + const EventMessage& aEventMessage, + const int32_t& aClipboardType) { + MOZ_ASSERT(aDataTransfer); + if (aWindow && aEventMessage == ePaste && + aClipboardType == nsIClipboard::kGlobalClipboard) { + aWindow->SetCurrentPasteDataTransfer(aDataTransfer); + mInnerWindow = aWindow; + } + } + + ~AutoHandlingPasteEvent() { + if (mInnerWindow) { + mInnerWindow->SetCurrentPasteDataTransfer(nullptr); + } + } + + private: + RefPtr<nsGlobalWindowInner> mInnerWindow; +}; + bool nsCopySupport::FireClipboardEvent(EventMessage aEventMessage, int32_t aClipboardType, PresShell* aPresShell, @@ -791,9 +818,16 @@ bool nsCopySupport::FireClipboardEvent(EventMessage aEventMessage, InternalClipboardEvent evt(true, originalEventMessage); evt.mClipboardData = clipboardData; - RefPtr<nsPresContext> presContext = presShell->GetPresContext(); - EventDispatcher::Dispatch(targetElement, presContext, &evt, nullptr, - &status); + { + AutoHandlingPasteEvent autoHandlingPasteEvent( + nsGlobalWindowInner::Cast(doc->GetInnerWindow()), clipboardData, + aEventMessage, aClipboardType); + + RefPtr<nsPresContext> presContext = presShell->GetPresContext(); + EventDispatcher::Dispatch(targetElement, presContext, &evt, nullptr, + &status); + } + // If the event was cancelled, don't do the clipboard operation doDefault = (status != nsEventStatus_eConsumeNoDefault); } diff --git a/dom/base/nsDOMNavigationTiming.cpp b/dom/base/nsDOMNavigationTiming.cpp index ec70d2b1ac..c174153531 100644 --- a/dom/base/nsDOMNavigationTiming.cpp +++ b/dom/base/nsDOMNavigationTiming.cpp @@ -7,12 +7,11 @@ #include "nsDOMNavigationTiming.h" #include "GeckoProfiler.h" -#include "ipc/IPCMessageUtilsSpecializations.h" #include "mozilla/ProfilerMarkers.h" #include "mozilla/Telemetry.h" #include "mozilla/TimeStamp.h" +#include "mozilla/glean/GleanMetrics.h" #include "mozilla/dom/Document.h" -#include "mozilla/dom/PerformanceNavigation.h" #include "mozilla/ipc/IPDLParamTraits.h" #include "mozilla/ipc/URIUtils.h" #include "nsCOMPtr.h" diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index 692b94e9bd..9bc8340b90 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -897,6 +897,7 @@ nsresult nsDOMWindowUtils::SendTouchEventCommon( return NS_ERROR_UNEXPECTED; } WidgetTouchEvent event(true, msg, widget); + event.mFlags.mIsSynthesizedForTests = true; event.mModifiers = nsContentUtils::GetWidgetModifiers(aModifiers); nsPresContext* presContext = GetPresContext(); diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp index 08a2641333..5a4cf78d65 100644 --- a/dom/base/nsFocusManager.cpp +++ b/dom/base/nsFocusManager.cpp @@ -3358,7 +3358,7 @@ nsresult nsFocusManager::DetermineElementToMoveFocus( } return GetNextTabbableContent(presShell, startContent, nullptr, startContent, true, 1, false, false, - aNavigateByKey, false, aNextContent); + aNavigateByKey, false, false, aNextContent); } if (aType == MOVEFOCUS_LAST) { if (!aStartContent) { @@ -3366,7 +3366,7 @@ nsresult nsFocusManager::DetermineElementToMoveFocus( } return GetNextTabbableContent(presShell, startContent, nullptr, startContent, false, 0, false, false, - aNavigateByKey, false, aNextContent); + aNavigateByKey, false, false, aNextContent); } bool forward = (aType == MOVEFOCUS_FORWARD || aType == MOVEFOCUS_FORWARDDOC || @@ -3537,7 +3537,7 @@ nsresult nsFocusManager::DetermineElementToMoveFocus( MOZ_KnownLive(skipOriginalContentCheck ? nullptr : originalStartContent.get()), startContent, forward, tabIndex, ignoreTabIndex, - forDocumentNavigation, aNavigateByKey, false, + forDocumentNavigation, aNavigateByKey, false, false, getter_AddRefs(nextFocus)); NS_ENSURE_SUCCESS(rv, rv); if (rv == NS_SUCCESS_DOM_NO_OPERATION) { @@ -3660,15 +3660,16 @@ nsresult nsFocusManager::DetermineElementToMoveFocus( // If the focus started in this window outside a popup however, we should // continue by looping around to the end again. if (forDocumentNavigation && (forward || mayFocusRoot || popupFrame)) { - // HTML content documents can have their root element focused (a focus - // ring appears around the entire content area frame). This root - // appears in the tab order before all of the elements in the document. - // Chrome documents however cannot be focused directly, so instead we - // focus the first focusable element within the window. + // HTML content documents can have their root element focused by + // pressing F6(a focus ring appears around the entire content area + // frame). This root appears in the tab order before all of the elements + // in the document. Chrome documents however cannot be focused directly, + // so instead we focus the first focusable element within the window. // For example, the urlbar. RefPtr<Element> rootElementForFocus = GetRootForFocus(piWindow, doc, true, true); - return FocusFirst(rootElementForFocus, aNextContent); + return FocusFirst(rootElementForFocus, aNextContent, + true /* aReachedToEndForDocumentNavigation */); } // Once we have hit the top-level and have iterated to the end again, we @@ -3889,7 +3890,7 @@ nsIContent* nsFocusManager::GetNextTabbableContentInScope( nsIContent* aOwner, nsIContent* aStartContent, nsIContent* aOriginalStartContent, bool aForward, int32_t aCurrentTabIndex, bool aIgnoreTabIndex, bool aForDocumentNavigation, bool aNavigateByKey, - bool aSkipOwner) { + bool aSkipOwner, bool aReachedToEndForDocumentNavigation) { MOZ_ASSERT( IsHostOrSlot(aOwner) || IsOpenPopoverWithInvoker(aOwner), "Scope owner should be host, slot or an open popover with invoker set."); @@ -3972,6 +3973,7 @@ nsIContent* nsFocusManager::GetNextTabbableContentInScope( if (TryToMoveFocusToSubDocument(iterContent, aOriginalStartContent, aForward, aForDocumentNavigation, aNavigateByKey, + aReachedToEndForDocumentNavigation, getter_AddRefs(elementInFrame))) { return elementInFrame; } @@ -3984,7 +3986,8 @@ nsIContent* nsFocusManager::GetNextTabbableContentInScope( nsIContent* contentToFocus = GetNextTabbableContentInScope( iterContent, iterContent, aOriginalStartContent, aForward, aForward ? 1 : 0, aIgnoreTabIndex, aForDocumentNavigation, - aNavigateByKey, false /* aSkipOwner */); + aNavigateByKey, false /* aSkipOwner */, + aReachedToEndForDocumentNavigation); if (contentToFocus) { return contentToFocus; } @@ -4024,7 +4027,8 @@ nsIContent* nsFocusManager::GetNextTabbableContentInScope( nsIContent* nsFocusManager::GetNextTabbableContentInAncestorScopes( nsIContent* aStartOwner, nsCOMPtr<nsIContent>& aStartContent /* inout */, nsIContent* aOriginalStartContent, bool aForward, int32_t* aCurrentTabIndex, - bool* aIgnoreTabIndex, bool aForDocumentNavigation, bool aNavigateByKey) { + bool* aIgnoreTabIndex, bool aForDocumentNavigation, bool aNavigateByKey, + bool aReachedToEndForDocumentNavigation) { MOZ_ASSERT(aStartOwner == FindScopeOwner(aStartContent), "aStartOWner should be the scope owner of aStartContent"); MOZ_ASSERT(IsHostOrSlot(aStartOwner), "scope owner should be host or slot"); @@ -4043,7 +4047,7 @@ nsIContent* nsFocusManager::GetNextTabbableContentInAncestorScopes( nsIContent* contentToFocus = GetNextTabbableContentInScope( owner, startContent, aOriginalStartContent, aForward, tabIndex, tabIndex < 0, aForDocumentNavigation, aNavigateByKey, - false /* aSkipOwner */); + false /* aSkipOwner */, aReachedToEndForDocumentNavigation); if (contentToFocus) { return contentToFocus; } @@ -4089,7 +4093,8 @@ nsresult nsFocusManager::GetNextTabbableContent( PresShell* aPresShell, nsIContent* aRootContent, nsIContent* aOriginalStartContent, nsIContent* aStartContent, bool aForward, int32_t aCurrentTabIndex, bool aIgnoreTabIndex, bool aForDocumentNavigation, - bool aNavigateByKey, bool aSkipPopover, nsIContent** aResultContent) { + bool aNavigateByKey, bool aSkipPopover, + bool aReachedToEndForDocumentNavigation, nsIContent** aResultContent) { *aResultContent = nullptr; if (!aStartContent) { @@ -4109,7 +4114,7 @@ nsresult nsFocusManager::GetNextTabbableContent( nsIContent* contentToFocus = GetNextTabbableContentInScope( startContent, startContent, aOriginalStartContent, aForward, 1, aIgnoreTabIndex, aForDocumentNavigation, aNavigateByKey, - true /* aSkipOwner */); + true /* aSkipOwner */, aReachedToEndForDocumentNavigation); if (contentToFocus) { NS_ADDREF(*aResultContent = contentToFocus); return NS_OK; @@ -4125,7 +4130,7 @@ nsresult nsFocusManager::GetNextTabbableContent( nsIContent* contentToFocus = GetNextTabbableContentInScope( popover, popover, aOriginalStartContent, aForward, 1, aIgnoreTabIndex, aForDocumentNavigation, aNavigateByKey, - true /* aSkipOwner */); + true /* aSkipOwner */, aReachedToEndForDocumentNavigation); if (contentToFocus) { NS_ADDREF(*aResultContent = contentToFocus); return NS_OK; @@ -4140,7 +4145,7 @@ nsresult nsFocusManager::GetNextTabbableContent( nsIContent* contentToFocus = GetNextTabbableContentInAncestorScopes( owner, startContent /* inout */, aOriginalStartContent, aForward, &aCurrentTabIndex, &aIgnoreTabIndex, aForDocumentNavigation, - aNavigateByKey); + aNavigateByKey, aReachedToEndForDocumentNavigation); if (contentToFocus) { NS_ADDREF(*aResultContent = contentToFocus); return NS_OK; @@ -4185,7 +4190,8 @@ nsresult nsFocusManager::GetNextTabbableContent( nsIContent* contentToFocus = GetNextTabbableContentInScope( iterStartContent, iterStartContent, aOriginalStartContent, aForward, aForward ? 1 : 0, aIgnoreTabIndex, - aForDocumentNavigation, aNavigateByKey, true /* aSkipOwner */); + aForDocumentNavigation, aNavigateByKey, true /* aSkipOwner */, + aReachedToEndForDocumentNavigation); if (contentToFocus) { NS_ADDREF(*aResultContent = contentToFocus); return NS_OK; @@ -4276,7 +4282,8 @@ nsresult nsFocusManager::GetNextTabbableContent( (aIgnoreTabIndex || aCurrentTabIndex == tabIndex)) { nsresult rv = GetNextTabbableContent( aPresShell, rootElement, nullptr, invokerContent, true, - tabIndex, false, false, aNavigateByKey, true, aResultContent); + tabIndex, false, false, aNavigateByKey, true, + aReachedToEndForDocumentNavigation, aResultContent); if (NS_SUCCEEDED(rv) && *aResultContent) { return rv; } @@ -4298,7 +4305,7 @@ nsresult nsFocusManager::GetNextTabbableContent( nsIContent* contentToFocus = GetNextTabbableContentInScope( popover, popover, aOriginalStartContent, aForward, 0, aIgnoreTabIndex, aForDocumentNavigation, aNavigateByKey, - true /* aSkipOwner */); + true /* aSkipOwner */, aReachedToEndForDocumentNavigation); if (contentToFocus) { NS_ADDREF(*aResultContent = contentToFocus); @@ -4340,7 +4347,8 @@ nsresult nsFocusManager::GetNextTabbableContent( // want to locate the first content, not the first document. nsresult rv = GetNextTabbableContent( aPresShell, currentContent, nullptr, currentContent, true, 1, - false, false, aNavigateByKey, false, aResultContent); + false, false, aNavigateByKey, false, + aReachedToEndForDocumentNavigation, aResultContent); if (NS_SUCCEEDED(rv) && *aResultContent) { return rv; } @@ -4368,7 +4376,7 @@ nsresult nsFocusManager::GetNextTabbableContent( currentTopLevelScopeOwner, currentTopLevelScopeOwner, aOriginalStartContent, aForward, aForward ? 1 : 0, aIgnoreTabIndex, aForDocumentNavigation, aNavigateByKey, - true /* aSkipOwner */); + true /* aSkipOwner */, aReachedToEndForDocumentNavigation); if (contentToFocus) { NS_ADDREF(*aResultContent = contentToFocus); return NS_OK; @@ -4469,7 +4477,8 @@ nsresult nsFocusManager::GetNextTabbableContent( // frame. If so, navigate into the child frame instead. if (TryToMoveFocusToSubDocument( currentContent, aOriginalStartContent, aForward, - aForDocumentNavigation, aNavigateByKey, aResultContent)) { + aForDocumentNavigation, aNavigateByKey, + aReachedToEndForDocumentNavigation, aResultContent)) { MOZ_ASSERT(*aResultContent); return NS_OK; } @@ -4485,6 +4494,24 @@ nsresult nsFocusManager::GetNextTabbableContent( NS_ADDREF(*aResultContent = currentContent); return NS_OK; } + } else if (currentContent && aReachedToEndForDocumentNavigation && + StaticPrefs::dom_disable_tab_focus_to_root_element() && + nsContentUtils::IsChromeDoc( + currentContent->GetComposedDoc())) { + // aReachedToEndForDocumentNavigation is true means + // 1. This is a document navigation (VK_F6) + // 2. This is the top-level document (Note that we may start from + // a subdocument) + // 3. We've searched through the this top-level document already + if (!GetRootForChildDocument(currentContent)) { + // We'd like to focus the first focusable element of this + // top-level chrome document. + if (currentContent == aRootContent || + currentContent != startContent) { + NS_ADDREF(*aResultContent = currentContent); + return NS_OK; + } + } } } } else if (aOriginalStartContent && @@ -4522,13 +4549,14 @@ nsresult nsFocusManager::GetNextTabbableContent( if (aCurrentTabIndex == (aForward ? 0 : 1)) { // if going backwards, the canvas should be focused once the beginning // has been reached, so get the root element. - if (!aForward) { + if (!aForward && !StaticPrefs::dom_disable_tab_focus_to_root_element()) { nsCOMPtr<nsPIDOMWindowOuter> window = GetCurrentWindow(aRootContent); NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); RefPtr<Element> docRoot = GetRootForFocus( window, aRootContent->GetComposedDoc(), false, true); - FocusFirst(docRoot, aResultContent); + FocusFirst(docRoot, aResultContent, + false /* aReachedToEndForDocumentNavigation */); } break; } @@ -4556,7 +4584,8 @@ bool nsFocusManager::TryDocumentNavigation(nsIContent* aCurrentContent, // the frameset's frames and locate the first focusable frame. if (!rootElementForChildDocument->IsHTMLElement(nsGkAtoms::frameset)) { *aCheckSubDocument = false; - Unused << FocusFirst(rootElementForChildDocument, aResultContent); + Unused << FocusFirst(rootElementForChildDocument, aResultContent, + false /* aReachedToEndForDocumentNavigation */); return *aResultContent != nullptr; } } else { @@ -4571,12 +4600,12 @@ bool nsFocusManager::TryDocumentNavigation(nsIContent* aCurrentContent, bool nsFocusManager::TryToMoveFocusToSubDocument( nsIContent* aCurrentContent, nsIContent* aOriginalStartContent, bool aForward, bool aForDocumentNavigation, bool aNavigateByKey, - nsIContent** aResultContent) { + bool aReachedToEndForDocumentNavigation, nsIContent** aResultContent) { Document* doc = aCurrentContent->GetComposedDoc(); NS_ASSERTION(doc, "content not in document"); Document* subdoc = doc->GetSubDocumentFor(aCurrentContent); if (subdoc && !subdoc->EventHandlingSuppressed()) { - if (aForward) { + if (aForward && !StaticPrefs::dom_disable_tab_focus_to_root_element()) { // When tabbing forward into a frame, return the root // frame so that the canvas becomes focused. if (nsCOMPtr<nsPIDOMWindowOuter> subframe = subdoc->GetWindow()) { @@ -4592,11 +4621,19 @@ bool nsFocusManager::TryToMoveFocusToSubDocument( nsresult rv = GetNextTabbableContent( subPresShell, rootElement, aOriginalStartContent, rootElement, aForward, (aForward ? 1 : 0), false, aForDocumentNavigation, - aNavigateByKey, false, aResultContent); + aNavigateByKey, false, aReachedToEndForDocumentNavigation, + aResultContent); NS_ENSURE_SUCCESS(rv, false); if (*aResultContent) { return true; } + if (rootElement->IsEditable() && + StaticPrefs::dom_disable_tab_focus_to_root_element()) { + // Only move to the root element with a valid reason + *aResultContent = rootElement; + NS_ADDREF(*aResultContent); + return true; + } } } } @@ -4711,7 +4748,8 @@ int32_t nsFocusManager::GetNextTabIndex(nsIContent* aParent, } nsresult nsFocusManager::FocusFirst(Element* aRootElement, - nsIContent** aNextContent) { + nsIContent** aNextContent, + bool aReachedToEndForDocumentNavigation) { if (!aRootElement) { return NS_OK; } @@ -4741,9 +4779,12 @@ nsresult nsFocusManager::FocusFirst(Element* aRootElement, // tabbable item so that the first item is focused. Note that we // always go forward and not back here. if (RefPtr<PresShell> presShell = doc->GetPresShell()) { - return GetNextTabbableContent(presShell, aRootElement, nullptr, - aRootElement, true, 1, false, false, true, - false, aNextContent); + return GetNextTabbableContent( + presShell, aRootElement, nullptr, aRootElement, true, 1, false, + StaticPrefs::dom_disable_tab_focus_to_root_element() + ? aReachedToEndForDocumentNavigation + : false, + true, false, aReachedToEndForDocumentNavigation, aNextContent); } } } diff --git a/dom/base/nsFocusManager.h b/dom/base/nsFocusManager.h index 6e5c761f4c..4fb9d05e1c 100644 --- a/dom/base/nsFocusManager.h +++ b/dom/base/nsFocusManager.h @@ -578,6 +578,10 @@ class nsFocusManager final : public nsIFocusManager, * aSkipOwner to skip owner while searching. The flag is set when caller is * |GetNextTabbableContent| in order to let caller handle owner. * + * aReachedToEndForDocumentNavigation is true when this is a document + * navigation and the focus algorithm has reached to the end of the top-level + * document. + * * NOTE: * Consider the method searches downwards in flattened subtree * rooted at aOwner. @@ -586,7 +590,8 @@ class nsFocusManager final : public nsIFocusManager, nsIContent* aOwner, nsIContent* aStartContent, nsIContent* aOriginalStartContent, bool aForward, int32_t aCurrentTabIndex, bool aIgnoreTabIndex, - bool aForDocumentNavigation, bool aNavigateByKey, bool aSkipOwner); + bool aForDocumentNavigation, bool aNavigateByKey, bool aSkipOwner, + bool aReachedToEndForDocumentNavigation); /** * Retrieve the next tabbable element in scope including aStartContent @@ -619,6 +624,10 @@ class nsFocusManager final : public nsIFocusManager, * aNavigateByKey to move focus by keyboard as a side effect of computing the * next target. * + * aReachedToEndForDocumentNavigation is true when this is a document + * navigation and the focus algorithm has reached to the end of the top-level + * document. + * * NOTE: * Consider the method searches upwards in all shadow host- or slot-rooted * flattened subtrees that contains aStartContent as non-root, except @@ -628,7 +637,8 @@ class nsFocusManager final : public nsIFocusManager, nsIContent* aStartOwner, nsCOMPtr<nsIContent>& aStartContent /* inout */, nsIContent* aOriginalStartContent, bool aForward, int32_t* aCurrentTabIndex, bool* aIgnoreTabIndex, - bool aForDocumentNavigation, bool aNavigateByKey); + bool aForDocumentNavigation, bool aNavigateByKey, + bool aReachedToEndForDocumentNavigation); /** * Retrieve the next tabbable element within a document, using focusability @@ -663,13 +673,17 @@ class nsFocusManager final : public nsIFocusManager, * * aNavigateByKey to move focus by keyboard as a side effect of computing the * next target. + * + * aReachedToEndForDocumentNavigation is true when this is a document + * navigation and the focus algorithm has reached to the end of the top-level + * document. */ MOZ_CAN_RUN_SCRIPT nsresult GetNextTabbableContent( mozilla::PresShell* aPresShell, nsIContent* aRootContent, nsIContent* aOriginalStartContent, nsIContent* aStartContent, bool aForward, int32_t aCurrentTabIndex, bool aIgnoreTabIndex, bool aForDocumentNavigation, bool aNavigateByKey, bool aSkipPopover, - nsIContent** aResultContent); + bool aReachedToEndForDocumentNavigation, nsIContent** aResultContent); /** * Get the next tabbable image map area and returns it. @@ -699,9 +713,13 @@ class nsFocusManager final : public nsIFocusManager, * Focus the first focusable content within the document with a root node of * aRootContent. For content documents, this will be aRootContent itself, but * for chrome documents, this will locate the next focusable content. + * + * aReachedToEndForDocumentNavigation is true when the focus algorithm has + * reached to the end of the top-level document. */ - MOZ_CAN_RUN_SCRIPT nsresult FocusFirst(mozilla::dom::Element* aRootContent, - nsIContent** aNextContent); + MOZ_CAN_RUN_SCRIPT nsresult + FocusFirst(mozilla::dom::Element* aRootContent, nsIContent** aNextContent, + bool aReachedToEndForDocumentNavigation); /** * Retrieves and returns the root node from aDocument to be focused. Will @@ -761,7 +779,7 @@ class nsFocusManager final : public nsIFocusManager, MOZ_CAN_RUN_SCRIPT bool TryToMoveFocusToSubDocument( nsIContent* aCurrentContent, nsIContent* aOriginalStartContent, bool aForward, bool aForDocumentNavigation, bool aNavigateByKey, - nsIContent** aResultContent); + bool aReachedToEndForDocumentNavigation, nsIContent** aResultContent); // Sets the focused BrowsingContext and, if appropriate, syncs it to // other processes. diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp index a40bc427dd..eca528f258 100644 --- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -39,7 +39,6 @@ #include "nsSubDocumentFrame.h" #include "nsError.h" #include "nsIAppWindow.h" -#include "nsIMozBrowserFrame.h" #include "nsIScriptError.h" #include "nsGlobalWindowInner.h" #include "nsGlobalWindowOuter.h" @@ -262,13 +261,6 @@ static bool IsTopContent(BrowsingContext* aParent, Element* aOwner) { return false; } - // If we have a (deprecated) mozbrowser element, we want to start a new - // BrowsingContext tree regardless of whether the parent is chrome or content. - nsCOMPtr<nsIMozBrowserFrame> mozbrowser = aOwner->GetAsMozBrowserFrame(); - if (mozbrowser && mozbrowser->GetReallyIsBrowser()) { - return true; - } - if (aParent->IsContent()) { // If we're already in content, we may still want to create a new // BrowsingContext tree if our element is a xul browser element with a @@ -365,17 +357,8 @@ static bool InitialLoadIsRemote(Element* aOwner) { return false; } - // If we're an <iframe mozbrowser> and we don't have a "remote" attribute, - // fall back to the default. - nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(aOwner); - bool isMozBrowserFrame = browserFrame && browserFrame->GetReallyIsBrowser(); - if (isMozBrowserFrame && !aOwner->HasAttr(nsGkAtoms::remote)) { - return Preferences::GetBool("dom.ipc.browser_frames.oop_by_default", false); - } - - // Otherwise, we're remote if we have "remote=true" and we're either a - // browser frame or a XUL element. - return (isMozBrowserFrame || aOwner->GetNameSpaceID() == kNameSpaceID_XUL) && + // Otherwise, we're remote if we have "remote=true" and we're a XUL element. + return (aOwner->GetNameSpaceID() == kNameSpaceID_XUL) && aOwner->AttrValueIs(kNameSpaceID_None, nsGkAtoms::remote, nsGkAtoms::_true, eCaseMatters); } @@ -706,12 +689,6 @@ nsresult nsFrameLoader::ReallyStartLoadingInternal() { // Default flags: int32_t flags = nsIWebNavigation::LOAD_FLAGS_NONE; - - // Flags for browser frame: - if (OwnerIsMozBrowserFrame()) { - flags = nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP | - nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL; - } loadState->SetLoadFlags(flags); loadState->SetFirstParty(false); @@ -875,14 +852,6 @@ static bool CheckDocShellType(mozilla::dom::Element* aOwnerContent, bool isContent = aOwnerContent->AttrValueIs(kNameSpaceID_None, aAtom, nsGkAtoms::content, eIgnoreCase); - if (!isContent) { - nsCOMPtr<nsIMozBrowserFrame> mozbrowser = - aOwnerContent->GetAsMozBrowserFrame(); - if (mozbrowser) { - mozbrowser->GetMozbrowser(&isContent); - } - } - if (isContent) { return aDocShell->ItemType() == nsIDocShellTreeItem::typeContent; } @@ -1156,7 +1125,6 @@ bool nsFrameLoader::ShowRemoteFrame(const ScreenIntSize& size, if (nsCOMPtr<nsIObserverService> os = services::GetObserverService()) { os->NotifyObservers(ToSupports(this), "remote-browser-shown", nullptr); } - ProcessPriorityManager::RemoteBrowserFrameShown(this); } } else { nsIntRect dimensions; @@ -1329,14 +1297,6 @@ nsresult nsFrameLoader::SwapWithOtherRemoteLoader( return NS_ERROR_NOT_IMPLEMENTED; } - // Destroy browser frame scripts for content leaving a frame with browser API - if (OwnerIsMozBrowserFrame() && !aOther->OwnerIsMozBrowserFrame()) { - DestroyBrowserFrameScripts(); - } - if (!OwnerIsMozBrowserFrame() && aOther->OwnerIsMozBrowserFrame()) { - aOther->DestroyBrowserFrameScripts(); - } - otherBrowserParent->SetBrowserDOMWindow(browserDOMWindow); browserParent->SetBrowserDOMWindow(otherBrowserDOMWindow); @@ -1405,10 +1365,6 @@ nsresult nsFrameLoader::SwapWithOtherRemoteLoader( ourPresShell->BackingScaleFactorChanged(); otherPresShell->BackingScaleFactorChanged(); - // Initialize browser API if needed now that owner content has changed. - InitializeBrowserAPI(); - aOther->InitializeBrowserAPI(); - mInSwap = aOther->mInSwap = false; // Send an updated tab context since owner content type may have changed. @@ -1536,13 +1492,8 @@ nsresult nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther, return NS_ERROR_NOT_IMPLEMENTED; } - bool ourFullscreenAllowed = ourContent->IsXULElement() || - (OwnerIsMozBrowserFrame() && - ourContent->HasAttr(nsGkAtoms::allowfullscreen)); - bool otherFullscreenAllowed = - otherContent->IsXULElement() || - (aOther->OwnerIsMozBrowserFrame() && - otherContent->HasAttr(nsGkAtoms::allowfullscreen)); + bool ourFullscreenAllowed = ourContent->IsXULElement(); + bool otherFullscreenAllowed = otherContent->IsXULElement(); if (ourFullscreenAllowed != otherFullscreenAllowed) { return NS_ERROR_NOT_IMPLEMENTED; } @@ -1732,14 +1683,6 @@ nsresult nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther, return rv; } - // Destroy browser frame scripts for content leaving a frame with browser API - if (OwnerIsMozBrowserFrame() && !aOther->OwnerIsMozBrowserFrame()) { - DestroyBrowserFrameScripts(); - } - if (!OwnerIsMozBrowserFrame() && aOther->OwnerIsMozBrowserFrame()) { - aOther->DestroyBrowserFrameScripts(); - } - // Now move the docshells to the right docshell trees. Note that this // resets their treeowners to null. ourParentItem->RemoveChild(ourDocshell); @@ -1837,10 +1780,6 @@ nsresult nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther, ourFrame->PresShell()->BackingScaleFactorChanged(); otherFrame->PresShell()->BackingScaleFactorChanged(); - // Initialize browser API if needed now that owner content has changed - InitializeBrowserAPI(); - aOther->InitializeBrowserAPI(); - return NS_OK; } @@ -2174,11 +2113,6 @@ void nsFrameLoader::SetOwnerContent(Element* aContent) { } } -bool nsFrameLoader::OwnerIsMozBrowserFrame() { - nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent); - return browserFrame ? browserFrame->GetReallyIsBrowser() : false; -} - nsIContent* nsFrameLoader::GetParentObject() const { return mOwnerContent; } void nsFrameLoader::AssertSafeToInit() { @@ -2338,16 +2272,7 @@ nsresult nsFrameLoader::MaybeCreateDocShell() { MOZ_ALWAYS_SUCCEEDS(mPendingBrowsingContext->SetInitialSandboxFlags( mPendingBrowsingContext->GetSandboxFlags())); - if (OwnerIsMozBrowserFrame()) { - // For inproc frames, set the docshell properties. - nsAutoString name; - if (mOwnerContent->GetAttr(nsGkAtoms::name, name)) { - docShell->SetName(name); - } - } - ReallyLoadFrameScripts(); - InitializeBrowserAPI(); // Previously we would forcibly create the initial about:blank document for // in-process content frames from a frame script which eagerly loaded in @@ -2588,11 +2513,8 @@ bool nsFrameLoader::TryRemoteBrowserInternal() { // Graphics initialization code relies on having a frame for the // remote browser case, as we can be inside a popup, which is a different // widget. - // - // FIXME: Ideally this should be unconditional, but we skip if for <iframe - // mozbrowser> because the old RDM ui depends on current behavior, and the - // mozbrowser frame code is scheduled for deletion, see bug 1574886. - if (!OwnerIsMozBrowserFrame() && !mOwnerContent->GetPrimaryFrame()) { + + if (!mOwnerContent->GetPrimaryFrame()) { doc->FlushPendingNotifications(FlushType::Frames); } @@ -2647,12 +2569,11 @@ bool nsFrameLoader::TryRemoteBrowserInternal() { mPendingBrowsingContext->InitSessionHistory(); } - // <iframe mozbrowser> gets to skip these checks. // iframes for JS plugins also get to skip these checks. We control the URL // that gets loaded, but the load is triggered from the document containing // the plugin. // out of process iframes also get to skip this check. - if (!OwnerIsMozBrowserFrame() && !XRE_IsContentProcess()) { + if (!XRE_IsContentProcess()) { if (parentDocShell->ItemType() != nsIDocShellTreeItem::typeChrome) { // Allow three exceptions to this rule : // - about:addons so it can load remote extension options pages @@ -2817,7 +2738,6 @@ bool nsFrameLoader::TryRemoteBrowserInternal() { } ReallyLoadFrameScripts(); - InitializeBrowserAPI(); return true; } @@ -3053,7 +2973,7 @@ nsresult nsFrameLoader::EnsureMessageManager() { return NS_OK; } - if (!mIsTopLevelContent && !OwnerIsMozBrowserFrame() && !IsRemoteFrame() && + if (!mIsTopLevelContent && !IsRemoteFrame() && !(mOwnerContent->IsXULElement() && mOwnerContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::forcemessagemanager, @@ -3096,7 +3016,7 @@ nsresult nsFrameLoader::EnsureMessageManager() { NS_ENSURE_TRUE(mChildMessageManager, NS_ERROR_UNEXPECTED); // Set up session store - if (StaticPrefs::browser_sessionstore_platform_collection_AtStartup()) { + if (SessionStorePlatformCollection()) { if (XRE_IsParentProcess() && mIsTopLevelContent) { mSessionStoreChild = SessionStoreChild::GetOrCreate( GetExtantBrowsingContext(), mOwnerContent); @@ -3538,36 +3458,6 @@ BrowsingContext* nsFrameLoader::GetExtantBrowsingContext() { return mPendingBrowsingContext; } -void nsFrameLoader::InitializeBrowserAPI() { - if (!OwnerIsMozBrowserFrame()) { - return; - } - - nsresult rv = EnsureMessageManager(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return; - } - mMessageManager->LoadFrameScript( - u"chrome://global/content/BrowserElementChild.js"_ns, - /* allowDelayedLoad = */ true, - /* aRunInGlobalScope */ true, IgnoreErrors()); - - nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent); - if (browserFrame) { - browserFrame->InitializeBrowserAPI(); - } -} - -void nsFrameLoader::DestroyBrowserFrameScripts() { - if (!OwnerIsMozBrowserFrame()) { - return; - } - nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent); - if (browserFrame) { - browserFrame->DestroyBrowserFrameScripts(); - } -} - void nsFrameLoader::StartPersistence( BrowsingContext* aContext, nsIWebBrowserPersistDocumentReceiver* aRecv, ErrorResult& aRv) { @@ -3673,9 +3563,9 @@ nsresult nsFrameLoader::GetNewTabContext(MutableTabContext* aTabContext, nsresult nsFrameLoader::PopulateOriginContextIdsFromAttributes( OriginAttributes& aAttr) { - // Only XUL or mozbrowser frames are allowed to set context IDs + // Only XUL are allowed to set context IDs uint32_t namespaceID = mOwnerContent->GetNameSpaceID(); - if (namespaceID != kNameSpaceID_XUL && !OwnerIsMozBrowserFrame()) { + if (namespaceID != kNameSpaceID_XUL) { return NS_OK; } @@ -3693,7 +3583,7 @@ nsresult nsFrameLoader::PopulateOriginContextIdsFromAttributes( mOwnerContent->GetAttr(nsGkAtoms::geckoViewSessionContextId, attributeValue) && !attributeValue.IsEmpty()) { - // XXX: Should we check the format from `GeckoViewNavigation.jsm` here? + // XXX: Should we check the format from `GeckoViewNavigation.sys.mjs` here? aAttr.mGeckoViewSessionContextId = attributeValue; } @@ -3899,8 +3789,7 @@ bool nsFrameLoader::EnsureBrowsingContextAttached() { // Inherit the `mFirstPartyDomain` flag from our parent document's result // principal, if it was set. if (parentContext->IsContent() && - !parentDoc->NodePrincipal()->IsSystemPrincipal() && - !OwnerIsMozBrowserFrame()) { + !parentDoc->NodePrincipal()->IsSystemPrincipal()) { OriginAttributes docAttrs = parentDoc->NodePrincipal()->OriginAttributesRef(); // We only want to inherit firstPartyDomain here, other attributes should @@ -3918,15 +3807,6 @@ bool nsFrameLoader::EnsureBrowsingContextAttached() { if (NS_WARN_IF(NS_FAILED(rv))) { return false; } - - // <iframe mozbrowser> is allowed to set `mozprivatebrowsing` to - // force-enable private browsing. - if (OwnerIsMozBrowserFrame()) { - if (mOwnerContent->HasAttr(nsGkAtoms::mozprivatebrowsing)) { - attrs.SyncAttributesWithPrivateBrowsing(true); - usePrivateBrowsing = true; - } - } } // If we've already been attached, return. diff --git a/dom/base/nsFrameLoader.h b/dom/base/nsFrameLoader.h index 159e3865a6..33c8000868 100644 --- a/dom/base/nsFrameLoader.h +++ b/dom/base/nsFrameLoader.h @@ -264,12 +264,6 @@ class nsFrameLoader final : public nsStubMutationObserver, bool IsNetworkCreated() const { return mNetworkCreated; } - /** - * Is this a frame loader for a bona fide <iframe mozbrowser>? - * <xul:browser> is not a mozbrowser, so this is false for that case. - */ - bool OwnerIsMozBrowserFrame(); - nsIContent* GetParentObject() const; /** @@ -481,9 +475,6 @@ class nsFrameLoader final : public nsStubMutationObserver, void AddTreeItemToTreeOwner(nsIDocShellTreeItem* aItem, nsIDocShellTreeOwner* aOwner); - void InitializeBrowserAPI(); - void DestroyBrowserFrameScripts(); - nsresult GetNewTabContext(mozilla::dom::MutableTabContext* aTabContext, nsIURI* aURI = nullptr); diff --git a/dom/base/nsGlobalWindowInner.cpp b/dom/base/nsGlobalWindowInner.cpp index 8b69389790..5337e1588f 100644 --- a/dom/base/nsGlobalWindowInner.cpp +++ b/dom/base/nsGlobalWindowInner.cpp @@ -167,6 +167,7 @@ #include "mozilla/dom/TimeoutHandler.h" #include "mozilla/dom/TimeoutManager.h" #include "mozilla/dom/ToJSValue.h" +#include "mozilla/dom/TrustedTypePolicyFactory.h" #include "mozilla/dom/VRDisplay.h" #include "mozilla/dom/VRDisplayEvent.h" #include "mozilla/dom/VRDisplayEventBinding.h" @@ -224,6 +225,7 @@ #include "nsIBrowserChild.h" #include "nsICancelableRunnable.h" #include "nsIChannel.h" +#include "nsIClipboard.h" #include "nsIContentSecurityPolicy.h" #include "nsIControllers.h" #include "nsICookieJarSettings.h" @@ -1283,6 +1285,8 @@ void nsGlobalWindowInner::FreeInnerObjects() { mWebTaskScheduler = nullptr; } + mTrustedTypePolicyFactory = nullptr; + mSharedWorkers.Clear(); #ifdef MOZ_WEBSPEECH @@ -1378,6 +1382,8 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindowInner) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebTaskScheduler) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTrustedTypePolicyFactory) + #ifdef MOZ_WEBSPEECH NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechSynthesis) #endif @@ -1446,6 +1452,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindowInner) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInstallTrigger) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIntlUtils) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVisualViewport) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCurrentPasteDataTransfer) tmp->TraverseObjectsInGlobal(cb); @@ -1481,6 +1488,8 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindowInner) NS_IMPL_CYCLE_COLLECTION_UNLINK(mWebTaskScheduler) } + NS_IMPL_CYCLE_COLLECTION_UNLINK(mTrustedTypePolicyFactory) + #ifdef MOZ_WEBSPEECH NS_IMPL_CYCLE_COLLECTION_UNLINK(mSpeechSynthesis) #endif @@ -1554,6 +1563,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindowInner) NS_IMPL_CYCLE_COLLECTION_UNLINK(mInstallTrigger) NS_IMPL_CYCLE_COLLECTION_UNLINK(mIntlUtils) NS_IMPL_CYCLE_COLLECTION_UNLINK(mVisualViewport) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mCurrentPasteDataTransfer) tmp->UnlinkObjectsInGlobal(); @@ -5082,7 +5092,7 @@ nsGlobalWindowInner::ShowSlowScriptDialog(JSContext* aCx, } // Reached only on non-e10s - once per slow script dialog. - // On e10s - we probe once at ProcessHangsMonitor.jsm + // On e10s - we probe once at ProcessHangsMonitor.sys.mjs Telemetry::Accumulate(Telemetry::SLOW_SCRIPT_NOTICE_COUNT, 1); // Get the nsIPrompt interface from the docshell @@ -7603,6 +7613,27 @@ JS::loader::ModuleLoaderBase* nsGlobalWindowInner::GetModuleLoader( return loader->GetModuleLoader(); } +void nsGlobalWindowInner::SetCurrentPasteDataTransfer( + DataTransfer* aDataTransfer) { + MOZ_ASSERT_IF(aDataTransfer, aDataTransfer->GetEventMessage() == ePaste); + MOZ_ASSERT_IF(aDataTransfer, aDataTransfer->ClipboardType() == + nsIClipboard::kGlobalClipboard); + MOZ_ASSERT_IF(aDataTransfer, aDataTransfer->GetAsyncGetClipboardData()); + mCurrentPasteDataTransfer = aDataTransfer; +} + +DataTransfer* nsGlobalWindowInner::GetCurrentPasteDataTransfer() const { + return mCurrentPasteDataTransfer; +} + +TrustedTypePolicyFactory* nsGlobalWindowInner::TrustedTypes() { + if (!mTrustedTypePolicyFactory) { + mTrustedTypePolicyFactory = MakeRefPtr<TrustedTypePolicyFactory>(this); + } + + return mTrustedTypePolicyFactory; +} + nsIURI* nsPIDOMWindowInner::GetDocumentURI() const { return mDoc ? mDoc->GetDocumentURI() : mDocumentURI.get(); } diff --git a/dom/base/nsGlobalWindowInner.h b/dom/base/nsGlobalWindowInner.h index 100dbb9699..15fbc4259f 100644 --- a/dom/base/nsGlobalWindowInner.h +++ b/dom/base/nsGlobalWindowInner.h @@ -104,6 +104,7 @@ class ClientSource; class Console; class Crypto; class CustomElementRegistry; +class DataTransfer; class DocGroup; class External; class Function; @@ -127,6 +128,7 @@ class WebTaskScheduler; class WebTaskSchedulerMainThread; class SpeechSynthesis; class Timeout; +class TrustedTypePolicyFactory; class VisualViewport; class VRDisplay; enum class VRDisplayEventReason : uint8_t; @@ -1254,11 +1256,18 @@ class nsGlobalWindowInner final : public mozilla::dom::EventTarget, virtual JS::loader::ModuleLoaderBase* GetModuleLoader( JSContext* aCx) override; + mozilla::dom::TrustedTypePolicyFactory* TrustedTypes(); + + void SetCurrentPasteDataTransfer(mozilla::dom::DataTransfer* aDataTransfer); + mozilla::dom::DataTransfer* GetCurrentPasteDataTransfer() const; + private: RefPtr<mozilla::dom::ContentMediaController> mContentMediaController; RefPtr<mozilla::dom::WebTaskSchedulerMainThread> mWebTaskScheduler; + RefPtr<mozilla::dom::TrustedTypePolicyFactory> mTrustedTypePolicyFactory; + protected: // Whether we need to care about orientation changes. bool mHasOrientationChangeListeners : 1; @@ -1460,6 +1469,10 @@ class nsGlobalWindowInner final : public mozilla::dom::EventTarget, mGroupMessageManagers{1}; } mChromeFields; + // Cache the DataTransfer created for a paste event, this will be reset after + // the event is dispatched. + RefPtr<mozilla::dom::DataTransfer> mCurrentPasteDataTransfer; + // These fields are used by the inner and outer windows to prevent // programatically moving the window while the mouse is down. static bool sMouseDown; diff --git a/dom/base/nsGlobalWindowOuter.cpp b/dom/base/nsGlobalWindowOuter.cpp index 87874d49be..e28dcdb092 100644 --- a/dom/base/nsGlobalWindowOuter.cpp +++ b/dom/base/nsGlobalWindowOuter.cpp @@ -99,7 +99,6 @@ // Other Classes #include "mozilla/dom/BarProps.h" -#include "nsContentCID.h" #include "nsLayoutStatics.h" #include "nsCCUncollectableMarker.h" #include "mozilla/dom/WorkerCommon.h" diff --git a/dom/base/nsIContent.h b/dom/base/nsIContent.h index de00012a01..700855370f 100644 --- a/dom/base/nsIContent.h +++ b/dom/base/nsIContent.h @@ -21,6 +21,7 @@ class HTMLEditor; struct URLExtraData; namespace dom { struct BindContext; +struct UnbindContext; class ShadowRoot; class HTMLSlotElement; } // namespace dom @@ -58,6 +59,7 @@ class nsIContent : public nsINode { using IMEEnabled = mozilla::widget::IMEEnabled; using IMEState = mozilla::widget::IMEState; using BindContext = mozilla::dom::BindContext; + using UnbindContext = mozilla::dom::UnbindContext; void ConstructUbiNode(void* storage) override; @@ -111,15 +113,10 @@ class nsIContent : public nsINode { * from a parent, this will be called after it has been removed from the * parent's child list and after the nsIDocumentObserver notifications for * the removal have been dispatched. - * @param aDeep Whether to recursively unbind the entire subtree rooted at - * this node. The only time false should be passed is when the - * parent node of the content is being destroyed. - * @param aNullParent Whether to null out the parent pointer as well. This - * is usually desirable. This argument should only be false while - * recursively calling UnbindFromTree when a subtree is detached. * @note This method is safe to call on nodes that are not bound to a tree. */ - virtual void UnbindFromTree(bool aNullParent = true) = 0; + virtual void UnbindFromTree(UnbindContext&) = 0; + void UnbindFromTree(); enum { /** diff --git a/dom/base/nsIDOMRequestService.idl b/dom/base/nsIDOMRequestService.idl deleted file mode 100644 index 4f6fcd5784..0000000000 --- a/dom/base/nsIDOMRequestService.idl +++ /dev/null @@ -1,21 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=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 "nsISupports.idl" - -interface mozIDOMWindow; -webidl DOMRequest; - -[scriptable, builtinclass, uuid(9a57e5de-ce93-45fa-8145-755722834f7c)] -interface nsIDOMRequestService : nsISupports -{ - DOMRequest createRequest(in mozIDOMWindow window); - - void fireSuccess(in DOMRequest request, in jsval result); - void fireError(in DOMRequest request, in AString error); - void fireSuccessAsync(in DOMRequest request, in jsval result); - void fireErrorAsync(in DOMRequest request, in AString error); -}; diff --git a/dom/base/nsINode.cpp b/dom/base/nsINode.cpp index 3a9fe23899..d5455e5596 100644 --- a/dom/base/nsINode.cpp +++ b/dom/base/nsINode.cpp @@ -16,6 +16,7 @@ #include "js/JSON.h" // JS_ParseJSON #include "mozAutoDocUpdate.h" #include "mozilla/AsyncEventDispatcher.h" +#include "mozilla/ClearOnShutdown.h" #include "mozilla/CORSMode.h" #include "mozilla/EventDispatcher.h" #include "mozilla/EventListenerManager.h" @@ -304,7 +305,7 @@ class IsItemInRangeComparator { // @param aStartOffset has to be less or equal to aEndOffset. IsItemInRangeComparator(const nsINode& aNode, const uint32_t aStartOffset, const uint32_t aEndOffset, - nsContentUtils::ComparePointsCache* aCache) + nsContentUtils::NodeIndexCache* aCache) : mNode(aNode), mStartOffset(aStartOffset), mEndOffset(aEndOffset), @@ -332,7 +333,7 @@ class IsItemInRangeComparator { const nsINode& mNode; const uint32_t mStartOffset; const uint32_t mEndOffset; - nsContentUtils::ComparePointsCache* mCache; + nsContentUtils::NodeIndexCache* mCache; }; bool nsINode::IsSelected(const uint32_t aStartOffset, @@ -367,7 +368,7 @@ bool nsINode::IsSelected(const uint32_t aStartOffset, } } - nsContentUtils::ComparePointsCache cache; + nsContentUtils::NodeIndexCache cache; IsItemInRangeComparator comparator{*this, aStartOffset, aEndOffset, &cache}; for (Selection* selection : ancestorSelections) { // Binary search the sorted ranges in this selection. @@ -1810,6 +1811,10 @@ Maybe<uint32_t> nsINode::ComputeIndexOf(const nsINode* aPossibleChild) const { return Nothing(); } + if (aPossibleChild == GetFirstChild()) { + return Some(0); + } + if (aPossibleChild == GetLastChild()) { MOZ_ASSERT(GetChildCount()); return Some(GetChildCount() - 1); @@ -3672,15 +3677,13 @@ already_AddRefed<nsINode> nsINode::CloneAndAdopt( } newShadowRoot->SetIsDeclarative(originalShadowRoot->IsDeclarative()); - if (aDeep) { - for (nsIContent* origChild = originalShadowRoot->GetFirstChild(); - origChild; origChild = origChild->GetNextSibling()) { - nsCOMPtr<nsINode> child = - CloneAndAdopt(origChild, aClone, aDeep, nodeInfoManager, - aReparentScope, newShadowRoot, aError); - if (NS_WARN_IF(aError.Failed())) { - return nullptr; - } + for (nsIContent* origChild = originalShadowRoot->GetFirstChild(); + origChild; origChild = origChild->GetNextSibling()) { + nsCOMPtr<nsINode> child = + CloneAndAdopt(origChild, aClone, true, nodeInfoManager, + aReparentScope, newShadowRoot, aError); + if (NS_WARN_IF(aError.Failed())) { + return nullptr; } } } diff --git a/dom/base/nsINode.h b/dom/base/nsINode.h index ce9fbd55be..3a47992cc8 100644 --- a/dom/base/nsINode.h +++ b/dom/base/nsINode.h @@ -1895,13 +1895,9 @@ class nsINode : public mozilla::dom::EventTarget { // flags, because we can't use those to distinguish // <bdi dir="some-invalid-value"> and <bdi dir="auto">. NodeHasValidDirAttribute, - // Set if the node has dir=auto and has a property pointing to the text - // node that determines its direction - NodeHasDirAutoSet, - // Set if the node is a text node descendant of a node with dir=auto - // and has a TextNodeDirectionalityMap property listing the elements whose - // direction it determines. - NodeHasTextNodeDirectionalityMap, + // Set if this node, which must be a text node, might be responsible for + // setting the directionality of a dir="auto" ancestor. + NodeMaySetDirAuto, // Set if a node in the node's parent chain has dir=auto. NodeAncestorHasDirAuto, // Set if the node is handling a click. @@ -2028,31 +2024,19 @@ class nsINode : public mozilla::dom::EventTarget { void SetHasValidDir() { SetBoolFlag(NodeHasValidDirAttribute); } void ClearHasValidDir() { ClearBoolFlag(NodeHasValidDirAttribute); } bool HasValidDir() const { return GetBoolFlag(NodeHasValidDirAttribute); } - void SetHasDirAutoSet() { - MOZ_ASSERT(NodeType() != TEXT_NODE, "SetHasDirAutoSet on text node"); - SetBoolFlag(NodeHasDirAutoSet); + void SetMaySetDirAuto() { + // FIXME(bug 1881225): dir=auto should probably work on CDATA too. + MOZ_ASSERT(NodeType() == TEXT_NODE); + SetBoolFlag(NodeMaySetDirAuto); } - void ClearHasDirAutoSet() { - MOZ_ASSERT(NodeType() != TEXT_NODE, "ClearHasDirAutoSet on text node"); - ClearBoolFlag(NodeHasDirAutoSet); + bool MaySetDirAuto() const { + MOZ_ASSERT(NodeType() == TEXT_NODE); + return GetBoolFlag(NodeMaySetDirAuto); } - bool HasDirAutoSet() const { return GetBoolFlag(NodeHasDirAutoSet); } - void SetHasTextNodeDirectionalityMap() { - MOZ_ASSERT(NodeType() == TEXT_NODE, - "SetHasTextNodeDirectionalityMap on non-text node"); - SetBoolFlag(NodeHasTextNodeDirectionalityMap); + void ClearMaySetDirAuto() { + MOZ_ASSERT(NodeType() == TEXT_NODE); + ClearBoolFlag(NodeMaySetDirAuto); } - void ClearHasTextNodeDirectionalityMap() { - MOZ_ASSERT(NodeType() == TEXT_NODE, - "ClearHasTextNodeDirectionalityMap on non-text node"); - ClearBoolFlag(NodeHasTextNodeDirectionalityMap); - } - bool HasTextNodeDirectionalityMap() const { - MOZ_ASSERT(NodeType() == TEXT_NODE, - "HasTextNodeDirectionalityMap on non-text node"); - return GetBoolFlag(NodeHasTextNodeDirectionalityMap); - } - void SetAncestorHasDirAuto() { SetBoolFlag(NodeAncestorHasDirAuto); } void ClearAncestorHasDirAuto() { ClearBoolFlag(NodeAncestorHasDirAuto); } bool AncestorHasDirAuto() const { diff --git a/dom/base/nsIScriptableContentIterator.idl b/dom/base/nsIScriptableContentIterator.idl index caf689f550..370cd8c8a7 100644 --- a/dom/base/nsIScriptableContentIterator.idl +++ b/dom/base/nsIScriptableContentIterator.idl @@ -64,11 +64,3 @@ interface nsIScriptableContentIterator : nsISupports // See ContentIteratorBase::PositionAt(nsINode*) void positionAt(in Node aNode); }; - -%{C++ -#define SCRIPTABLE_CONTENT_ITERATOR_CID \ - { 0xf68037ec, 0x2790, 0x44c5, \ - { 0x8e, 0x5f, 0xdf, 0x5d, 0xa5, 0x8b, 0x93, 0xa7 } } -#define SCRIPTABLE_CONTENT_ITERATOR_CONTRACTID \ - "@mozilla.org/scriptable-content-iterator;1" -%} diff --git a/dom/base/nsImageLoadingContent.cpp b/dom/base/nsImageLoadingContent.cpp index c1320a3472..fa1798ce35 100644 --- a/dom/base/nsImageLoadingContent.cpp +++ b/dom/base/nsImageLoadingContent.cpp @@ -47,6 +47,7 @@ #include "mozilla/dom/BindContext.h" #include "mozilla/dom/Document.h" #include "mozilla/dom/Element.h" +#include "mozilla/dom/FetchPriority.h" #include "mozilla/dom/PContent.h" // For TextRecognitionResult #include "mozilla/dom/HTMLImageElement.h" #include "mozilla/dom/ImageTextBinding.h" @@ -1143,7 +1144,8 @@ nsresult nsImageLoadingContent::LoadImage(nsIURI* aNewURI, bool aForce, nsresult rv = nsContentUtils::LoadImage( aNewURI, element, aDocument, triggeringPrincipal, 0, referrerInfo, this, loadFlags, element->LocalName(), getter_AddRefs(req), policyType, - mUseUrgentStartForChannel); + mUseUrgentStartForChannel, /* aLinkPreload */ false, + /* aEarlyHintPreloaderId */ 0, GetFetchPriorityForImage()); // Reset the flag to avoid loading from XPCOM or somewhere again else without // initiated by user interaction. @@ -1639,10 +1641,12 @@ void nsImageLoadingContent::BindToTree(BindContext& aContext, } } -void nsImageLoadingContent::UnbindFromTree(bool aNullParent) { +void nsImageLoadingContent::UnbindFromTree() { // We may be leaving the document, so if our image is tracked, untrack it. nsCOMPtr<Document> doc = GetOurCurrentDoc(); - if (!doc) return; + if (!doc) { + return; + } UntrackImage(mCurrentRequest); UntrackImage(mPendingRequest); @@ -1860,3 +1864,7 @@ nsLoadFlags nsImageLoadingContent::LoadFlags() { } return nsIRequest::LOAD_NORMAL; } + +FetchPriority nsImageLoadingContent::GetFetchPriorityForImage() const { + return FetchPriority::Auto; +} diff --git a/dom/base/nsImageLoadingContent.h b/dom/base/nsImageLoadingContent.h index 6929f20a23..2b6dac53fb 100644 --- a/dom/base/nsImageLoadingContent.h +++ b/dom/base/nsImageLoadingContent.h @@ -40,6 +40,7 @@ namespace dom { struct BindContext; class Document; class Element; +enum class FetchPriority : uint8_t; } // namespace dom } // namespace mozilla @@ -221,7 +222,7 @@ class nsImageLoadingContent : public nsIImageLoadingContent { // Subclasses are *required* to call BindToTree/UnbindFromTree. void BindToTree(mozilla::dom::BindContext&, nsINode& aParent); - void UnbindFromTree(bool aNullParent); + void UnbindFromTree(); void OnLoadComplete(imgIRequest* aRequest, nsresult aStatus); void OnUnlockedDraw(); @@ -236,6 +237,8 @@ class nsImageLoadingContent : public nsIImageLoadingContent { // want a non-const nsIContent. virtual nsIContent* AsContent() = 0; + virtual mozilla::dom::FetchPriority GetFetchPriorityForImage() const; + /** * Get width and height of the current request, using given image request if * attributes are unset. diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 1d2eb16885..1397bd25b5 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -2227,6 +2227,21 @@ void nsJSContext::EnsureStatics() { "javascript.options.mem.gc_max_helper_threads", (void*)JSGC_MAX_HELPER_THREADS); + Preferences::RegisterCallbackAndCall( + SetMemoryPrefChangedCallbackInt, + "javascript.options.mem.nursery_eager_collection_threshold_kb", + (void*)JSGC_NURSERY_EAGER_COLLECTION_THRESHOLD_KB); + + Preferences::RegisterCallbackAndCall( + SetMemoryPrefChangedCallbackInt, + "javascript.options.mem.nursery_eager_collection_threshold_percent", + (void*)JSGC_NURSERY_EAGER_COLLECTION_THRESHOLD_PERCENT); + + Preferences::RegisterCallbackAndCall( + SetMemoryPrefChangedCallbackInt, + "javascript.options.mem.nursery_eager_collection_timeout_ms", + (void*)JSGC_NURSERY_EAGER_COLLECTION_TIMEOUT_MS); + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); if (!obs) { MOZ_CRASH(); diff --git a/dom/base/nsObjectLoadingContent.cpp b/dom/base/nsObjectLoadingContent.cpp index 0a2ea3e62d..ab87c58e87 100644 --- a/dom/base/nsObjectLoadingContent.cpp +++ b/dom/base/nsObjectLoadingContent.cpp @@ -223,7 +223,7 @@ already_AddRefed<nsIDocShell> nsObjectLoadingContent::SetupDocShell( return docShell.forget(); } -void nsObjectLoadingContent::UnbindFromTree(bool aNullParent) { +void nsObjectLoadingContent::UnbindFromTree() { // Reset state and clear pending events /// XXX(johns): The implementation for GenericFrame notes that ideally we /// would keep the docshell around, but trash the frameloader @@ -1249,7 +1249,11 @@ nsresult nsObjectLoadingContent::LoadObject(bool aNotify, bool aForceLoad, break; } - rv = uriLoader->OpenChannel(mChannel, nsIURILoader::DONT_RETARGET, req, + uint32_t uriLoaderFlags = nsDocShell::ComputeURILoaderFlags( + docShell->GetBrowsingContext(), LOAD_NORMAL, + /* aIsDocumentLoad */ false); + + rv = uriLoader->OpenChannel(mChannel, uriLoaderFlags, req, getter_AddRefs(finalListener)); // finalListener will receive OnStartRequest either below, or if // `mChannel` is a `DocumentChannel`, it will be received after diff --git a/dom/base/nsObjectLoadingContent.h b/dom/base/nsObjectLoadingContent.h index 563ad4df3f..c679a7cc5d 100644 --- a/dom/base/nsObjectLoadingContent.h +++ b/dom/base/nsObjectLoadingContent.h @@ -183,7 +183,7 @@ class nsObjectLoadingContent : public nsIStreamListener, void CreateStaticClone(nsObjectLoadingContent* aDest) const; - void UnbindFromTree(bool aNullParent = true); + void UnbindFromTree(); /** * Return the content policy type used for loading the element. diff --git a/dom/base/nsTextNode.cpp b/dom/base/nsTextNode.cpp index df08c547be..e34b581c40 100644 --- a/dom/base/nsTextNode.cpp +++ b/dom/base/nsTextNode.cpp @@ -43,13 +43,13 @@ class nsAttributeTextNode final : public nsTextNode, NS_ASSERTION(mAttrName, "Must have attr name"); } - virtual nsresult BindToTree(BindContext&, nsINode& aParent) override; - virtual void UnbindFromTree(bool aNullParent = true) override; + nsresult BindToTree(BindContext&, nsINode& aParent) override; + void UnbindFromTree(UnbindContext&) override; NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED - virtual already_AddRefed<CharacterData> CloneDataNode( + already_AddRefed<CharacterData> CloneDataNode( mozilla::dom::NodeInfo* aNodeInfo, bool aCloneText) const override { RefPtr<nsAttributeTextNode> it = new (aNodeInfo->NodeInfoManager()) nsAttributeTextNode( @@ -123,10 +123,9 @@ nsresult nsTextNode::BindToTree(BindContext& aContext, nsINode& aParent) { return NS_OK; } -void nsTextNode::UnbindFromTree(bool aNullParent) { - ResetDirectionSetByTextNode(this); - - CharacterData::UnbindFromTree(aNullParent); +void nsTextNode::UnbindFromTree(UnbindContext& aContext) { + CharacterData::UnbindFromTree(aContext); + ResetDirectionSetByTextNode(this, aContext); } #ifdef MOZ_DOM_LIST @@ -209,16 +208,16 @@ nsresult nsAttributeTextNode::BindToTree(BindContext& aContext, return NS_OK; } -void nsAttributeTextNode::UnbindFromTree(bool aNullParent) { +void nsAttributeTextNode::UnbindFromTree(UnbindContext& aContext) { // UnbindFromTree can be called anytime so we have to be safe. if (mGrandparent) { - // aNullParent might not be true here, but we want to remove the + // aContext might not be true here, but we want to remove the // mutation observer anyway since we only need it while we're // in the document. mGrandparent->RemoveMutationObserver(this); mGrandparent = nullptr; } - nsTextNode::UnbindFromTree(aNullParent); + nsTextNode::UnbindFromTree(aContext); } void nsAttributeTextNode::AttributeChanged(Element* aElement, diff --git a/dom/base/nsTextNode.h b/dom/base/nsTextNode.h index 23d7aff8a8..b39497a3b1 100644 --- a/dom/base/nsTextNode.h +++ b/dom/base/nsTextNode.h @@ -45,7 +45,7 @@ class nsTextNode : public mozilla::dom::Text { mozilla::dom::NodeInfo* aNodeInfo, bool aCloneText) const override; nsresult BindToTree(BindContext&, nsINode& aParent) override; - void UnbindFromTree(bool aNullParent = true) override; + void UnbindFromTree(UnbindContext&) override; nsresult AppendTextForNormalize(const char16_t* aBuffer, uint32_t aLength, bool aNotify, nsIContent* aNextSibling); diff --git a/dom/base/nsWindowRoot.cpp b/dom/base/nsWindowRoot.cpp index 1ee2368f1e..1874898b68 100644 --- a/dom/base/nsWindowRoot.cpp +++ b/dom/base/nsWindowRoot.cpp @@ -13,8 +13,6 @@ #include "nsWindowRoot.h" #include "nsPIDOMWindow.h" #include "nsPresContext.h" -#include "nsLayoutCID.h" -#include "nsContentCID.h" #include "nsString.h" #include "nsFrameLoaderOwner.h" #include "nsFrameLoader.h" diff --git a/dom/base/test/browser.toml b/dom/base/test/browser.toml index 1deeb88d89..a68bd2e873 100644 --- a/dom/base/test/browser.toml +++ b/dom/base/test/browser.toml @@ -109,6 +109,16 @@ skip-if = [ ] support-files = ["browser_multiple_popups.html"] +["browser_object_attachment.js"] +support-files = [ + "file_img_object_attachment.html", + "file_img_attachment.jpg", + "file_img_attachment.jpg^headers^", + "file_pdf_object_attachment.html", + "file_pdf_attachment.pdf", + "file_pdf_attachment.pdf^headers^", +] + ["browser_outline_refocus.js"] ["browser_page_load_event_telemetry.js"] diff --git a/dom/base/test/browser_object_attachment.js b/dom/base/test/browser_object_attachment.js new file mode 100644 index 0000000000..b4432862f0 --- /dev/null +++ b/dom/base/test/browser_object_attachment.js @@ -0,0 +1,168 @@ +ChromeUtils.defineESModuleGetters(this, { + Downloads: "resource://gre/modules/Downloads.sys.mjs", +}); + +const httpsTestRoot = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "https://example.com" +); + +add_task(async function test_pdf_object_attachment() { + await SpecialPowers.pushPrefEnv({ + set: [["dom.navigation.object_embed.allow_retargeting", false]], + }); + + await BrowserTestUtils.withNewTab( + `${httpsTestRoot}/file_pdf_object_attachment.html`, + async browser => { + is( + browser.browsingContext.children.length, + 1, + "Should have a child frame" + ); + await SpecialPowers.spawn(browser, [], async () => { + let obj = content.document.querySelector("object"); + is( + obj.displayedType, + Ci.nsIObjectLoadingContent.TYPE_DOCUMENT, + "should be displaying TYPE_DOCUMENT" + ); + }); + } + ); +}); + +add_task(async function test_img_object_attachment() { + await SpecialPowers.pushPrefEnv({ + set: [["dom.navigation.object_embed.allow_retargeting", false]], + }); + + await BrowserTestUtils.withNewTab( + `${httpsTestRoot}/file_img_object_attachment.html`, + async browser => { + is( + browser.browsingContext.children.length, + 1, + "Should have a child frame" + ); + await SpecialPowers.spawn(browser, [], async () => { + let obj = content.document.querySelector("object"); + is( + obj.displayedType, + Ci.nsIObjectLoadingContent.TYPE_DOCUMENT, + "should be displaying TYPE_DOCUMENT" + ); + }); + } + ); +}); + +async function waitForDownload() { + // Get the downloads list and add a view to listen for a download to be added. + let downloadList = await Downloads.getList(Downloads.ALL); + + // Wait for a single download + let downloadView; + let finishedAllDownloads = new Promise(resolve => { + downloadView = { + onDownloadAdded(aDownload) { + info("download added"); + resolve(aDownload); + }, + }; + }); + await downloadList.addView(downloadView); + let download = await finishedAllDownloads; + await downloadList.removeView(downloadView); + + // Clean up the download from the list. + await downloadList.remove(download); + await download.finalize(true); + + // Return the download + return download; +} + +add_task(async function test_pdf_object_attachment_download() { + await SpecialPowers.pushPrefEnv({ + set: [["dom.navigation.object_embed.allow_retargeting", true]], + }); + + // Set the behaviour to save pdfs to disk and not handle internally, so we + // don't end up with extra tabs after the test. + var gMimeSvc = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); + var gHandlerSvc = Cc["@mozilla.org/uriloader/handler-service;1"].getService( + Ci.nsIHandlerService + ); + const mimeInfo = gMimeSvc.getFromTypeAndExtension("application/pdf", "pdf"); + let previousAction = mimeInfo.preferredAction; + mimeInfo.preferredAction = Ci.nsIHandlerInfo.saveToDisk; + gHandlerSvc.store(mimeInfo); + registerCleanupFunction(() => { + mimeInfo.preferredAction = previousAction; + gHandlerSvc.store(mimeInfo); + }); + + // Start listening for the download before opening the new tab. + let downloadPromise = waitForDownload(); + await BrowserTestUtils.withNewTab( + `${httpsTestRoot}/file_pdf_object_attachment.html`, + async browser => { + let download = await downloadPromise; + is( + download.source.url, + `${httpsTestRoot}/file_pdf_attachment.pdf`, + "download should be the pdf" + ); + + await SpecialPowers.spawn(browser, [], async () => { + let obj = content.document.querySelector("object"); + is( + obj.displayedType, + Ci.nsIObjectLoadingContent.TYPE_FALLBACK, + "should be displaying TYPE_FALLBACK" + ); + }); + } + ); +}); + +add_task(async function test_img_object_attachment_download() { + // NOTE: This is testing our current behaviour here as of bug 1868001 (which + // is to download an image with `Content-Disposition: attachment` embedded + // within an object or embed element). + // + // Other browsers ignore the `Content-Disposition: attachment` header when + // loading images within object or embed element as-of december 2023, as + // we did prior to the changes in bug 1595491. + // + // If this turns out to be a web-compat issue, we may want to introduce + // special handling to ignore content-disposition when loading images within + // an object or embed element. + await SpecialPowers.pushPrefEnv({ + set: [["dom.navigation.object_embed.allow_retargeting", true]], + }); + + // Start listening for the download before opening the new tab. + let downloadPromise = waitForDownload(); + await BrowserTestUtils.withNewTab( + `${httpsTestRoot}/file_img_object_attachment.html`, + async browser => { + let download = await downloadPromise; + is( + download.source.url, + `${httpsTestRoot}/file_img_attachment.jpg`, + "download should be the jpg" + ); + + await SpecialPowers.spawn(browser, [], async () => { + let obj = content.document.querySelector("object"); + is( + obj.displayedType, + Ci.nsIObjectLoadingContent.TYPE_FALLBACK, + "should be displaying TYPE_FALLBACK" + ); + }); + } + ); +}); diff --git a/dom/base/test/chrome.toml b/dom/base/test/chrome.toml index 687d778cac..ec736a086c 100644 --- a/dom/base/test/chrome.toml +++ b/dom/base/test/chrome.toml @@ -1,6 +1,5 @@ [DEFAULT] skip-if = ["os == 'android'"] -prefs = ["dom.domrequest.enabled=true"] support-files = [ "file_empty.html", "file_blocking_image.html", @@ -38,8 +37,6 @@ support-files = [ ["test_bug1120222.html"] -["test_domrequesthelper.xhtml"] - ["test_fragment_sanitization.xhtml"] ["test_getLastOverWindowPointerLocationInCSSPixels.html"] diff --git a/dom/base/test/chrome/bug418986-1.js b/dom/base/test/chrome/bug418986-1.js index 7c39df0c13..e7e3c63b5c 100644 --- a/dom/base/test/chrome/bug418986-1.js +++ b/dom/base/test/chrome/bug418986-1.js @@ -1,4 +1,7 @@ /* globals chromeWindow */ + +/* eslint-disable mozilla/no-comparison-or-assignment-inside-ok */ + // The main test function. var test = function (isContent) { SimpleTest.waitForExplicitFinish(); diff --git a/dom/base/test/chrome/chrome.toml b/dom/base/test/chrome/chrome.toml index b8439a2d2e..08265bcb97 100644 --- a/dom/base/test/chrome/chrome.toml +++ b/dom/base/test/chrome/chrome.toml @@ -18,7 +18,6 @@ support-files = [ "custom_element_ep.js", "window_nsITextInputProcessor.xhtml", "title_window.xhtml", - "window_swapFrameLoaders.xhtml", ] prefs = ["gfx.font_rendering.fallback.async=false"] @@ -126,9 +125,6 @@ support-files = ["../dummy.html"] ["test_range_getClientRectsAndTexts.html"] -["test_swapFrameLoaders.xhtml"] -skip-if = ["os == 'mac'"] # bug 1674413 - ["test_title.xhtml"] support-files = ["file_title.xhtml"] diff --git a/dom/base/test/chrome/file_bug549682.xhtml b/dom/base/test/chrome/file_bug549682.xhtml index 8ae05d38d8..0bb3080507 100644 --- a/dom/base/test/chrome/file_bug549682.xhtml +++ b/dom/base/test/chrome/file_bug549682.xhtml @@ -28,11 +28,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=549682 } var asyncPPML = false; - function ppmASL(m) { + function ppmASL() { asyncPPML = true; } var syncPPML = false; - function ppmSL(m) { + function ppmSL() { syncPPML = true; } ppm.addMessageListener("processmessageAsync", ppmASL); @@ -42,7 +42,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=549682 cpm.sendSyncMessage("processmessageSync", ""); var asyncCPML = false; - function cpmASL(m) { + function cpmASL() { asyncCPML = true; } cpm.addMessageListener("childprocessmessage", cpmASL); @@ -93,7 +93,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=549682 var weakListener = { QueryInterface: ChromeUtils.generateQI(["nsISupportsWeakReference"]), - receiveMessage(msg) { + receiveMessage() { if (weakMessageReceived) { ok(false, 'Weak listener fired twice.'); return; @@ -109,7 +109,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=549682 var weakListener2 = { QueryInterface: ChromeUtils.generateQI(["nsISupportsWeakReference"]), - receiveMessage(msg) { + receiveMessage() { ok(false, 'Should not have received a message.'); } }; diff --git a/dom/base/test/chrome/file_bug616841.xhtml b/dom/base/test/chrome/file_bug616841.xhtml index b0512d162c..3651a00226 100644 --- a/dom/base/test/chrome/file_bug616841.xhtml +++ b/dom/base/test/chrome/file_bug616841.xhtml @@ -27,7 +27,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=616841 [ "D", "\u010C" ] ]; var nCmps = 0; - function recvContentReady(m) { + function recvContentReady() { for (var i = 0; i < toCompare.length; ++i) { var pair = toCompare[i]; messageManager.broadcastAsyncMessage("cmp", diff --git a/dom/base/test/chrome/test_bug1339722.html b/dom/base/test/chrome/test_bug1339722.html index d8d95f1faa..7655ff95fa 100644 --- a/dom/base/test/chrome/test_bug1339722.html +++ b/dom/base/test/chrome/test_bug1339722.html @@ -29,7 +29,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1339722 // behave similarly. const TOPIC = "document-on-modify-request"; let win; - const observe = (subject, topic, data) => { + const observe = (subject, topic) => { info("Got " + topic); Services.obs.removeObserver(observe, TOPIC); @@ -58,7 +58,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1339722 // Remove the iframe to cause frameloader destroy. iframe.remove(); - setTimeout($ => { + setTimeout(() => { ok(!document.getElementById("testFrame"), "verify iframe removed"); SimpleTest.finish(); }, 0); diff --git a/dom/base/test/chrome/test_bug339494.xhtml b/dom/base/test/chrome/test_bug339494.xhtml index 203f6e644d..afab41b65c 100644 --- a/dom/base/test/chrome/test_bug339494.xhtml +++ b/dom/base/test/chrome/test_bug339494.xhtml @@ -55,7 +55,7 @@ SimpleTest.waitForExplicitFinish(); s.setAttribute("ggg", "testvalue"); await promiseFlushingMutationObserver(); - const observer = new MutationObserver((aMutationList, aObserver) => { + const observer = new MutationObserver(() => { ok(s.hasAttribute("ggg"), "Value check 3. There should be a value"); isnot(s.getAttribute("ggg"), "testvalue", "Value check 4"); is(s.getAttribute("ggg"), "othervalue", "Value check 5"); diff --git a/dom/base/test/chrome/test_bug429785.xhtml b/dom/base/test/chrome/test_bug429785.xhtml index fb51634fab..10c9977ccb 100644 --- a/dom/base/test/chrome/test_bug429785.xhtml +++ b/dom/base/test/chrome/test_bug429785.xhtml @@ -21,7 +21,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=429785 var errorLogged = false; var listener = { QueryInterface: ChromeUtils.generateQI(["nsIConsoleListener"]), - observe(msg) { errorLogged = true; } + observe() { errorLogged = true; } }; function step2() { diff --git a/dom/base/test/chrome/test_bug430050.xhtml b/dom/base/test/chrome/test_bug430050.xhtml index d7d6cf656c..dfe1e3c8ee 100644 --- a/dom/base/test/chrome/test_bug430050.xhtml +++ b/dom/base/test/chrome/test_bug430050.xhtml @@ -27,7 +27,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=430050 } function startTest() { - const observer = new MutationObserver((aMutationList, aObserver) => { + const observer = new MutationObserver(() => { document.getElementById('b').setAttribute("src", "data:text/plain,failed"); const systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal(); diff --git a/dom/base/test/chrome/test_chromeOuterWindowID.xhtml b/dom/base/test/chrome/test_chromeOuterWindowID.xhtml index 1feb7c7c74..3aa482b636 100644 --- a/dom/base/test/chrome/test_chromeOuterWindowID.xhtml +++ b/dom/base/test/chrome/test_chromeOuterWindowID.xhtml @@ -42,7 +42,7 @@ windows. "Both browsers should belong to the same document."); let winID = getOuterWindowID(browser1.ownerGlobal); - let getChildRootOuterId = browser => { + let getChildRootOuterId = () => { try { return docShell.browserChild?.chromeOuterWindowID; } catch(ex) { } diff --git a/dom/base/test/chrome/test_swapFrameLoaders.xhtml b/dom/base/test/chrome/test_swapFrameLoaders.xhtml deleted file mode 100644 index 4ea11a1a62..0000000000 --- a/dom/base/test/chrome/test_swapFrameLoaders.xhtml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0"?> -<?xml-stylesheet type="text/css" href="chrome://global/skin"?> -<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=1242644 -Test swapFrameLoaders with different frame types and remoteness ---> -<window title="Mozilla Bug 1242644" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> - - <!-- test results are displayed in the html:body --> - <body xmlns="http://www.w3.org/1999/xhtml"> - <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1242644" - target="_blank">Mozilla Bug 1242644</a> - </body> - - <!-- test code goes here --> - <script type="application/javascript"><![CDATA[ - SimpleTest.waitForExplicitFinish(); - - window.openDialog("window_swapFrameLoaders.xhtml", "bug1242644", - "chrome,width=600,height=600,noopener", window); - ]]></script> -</window> diff --git a/dom/base/test/chrome/title_window.xhtml b/dom/base/test/chrome/title_window.xhtml index f48cdaaaf1..5f9840c36c 100644 --- a/dom/base/test/chrome/title_window.xhtml +++ b/dom/base/test/chrome/title_window.xhtml @@ -63,10 +63,10 @@ } } - function listener2(ev) { + function listener2() { inProgressDoc[description] = false; } - function listener3(ev) { + function listener3() { inProgressWin[description] = false; } frame.addEventListener("DOMTitleChanged", listener); diff --git a/dom/base/test/chrome/window_nsITextInputProcessor.xhtml b/dom/base/test/chrome/window_nsITextInputProcessor.xhtml index c8ce6ee5e7..c62ba2ce47 100644 --- a/dom/base/test/chrome/window_nsITextInputProcessor.xhtml +++ b/dom/base/test/chrome/window_nsITextInputProcessor.xhtml @@ -4120,7 +4120,7 @@ function runUnloadTests1() let oldSrc = iframe.src; let parentWindow = window; - iframe.addEventListener("load", function (aEvent) { + iframe.addEventListener("load", function () { ok(true, description + "dummy page is loaded"); childWindow = iframe.contentWindow; textareaInFrame = null; @@ -4181,7 +4181,7 @@ function runUnloadTests2() let oldSrc = iframe.src; - iframe.addEventListener("load", function (aEvent) { + iframe.addEventListener("load", function () { ok(true, description + "dummy page is loaded"); childWindow = iframe.contentWindow; textareaInFrame = null; diff --git a/dom/base/test/chrome/window_swapFrameLoaders.xhtml b/dom/base/test/chrome/window_swapFrameLoaders.xhtml deleted file mode 100644 index 4a38bcc1fc..0000000000 --- a/dom/base/test/chrome/window_swapFrameLoaders.xhtml +++ /dev/null @@ -1,223 +0,0 @@ -<?xml version="1.0"?> -<?xml-stylesheet type="text/css" href="chrome://global/skin"?> -<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=1242644 -Test swapFrameLoaders with different frame types and remoteness ---> -<window title="Mozilla Bug 1242644" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - - <script type="application/javascript"><![CDATA[ - ["SimpleTest", "SpecialPowers", "info", "is", "ok", "add_task"].forEach(key => { - window[key] = window.arguments[0][key]; - }) - - const NS = { - xul: "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", - html: "http://www.w3.org/1999/xhtml", - } - - const TAG = { - xul: "browser", - html: "iframe", // mozbrowser - } - - const SCENARIOS = [ - ["xul", "xul"], - ["xul", "html"], - ["html", "xul"], - ["html", "html"], - ["xul", "xul", { remote: true }], - ["xul", "html", { remote: true }], - ["html", "xul", { remote: true }], - ["html", "html", { remote: true }], - ["xul", "html", { userContextId: 2 }], - ["xul", "html", { userContextId: 2, remote: true }], - ]; - - const HEIGHTS = [ - 200, - 400 - ]; - - function frameScript() { - /* eslint-env mozilla/frame-script */ - addEventListener("load", function onLoad() { - sendAsyncMessage("test:load"); - }, true); - } - - // Watch for loads in new frames - window.messageManager.loadFrameScript(`data:,(${frameScript})();`, true); - - function once(target, eventName, useCapture = false) { - info("Waiting for event: '" + eventName + "' on " + target + "."); - - return new Promise(resolve => { - for (let [add, remove] of [ - ["addEventListener", "removeEventListener"], - ["addMessageListener", "removeMessageListener"], - ]) { - if ((add in target) && (remove in target)) { - target[add](eventName, function onEvent(...aArgs) { - info("Got event: '" + eventName + "' on " + target + "."); - target[remove](eventName, onEvent, useCapture); - resolve(aArgs); - }, useCapture); - break; - } - } - }); - } - - async function addFrame(type, options, height) { - let remote = options && options.remote; - let userContextId = options && options.userContextId; - let frame = document.createElementNS(NS[type], TAG[type]); - frame.setAttribute("remote", remote); - if (remote && type == "xul") { - frame.setAttribute("style", "-moz-binding: none;"); - } - if (userContextId) { - frame.setAttribute("usercontextid", userContextId); - } - if (type == "html") { - frame.setAttribute("mozbrowser", "true"); - frame.setAttribute("noisolation", "true"); - frame.setAttribute("allowfullscreen", "true"); - } else if (type == "xul") { - frame.setAttribute("type", "content"); - } - let src = `data:text/html,<!doctype html>` + - `<body style="height:${height}px"/>`; - frame.setAttribute("src", src); - document.documentElement.appendChild(frame); - let mm = frame.frameLoader.messageManager; - await once(mm, "test:load"); - return frame; - } - - add_task(async function() { - for (let scenario of SCENARIOS) { - let [ typeA, typeB, options ] = scenario; - let heightA = HEIGHTS[0]; - info(`Adding frame A, type ${typeA}, options ${JSON.stringify(options)}, height ${heightA}`); - let frameA = await addFrame(typeA, options, heightA); - - let heightB = HEIGHTS[1]; - info(`Adding frame B, type ${typeB}, options ${JSON.stringify(options)}, height ${heightB}`); - let frameB = await addFrame(typeB, options, heightB); - - let frameScriptFactory = function(name) { - /* eslint-env mozilla/frame-script */ - return `function() { - addMessageListener("ping", function() { - sendAsyncMessage("pong", "${name}"); - }); - addMessageListener("check-browser-api", function() { - let exists = "api" in this; - sendAsyncMessage("check-browser-api", { - exists, - running: exists && !this.api._shuttingDown, - }); - }); - addEventListener("pagehide", function({ inFrameSwap }) { - sendAsyncMessage("pagehide", inFrameSwap); - }, {mozSystemGroup: true}); - }`; - } - - // Load frame script into each frame - { - let mmA = frameA.frameLoader.messageManager; - let mmB = frameB.frameLoader.messageManager; - - mmA.loadFrameScript("data:,(" + frameScriptFactory("A") + ")()", false); - mmB.loadFrameScript("data:,(" + frameScriptFactory("B") + ")()", false); - } - - // Ping before swap - { - let mmA = frameA.frameLoader.messageManager; - let mmB = frameB.frameLoader.messageManager; - - let inflightA = once(mmA, "pong"); - let inflightB = once(mmB, "pong"); - - info("Ping message manager for frame A"); - mmA.sendAsyncMessage("ping"); - let [ { data: pongA } ] = await inflightA; - is(pongA, "A", "Frame A message manager gets reply A before swap"); - - info("Ping message manager for frame B"); - mmB.sendAsyncMessage("ping"); - let [ { data: pongB } ] = await inflightB; - is(pongB, "B", "Frame B message manager gets reply B before swap"); - } - - // Ping after swap using message managers acquired before - { - let mmA = frameA.frameLoader.messageManager; - let mmB = frameB.frameLoader.messageManager; - - let pagehideA = once(mmA, "pagehide"); - let pagehideB = once(mmB, "pagehide"); - - info("swapFrameLoaders"); - frameA.swapFrameLoaders(frameB); - - let [ { data: inFrameSwapA } ] = await pagehideA; - ok(inFrameSwapA, "Frame A got pagehide with inFrameSwap: true"); - let [ { data: inFrameSwapB } ] = await pagehideB; - ok(inFrameSwapB, "Frame B got pagehide with inFrameSwap: true"); - - let inflightA = once(mmA, "pong"); - let inflightB = once(mmB, "pong"); - - info("Ping message manager for frame A"); - mmA.sendAsyncMessage("ping"); - let [ { data: pongA } ] = await inflightA; - is(pongA, "B", "Frame A message manager acquired before swap gets reply B after swap"); - - info("Ping message manager for frame B"); - mmB.sendAsyncMessage("ping"); - let [ { data: pongB } ] = await inflightB; - is(pongB, "A", "Frame B message manager acquired before swap gets reply A after swap"); - } - - // Check height after swap - if (frameA.getContentDimensions) { - let { height } = await frameA.getContentDimensions(); - is(height, heightB, "Frame A's content height is 400px after swap"); - } - if (frameB.getContentDimensions) { - let { height } = await frameB.getContentDimensions(); - is(height, heightA, "Frame B's content height is 200px after swap"); - } - - // Ping after swap using message managers acquired after - { - let mmA = frameA.frameLoader.messageManager; - let mmB = frameB.frameLoader.messageManager; - - let inflightA = once(mmA, "pong"); - let inflightB = once(mmB, "pong"); - - info("Ping message manager for frame A"); - mmA.sendAsyncMessage("ping"); - let [ { data: pongA } ] = await inflightA; - is(pongA, "B", "Frame A message manager acquired after swap gets reply B after swap"); - - info("Ping message manager for frame B"); - mmB.sendAsyncMessage("ping"); - let [ { data: pongB } ] = await inflightB; - is(pongB, "A", "Frame B message manager acquired after swap gets reply A after swap"); - } - - frameA.remove(); - frameB.remove(); - } - }); - ]]></script> -</window> diff --git a/dom/base/test/common_postMessages.js b/dom/base/test/common_postMessages.js index c4836fdd77..044f3e7f34 100644 --- a/dom/base/test/common_postMessages.js +++ b/dom/base/test/common_postMessages.js @@ -1,3 +1,5 @@ +/* eslint-disable mozilla/no-comparison-or-assignment-inside-ok */ + function getType(a) { if (a === null || a === undefined) { return "null"; diff --git a/dom/base/test/file_bug1008126_worker.js b/dom/base/test/file_bug1008126_worker.js index aaba278de5..4e418d777e 100644 --- a/dom/base/test/file_bug1008126_worker.js +++ b/dom/base/test/file_bug1008126_worker.js @@ -3,6 +3,8 @@ * http://creativecommons.org/publicdomain/zero/1.0/ */ +/* eslint-disable mozilla/no-comparison-or-assignment-inside-ok */ + var gEntry1 = "data_1.txt"; var gEntry2 = "data_2.txt"; var gEntry3 = "data_big.txt"; diff --git a/dom/base/test/file_bug945152_worker.js b/dom/base/test/file_bug945152_worker.js index 9664045b6d..c0feebf0d6 100644 --- a/dom/base/test/file_bug945152_worker.js +++ b/dom/base/test/file_bug945152_worker.js @@ -1,3 +1,4 @@ +/* eslint-disable mozilla/no-comparison-or-assignment-inside-ok */ var gData1 = "TEST_DATA_1:ABCDEFGHIJKLMNOPQRSTUVWXYZ"; var gData2 = "TEST_DATA_2:1234567890"; var gPaddingChar = "."; diff --git a/dom/base/test/file_focus_shadow_dom.html b/dom/base/test/file_focus_shadow_dom.html index 6fa9d1b88e..7a0a0da729 100644 --- a/dom/base/test/file_focus_shadow_dom.html +++ b/dom/base/test/file_focus_shadow_dom.html @@ -79,10 +79,16 @@ opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (3)"); synthesizeKey("KEY_Tab"); opener.is(lastFocusTarget, shadowDate, "Should have focused date element with a calendar button in shadow DOM. (3)"); - synthesizeKey("KEY_Tab"); - opener.is(shadowIframe.contentDocument.activeElement, - shadowIframe.contentDocument.documentElement, - "Should have focused document element in shadow iframe. (3)"); + + let canTabMoveFocusToRootElement = + !SpecialPowers.getBoolPref("dom.disable_tab_focus_to_root_element"); + if (canTabMoveFocusToRootElement) { + synthesizeKey("KEY_Tab"); + opener.is(shadowIframe.contentDocument.activeElement, + shadowIframe.contentDocument.documentElement, + "Should have focused document element in shadow iframe. (3)"); + } + synthesizeKey("KEY_Tab"); opener.is(shadowIframe.contentDocument.activeElement, shadowIframe.contentDocument.body.firstChild, @@ -99,10 +105,12 @@ opener.is(shadowIframe.contentDocument.activeElement, shadowIframe.contentDocument.body.firstChild, "Should have focused input element in shadow iframe. (4)"); - synthesizeKey("KEY_Tab", {shiftKey: true}); - opener.is(shadowIframe.contentDocument.activeElement, - shadowIframe.contentDocument.documentElement, - "Should have focused document element in shadow iframe. (4)"); + if (canTabMoveFocusToRootElement) { + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(shadowIframe.contentDocument.activeElement, + shadowIframe.contentDocument.documentElement, + "Should have focused document element in shadow iframe. (4)"); + } synthesizeKey("KEY_Tab", {shiftKey: true}); opener.is(lastFocusTarget, shadowDate, "Should have focused date element with a calendar button in shadow DOM. (4)"); synthesizeKey("KEY_Tab", {shiftKey: true}); diff --git a/dom/base/test/file_img_attachment.jpg b/dom/base/test/file_img_attachment.jpg Binary files differnew file mode 100644 index 0000000000..dcd99b9670 --- /dev/null +++ b/dom/base/test/file_img_attachment.jpg diff --git a/dom/base/test/file_img_attachment.jpg^headers^ b/dom/base/test/file_img_attachment.jpg^headers^ new file mode 100644 index 0000000000..71ed8e7805 --- /dev/null +++ b/dom/base/test/file_img_attachment.jpg^headers^ @@ -0,0 +1 @@ +Content-Disposition: attachment diff --git a/dom/base/test/file_img_object_attachment.html b/dom/base/test/file_img_object_attachment.html new file mode 100644 index 0000000000..502894041e --- /dev/null +++ b/dom/base/test/file_img_object_attachment.html @@ -0,0 +1,6 @@ +<!DOCTYPE html> +<html> + <body> + <object data="file_img_attachment.jpg" type="image/jpeg" width="100%" height="600">fallback</object> + </body> +</html> diff --git a/dom/base/test/file_pdf_attachment.pdf b/dom/base/test/file_pdf_attachment.pdf Binary files differnew file mode 100644 index 0000000000..89066463f1 --- /dev/null +++ b/dom/base/test/file_pdf_attachment.pdf diff --git a/dom/base/test/file_pdf_attachment.pdf^headers^ b/dom/base/test/file_pdf_attachment.pdf^headers^ new file mode 100644 index 0000000000..562009a8de --- /dev/null +++ b/dom/base/test/file_pdf_attachment.pdf^headers^ @@ -0,0 +1,2 @@ +Content-Type: application/octet-stream +Content-Disposition: attachment diff --git a/dom/base/test/file_pdf_object_attachment.html b/dom/base/test/file_pdf_object_attachment.html new file mode 100644 index 0000000000..d87eff9923 --- /dev/null +++ b/dom/base/test/file_pdf_object_attachment.html @@ -0,0 +1,6 @@ +<!DOCTYPE html> +<html> + <body> + <object data="file_pdf_attachment.pdf" type="application/pdf" width="100%" height="600">fallback</object> + </body> +</html> diff --git a/dom/base/test/fullscreen/MozDomFullscreen_chrome.xhtml b/dom/base/test/fullscreen/MozDomFullscreen_chrome.xhtml index 93f00311e7..bc92abf3e0 100644 --- a/dom/base/test/fullscreen/MozDomFullscreen_chrome.xhtml +++ b/dom/base/test/fullscreen/MozDomFullscreen_chrome.xhtml @@ -79,7 +79,7 @@ function thirdEntry(event) { gOuterDoc.exitFullscreen(); } -function earlyExit(event) { +function earlyExit() { ok(false, "MozDOMFullscreen:Exited should only be triggered after cancel all fullscreen"); } diff --git a/dom/base/test/fullscreen/browser_fullscreen-navigation-history-race.js b/dom/base/test/fullscreen/browser_fullscreen-navigation-history-race.js index 2ea2b9ee40..49a48c3177 100644 --- a/dom/base/test/fullscreen/browser_fullscreen-navigation-history-race.js +++ b/dom/base/test/fullscreen/browser_fullscreen-navigation-history-race.js @@ -82,7 +82,7 @@ function preventBFCache(aBrowsingContext, aPrevent) { let target = content.document.getElementById("div"); target.addEventListener( "mousedown", - function (e) { + function () { content.window.history.back(); }, { once: true } diff --git a/dom/base/test/fullscreen/browser_fullscreen-tab-close-race.js b/dom/base/test/fullscreen/browser_fullscreen-tab-close-race.js index 10d10a0b0f..1338c4a550 100644 --- a/dom/base/test/fullscreen/browser_fullscreen-tab-close-race.js +++ b/dom/base/test/fullscreen/browser_fullscreen-tab-close-race.js @@ -70,7 +70,7 @@ async function WaitRemoveDocumentAndCloseTab(aBrowser, aBrowsingContext) { return new Promise(resolve => { content.document.addEventListener( "fullscreenchange", - e => { + () => { resolve(); }, { once: true } diff --git a/dom/base/test/fullscreen/file_fullscreen-api.html b/dom/base/test/fullscreen/file_fullscreen-api.html index 645e6ece46..9661b10de9 100644 --- a/dom/base/test/fullscreen/file_fullscreen-api.html +++ b/dom/base/test/fullscreen/file_fullscreen-api.html @@ -112,7 +112,7 @@ function enter2(event) { promise = document.exitFullscreen(); } -function exit2(event) { +function exit2() { is(document.fullscreenElement, null, "Full-screen element should have rolled back."); is(iframe.contentDocument.fullscreenElement, null, @@ -156,7 +156,7 @@ function exit3(event) { promise = outOfDocElement.requestFullscreen(); } -function error1(event) { +function error1() { ok(!document.fullscreenElement, "Requests for full-screen from not-in-doc elements should fail."); assertPromiseRejected(promise, "in error1"); @@ -181,7 +181,7 @@ function enter4(event) { "Should not have a full-screen element again."); } -async function exit_to_arg_test_1(event) { +async function exit_to_arg_test_1() { ok(!document.fullscreenElement, "Should have left full-screen mode (third time)."); addFullscreenChangeContinuation("enter", enter_from_arg_test_1); @@ -196,14 +196,14 @@ async function exit_to_arg_test_1(event) { ok(!threw, "requestFullscreen with bogus arg (123) shouldn't throw exception"); } -function enter_from_arg_test_1(event) { +function enter_from_arg_test_1() { ok(document.fullscreenElement, "Should have entered full-screen after calling with bogus (ignored) argument (fourth time)"); addFullscreenChangeContinuation("exit", exit_to_arg_test_2); document.exitFullscreen(); } -async function exit_to_arg_test_2(event) { +async function exit_to_arg_test_2() { ok(!document.fullscreenElement, "Should have left full-screen mode (fourth time)."); addFullscreenChangeContinuation("enter", enter_from_arg_test_2); @@ -218,14 +218,14 @@ async function exit_to_arg_test_2(event) { ok(!threw, "requestFullscreen with { vrDisplay: null } shouldn't throw exception"); } -function enter_from_arg_test_2(event) { +function enter_from_arg_test_2() { ok(document.fullscreenElement, "Should have entered full-screen after calling with vrDisplay null argument (fifth time)"); addFullscreenChangeContinuation("exit", exit4); document.exitFullscreen(); } -function exit4(event) { +function exit4() { ok(!document.fullscreenElement, "Should be back in non-full-screen mode (fifth time)"); SpecialPowers.pushPrefEnv({"set":[["full-screen-api.allow-trusted-requests-only", true]]}, function() { @@ -234,7 +234,7 @@ function exit4(event) { }); } -function error2(event) { +function error2() { ok(!document.fullscreenElement, "Should still be in normal mode, because calling context isn't trusted."); button = document.createElement("button"); @@ -246,13 +246,13 @@ function error2(event) { sendMouseClick(button); } -function enter5(event) { +function enter5() { ok(document.fullscreenElement, "Moved to full-screen after mouse click"); addFullscreenChangeContinuation("exit", exit5); document.exitFullscreen(); } -function exit5(event) { +function exit5() { ok(!document.fullscreenElement, "Should have left full-screen mode (last time)."); SpecialPowers.pushPrefEnv({ @@ -264,7 +264,7 @@ function exit5(event) { }); } -function error3(event) { +function error3() { ok(!document.fullscreenElement, "Should still be in normal mode, because pref is not enabled."); diff --git a/dom/base/test/fullscreen/file_fullscreen-bug-1798219-2.html b/dom/base/test/fullscreen/file_fullscreen-bug-1798219-2.html index 61db80c228..48f2dea09d 100644 --- a/dom/base/test/fullscreen/file_fullscreen-bug-1798219-2.html +++ b/dom/base/test/fullscreen/file_fullscreen-bug-1798219-2.html @@ -2,7 +2,7 @@ <button>Launch</button> <script> let button = document.querySelector("button"); -button.addEventListener("click", function(e) { +button.addEventListener("click", function() { let newWindow = window.open("", "", "newWindow"); newWindow.document.write(`<!DOCTYPE HTML> <button>click me!</button> diff --git a/dom/base/test/fullscreen/file_fullscreen-bug-1798219.html b/dom/base/test/fullscreen/file_fullscreen-bug-1798219.html index 7490f12936..beafd79661 100644 --- a/dom/base/test/fullscreen/file_fullscreen-bug-1798219.html +++ b/dom/base/test/fullscreen/file_fullscreen-bug-1798219.html @@ -2,7 +2,7 @@ <button>click me!</button> <script> let button = document.querySelector("button"); -button.addEventListener("click", function(e) { +button.addEventListener("click", function() { document.documentElement.requestFullscreen(); setTimeout(() => { while(true) { diff --git a/dom/base/test/fullscreen/file_fullscreen-denied.html b/dom/base/test/fullscreen/file_fullscreen-denied.html index db9a69e71a..fe4244ec7f 100644 --- a/dom/base/test/fullscreen/file_fullscreen-denied.html +++ b/dom/base/test/fullscreen/file_fullscreen-denied.html @@ -137,7 +137,7 @@ function requestFullscreenMouseBtn(event, button) { synthesizeMouseAtCenter(clickEl, { button }); } -async function testFullscreenMouseBtn(event, button, next) { +async function testFullscreenMouseBtn() { await SpecialPowers.pushPrefEnv({ "set": [["full-screen-api.mouse-event-allow-left-button-only", true]] }); diff --git a/dom/base/test/fullscreen/file_fullscreen-esc-exit-inner.html b/dom/base/test/fullscreen/file_fullscreen-esc-exit-inner.html index d7d8a90aaf..210b2af1b6 100644 --- a/dom/base/test/fullscreen/file_fullscreen-esc-exit-inner.html +++ b/dom/base/test/fullscreen/file_fullscreen-esc-exit-inner.html @@ -33,7 +33,7 @@ function is(a, b, msg) { var escKeyReceived = false; var escKeySent = false; -function keyHandler(event) { +function keyHandler() { if (escKeyReceived == KeyboardEvent.DOM_VK_ESC) { escKeyReceived = true; } diff --git a/dom/base/test/fullscreen/file_fullscreen-esc-exit.html b/dom/base/test/fullscreen/file_fullscreen-esc-exit.html index f65f930b3f..1e2252f1ab 100644 --- a/dom/base/test/fullscreen/file_fullscreen-esc-exit.html +++ b/dom/base/test/fullscreen/file_fullscreen-esc-exit.html @@ -34,14 +34,14 @@ function finish() { opener.nextTest(); } -function fullscreenchange1(event) { +function fullscreenchange1() { is(document.fullscreenElement, document.body, "FSE should be doc"); addFullscreenChangeContinuation("exit", fullscreenchange2); ok(!document.getElementById("subdoc").contentWindow.escKeySent, "Should not yet have sent ESC key press."); document.getElementById("subdoc").contentWindow.startTest(); } -function fullscreenchange2(event) { +function fullscreenchange2() { ok(document.getElementById("subdoc").contentWindow.escKeySent, "Should have sent ESC key press."); ok(!document.getElementById("subdoc").contentWindow.escKeyReceived, "ESC key press to exit should not be delivered."); ok(!document.fullscreenElement, "Should have left full-screen mode on ESC key press"); diff --git a/dom/base/test/fullscreen/file_fullscreen-shadowdom.html b/dom/base/test/fullscreen/file_fullscreen-shadowdom.html index 348e08ae87..1972e1457e 100644 --- a/dom/base/test/fullscreen/file_fullscreen-shadowdom.html +++ b/dom/base/test/fullscreen/file_fullscreen-shadowdom.html @@ -29,7 +29,7 @@ var elem = shadowRoot.firstChild; var gotFullscreenEvent = false; - document.addEventListener("fullscreenchange", function (e) { + document.addEventListener("fullscreenchange", function () { if (document.fullscreenElement === host) { is(shadowRoot.fullscreenElement, elem, "Expected element entered fullsceen"); diff --git a/dom/base/test/fullscreen/file_fullscreen-svg-element.html b/dom/base/test/fullscreen/file_fullscreen-svg-element.html index 1dfc78aa1c..d03a13ee84 100644 --- a/dom/base/test/fullscreen/file_fullscreen-svg-element.html +++ b/dom/base/test/fullscreen/file_fullscreen-svg-element.html @@ -32,7 +32,7 @@ var elem = document.getElementById("svg-elem") , elemWasLocked = false; - document.addEventListener("fullscreenchange", function (e) { + document.addEventListener("fullscreenchange", function () { if (document.fullscreenElement === elem) { elemWasLocked = true; document.exitFullscreen(); diff --git a/dom/base/test/fullscreen/fullscreen.xhtml b/dom/base/test/fullscreen/fullscreen.xhtml index 2cc95642b6..76986db540 100644 --- a/dom/base/test/fullscreen/fullscreen.xhtml +++ b/dom/base/test/fullscreen/fullscreen.xhtml @@ -11,7 +11,7 @@ window.addEventListener("fullscreen", onFullScreen, true); -function onFullScreen(event) +function onFullScreen() { window.arguments[0].done(window.fullScreen); } diff --git a/dom/base/test/fullscreen/fullscreen_helpers.js b/dom/base/test/fullscreen/fullscreen_helpers.js index 6e78015cd8..f097ae316e 100644 --- a/dom/base/test/fullscreen/fullscreen_helpers.js +++ b/dom/base/test/fullscreen/fullscreen_helpers.js @@ -83,7 +83,7 @@ function waitWidgetFullscreenEvent( aIsInFullscreen, aWaitUntil = false ) { - return BrowserTestUtils.waitForEvent(aWindow, "fullscreen", false, aEvent => { + return BrowserTestUtils.waitForEvent(aWindow, "fullscreen", false, () => { if ( aWaitUntil && aIsInFullscreen != @@ -106,7 +106,7 @@ function waitForFullScreenObserver( aIsInFullscreen, aWaitUntil = false ) { - return TestUtils.topicObserved("fullscreen-painted", (subject, data) => { + return TestUtils.topicObserved("fullscreen-painted", () => { if ( aWaitUntil && aIsInFullscreen != diff --git a/dom/base/test/fullscreen/test_fullscreen-api.html b/dom/base/test/fullscreen/test_fullscreen-api.html index 2a59d6eeb0..ef07a5f97f 100644 --- a/dom/base/test/fullscreen/test_fullscreen-api.html +++ b/dom/base/test/fullscreen/test_fullscreen-api.html @@ -111,7 +111,7 @@ function runNextTest() { // otherwise, the fullscreen request might be denied. if (testWindow.document.hidden) { info("Waiting for document to unhide"); - waitForEvent(testWindow.document, "visibilitychange", (event) => { + waitForEvent(testWindow.document, "visibilitychange", () => { return !testWindow.document.hidden; }, testWindow.begin); return; diff --git a/dom/base/test/fullscreen/test_fullscreen_modal.html b/dom/base/test/fullscreen/test_fullscreen_modal.html index 78e70d9052..fef4e3867d 100644 --- a/dom/base/test/fullscreen/test_fullscreen_modal.html +++ b/dom/base/test/fullscreen/test_fullscreen_modal.html @@ -21,7 +21,7 @@ const button = document.querySelector("button"); let clickCount = 0; let lastFullscreenPromise = null; let shouldEnterFullscreen = false; -button.addEventListener("click", function(e) { +button.addEventListener("click", function() { clickCount++; if (shouldEnterFullscreen) { const fullscreenElement = document.getElementById("fullscreen"); @@ -58,6 +58,15 @@ async function testFullscreenIsModal(modal) { ok(document.fullscreenElement.matches(":fullscreen"), "Fullscreen element matches :fullscreen"); is(document.fullscreenElement.matches(":modal"), modal, "Fullscreen element matches :modal"); + // Before exiting fullscreen, wait on the document becoming active, + // which signifies that the fullscreen request has been completely + // processed. It is important that the fullscreen request is all + // the way done, or the exit fullscreen request could be dropped. + let documentWrapper = SpecialPowers.wrap(document); + await SimpleTest.promiseWaitForCondition(() => documentWrapper.isActive(), + "Timed out waiting for document to become active."); + ok(documentWrapper.isActive(), "Document is active."); + await document.exitFullscreen(); clickButton(/* expectEvent = */ true); } diff --git a/dom/base/test/gtest/TestMimeType.cpp b/dom/base/test/gtest/TestMimeType.cpp index fecb3f8678..40916130a8 100644 --- a/dom/base/test/gtest/TestMimeType.cpp +++ b/dom/base/test/gtest/TestMimeType.cpp @@ -9,12 +9,10 @@ #include "MimeType.h" #include "nsString.h" -using mozilla::UniquePtr; - TEST(MimeType, EmptyString) { const auto in = u""_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_FALSE(parsed) << "Empty string"; } @@ -22,7 +20,7 @@ TEST(MimeType, EmptyString) TEST(MimeType, JustWhitespace) { const auto in = u" \t\r\n "_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_FALSE(parsed) << "Just whitespace"; } @@ -30,7 +28,7 @@ TEST(MimeType, JustWhitespace) TEST(MimeType, JustBackslash) { const auto in = u"\\"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_FALSE(parsed) << "Just backslash"; } @@ -38,7 +36,7 @@ TEST(MimeType, JustBackslash) TEST(MimeType, JustForwardslash) { const auto in = u"/"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_FALSE(parsed) << "Just forward slash"; } @@ -46,7 +44,7 @@ TEST(MimeType, JustForwardslash) TEST(MimeType, MissingType1) { const auto in = u"/bogus"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_FALSE(parsed) << "Missing type #1"; } @@ -54,7 +52,7 @@ TEST(MimeType, MissingType1) TEST(MimeType, MissingType2) { const auto in = u" \r\n\t/bogus"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_FALSE(parsed) << "Missing type #2"; } @@ -62,7 +60,7 @@ TEST(MimeType, MissingType2) TEST(MimeType, MissingSubtype1) { const auto in = u"bogus"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_FALSE(parsed) << "Missing subtype #1"; } @@ -70,7 +68,7 @@ TEST(MimeType, MissingSubtype1) TEST(MimeType, MissingSubType2) { const auto in = u"bogus/"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_FALSE(parsed) << "Missing subtype #2"; } @@ -78,7 +76,7 @@ TEST(MimeType, MissingSubType2) TEST(MimeType, MissingSubType3) { const auto in = u"bogus;"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_FALSE(parsed) << "Missing subtype #3"; } @@ -86,7 +84,7 @@ TEST(MimeType, MissingSubType3) TEST(MimeType, MissingSubType4) { const auto in = u"bogus; \r\n\t"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_FALSE(parsed) << "Missing subtype #3"; } @@ -94,7 +92,7 @@ TEST(MimeType, MissingSubType4) TEST(MimeType, ExtraForwardSlash) { const auto in = u"bogus/bogus/;"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_FALSE(parsed) << "Extra forward slash"; } @@ -102,7 +100,7 @@ TEST(MimeType, ExtraForwardSlash) TEST(MimeType, WhitespaceInType) { const auto in = u"t\re\nx\tt /html"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_FALSE(parsed) << "Type with whitespace"; } @@ -110,7 +108,7 @@ TEST(MimeType, WhitespaceInType) TEST(MimeType, WhitespaceInSubtype) { const auto in = u"text/ h\rt\nm\tl"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_FALSE(parsed) << "Subtype with whitespace"; } @@ -118,7 +116,7 @@ TEST(MimeType, WhitespaceInSubtype) TEST(MimeType, NonAlphanumericMediaType1) { const auto in = u"</>"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_FALSE(parsed) << "Non-alphanumeric media type #1"; } @@ -126,7 +124,7 @@ TEST(MimeType, NonAlphanumericMediaType1) TEST(MimeType, NonAlphanumericMediaType2) { const auto in = u"(/)"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_FALSE(parsed) << "Non-alphanumeric media type #2"; } @@ -134,7 +132,7 @@ TEST(MimeType, NonAlphanumericMediaType2) TEST(MimeType, NonAlphanumericMediaType3) { const auto in = u"{/}"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_FALSE(parsed) << "Non-alphanumeric media type #3"; } @@ -142,7 +140,7 @@ TEST(MimeType, NonAlphanumericMediaType3) TEST(MimeType, NonAlphanumericMediaType4) { const auto in = u"\"/\""_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_FALSE(parsed) << "Non-alphanumeric media type #4"; } @@ -150,7 +148,7 @@ TEST(MimeType, NonAlphanumericMediaType4) TEST(MimeType, NonAlphanumericMediaType5) { const auto in = u"\0/\0"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_FALSE(parsed) << "Non-alphanumeric media type #5"; } @@ -158,7 +156,7 @@ TEST(MimeType, NonAlphanumericMediaType5) TEST(MimeType, NonAlphanumericMediaType6) { const auto in = u"text/html(;doesnot=matter"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_FALSE(parsed) << "Non-alphanumeric media type #6"; } @@ -166,7 +164,7 @@ TEST(MimeType, NonAlphanumericMediaType6) TEST(MimeType, NonLatin1MediaType1) { const auto in = u"ÿ/ÿ"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_FALSE(parsed) << "Non-latin1 media type #1"; } @@ -174,7 +172,7 @@ TEST(MimeType, NonLatin1MediaType1) TEST(MimeType, NonLatin1MediaType2) { const auto in = u"\x0100/\x0100"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_FALSE(parsed) << "Non-latin1 media type #2"; } @@ -182,7 +180,7 @@ TEST(MimeType, NonLatin1MediaType2) TEST(MimeType, MultipleParameters) { const auto in = u"text/html;charset=gbk;no=1;charset_=gbk_;yes=2"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsString out; @@ -194,7 +192,7 @@ TEST(MimeType, MultipleParameters) TEST(MimeType, DuplicateParameter1) { const auto in = u"text/html;charset=gbk;charset=windows-1255"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsString out; @@ -206,7 +204,7 @@ TEST(MimeType, DuplicateParameter1) TEST(MimeType, DuplicateParameter2) { const auto in = u"text/html;charset=();charset=GBK"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsString out; @@ -218,7 +216,7 @@ TEST(MimeType, DuplicateParameter2) TEST(MimeType, CString) { const auto in = "text/html;charset=();charset=GBK"_ns; - UniquePtr<CMimeType> parsed = CMimeType::Parse(in); + RefPtr<CMimeType> parsed = CMimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsCString out; @@ -234,7 +232,7 @@ TEST(MimeType, CString) TEST(MimeType, NonAlphanumericParametersAreQuoted) { const auto in = u"text/html;test=\x00FF\\;charset=gbk"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsString out; @@ -249,7 +247,7 @@ TEST(MimeType, NonAlphanumericParametersAreQuoted) TEST(MimeType, ParameterQuotedIfHasLeadingWhitespace1) { const auto in = u"text/html;charset= g\\\"bk"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -261,7 +259,7 @@ TEST(MimeType, ParameterQuotedIfHasLeadingWhitespace1) TEST(MimeType, ParameterQuotedIfHasLeadingWhitespace2) { const auto in = u"text/html;charset= \"g\\bk\""_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -273,7 +271,7 @@ TEST(MimeType, ParameterQuotedIfHasLeadingWhitespace2) TEST(MimeType, ParameterQuotedIfHasInternalWhitespace) { const auto in = u"text/html;charset=g \\b\"k"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -285,7 +283,7 @@ TEST(MimeType, ParameterQuotedIfHasInternalWhitespace) TEST(MimeType, ImproperlyQuotedParameter1) { const auto in = u"x/x;test=\""_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -297,7 +295,7 @@ TEST(MimeType, ImproperlyQuotedParameter1) TEST(MimeType, ImproperlyQuotedParameter2) { const auto in = u"x/x;test=\"\\"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -309,7 +307,7 @@ TEST(MimeType, ImproperlyQuotedParameter2) TEST(MimeType, NonLatin1ParameterIgnored) { const auto in = u"x/x;test=\xFFFD;x=x"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -321,7 +319,7 @@ TEST(MimeType, NonLatin1ParameterIgnored) TEST(MimeType, ParameterIgnoredIfWhitespaceInName1) { const auto in = u"text/html;charset =gbk;charset=123"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -333,7 +331,7 @@ TEST(MimeType, ParameterIgnoredIfWhitespaceInName1) TEST(MimeType, ParameterIgnoredIfWhitespaceInName2) { const auto in = u"text/html;cha rset =gbk;charset=123"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -345,7 +343,7 @@ TEST(MimeType, ParameterIgnoredIfWhitespaceInName2) TEST(MimeType, WhitespaceTrimmed) { const auto in = u"\n\r\t text/plain\n\r\t ;\n\r\t charset=123\n\r\t "_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -357,7 +355,7 @@ TEST(MimeType, WhitespaceTrimmed) TEST(MimeType, WhitespaceOnlyParameterIgnored) { const auto in = u"x/x;x= \r\n\t"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -369,7 +367,7 @@ TEST(MimeType, WhitespaceOnlyParameterIgnored) TEST(MimeType, IncompleteParameterIgnored1) { const auto in = u"x/x;test"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -381,7 +379,7 @@ TEST(MimeType, IncompleteParameterIgnored1) TEST(MimeType, IncompleteParameterIgnored2) { const auto in = u"x/x;test="_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -393,7 +391,7 @@ TEST(MimeType, IncompleteParameterIgnored2) TEST(MimeType, IncompleteParameterIgnored3) { const auto in = u"x/x;test= \r\n\t"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -405,7 +403,7 @@ TEST(MimeType, IncompleteParameterIgnored3) TEST(MimeType, IncompleteParameterIgnored4) { const auto in = u"text/html;test;charset=gbk"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -417,7 +415,7 @@ TEST(MimeType, IncompleteParameterIgnored4) TEST(MimeType, IncompleteParameterIgnored5) { const auto in = u"text/html;test=;charset=gbk"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -429,7 +427,7 @@ TEST(MimeType, IncompleteParameterIgnored5) TEST(MimeType, EmptyParameterIgnored1) { const auto in = u"text/html ; ; charset=gbk"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -441,7 +439,7 @@ TEST(MimeType, EmptyParameterIgnored1) TEST(MimeType, EmptyParameterIgnored2) { const auto in = u"text/html;;;;charset=gbk"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -453,7 +451,7 @@ TEST(MimeType, EmptyParameterIgnored2) TEST(MimeType, InvalidParameterIgnored1) { const auto in = u"text/html;';charset=gbk"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -465,7 +463,7 @@ TEST(MimeType, InvalidParameterIgnored1) TEST(MimeType, InvalidParameterIgnored2) { const auto in = u"text/html;\";charset=gbk;=123; =321"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -477,7 +475,7 @@ TEST(MimeType, InvalidParameterIgnored2) TEST(MimeType, InvalidParameterIgnored3) { const auto in = u"text/html;charset= \"\u007F;charset=GBK"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -490,7 +488,7 @@ TEST(MimeType, InvalidParameterIgnored4) { const auto in = nsLiteralString( u"text/html;charset=\"\u007F;charset=foo\";charset=GBK;charset="); - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -502,7 +500,7 @@ TEST(MimeType, InvalidParameterIgnored4) TEST(MimeType, SingleQuotes1) { const auto in = u"text/html;charset='gbk'"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -514,7 +512,7 @@ TEST(MimeType, SingleQuotes1) TEST(MimeType, SingleQuotes2) { const auto in = u"text/html;charset='gbk"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -526,7 +524,7 @@ TEST(MimeType, SingleQuotes2) TEST(MimeType, SingleQuotes3) { const auto in = u"text/html;charset=gbk'"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -538,7 +536,7 @@ TEST(MimeType, SingleQuotes3) TEST(MimeType, SingleQuotes4) { const auto in = u"text/html;charset=';charset=GBK"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -550,7 +548,7 @@ TEST(MimeType, SingleQuotes4) TEST(MimeType, SingleQuotes5) { const auto in = u"text/html;charset=''';charset=GBK"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -562,7 +560,7 @@ TEST(MimeType, SingleQuotes5) TEST(MimeType, DoubleQuotes1) { const auto in = u"text/html;charset=\"gbk\""_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -574,7 +572,7 @@ TEST(MimeType, DoubleQuotes1) TEST(MimeType, DoubleQuotes2) { const auto in = u"text/html;charset=\"gbk"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -586,7 +584,7 @@ TEST(MimeType, DoubleQuotes2) TEST(MimeType, DoubleQuotes3) { const auto in = u"text/html;charset=gbk\""_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -598,7 +596,7 @@ TEST(MimeType, DoubleQuotes3) TEST(MimeType, DoubleQuotes4) { const auto in = u"text/html;charset=\" gbk\""_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -610,7 +608,7 @@ TEST(MimeType, DoubleQuotes4) TEST(MimeType, DoubleQuotes5) { const auto in = u"text/html;charset=\"gbk \""_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -622,7 +620,7 @@ TEST(MimeType, DoubleQuotes5) TEST(MimeType, DoubleQuotes6) { const auto in = u"text/html;charset=\"\\ gbk\""_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -634,7 +632,7 @@ TEST(MimeType, DoubleQuotes6) TEST(MimeType, DoubleQuotes7) { const auto in = u"text/html;charset=\"\\g\\b\\k\""_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -646,7 +644,7 @@ TEST(MimeType, DoubleQuotes7) TEST(MimeType, DoubleQuotes8) { const auto in = u"text/html;charset=\"gbk\"x"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -658,7 +656,7 @@ TEST(MimeType, DoubleQuotes8) TEST(MimeType, DoubleQuotes9) { const auto in = u"text/html;charset=\"\";charset=GBK"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -670,7 +668,7 @@ TEST(MimeType, DoubleQuotes9) TEST(MimeType, DoubleQuotes10) { const auto in = u"text/html;charset=\";charset=GBK"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -682,7 +680,7 @@ TEST(MimeType, DoubleQuotes10) TEST(MimeType, UnexpectedCodePoints) { const auto in = u"text/html;charset={gbk}"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -699,7 +697,7 @@ TEST(MimeType, LongTypesSubtypesAccepted) "2345678901234567890123456789012345678901234567890123456789/" "012345678901234567890123456789012345678901234567890123456789012345678901" "2345678901234567890123456789012345678901234567890123456789"); - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -716,7 +714,7 @@ TEST(MimeType, LongParametersAccepted) "012345678901234567890123456789012345678901234567890123456789012345678901" "2345678901234567890123456789012345678901234567890123456789=x;charset=" "gbk"); - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -744,7 +742,7 @@ TEST(MimeType, AllValidCharactersAccepted1) u"\u00E3\u00E4\u00E5\u00E6\u00E7\u00E8\u00E9\u00EA\u00EB\u00EC\u00ED" u"\u00EE\u00EF\u00F0\u00F1\u00F2\u00F3\u00F4\u00F5\u00F6\u00F7\u00F8" u"\u00F9\u00FA\u00FB\u00FC\u00FD\u00FE\u00FF\""); - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -756,7 +754,7 @@ TEST(MimeType, AllValidCharactersAccepted1) TEST(MimeType, CaseNormalization1) { const auto in = u"TEXT/PLAIN;CHARSET=TEST"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -775,7 +773,7 @@ TEST(MimeType, CaseNormalization2) ".^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=!#$" "%&'*+-.^_`|~" "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -794,7 +792,7 @@ TEST(MimeType, CaseNormalization2) TEST(MimeType, LegacyCommentSyntax1) { const auto in = u"text/html;charset=gbk("_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; @@ -806,7 +804,7 @@ TEST(MimeType, LegacyCommentSyntax1) TEST(MimeType, LegacyCommentSyntax2) { const auto in = u"text/html;x=(;charset=gbk"_ns; - UniquePtr<MimeType> parsed = MimeType::Parse(in); + RefPtr<MimeType> parsed = MimeType::Parse(in); ASSERT_TRUE(parsed) << "Parsing succeeded"; nsAutoString out; diff --git a/dom/base/test/meta_viewport/viewport_helpers.js b/dom/base/test/meta_viewport/viewport_helpers.js index d4d346b5d0..cad6613549 100644 --- a/dom/base/test/meta_viewport/viewport_helpers.js +++ b/dom/base/test/meta_viewport/viewport_helpers.js @@ -40,5 +40,6 @@ function getViewportInfo(aDisplayWidth, aDisplayHeight) { } function fuzzeq(a, b, msg) { + // eslint-disable-next-line mozilla/no-comparison-or-assignment-inside-ok ok(Math.abs(a - b) < 1e-6, msg); } diff --git a/dom/base/test/mochitest.toml b/dom/base/test/mochitest.toml index 6415c4b33b..fb6724e497 100644 --- a/dom/base/test/mochitest.toml +++ b/dom/base/test/mochitest.toml @@ -3,7 +3,6 @@ tags = "condprof" prefs = [ "formhelper.autozoom.force-disable.test-only=true", "plugins.rewrite_youtube_embeds=true", - "dom.domrequest.enabled=true", ] support-files = [ "audio.ogg", @@ -1232,8 +1231,6 @@ skip-if = [ ["test_domparsing.html"] -["test_domrequest.html"] - ["test_domwindowutils.html"] skip-if = ["os == 'android'"] # Bug 1525959 @@ -1301,6 +1298,8 @@ skip-if = [ "http2", ] +["test_focus_radio.html"] + ["test_focus_scroll_padding_tab.html"] ["test_focus_scrollable_fieldset.html"] diff --git a/dom/base/test/test_domrequest.html b/dom/base/test/test_domrequest.html deleted file mode 100644 index 1aea26f657..0000000000 --- a/dom/base/test/test_domrequest.html +++ /dev/null @@ -1,233 +0,0 @@ -<!DOCTYPE HTML> -<html> -<head> - <title>Test for DOMRequest</title> - <script src="/tests/SimpleTest/SimpleTest.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> -</head> -<body> -<p id="display"></p> -<div id="content" style="display: none"> - -</div> -<pre id="test"> -<script class="testbody" type="application/javascript"> -"use strict"; - -var reqserv = SpecialPowers.getDOMRequestService(); -ok("createRequest" in reqserv, "appears to be a service"); - -function testBasics() { - // create a request - var req = reqserv.createRequest(window); - ok("result" in req, "request has result"); - ok("error" in req, "request has error"); - ok("onsuccess" in req, "request has onsuccess"); - ok("onerror" in req, "request has onerror"); - ok("readyState" in req, "request has readyState"); - ok("then" in req, "request has then"); - - is(req.readyState, "pending", "readyState is pending"); - is(req.result, undefined, "result is undefined"); - is(req.onsuccess, null, "onsuccess is null"); - is(req.onerror, null, "onerror is null"); - - runTest(); -} - -function testSuccess() { - // fire success - var req = reqserv.createRequest(window); - var ev = null; - req.onsuccess = function(e) { - ev = e; - } - var result = null; - var promise = req.then(function(r) { - is(r, "my result", "correct result when resolving the promise"); - result = r; - runTest(); - }, function(e) { - ok(false, "promise should not be rejected"); - runTest(); - }); - ok(promise instanceof Promise, "then() should return a Promise"); - reqserv.fireSuccess(req, "my result"); - ok(ev, "got success event"); - is(ev.type, "success", "correct type during success"); - is(ev.target, req, "correct target during success"); - is(req.readyState, "done", "correct readyState after success"); - is(req.error, null, "correct error after success"); - is(req.result, "my result", "correct result after success"); - is(result, null, "Promise should not be resolved synchronously"); -} - -function testError() { - // fire error - var req = reqserv.createRequest(window); - var ev = null; - req.onerror = function(e) { - ev = e; - } - var error = null; - var promise = req.then(function(r) { - ok(false, "promise should not be resolved"); - runTest(); - }, function(e) { - ok(e instanceof DOMException, "got error rejection"); - ok(e === req.error, "got correct error when rejecting the promise"); - error = e; - runTest(); - }); - ok(promise instanceof Promise, "then() should return a Promise"); - reqserv.fireError(req, "OhMyError"); - ok(ev, "got error event"); - is(ev.type, "error", "correct type during error"); - is(ev.target, req, "correct target during error"); - is(req.readyState, "done", "correct readyState after error"); - is(req.error.name, "UnknownError", "correct error type after error"); - is(req.error.message, "OhMyError", "correct error message after error"); - is(req.result, undefined, "correct result after error"); - is(error, null, "Promise should not be rejected synchronously"); -} - -function testDetailedError() { - // fire detailed error - var req = reqserv.createRequest(window); - var ev = null; - req.onerror = function(e) { - ev = e; - }; - var error = null; - var promise = req.then(function(r) { - ok(false, "promise should not be resolved"); - runTest(); - }, function(e) { - ok(e instanceof DOMException, "got error rejection"); - ok(e === req.error, "got correct error when rejecting the promise"); - error = e; - runTest(); - }); - ok(promise instanceof Promise, "then() should return a Promise"); - SpecialPowers.wrwp(req).fireDetailedError(new DOMException("detailedError")); - ok(ev, "got error event"); - is(ev.type, "error", "correct type during error"); - is(ev.target, req, "correct target during error"); - is(req.readyState, "done", "correct readyState after error"); - is(req.error.name, "UnknownError", "correct error type after error"); - is(req.error.message, "detailedError", "correct error message after error"); - is(req.result, undefined, "correct result after error"); - is(error, null, "Promise should not be rejected synchronously"); -} - -function testThenAfterSuccess() { - // fire success - var req = reqserv.createRequest(window); - var ev = null; - req.onsuccess = function(e) { - ev = e; - } - reqserv.fireSuccess(req, "my result"); - ok(ev, "got success event"); - is(ev.type, "success", "correct type during success"); - is(ev.target, req, "correct target during success"); - is(req.readyState, "done", "correct readyState after success"); - is(req.error, null, "correct error after success"); - is(req.result, "my result", "correct result after success"); - var result = null; - var promise = req.then(function(r) { - is(r, "my result", "correct result when resolving the promise"); - result = r; - runTest(); - }, function(e) { - ok(false, "promise should not be rejected"); - runTest(); - }); - ok(promise instanceof Promise, "then() should return a Promise"); - is(result, null, "Promise should not be resolved synchronously"); -} - -function testThenAfterError() { - // fire error - var req = reqserv.createRequest(window); - var ev = null; - req.onerror = function(e) { - ev = e; - } - reqserv.fireError(req, "OhMyError"); - ok(ev, "got error event"); - is(ev.type, "error", "correct type during error"); - is(ev.target, req, "correct target during error"); - is(req.readyState, "done", "correct readyState after error"); - is(req.error.name, "UnknownError", "correct error type after error"); - is(req.error.message, "OhMyError", "correct error message after error"); - is(req.result, undefined, "correct result after error"); - var error = null; - var promise = req.then(function(r) { - ok(false, "promise should not be resolved"); - runTest(); - }, function(e) { - ok(e instanceof DOMException, "got error rejection"); - ok(e === req.error, "got correct error when rejecting the promise"); - error = e; - runTest(); - }); - ok(promise instanceof Promise, "then() should return a Promise"); - is(error, null, "Promise should not be rejected synchronously"); -} - -function testDetailedError() { - // fire detailed error - var req = reqserv.createRequest(window); - var ev = null; - req.onerror = function(e) { - ev = e; - }; - var error = null; - var promise = req.then(function(r) { - ok(false, "promise should not be resolved"); - runTest(); - }, function(e) { - ok(e instanceof DOMException, "got error rejection"); - ok(e === req.error, "got correct error when rejecting the promise"); - error = e; - runTest(); - }); - ok(promise instanceof Promise, "then() should return a Promise"); - SpecialPowers.wrap(req).fireDetailedError(new DOMException("detailedError")); - ok(ev, "got error event"); - is(ev.type, "error", "correct type during error"); - is(ev.target, req, "correct target during error"); - is(req.readyState, "done", "correct readyState after error"); - is(req.error.name, "Error", "correct error type after error"); - is(req.error.message, "detailedError", "correct error message after error"); - is(req.result, undefined, "correct result after error"); - is(error, null, "Promise should not be rejected synchronously"); -} - -var tests = [ - testBasics, - testSuccess, - testError, - testDetailedError, - testThenAfterSuccess, - testThenAfterError, -]; - -function runTest() { - if (!tests.length) { - SimpleTest.finish(); - return; - } - - var test = tests.shift(); - test(); -} - -SimpleTest.waitForExplicitFinish(); -runTest(); - -</script> -</pre> -</body> -</html> diff --git a/dom/base/test/test_domrequesthelper.xhtml b/dom/base/test/test_domrequesthelper.xhtml deleted file mode 100644 index 6b9061b8a2..0000000000 --- a/dom/base/test/test_domrequesthelper.xhtml +++ /dev/null @@ -1,549 +0,0 @@ -<?xml version="1.0"?> -<!-- - Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ ---> -<?xml-stylesheet href="chrome://global/skin" type="text/css"?> -<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> - -<window title="DOMRequestHelper Test" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - onload="start();"> - - <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> - - <script type="application/javascript"> - <![CDATA[ - const {DOMRequestIpcHelper} = ChromeUtils.importESModule( - "resource://gre/modules/DOMRequestHelper.sys.mjs" - ); - let obs = Cc["@mozilla.org/observer-service;1"]. - getService(Ci.nsIObserverService); - let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(); - - function DummyHelperSubclass() { - this.onuninit = null; - } - DummyHelperSubclass.prototype = { - __proto__: DOMRequestIpcHelper.prototype, - uninit() { - if (typeof this.onuninit === "function") { - this.onuninit(); - } - this.onuninit = null; - } - }; - - var dummy = new DummyHelperSubclass(); - var isDOMRequestHelperDestroyed = false; - - /** - * Init & destroy. - */ - function initDOMRequestHelperTest(aMessages) { - // If we haven't initialized the DOMRequestHelper object, its private - // properties will be undefined, but once destroyDOMRequestHelper is - // called, they're set to null. - var expectedPrivatePropertyValues = - isDOMRequestHelperDestroyed ? null : undefined; - - is(dummy._requests, expectedPrivatePropertyValues, "Request is expected"); - is(dummy._messages, undefined, "Messages is undefined"); - is(dummy._window, expectedPrivatePropertyValues, "Window is expected"); - - dummy.initDOMRequestHelper(window, aMessages); - - ok(dummy._window, "Window exists"); - is(dummy._window, window, "Correct window"); - if (aMessages) { - is(typeof dummy._listeners, "object", "Listeners is an object"); - } - } - - function destroyDOMRequestHelperTest() { - dummy.destroyDOMRequestHelper(); - isDOMRequestHelperDestroyed = true; - - is(dummy._requests, null, "Request is null"); - is(dummy._messages, undefined, "Messages is undefined"); - is(dummy._window, null, "Window is null"); - } - - /** - * Message listeners. - */ - function checkMessageListeners(aExpectedListeners, aCount) { - info("Checking message listeners\n" + "Expected listeners " + - JSON.stringify(aExpectedListeners) + " \nExpected count " + aCount); - let count = 0; - Object.keys(dummy._listeners).forEach(function(name) { - count++; - is(aExpectedListeners[name].weakRef, dummy._listeners[name].weakRef, - "Message found " + name + " - Same weakRef"); - is(aExpectedListeners[name].count, dummy._listeners[name].count, - "Message found " + name + " - Same count"); - }); - is(aCount, count, "Correct number of listeners"); - } - - function addMessageListenersTest(aMessages, aExpectedListeners, aCount) { - dummy.addMessageListeners(aMessages); - info(JSON.stringify(dummy._listeners)); - checkMessageListeners(aExpectedListeners, aCount); - } - - function removeMessageListenersTest(aMessages, aExpectedListeners, aCount) { - dummy.removeMessageListeners(aMessages); - checkMessageListeners(aExpectedListeners, aCount); - } - - /** - * Utility function to test window destruction behavior. In general this - * function does the following: - * - * 1) Create a new iframe - * 2) Create a new DOMRequestHelper - * 3) initDOMRequestHelper(), optionally with weak or strong listeners - * 4) Optionally force a garbage collection to reap weak references - * 5) Destroy the iframe triggering an inner-window-destroyed event - * 6) Callback with a boolean indicating if DOMRequestHelper.uninit() was - * called. - * - * Example usage: - * - * checkWindowDestruction({ messages: ["foo"], gc: true }, - * function(uninitCalled) { - * // expect uninitCalled === false since GC with only weak refs - * }); - */ - const TOPIC = "inner-window-destroyed"; - function checkWindowDestruction(aOptions, aCallback) { - aOptions = aOptions || {}; - aOptions.messages = aOptions.messages || []; - aOptions.gc = !!aOptions.gc; - - if (typeof aCallback !== "function") { - aCallback = function() { }; - } - - let uninitCalled = false; - - // Use a secondary observer so we know when to expect the uninit(). We - // can then reasonably expect uninitCalled to be set properly on the - // next tick. - let observer = { - observe(aSubject, aTopic, aData) { - if (aTopic !== TOPIC) { - return; - } - obs.removeObserver(observer, TOPIC); - setTimeout(function() { - aCallback(uninitCalled); - }); - } - }; - - let frame = document.createXULElement("iframe"); - frame.onload = function() { - obs.addObserver(observer, TOPIC); - // Create dummy DOMRequestHelper specific to checkWindowDestruction() - let cwdDummy = new DummyHelperSubclass(); - cwdDummy.onuninit = function() { - uninitCalled = true; - - if (!aOptions.messages || !aOptions.messages.length) { - return; - } - - // If all message listeners are removed, cwdDummy.receiveMessage - // should never be called. - ppmm.broadcastAsyncMessage(aOptions.messages[0].name); - }; - - // Test if we still receive messages from ppmm. - cwdDummy.receiveMessage = function(aMessage) { - ok(false, "cwdDummy.receiveMessage should NOT be called: " + aMessage.name); - }; - - cwdDummy.initDOMRequestHelper(frame.contentWindow, aOptions.messages); - // Make sure to clear our strong ref here so that we can test our - // weak reference listeners and observer. - cwdDummy = null; - if (aOptions.gc) { - Cu.schedulePreciseGC(function() { - SpecialPowers.DOMWindowUtils.cycleCollect(); - SpecialPowers.DOMWindowUtils.garbageCollect(); - SpecialPowers.DOMWindowUtils.garbageCollect(); - document.documentElement.removeChild(frame); - }); - return; - } - document.documentElement.removeChild(frame); - }; - document.documentElement.appendChild(frame); - } - - /** - * Test steps. - */ - var tests = [ - function() { - info("== InitDOMRequestHelper no messages"); - initDOMRequestHelperTest(null); - next(); - }, - function() { - info("== DestroyDOMRequestHelper"); - destroyDOMRequestHelperTest(); - next(); - }, - function() { - info("== InitDOMRequestHelper empty array"); - initDOMRequestHelperTest([]); - checkMessageListeners({}, 0); - next(); - }, - function() { - info("== DestroyDOMRequestHelper"); - destroyDOMRequestHelperTest(); - next(); - }, - function() { - info("== InitDOMRequestHelper with strings array"); - initDOMRequestHelperTest(["name1", "nameN"]); - checkMessageListeners({"name1": {weakRef: false, count: 1}, - "nameN": {weakRef: false, count: 1}}, 2); - next(); - }, - function() { - info("== DestroyDOMRequestHelper"); - destroyDOMRequestHelperTest(); - next(); - }, - function() { - info("== InitDOMRequestHelper with objects array"); - initDOMRequestHelperTest([{ - name: "name1", - weakRef: false - }, { - name: "nameN", - weakRef: true - }]); - checkMessageListeners({ - "name1": {weakRef: false, count: 1}, - "nameN": {weakRef: true, count: 1} - }, 2); - next(); - }, - function() { - info("== AddMessageListeners empty array"); - addMessageListenersTest([], { - "name1": {weakRef: false, count: 1}, - "nameN": {weakRef: true, count: 1} - }, 2); - next(); - }, - function() { - info("== AddMessageListeners null"); - addMessageListenersTest(null, { - "name1": {weakRef: false, count: 1}, - "nameN": {weakRef: true, count: 1} - }, 2); - next(); - }, - function() { - info("== AddMessageListeners new listener, string only"); - addMessageListenersTest("name2", { - "name1": {weakRef: false, count: 1}, - "name2": {weakRef: false, count: 1}, - "nameN": {weakRef: true, count: 1} - }, 3); - next(); - }, - function() { - info("== AddMessageListeners new listeners, strings array"); - addMessageListenersTest(["name3", "name4"], { - "name1": {weakRef: false, count: 1}, - "name2": {weakRef: false, count: 1}, - "name3": {weakRef: false, count: 1}, - "name4": {weakRef: false, count: 1}, - "nameN": {weakRef: true, count: 1} - }, 5); - next(); - }, - function() { - info("== AddMessageListeners new listeners, objects array"); - addMessageListenersTest([{ - name: "name5", - weakRef: true - }, { - name: "name6", - weakRef: false - }], { - "name1": {weakRef: false, count: 1}, - "name2": {weakRef: false, count: 1}, - "name3": {weakRef: false, count: 1}, - "name4": {weakRef: false, count: 1}, - "name5": {weakRef: true, count: 1}, - "name6": {weakRef: false, count: 1}, - "nameN": {weakRef: true, count: 1} - }, 7); - next(); - }, - function() { - info("== RemoveMessageListeners, null"); - removeMessageListenersTest(null, { - "name1": {weakRef: false, count: 1}, - "name2": {weakRef: false, count: 1}, - "name3": {weakRef: false, count: 1}, - "name4": {weakRef: false, count: 1}, - "name5": {weakRef: true, count: 1}, - "name6": {weakRef: false, count: 1}, - "nameN": {weakRef: true, count: 1} - }, 7); - next(); - }, - function() { - info("== RemoveMessageListeners, one message"); - removeMessageListenersTest("name1", { - "name2": {weakRef: false, count: 1}, - "name3": {weakRef: false, count: 1}, - "name4": {weakRef: false, count: 1}, - "name5": {weakRef: true, count: 1}, - "name6": {weakRef: false, count: 1}, - "nameN": {weakRef: true, count: 1} - }, 6); - next(); - }, - function() { - info("== RemoveMessageListeners, array of messages"); - removeMessageListenersTest(["name2", "name3"], { - "name4": {weakRef: false, count: 1}, - "name5": {weakRef: true, count: 1}, - "name6": {weakRef: false, count: 1}, - "nameN": {weakRef: true, count: 1} - }, 4); - next(); - }, - function() { - info("== RemoveMessageListeners, unknown message"); - removeMessageListenersTest("unknown", { - "name4": {weakRef: false, count: 1}, - "name5": {weakRef: true, count: 1}, - "name6": {weakRef: false, count: 1}, - "nameN": {weakRef: true, count: 1} - }, 4); - next(); - }, - function() { - try { - info("== AddMessageListeners, same message, same kind"); - addMessageListenersTest("name4", { - "name4": {weakRef: false, count: 2}, - "name5": {weakRef: true, count: 1}, - "name6": {weakRef: false, count: 1}, - "nameN": {weakRef: true, count: 1} - }, 4); - next(); - } catch (ex) { - ok(false, "Unexpected exception " + ex); - } - }, - function() { - info("== AddMessageListeners, same message, different kind"); - try { - addMessageListenersTest({name: "name4", weakRef: true}, { - "name4": {weakRef: false, count: 2}, - "name5": {weakRef: true, count: 1}, - "name6": {weakRef: false, count: 1}, - "nameN": {weakRef: true, count: 1} - }, 4); - ok(false, "Should have thrown an exception"); - } catch (ex) { - ok(true, "Expected exception"); - next(); - } - }, - function() { - info("== RemoveMessageListeners, message with two listeners"); - try { - removeMessageListenersTest(["name4", "name5"], { - "name4": {weakRef: false, count: 1}, - "name6": {weakRef: false, count: 1}, - "nameN": {weakRef: true, count: 1} - }, 3); - next(); - } catch (ex) { - ok(false, "Unexpected exception " + ex); - } - }, - function() { - info("== Test createRequest()"); - ok(DOMRequest, "DOMRequest object exists"); - var req = dummy.createRequest(); - ok(DOMRequest.isInstance(req), "Returned a DOMRequest"); - next(); - }, - function() { - info("== Test getRequestId(), removeRequest() and getRequest()"); - var req = dummy.createRequest(); - var id = dummy.getRequestId(req); - is(typeof id, "string", "id is a string"); - var req_ = dummy.getRequest(id); - is(req, req_, "Got correct request"); - dummy.removeRequest(id); - req = dummy.getRequest(id); - is(req, undefined, "No request"); - next(); - }, - function() { - info("== Test createPromise()"); - ok(Promise, "Promise object exists"); - var promise = dummy.createPromise(function(resolve, reject) { - resolve(true); - }); - ok(promise instanceof Promise, "Returned a Promise"); - promise.then(next); - }, - function() { - info("== Test createPromiseWithId()"); - var _resolverId; - var promise = dummy.createPromiseWithId(function(resolverId) { - _resolverId = resolverId; - }); - var resolver = dummy.getPromiseResolver(_resolverId); - ok(promise instanceof Promise, "Returned a Promise"); - ok(typeof _resolverId === "string", "resolverId is a string"); - ok(resolver != null, "resolverId is a valid id"); - next(); - }, - function() { - info("== Test getResolver()"); - var id; - var resolver; - var promise = dummy.createPromise(function(resolve, reject) { - var r = { resolve, reject }; - id = dummy.getPromiseResolverId(r); - resolver = r; - ok(typeof id === "string", "id is a string"); - r.resolve(true); - }).then(function(unused) { - var r = dummy.getPromiseResolver(id); - ok(resolver === r, "Get succeeded"); - next(); - }); - }, - function() { - info("== Test removeResolver"); - var id; - var promise = dummy.createPromise(function(resolve, reject) { - var r = { resolve, reject }; - id = dummy.getPromiseResolverId(r); - ok(typeof id === "string", "id is a string"); - - var resolver = dummy.getPromiseResolver(id); - info("Got resolver " + JSON.stringify(resolver)); - ok(resolver === r, "Resolver get succeeded"); - - r.resolve(true); - }).then(function(unused) { - dummy.removePromiseResolver(id); - var resolver = dummy.getPromiseResolver(id); - ok(resolver === undefined, "removeResolver: get failed"); - next(); - }); - }, - function() { - info("== Test takeResolver"); - var id; - var resolver; - var promise = dummy.createPromise(function(resolve, reject) { - var r = { resolve, reject }; - id = dummy.getPromiseResolverId(r); - resolver = r; - ok(typeof id === "string", "id is a string"); - - var gotR = dummy.getPromiseResolver(id); - ok(gotR === r, "resolver get succeeded"); - - r.resolve(true); - }).then(function(unused) { - var r = dummy.takePromiseResolver(id); - ok(resolver === r, "take should succeed"); - - r = dummy.getPromiseResolver(id); - ok(r === undefined, "takeResolver: get failed"); - next(); - }); - }, - function() { - info("== Test window destroyed without messages and without GC"); - checkWindowDestruction({ gc: false }, function(uninitCalled) { - ok(uninitCalled, "uninit() should have been called"); - next(); - }); - }, - function() { - info("== Test window destroyed without messages and with GC"); - checkWindowDestruction({ gc: true }, function(uninitCalled) { - ok(!uninitCalled, "uninit() should NOT have been called"); - next(); - }); - }, - function() { - info("== Test window destroyed with weak messages and without GC"); - checkWindowDestruction({ messages: [{ name: "foo", weakRef: true }], - gc: false }, function(uninitCalled) { - ok(uninitCalled, "uninit() should have been called"); - next(); - }); - }, - function() { - info("== Test window destroyed with weak messages and with GC"); - checkWindowDestruction({ messages: [{ name: "foo", weakRef: true }], - gc: true }, function(uninitCalled) { - ok(!uninitCalled, "uninit() should NOT have been called"); - next(); - }); - }, - function() { - info("== Test window destroyed with strong messages and without GC"); - checkWindowDestruction({ messages: [{ name: "foo", weakRef: false }], - gc: false }, function(uninitCalled) { - ok(uninitCalled, "uninit() should have been called"); - next(); - }); - }, - function() { - info("== Test window destroyed with strong messages and with GC"); - checkWindowDestruction({ messages: [{ name: "foo", weakRef: false }], - gc: true }, function(uninitCalled) { - ok(uninitCalled, "uninit() should have been called"); - next(); - }); - } - ]; - - function next() { - if (!tests.length) { - SimpleTest.finish(); - return; - } - - var test = tests.shift(); - test(); - } - - function start() { - SimpleTest.waitForExplicitFinish(); - next(); - } - ]]> - </script> - - <body xmlns="http://www.w3.org/1999/xhtml"> - <p id="display"></p> - <div id="content" style="display: none"></div> - <pre id="test"></pre> - </body> -</window> diff --git a/dom/base/test/test_focus_radio.html b/dom/base/test/test_focus_radio.html new file mode 100644 index 0000000000..8e97012745 --- /dev/null +++ b/dom/base/test/test_focus_radio.html @@ -0,0 +1,90 @@ +<!doctype html> +<title>Test for input radio focus</title> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<script src="/tests/SimpleTest/EventUtils.js"></script> + +<button id="before">before</button> +<fieldset> + <legend>a</legend> + <label><input id="a1" type="radio" name="a" checked>a1</label> + <label><input id="a2" type="radio" name="a">a2</label> +</fieldset> +<fieldset> + <legend>b</legend> + <label><input id="b1" type="radio" name="b">b1</label> + <label><input id="b2" type="radio" name="b" checked>b2</label> +</fieldset> +<fieldset> + <legend>c</legend> + <label><input id="c1" type="radio" name="c">c1</label> + <label><input id="c2" type="radio" name="c">c2</label> +</fieldset> +<fieldset> + <legend>d</legend> + <label><input id="d1" type="radio" name="d" disabled>d1</label> + <label><input id="d2" type="radio" name="d">d2</label> + <label><input id="d3" type="radio" name="d" disabled>d3</label> + <label><input id="d4" type="radio" name="d">d4</label> +</fieldset> +<button id="after">after</button> + +<script> + SimpleTest.waitForExplicitFinish(); + + function expectFocusAfterKey(key, id) { + const res = key.match(/(Shift\+)?(.*)/); + const shiftKey = Boolean(res[1]); + const rawKey = res[2]; + synthesizeKey(`KEY_${rawKey}`, { shiftKey }); + is(document.activeElement.id, id, `${id} is focused after ${key}`); + } + + SimpleTest.waitForFocus(async function() { + await SpecialPowers.pushPrefEnv({"set": [["accessibility.tabfocus", 7]]}); + + expectFocusAfterKey("Tab", "before"); + // a1 is checked. + expectFocusAfterKey("Tab", "a1"); + // b2 is checked. + expectFocusAfterKey("Tab", "b2"); + // Nothing is checked in group c, so c1 should get focus. + expectFocusAfterKey("Tab", "c1"); + // d1 is disabled, so d2 should get focus. + expectFocusAfterKey("Tab", "d2"); + expectFocusAfterKey("Tab", "after"); + + expectFocusAfterKey("Shift+Tab", "d2"); + expectFocusAfterKey("Shift+Tab", "c1"); + expectFocusAfterKey("Shift+Tab", "b2"); + expectFocusAfterKey("Shift+Tab", "a1"); + expectFocusAfterKey("Shift+Tab", "before"); + + expectFocusAfterKey("Tab", "a1"); + expectFocusAfterKey("ArrowDown", "a2"); + expectFocusAfterKey("Tab", "b2"); + // a2 is now checked, so shift+tab should focus it. + expectFocusAfterKey("Shift+Tab", "a2"); + + expectFocusAfterKey("Tab", "b2"); + expectFocusAfterKey("ArrowUp", "b1"); + expectFocusAfterKey("Shift+Tab", "a2"); + expectFocusAfterKey("Tab", "b1"); + + expectFocusAfterKey("Tab", "c1"); + expectFocusAfterKey("ArrowDown", "c2"); + expectFocusAfterKey("Tab", "d2"); + expectFocusAfterKey("Shift+Tab", "c2"); + + expectFocusAfterKey("Tab", "d2"); + // d3 is disabled, so down arrow should focus d4. + expectFocusAfterKey("ArrowDown", "d4"); + expectFocusAfterKey("ArrowUp", "d2"); + expectFocusAfterKey("ArrowDown", "d4"); + // Down arrow should wrap at the bottom, skipping disabled. + expectFocusAfterKey("ArrowDown", "d2"); + // Up arrow should wrap at the top. + expectFocusAfterKey("ArrowUp", "d4"); + + SimpleTest.finish(); + }); +</script> diff --git a/dom/base/test/unit/test_error_codes.js b/dom/base/test/unit/test_error_codes.js index 73c893c512..5c26331d11 100644 --- a/dom/base/test/unit/test_error_codes.js +++ b/dom/base/test/unit/test_error_codes.js @@ -10,7 +10,7 @@ function asyncXHR(expectedStatus, nextTestFunc) { xhr.open("GET", "http://localhost:4444/test_error_code.xml", true); var sawError = false; - xhr.addEventListener("loadend", function doAsyncRequest_onLoad(event) { + xhr.addEventListener("loadend", function doAsyncRequest_onLoad() { Assert.ok(sawError, "Should have received an error"); nextTestFunc(); }); diff --git a/dom/base/test/useractivation/file_clipboard_common.js b/dom/base/test/useractivation/file_clipboard_common.js index fe172e52c8..3925404350 100644 --- a/dom/base/test/useractivation/file_clipboard_common.js +++ b/dom/base/test/useractivation/file_clipboard_common.js @@ -373,10 +373,10 @@ function allMechanisms(aCb, aClipOverride, aNegateAll) { case 0: // Keyboard issued cutCopyAll( - function docut(aSucc) { + function docut() { synthesizeKey("x", { accelKey: true }); }, - function docopy(aSucc) { + function docopy() { synthesizeKey("c", { accelKey: true }); }, function done() { @@ -411,14 +411,14 @@ function allMechanisms(aCb, aClipOverride, aNegateAll) { case 2: // Not triggered by user gesture cutCopyAll( - function doCut(aSucc) { + function doCut() { is( false, document.execCommand("cut"), "Can't directly execCommand not in a user callback" ); }, - function doCopy(aSucc) { + function doCopy() { is( false, document.execCommand("copy"), diff --git a/dom/base/test/useractivation/test_popup_blocker_async_callback.html b/dom/base/test/useractivation/test_popup_blocker_async_callback.html index dc53596531..4667229116 100644 --- a/dom/base/test/useractivation/test_popup_blocker_async_callback.html +++ b/dom/base/test/useractivation/test_popup_blocker_async_callback.html @@ -16,7 +16,7 @@ SimpleTest.requestFlakyTimeout("Need to test setTimeout"); function startTest(aTestAsyncFun, aAllowPopup = true) { return new Promise(aResolve => { let target = document.getElementById("target"); - target.addEventListener("click", (e) => { + target.addEventListener("click", () => { aTestAsyncFun(() => { let w = window.open("about:blank"); is(!!w, aAllowPopup, `Should ${aAllowPopup ? "allow" : "block"} popup`); diff --git a/dom/base/test/useractivation/test_useractivation_scrollbar.html b/dom/base/test/useractivation/test_useractivation_scrollbar.html index 778d6d0898..197cdc49fb 100644 --- a/dom/base/test/useractivation/test_useractivation_scrollbar.html +++ b/dom/base/test/useractivation/test_useractivation_scrollbar.html @@ -78,7 +78,7 @@ if (!opener) { SimpleTest.waitForFocus(async function() { function waitForEvent(aTarget, aEvent) { return new Promise((aResolve) => { - aTarget.addEventListener(aEvent, function listener(event) { + aTarget.addEventListener(aEvent, function listener() { aResolve(); }, { once: true }); }); diff --git a/dom/base/use_counter_metrics.yaml b/dom/base/use_counter_metrics.yaml index f94029689c..211d3e1344 100644 --- a/dom/base/use_counter_metrics.yaml +++ b/dom/base/use_counter_metrics.yaml @@ -20150,6 +20150,23 @@ use.counter.css.page: send_in_pings: - use-counters + css_transition_behavior: + type: counter + description: > + Whether a page used the CSS property transition-behavior. + Compare against `use.counter.top_level_content_documents_destroyed` + to calculate the rate. + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098 + notification_emails: + - dom-core@mozilla.com + - emilio@mozilla.com + expires: never + send_in_pings: + - use-counters + css_transition_delay: type: counter description: > @@ -31984,6 +32001,23 @@ use.counter.css.doc: send_in_pings: - use-counters + css_transition_behavior: + type: counter + description: > + Whether a document used the CSS property transition-behavior. + Compare against `use.counter.content_documents_destroyed` + to calculate the rate. + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098 + notification_emails: + - dom-core@mozilla.com + - emilio@mozilla.com + expires: never + send_in_pings: + - use-counters + css_transition_delay: type: counter description: > |