author | Csoregi Natalia <ncsoregi@mozilla.com> |
Wed, 15 Feb 2023 19:03:59 +0200 | |
changeset 653227 | 7c9d11c84ac7a77507e4f8b7d136da56227e7ca3 |
parent 653226 | f82668f37d71b7c49f7e22ddb33c77c012b6ddfd |
child 653228 | c7e12c1b792ac21216b2c23b95e9ca4b26b35192 |
push id | 40635 |
push user | smolnar@mozilla.com |
push date | Thu, 16 Feb 2023 04:35:37 +0000 |
treeherder | mozilla-central@b2d97b936206 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
bugs | 1444491, 1801761 |
milestone | 112.0a1 |
backs out | 1cee414009cb12b0850d7ae0ce2aa1b7dc51c24d 30f786b7919124f57eb76fb27cacf7c5313fdceb ce06375518a7e73510070b1ed39dbe61a8c08db8 64c8bb293e5c86a53f82cdc2b253139dd9db589d 94aa0ce630f21da9aeedb591947327a66098118f 80010eabc0c10e4535c56f7724b4ce945bda13b2 7d8da1f44177d2ad95c070b0dbce30b57904a19d |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/base/Document.cpp +++ b/dom/base/Document.cpp @@ -376,17 +376,16 @@ #include "nsIURIMutator.h" #include "nsIVariant.h" #include "nsIWeakReference.h" #include "nsIWebNavigation.h" #include "nsIWidget.h" #include "nsIX509Cert.h" #include "nsIX509CertValidity.h" #include "nsIXMLContentSink.h" -#include "nsIHTMLContentSink.h" #include "nsIXULRuntime.h" #include "nsImageLoadingContent.h" #include "nsImportModule.h" #include "nsLanguageAtomService.h" #include "nsLayoutUtils.h" #include "nsNetCID.h" #include "nsNetUtil.h" #include "nsNodeInfoManager.h" @@ -6838,19 +6837,16 @@ already_AddRefed<PresShell> Document::Cr UpdateFrameRequestCallbackSchedulingState(); if (mDocumentL10n) { // In case we already accumulated mutations, // we'll trigger the refresh driver now. mDocumentL10n->OnCreatePresShell(); } - if (HasAutoFocusCandidates()) { - ScheduleFlushAutoFocusCandidates(); - } // Now that we have a shell, we might have @font-face rules (the presence of a // shell may change which rules apply to us). We don't need to do anything // like EnsureStyleFlush or such, there's nothing to update yet and when stuff // is ready to update we'll flush the font set. MarkUserFontSetDirty(); // Take the author style disabled state from the top browsing cvontext. // (PageStyleChild.jsm ensures this is up to date.) @@ -6947,17 +6943,16 @@ bool Document::ShouldThrottleFrameReques DOMIntersectionObserver::Intersect(input, *el); return !output.Intersects(); } void Document::DeletePresShell() { mExternalResourceMap.HideViewers(); if (nsPresContext* presContext = mPresShell->GetPresContext()) { presContext->RefreshDriver()->CancelPendingFullscreenEvents(this); - presContext->RefreshDriver()->CancelFlushAutoFocus(this); } // When our shell goes away, request that all our images be immediately // discarded, so we don't carry around decoded image data for a document we // no longer intend to paint. ImageTracker()->RequestDiscardAll(); // Now that we no longer have a shell, we need to forget about any FontFace @@ -12625,264 +12620,131 @@ Document* Document::GetTemplateContentsO // Set |mTemplateContentsOwner| as the template contents owner of itself so // that it is the template contents owner of nested template elements. mTemplateContentsOwner->mTemplateContentsOwner = mTemplateContentsOwner; } return mTemplateContentsOwner; } -// https://html.spec.whatwg.org/#the-autofocus-attribute -void Document::ElementWithAutoFocusInserted(Element* aAutoFocusCandidate) { - BrowsingContext* bc = GetBrowsingContext(); - if (!bc) { - return; - } - - // If target is not fully active, then return. - if (!IsCurrentActiveDocument()) { - return; - } - - // If target's active sandboxing flag set has the sandboxed automatic features - // browsing context flag, then return. - if (GetSandboxFlags() & SANDBOXED_AUTOMATIC_FEATURES) { - return; - } - - // For each ancestorBC of target's browsing context's ancestor browsing - // contexts: if ancestorBC's active document's origin is not same origin with - // target's origin, then return. - while (bc) { - BrowsingContext* parent = bc->GetParent(); - if (!parent) { - break; - } - // AncestorBC is not the same site - if (!parent->IsInProcess()) { - return; - } - - Document* currentDocument = bc->GetDocument(); - if (!currentDocument) { - return; - } - - Document* parentDocument = parent->GetDocument(); - if (!parentDocument) { - return; - } - - // Not same origin - if (!currentDocument->NodePrincipal()->Equals( - parentDocument->NodePrincipal())) { - return; - } - - bc = parent; - } - MOZ_ASSERT(bc->IsTop()); - - Document* topDocument = bc->GetDocument(); - MOZ_ASSERT(topDocument); - topDocument->AppendAutoFocusCandidateToTopDocument(aAutoFocusCandidate); -} - -void Document::ScheduleFlushAutoFocusCandidates() { - MOZ_ASSERT(mPresShell && mPresShell->DidInitialize()); - MOZ_ASSERT(GetBrowsingContext()->IsTop()); - if (nsRefreshDriver* rd = mPresShell->GetRefreshDriver()) { - rd->ScheduleAutoFocusFlush(this); - } -} - -void Document::AppendAutoFocusCandidateToTopDocument( - Element* aAutoFocusCandidate) { - MOZ_ASSERT(GetBrowsingContext()->IsTop()); - if (mAutoFocusFired) { - return; - } - - if (!HasAutoFocusCandidates()) { - // PresShell may be initialized later - if (mPresShell && mPresShell->DidInitialize()) { - ScheduleFlushAutoFocusCandidates(); - } - } - - nsWeakPtr element = do_GetWeakReference(aAutoFocusCandidate); - mAutoFocusCandidates.RemoveElement(element); - mAutoFocusCandidates.AppendElement(element); -} - -void Document::SetAutoFocusFired() { - mAutoFocusCandidates.Clear(); - mAutoFocusFired = true; -} - -// https://html.spec.whatwg.org/#flush-autofocus-candidates -void Document::FlushAutoFocusCandidates() { - MOZ_ASSERT(GetBrowsingContext()->IsTop()); +static already_AddRefed<nsPIDOMWindowOuter> FindTopWindowForElement( + Element* element) { + Document* document = element->OwnerDoc(); + if (!document) { + return nullptr; + } + + nsCOMPtr<nsPIDOMWindowOuter> window = document->GetWindow(); + if (!window) { + return nullptr; + } + + // Trying to find the top window (equivalent to window.top). + if (nsCOMPtr<nsPIDOMWindowOuter> top = window->GetInProcessTop()) { + window = std::move(top); + } + return window.forget(); +} + +/** + * nsAutoFocusEvent is used to dispatch a focus event for an + * nsGenericHTMLFormElement with the autofocus attribute enabled. + */ +class nsAutoFocusEvent : public Runnable { + public: + explicit nsAutoFocusEvent(nsCOMPtr<Element>&& aElement, + nsCOMPtr<nsPIDOMWindowOuter>&& aTopWindow) + : mozilla::Runnable("nsAutoFocusEvent"), + mElement(std::move(aElement)), + mTopWindow(std::move(aTopWindow)) {} + + NS_IMETHOD Run() override { + nsCOMPtr<nsPIDOMWindowOuter> currentTopWindow = + FindTopWindowForElement(mElement); + if (currentTopWindow != mTopWindow) { + // The element's top window changed from when the event was queued. + // Don't take away focus from an unrelated window. + return NS_OK; + } + + if (Document* doc = mTopWindow->GetExtantDoc()) { + if (doc->IsAutoFocusFired()) { + return NS_OK; + } + doc->SetAutoFocusFired(); + } + + // Don't steal focus from the user. + if (mTopWindow->GetFocusedElement()) { + return NS_OK; + } + + FocusOptions options; + ErrorResult rv; + mElement->Focus(options, CallerType::System, rv); + return rv.StealNSResult(); + } + + private: + nsCOMPtr<Element> mElement; + nsCOMPtr<nsPIDOMWindowOuter> mTopWindow; +}; + +void Document::SetAutoFocusElement(Element* aAutoFocusElement) { if (mAutoFocusFired) { - return; - } - - if (!mPresShell) { - return; - } - - MOZ_ASSERT(HasAutoFocusCandidates()); - MOZ_ASSERT(mPresShell->DidInitialize()); - - nsCOMPtr<nsPIDOMWindowOuter> topWindow = GetWindow(); - // We should be the top document - if (!topWindow) { - return; - } - -#ifdef DEBUG - { - // Trying to find the top window (equivalent to window.top). - nsCOMPtr<nsPIDOMWindowOuter> top = topWindow->GetInProcessTop(); - MOZ_ASSERT(topWindow == top); - } -#endif - - // Don't steal the focus from the user - if (topWindow->GetFocusedElement()) { - SetAutoFocusFired(); - return; - } - - MOZ_ASSERT(mDocumentURI); - nsAutoCString ref; - // GetRef never fails - nsresult rv = mDocumentURI->GetRef(ref); - if (NS_SUCCEEDED(rv) && - nsContentUtils::GetTargetElement(this, NS_ConvertUTF8toUTF16(ref))) { - SetAutoFocusFired(); - return; - } - - nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mAutoFocusCandidates); - while (iter.HasMore()) { - nsCOMPtr<Element> autoFocusElement = do_QueryReferent(iter.GetNext()); - if (!autoFocusElement) { - continue; - } - RefPtr<Document> autoFocusElementDoc = autoFocusElement->OwnerDoc(); - // Get the latest info about the frame and allow scripts - // to run which might affect the focusability of this element. - autoFocusElementDoc->FlushPendingNotifications(FlushType::Frames); - - // Above layout flush may cause the PresShell to disappear. - if (!mPresShell) { + // Too late. + return; + } + + if (mAutoFocusElement) { + // The spec disallows multiple autofocus elements, so we consider only the + // first one to preserve the old behavior. + return; + } + + mAutoFocusElement = do_GetWeakReference(aAutoFocusElement); + TriggerAutoFocus(); +} + +void Document::SetAutoFocusFired() { mAutoFocusFired = true; } + +bool Document::IsAutoFocusFired() { return mAutoFocusFired; } + +void Document::TriggerAutoFocus() { + if (mAutoFocusFired) { + return; + } + + if (!mPresShell || !mPresShell->DidInitialize()) { + // Delay autofocus until frames are constructed so that we don't thrash + // style and layout calculations. + return; + } + + nsCOMPtr<Element> autoFocusElement = do_QueryReferent(mAutoFocusElement); + if (autoFocusElement && autoFocusElement->OwnerDoc() == this) { + nsCOMPtr<nsPIDOMWindowOuter> topWindow = + FindTopWindowForElement(autoFocusElement); + if (!topWindow) { return; } - // Re-get the element because the ownerDoc() might have changed - autoFocusElementDoc = autoFocusElement->OwnerDoc(); - BrowsingContext* bc = autoFocusElementDoc->GetBrowsingContext(); - if (!bc) { - continue; - } - - // If doc is not fully active, then remove element from candidates, and - // continue. - if (!autoFocusElementDoc->IsCurrentActiveDocument()) { - iter.Remove(); - continue; - } - - nsCOMPtr<nsIContentSink> sink = - do_QueryInterface(autoFocusElementDoc->GetCurrentContentSink()); - if (sink) { - nsHtml5TreeOpExecutor* executor = - static_cast<nsHtml5TreeOpExecutor*>(sink->AsExecutor()); - if (executor) { - // This is a HTML5 document - MOZ_ASSERT(autoFocusElementDoc->IsHTMLDocument()); - // If doc's script-blocking style sheet counter is greater than 0, th - // return. - if (executor->WaitForPendingSheets()) { - // In this case, element is the currently-best candidate, but doc is - // not ready for autofocusing. We'll try again next time flush - // autofocus candidates is called. - ScheduleFlushAutoFocusCandidates(); - return; - } - } - } - - // The autofocus element could be moved to a different - // top level BC. - if (bc->Top()->GetDocument() != this) { - continue; - } - - iter.Remove(); - - // Let inclusiveAncestorDocuments be a list consisting of doc, plus the - // active documents of each of doc's browsing context's ancestor browsing - // contexts. - // If any Document in inclusiveAncestorDocuments has non-null target - // element, then continue. - bool shouldFocus = true; - while (bc) { - Document* doc = bc->GetDocument(); - if (!doc) { - shouldFocus = false; - break; - } - - nsIURI* uri = doc->GetDocumentURI(); - if (!uri) { - shouldFocus = false; - break; - } - - nsAutoCString ref; - nsresult rv = uri->GetRef(ref); - // If there is an element in the document tree that has an ID equal to - // fragment - if (NS_SUCCEEDED(rv) && - nsContentUtils::GetTargetElement(doc, NS_ConvertUTF8toUTF16(ref))) { - shouldFocus = false; - break; - } - bc = bc->GetParent(); - } - - if (!shouldFocus) { - continue; - } - - MOZ_ASSERT(topWindow); - if (TryAutoFocusCandidate(*autoFocusElement)) { - // We've successfully autofocused an element, don't - // need to try to focus the rest. - SetAutoFocusFired(); - break; - } - } - - if (HasAutoFocusCandidates()) { - ScheduleFlushAutoFocusCandidates(); - } -} - -bool Document::TryAutoFocusCandidate(Element& aElement) { - const FocusOptions options; - if (RefPtr<Element> target = nsFocusManager::GetTheFocusableArea( - &aElement, nsFocusManager::ProgrammaticFocusFlags(options))) { - target->Focus(options, CallerType::NonSystem, IgnoreErrors()); - return true; - } - - return false; + // NOTE: This may be removed in the future since the spec technically + // allows autofocus after load. + nsCOMPtr<Document> topDoc = topWindow->GetExtantDoc(); + if (topDoc && + topDoc->GetReadyStateEnum() == Document::READYSTATE_COMPLETE) { + return; + } + + nsCOMPtr<nsIRunnable> event = + new nsAutoFocusEvent(std::move(autoFocusElement), topWindow.forget()); + nsresult rv = NS_DispatchToCurrentThread(event.forget()); + NS_ENSURE_SUCCESS_VOID(rv); + } } void Document::SetScrollToRef(nsIURI* aDocumentURI) { if (!aDocumentURI) { return; } nsAutoCString ref;
--- a/dom/base/Document.h +++ b/dom/base/Document.h @@ -1922,18 +1922,16 @@ class Document : public nsINode, // Pushes aElement onto the top layer void TopLayerPush(Element&); // Removes the topmost element for which aPredicate returns true from the top // layer. The removed element, if any, is returned. Element* TopLayerPop(FunctionRef<bool(Element*)> aPredicate); - MOZ_CAN_RUN_SCRIPT bool TryAutoFocusCandidate(Element& aElement); - public: // Removes all the elements with fullscreen flag set from the top layer, and // clears their fullscreen flag. void CleanupFullscreenState(); // Pops the fullscreen element from the top layer and clears its // fullscreen flag. Returns whether there was any fullscreen element. enum class UpdateViewport : bool { No, Yes }; @@ -3100,24 +3098,20 @@ class Document : public nsINode, virtual enum MediaDocumentKind MediaDocumentKind() const { return MediaDocumentKind::NotMedia; } DocumentState GetDocumentState() const { return mDocumentState; } nsISupports* GetCurrentContentSink(); - void ElementWithAutoFocusInserted(Element* aAutoFocusCandidate); - MOZ_CAN_RUN_SCRIPT void FlushAutoFocusCandidates(); - void ScheduleFlushAutoFocusCandidates(); - bool HasAutoFocusCandidates() const { - return !mAutoFocusCandidates.IsEmpty(); - } - + void SetAutoFocusElement(Element* aAutoFocusElement); + void TriggerAutoFocus(); void SetAutoFocusFired(); + bool IsAutoFocusFired(); void SetScrollToRef(nsIURI* aDocumentURI); MOZ_CAN_RUN_SCRIPT void ScrollToRef(); void ResetScrolledToRefAlready() { mScrolledToRefAlready = false; } void SetChangeScrollPosWhenScrollingToRef(bool aValue) { mChangeScrollPosWhenScrollingToRef = aValue; } @@ -3951,18 +3945,16 @@ class Document : public nsINode, public: const OriginTrials& Trials() const { return mTrials; } private: void DoCacheAllKnownLangPrefs(); void RecomputeLanguageFromCharset(); bool GetSHEntryHasUserInteraction(); - void AppendAutoFocusCandidateToTopDocument(Element* aAutoFocusCandidate); - public: void SetMayNeedFontPrefsUpdate() { mMayNeedFontPrefsUpdate = true; } bool MayNeedFontPrefsUpdate() { return mMayNeedFontPrefsUpdate; } void SetSHEntryHasUserInteraction(bool aHasInteraction); already_AddRefed<nsAtom> GetContentLanguageAsAtomForStyle() const; @@ -5142,21 +5134,17 @@ class Document : public nsINode, nsRevocableEventPtr<nsRunnableMethod<Document, void, false>> mPendingTitleChangeEvent; RefPtr<nsDOMNavigationTiming> mTiming; // Recorded time of change to 'loading' state. TimeStamp mLoadingTimeStamp; - // Decided to use nsTObserverArray because it allows us to - // remove candidates while iterating them and this is what - // the spec defines. We could implement the spec without - // using nsTObserverArray, however using nsTObserverArray is more clear. - nsTObserverArray<nsWeakPtr> mAutoFocusCandidates; + nsWeakPtr mAutoFocusElement; nsCString mScrollToRef; // Weak reference to the scope object (aka the script global object) // that, unlike mScriptGlobalObject, is never unset once set. This // is a weak reference to avoid leaks due to circular references. nsWeakPtr mScopeObject;
--- a/dom/base/nsContentSink.h +++ b/dom/base/nsContentSink.h @@ -164,31 +164,31 @@ class nsContentSink : public nsICSSLoade // stylesheets are all done loading. public: void StartLayout(bool aIgnorePendingSheets); static void NotifyDocElementCreated(Document* aDoc); Document* GetDocument() { return mDocument; } - // Later on we might want to make this more involved somehow - // (e.g. stop waiting after some timeout or whatnot). - bool WaitForPendingSheets() { return mPendingSheetCount > 0; } - protected: inline int32_t GetNotificationInterval() { if (mDynamicLowerValue) { return 1000; } return mozilla::StaticPrefs::content_notify_interval(); } virtual nsresult FlushTags() = 0; + // Later on we might want to make this more involved somehow + // (e.g. stop waiting after some timeout or whatnot). + bool WaitForPendingSheets() { return mPendingSheetCount > 0; } + void DoProcessLinkHeader(); void StopDeflecting() { mDeflectedCount = mozilla::StaticPrefs::content_sink_perf_deflect_count(); } protected: RefPtr<Document> mDocument;
--- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -2978,69 +2978,16 @@ BrowserParent* nsContentUtils::GetCommon aBrowserParent1, aBrowserParent2, [](BrowserParent* aBrowserParent) { return aBrowserParent->GetBrowserBridgeParent() ? aBrowserParent->GetBrowserBridgeParent()->Manager() : nullptr; }); } /* static */ -Element* nsContentUtils::GetTargetElement(Document* aDocument, - const nsAString& aAnchorName) { - MOZ_ASSERT(aDocument); - - if (aAnchorName.IsEmpty()) { - return nullptr; - } - // 1. If there is an element in the document tree that has an ID equal to - // fragment, then return the first such element in tree order. - if (Element* el = aDocument->GetElementById(aAnchorName)) { - return el; - } - - // 2. If there is an a element in the document tree that has a name - // attribute whose value is equal to fragment, then return the first such - // element in tree order. - // - // FIXME(emilio): Why the different code-paths for HTML and non-HTML docs? - if (aDocument->IsHTMLDocument()) { - nsCOMPtr<nsINodeList> list = aDocument->GetElementsByName(aAnchorName); - // Loop through the named nodes looking for the first anchor - uint32_t length = list->Length(); - for (uint32_t i = 0; i < length; i++) { - nsIContent* node = list->Item(i); - if (node->IsHTMLElement(nsGkAtoms::a)) { - return node->AsElement(); - } - } - } else { - constexpr auto nameSpace = u"http://www.w3.org/1999/xhtml"_ns; - // Get the list of anchor elements - nsCOMPtr<nsINodeList> list = - aDocument->GetElementsByTagNameNS(nameSpace, u"a"_ns); - // Loop through the anchors looking for the first one with the given name. - for (uint32_t i = 0; true; i++) { - nsIContent* node = list->Item(i); - if (!node) { // End of list - break; - } - - // Compare the name attribute - if (node->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, - aAnchorName, eCaseMatters)) { - return node->AsElement(); - } - } - } - - // 3. Return null. - return nullptr; -} - -/* static */ template <typename FPT, typename FRT, typename SPT, typename SRT> Maybe<int32_t> nsContentUtils::ComparePoints( const RangeBoundaryBase<FPT, FRT>& aFirstBoundary, const RangeBoundaryBase<SPT, SRT>& aSecondBoundary) { if (!aFirstBoundary.IsSet() || !aSecondBoundary.IsSet()) { return Nothing{}; }
--- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -521,20 +521,16 @@ class nsContentUtils { /** * Returns the common BrowserParent ancestor, if any, for two given * BrowserParent. */ static mozilla::dom::BrowserParent* GetCommonBrowserParentAncestor( mozilla::dom::BrowserParent* aBrowserParent1, mozilla::dom::BrowserParent* aBrowserParent2); - // https://html.spec.whatwg.org/#target-element - // https://html.spec.whatwg.org/#find-a-potential-indicated-element - static Element* GetTargetElement(Document* aDocument, - const nsAString& aAnchorName); /** * Returns true if aNode1 is before aNode2 in the same connected * tree. * aNode1Index and aNode2Index are in/out arguments. If non-null, and value is * Some, that value is used instead of calling slow ComputeIndexOf on the * parent node. If value is Nothing, the value will be set to the return value * of ComputeIndexOf. */
--- a/dom/base/nsFocusManager.cpp +++ b/dom/base/nsFocusManager.cpp @@ -375,17 +375,18 @@ NS_IMETHODIMP nsFocusManager::GetActiveBrowsingContext(BrowsingContext** aBrowsingContext) { NS_IF_ADDREF(*aBrowsingContext = GetActiveBrowsingContext()); return NS_OK; } void nsFocusManager::FocusWindow(nsPIDOMWindowOuter* aWindow, CallerType aCallerType) { if (RefPtr<nsFocusManager> fm = sInstance) { - fm->SetFocusedWindowWithCallerType(aWindow, aCallerType); + fm->SetFocusedWindowWithCallerType(aWindow, aCallerType, + sInstance->GenerateFocusActionId()); } } NS_IMETHODIMP nsFocusManager::GetFocusedWindow(mozIDOMWindowProxy** aFocusedWindow) { NS_IF_ADDREF(*aFocusedWindow = mFocusedWindow); return NS_OK; } @@ -396,57 +397,55 @@ nsFocusManager::GetFocusedContentBrowsin MOZ_DIAGNOSTIC_ASSERT( XRE_IsParentProcess(), "We only have use cases for this in the parent process"); NS_IF_ADDREF(*aBrowsingContext = GetFocusedBrowsingContextInChrome()); return NS_OK; } nsresult nsFocusManager::SetFocusedWindowWithCallerType( - mozIDOMWindowProxy* aWindowToFocus, CallerType aCallerType) { - LOGFOCUS(("<<SetFocusedWindow begin>>")); + mozIDOMWindowProxy* aWindowToFocus, CallerType aCallerType, + uint64_t aActionId) { + LOGFOCUS(("<<SetFocusedWindow begin actionid: %" PRIu64 ">>", aActionId)); nsCOMPtr<nsPIDOMWindowOuter> windowToFocus = nsPIDOMWindowOuter::From(aWindowToFocus); NS_ENSURE_TRUE(windowToFocus, NS_ERROR_FAILURE); nsCOMPtr<Element> frameElement = windowToFocus->GetFrameElementInternal(); - Maybe<uint64_t> actionIdFromSetFocusInner; if (frameElement) { // pass false for aFocusChanged so that the caret does not get updated // and scrolling does not occur. - actionIdFromSetFocusInner = SetFocusInner(frameElement, 0, false, true); + SetFocusInner(frameElement, 0, false, true, aActionId); } else { // this is a top-level window. If the window has a child frame focused, // clear the focus. Otherwise, focus should already be in this frame, or // already cleared. This ensures that focus will be in this frame and not // in a child. nsIContent* content = windowToFocus->GetFocusedElement(); if (content) { if (nsCOMPtr<nsPIDOMWindowOuter> childWindow = GetContentWindow(content)) ClearFocus(windowToFocus); } } nsCOMPtr<nsPIDOMWindowOuter> rootWindow = windowToFocus->GetPrivateRoot(); - const uint64_t actionId = actionIdFromSetFocusInner.isSome() - ? actionIdFromSetFocusInner.value() - : sInstance->GenerateFocusActionId(); if (rootWindow) { - RaiseWindow(rootWindow, aCallerType, actionId); - } - - LOGFOCUS(("<<SetFocusedWindow end actionid: %" PRIu64 ">>", actionId)); + RaiseWindow(rootWindow, aCallerType, aActionId); + } + + LOGFOCUS(("<<SetFocusedWindow end actionid: %" PRIu64 ">>", aActionId)); return NS_OK; } NS_IMETHODIMP nsFocusManager::SetFocusedWindow( mozIDOMWindowProxy* aWindowToFocus) { - return SetFocusedWindowWithCallerType(aWindowToFocus, CallerType::System); + return SetFocusedWindowWithCallerType(aWindowToFocus, CallerType::System, + GenerateFocusActionId()); } NS_IMETHODIMP nsFocusManager::GetFocusedElement(Element** aFocusedElement) { RefPtr<Element> focusedElement = mFocusedElement; focusedElement.forget(aFocusedElement); return NS_OK; } @@ -466,17 +465,17 @@ nsFocusManager::GetLastFocusMethod(mozID } NS_IMETHODIMP nsFocusManager::SetFocus(Element* aElement, uint32_t aFlags) { LOGFOCUS(("<<SetFocus begin>>")); NS_ENSURE_ARG(aElement); - SetFocusInner(aElement, aFlags, true, true); + SetFocusInner(aElement, aFlags, true, true, GenerateFocusActionId()); LOGFOCUS(("<<SetFocus end>>")); return NS_OK; } NS_IMETHODIMP nsFocusManager::ElementIsFocusable(Element* aElement, uint32_t aFlags, @@ -539,17 +538,17 @@ nsFocusManager::MoveFocus(mozIDOMWindowP LOGCONTENTNAVIGATION("Element to be focused: %s", newFocus.get()); if (newFocus && newFocus->IsElement()) { // for caret movement, pass false for the aFocusChanged argument, // otherwise the caret will end up moving to the focus position. This // would be a problem because the caret would move to the beginning of the // focused link making it impossible to navigate the caret over a link. SetFocusInner(MOZ_KnownLive(newFocus->AsElement()), aFlags, - aType != MOVEFOCUS_CARET, true); + aType != MOVEFOCUS_CARET, true, GenerateFocusActionId()); *aElement = do_AddRef(newFocus->AsElement()).take(); } else if (aType == MOVEFOCUS_ROOT || aType == MOVEFOCUS_CARET) { // no content was found, so clear the focus for these two types. ClearFocus(window); } LOGFOCUS(("<<MoveFocus end>>")); @@ -1468,25 +1467,24 @@ static bool IsEmeddededInNoautofocusPopu nsIFrame* menuPopup = nsLayoutUtils::GetClosestFrameOfType(f, LayoutFrameType::MenuPopup); MOZ_ASSERT(menuPopup, "NS_FRAME_IN_POPUP lied?"); return static_cast<nsMenuPopupFrame*>(menuPopup) ->PopupElement() .GetXULBoolAttr(nsGkAtoms::noautofocus); } -Maybe<uint64_t> nsFocusManager::SetFocusInner(Element* aNewContent, - int32_t aFlags, - bool aFocusChanged, - bool aAdjustWidget) { +void nsFocusManager::SetFocusInner(Element* aNewContent, int32_t aFlags, + bool aFocusChanged, bool aAdjustWidget, + uint64_t aActionId) { // if the element is not focusable, just return and leave the focus as is RefPtr<Element> elementToFocus = FlushAndCheckIfFocusable(aNewContent, aFlags); if (!elementToFocus) { - return Nothing(); + return; } const RefPtr<BrowsingContext> focusedBrowsingContext = GetFocusedBrowsingContext(); // check if the element to focus is a frame (iframe) containing a child // document. Frames are never directly focused; instead focusing a frame // means focus what is inside the frame. To do this, the descendant content @@ -1512,72 +1510,72 @@ Maybe<uint64_t> nsFocusManager::SetFocus newBrowsingContext = newWindow->GetBrowsingContext(); } // if the element is already focused, just return. Note that this happens // after the frame check above so that we compare the element that will be // focused rather than the frame it is in. if (!newWindow || (newBrowsingContext == GetFocusedBrowsingContext() && elementToFocus == mFocusedElement)) { - return Nothing(); + return; } MOZ_ASSERT(newBrowsingContext); BrowsingContext* browsingContextToFocus = newBrowsingContext; if (RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(elementToFocus)) { // Only look at pre-existing browsing contexts. If this function is // called during reflow, calling GetBrowsingContext() could cause frame // loader initialization at a time when it isn't safe. if (BrowsingContext* bc = flo->GetExtantBrowsingContext()) { // If focus is already in the subtree rooted at bc, return early // to match the single-process focus semantics. Otherwise, we'd // blur and immediately refocus whatever is focused. BrowsingContext* walk = focusedBrowsingContext; while (walk) { if (walk == bc) { - return Nothing(); + return; } walk = walk->GetParent(); } browsingContextToFocus = bc; } } // don't allow focus to be placed in docshells or descendants of docshells // that are being destroyed. Also, ensure that the page hasn't been // unloaded. The prevents content from being refocused during an unload event. nsCOMPtr<nsIDocShell> newDocShell = newWindow->GetDocShell(); nsCOMPtr<nsIDocShell> docShell = newDocShell; while (docShell) { bool inUnload; docShell->GetIsInUnload(&inUnload); if (inUnload) { - return Nothing(); + return; } bool beingDestroyed; docShell->IsBeingDestroyed(&beingDestroyed); if (beingDestroyed) { - return Nothing(); + return; } BrowsingContext* bc = docShell->GetBrowsingContext(); nsCOMPtr<nsIDocShellTreeItem> parentDsti; docShell->GetInProcessParent(getter_AddRefs(parentDsti)); docShell = do_QueryInterface(parentDsti); if (!docShell && !XRE_IsParentProcess()) { // We don't have an in-process parent, but let's see if we have // an in-process ancestor or if an out-of-process ancestor // is discarded. do { bc = bc->GetParent(); if (bc && bc->IsDiscarded()) { - return Nothing(); + return; } } while (bc && !bc->IsInProcess()); if (bc) { docShell = bc->GetDocShell(); } else { docShell = nullptr; } } @@ -1613,22 +1611,22 @@ Maybe<uint64_t> nsFocusManager::SetFocus do_QueryInterface(browsingContextToFocus->GetDOMWindow()); MOZ_ASSERT(focused && newFocus, "BrowsingContext should always have a window here."); focusedPrincipal = focused->GetPrincipal(); newPrincipal = newFocus->GetPrincipal(); } if (!focusedPrincipal || !newPrincipal) { - return Nothing(); + return; } if (!focusedPrincipal->Subsumes(newPrincipal)) { NS_WARNING("Not allowed to focus the new window!"); - return Nothing(); + return; } } // to check if the new element is in the active window, compare the // new root docshell for the new element with the active window's docshell. RefPtr<BrowsingContext> newRootBrowsingContext = nullptr; bool isElementInActiveWindow = false; if (XRE_IsParentProcess()) { @@ -1725,22 +1723,21 @@ Maybe<uint64_t> nsFocusManager::SetFocus !nsContentUtils::CanCallerAccess(mFocusedElement)) { sendFocusEvent = false; } LOGCONTENT("Shift Focus: %s", elementToFocus.get()); LOGFOCUS((" Flags: %x Current Window: %p New Window: %p Current Element: %p", aFlags, mFocusedWindow.get(), newWindow.get(), mFocusedElement.get())); - const uint64_t actionId = GenerateFocusActionId(); LOGFOCUS( (" In Active Window: %d Moves to different BrowsingContext: %d " "SendFocus: %d actionid: %" PRIu64, isElementInActiveWindow, focusMovesToDifferentBC, sendFocusEvent, - actionId)); + aActionId)); if (sendFocusEvent) { Maybe<BlurredElementInfo> blurredInfo; if (mFocusedElement) { blurredInfo.emplace(*mFocusedElement); } // return if blurring fails or the focus changes during the blur if (focusedBrowsingContext) { @@ -1783,29 +1780,29 @@ Maybe<uint64_t> nsFocusManager::SetFocus focusMovesToDifferentBC && IsEmeddededInNoautofocusPopup(*browsingContextToFocus); // TODO: MOZ_KnownLive is required due to bug 1770680 if (!Blur(MOZ_KnownLive(needToClearFocusedElement ? focusedBrowsingContext.get() : nullptr), commonAncestor, focusMovesToDifferentBC, aAdjustWidget, - remainActive, actionId, elementToFocus)) { - return Some(actionId); + remainActive, aActionId, elementToFocus)) { + return; } } Focus(newWindow, elementToFocus, aFlags, focusMovesToDifferentBC, - aFocusChanged, false, aAdjustWidget, actionId, blurredInfo); + aFocusChanged, false, aAdjustWidget, aActionId, blurredInfo); } else { // otherwise, for inactive windows and when the caller cannot steal the // focus, update the node in the window, and raise the window if desired. if (allowFrameSwitch) { AdjustWindowFocus(newBrowsingContext, true, IsWindowVisible(newWindow), - actionId); + aActionId); } // set the focus node and method as needed uint32_t focusMethod = aFocusChanged ? aFlags & METHODANDRING_MASK : newWindow->GetFocusMethod() | (aFlags & (FLAG_SHOWRING | FLAG_NOSHOWRING)); newWindow->SetFocusedElement(elementToFocus, focusMethod); @@ -1827,31 +1824,30 @@ Maybe<uint64_t> nsFocusManager::SetFocus if (aFlags & FLAG_RAISE) { if (newRootBrowsingContext) { if (XRE_IsParentProcess() || newRootBrowsingContext->IsInProcess()) { nsCOMPtr<nsPIDOMWindowOuter> outerWindow = newRootBrowsingContext->GetDOMWindow(); RaiseWindow(outerWindow, aFlags & FLAG_NONSYSTEMCALLER ? CallerType::NonSystem : CallerType::System, - actionId); + aActionId); } else { mozilla::dom::ContentChild* contentChild = mozilla::dom::ContentChild::GetSingleton(); MOZ_ASSERT(contentChild); contentChild->SendRaiseWindow(newRootBrowsingContext, aFlags & FLAG_NONSYSTEMCALLER ? CallerType::NonSystem : CallerType::System, - actionId); + aActionId); } } } } - return Some(actionId); } static already_AddRefed<BrowsingContext> GetParentIgnoreChromeBoundary( BrowsingContext* aBC) { // Chrome BrowsingContexts are only available in the parent process, so if // we're in a content process, we only worry about the context tree. if (XRE_IsParentProcess()) { return aBC->Canonical()->GetParentCrossChromeBoundary();
--- a/dom/base/nsFocusManager.h +++ b/dom/base/nsFocusManager.h @@ -209,17 +209,18 @@ class nsFocusManager final : public nsIF MOZ_CAN_RUN_SCRIPT nsresult DetermineElementToMoveFocus( nsPIDOMWindowOuter* aWindow, nsIContent* aStart, int32_t aType, bool aNoParentTraversal, bool aNavigateByKey, nsIContent** aNextContent); /** * Setter for focusedWindow with CallerType */ MOZ_CAN_RUN_SCRIPT nsresult SetFocusedWindowWithCallerType( - mozIDOMWindowProxy* aWindowToFocus, mozilla::dom::CallerType aCallerType); + mozIDOMWindowProxy* aWindowToFocus, mozilla::dom::CallerType aCallerType, + uint64_t aActionId); /** * Given an element, which must be the focused element, activate the remote * frame it embeds, if any. */ void ActivateRemoteFrameIfNeeded(mozilla::dom::Element&, uint64_t aActionId); /** @@ -308,23 +309,20 @@ class nsFocusManager final : public nsIF * bitmask of the flags defined in nsIFocusManager. If aFocusChanged is * true, then the focus has actually shifted and the caret position will be * updated to the new focus, aNewContent will be scrolled into view (unless * a flag disables this) and the focus method for the window will be updated. * If aAdjustWidget is false, don't change the widget focus state. * * All actual focus changes must use this method to do so. (as opposed * to those that update the focus in an inactive window for instance). - * - * Returns Nothing() if we end up not trying to focus the element, - * otherwise returns the generated action id. */ - MOZ_CAN_RUN_SCRIPT Maybe<uint64_t> SetFocusInner( - mozilla::dom::Element* aNewContent, int32_t aFlags, bool aFocusChanged, - bool aAdjustWidget); + MOZ_CAN_RUN_SCRIPT void SetFocusInner(mozilla::dom::Element* aNewContent, + int32_t aFlags, bool aFocusChanged, + bool aAdjustWidget, uint64_t aActionId); /** * Returns true if aPossibleAncestor is the same as aWindow or an * ancestor of aWindow. */ bool IsSameOrAncestor(nsPIDOMWindowOuter* aPossibleAncestor, nsPIDOMWindowOuter* aWindow) const; bool IsSameOrAncestor(nsPIDOMWindowOuter* aPossibleAncestor,
--- a/dom/base/nsStyledElement.cpp +++ b/dom/base/nsStyledElement.cpp @@ -211,13 +211,13 @@ void nsStyledElement::ParseStyleAttribut } nsresult nsStyledElement::BindToTree(BindContext& aContext, nsINode& aParent) { nsresult rv = nsStyledElementBase::BindToTree(aContext, aParent); NS_ENSURE_SUCCESS(rv, rv); if (HasAttr(nsGkAtoms::autofocus) && aContext.AllowsAutoFocus() && (!IsSVGElement() || IsFocusable())) { - aContext.OwnerDoc().ElementWithAutoFocusInserted(this); + aContext.OwnerDoc().SetAutoFocusElement(this); } return NS_OK; }
--- a/dom/html/nsHTMLContentSink.cpp +++ b/dom/html/nsHTMLContentSink.cpp @@ -109,17 +109,16 @@ class HTMLContentSink : public nsContent NS_IMETHOD DidBuildModel(bool aTerminated) override; NS_IMETHOD WillInterrupt(void) override; void WillResume() override; NS_IMETHOD SetParser(nsParserBase* aParser) override; virtual void FlushPendingNotifications(FlushType aType) override; virtual void SetDocumentCharset(NotNull<const Encoding*> aEncoding) override; virtual nsISupports* GetTarget() override; virtual bool IsScriptExecuting() override; - virtual bool WaitForPendingSheets() override; virtual void ContinueInterruptedParsingAsync() override; // nsIHTMLContentSink NS_IMETHOD OpenContainer(ElementType aNodeType) override; NS_IMETHOD CloseContainer(ElementType aTag) override; protected: virtual ~HTMLContentSink(); @@ -920,20 +919,16 @@ nsISupports* HTMLContentSink::GetTarget( bool HTMLContentSink::IsScriptExecuting() { return IsScriptExecutingImpl(); } void HTMLContentSink::ContinueInterruptedParsingIfEnabled() { if (mParser && mParser->IsParserEnabled()) { static_cast<nsIParser*>(mParser.get())->ContinueInterruptedParsing(); } } -bool HTMLContentSink::WaitForPendingSheets() { - return nsContentSink::WaitForPendingSheets(); -} - void HTMLContentSink::ContinueInterruptedParsingAsync() { nsCOMPtr<nsIRunnable> ev = NewRunnableMethod( "HTMLContentSink::ContinueInterruptedParsingIfEnabled", this, &HTMLContentSink::ContinueInterruptedParsingIfEnabled); RefPtr<Document> doc = mHTMLDocument; doc->Dispatch(mozilla::TaskCategory::Other, ev.forget()); }
--- a/dom/html/reftests/autofocus/autofocus-after-load-ref.html +++ b/dom/html/reftests/autofocus/autofocus-after-load-ref.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <link rel='stylesheet' type='text/css' href='style.css'> <body> - <input autofocus><textarea></textarea><select></select><button></button> + <input><textarea></textarea><select></select><button></button> </body> </html>
--- a/dom/tests/browser/browser_autofocus_background.js +++ b/dom/tests/browser/browser_autofocus_background.js @@ -20,23 +20,16 @@ add_task(async function() { BrowserTestUtils.loadURIString(backgroundTab.linkedBrowser, URL); await loadedPromise; // Get active element in the tab. let tagName = await SpecialPowers.spawn( backgroundTab.linkedBrowser, [], async function() { - // Spec asks us to flush autofocus candidates in the - // `update-the-rendering` step, so we need to wait - // for a rAF to ensure autofocus candidates are - // flushed. - await new Promise(r => { - content.requestAnimationFrame(r); - }); return content.document.activeElement.tagName; } ); is( tagName, "INPUT", "The background tab's focused element should be the <input>"
--- a/layout/base/PresShell.cpp +++ b/layout/base/PresShell.cpp @@ -1845,19 +1845,17 @@ nsresult PresShell::Initialize() { } // Something in mFrameConstructor->ContentInserted may have caused // Destroy() to get called, bug 337586. Or, nsAutoCauseReflowNotifier // (which sets up a script blocker) going out of scope may have killed us // too NS_ENSURE_STATE(!mHaveShutDown); } - if (mDocument->HasAutoFocusCandidates()) { - mDocument->ScheduleFlushAutoFocusCandidates(); - } + mDocument->TriggerAutoFocus(); NS_ASSERTION(rootFrame, "How did that happen?"); // Note: when the frame was created above it had the NS_FRAME_IS_DIRTY bit // set, but XBL processing could have caused a reflow which clears it. if (MOZ_LIKELY(rootFrame->HasAnyStateBits(NS_FRAME_IS_DIRTY))) { // Unset the DIRTY bits so that FrameNeedsReflow() will work right. rootFrame->RemoveStateBits(NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN); @@ -3133,18 +3131,62 @@ nsresult PresShell::GoToAnchor(const nsA // 2. If the indicated part of the document is the top of the document, // then: // (handled below when `target` is null, and anchor is `top`) // 3.1. Let target be element that is the indicated part of the document. // // https://html.spec.whatwg.org/#target-element // https://html.spec.whatwg.org/#find-a-potential-indicated-element - RefPtr<Element> target = - nsContentUtils::GetTargetElement(mDocument, aAnchorName); + RefPtr<Element> target = [&]() -> Element* { + // 1. If there is an element in the document tree that has an ID equal to + // fragment, then return the first such element in tree order. + if (Element* el = mDocument->GetElementById(aAnchorName)) { + return el; + } + + // 2. If there is an a element in the document tree that has a name + // attribute whose value is equal to fragment, then return the first such + // element in tree order. + // + // FIXME(emilio): Why the different code-paths for HTML and non-HTML docs? + if (mDocument->IsHTMLDocument()) { + nsCOMPtr<nsINodeList> list = mDocument->GetElementsByName(aAnchorName); + // Loop through the named nodes looking for the first anchor + uint32_t length = list->Length(); + for (uint32_t i = 0; i < length; i++) { + nsIContent* node = list->Item(i); + if (node->IsHTMLElement(nsGkAtoms::a)) { + return node->AsElement(); + } + } + } else { + constexpr auto nameSpace = u"http://www.w3.org/1999/xhtml"_ns; + // Get the list of anchor elements + nsCOMPtr<nsINodeList> list = + mDocument->GetElementsByTagNameNS(nameSpace, u"a"_ns); + // Loop through the anchors looking for the first one with the given name. + for (uint32_t i = 0; true; i++) { + nsIContent* node = list->Item(i); + if (!node) { // End of list + break; + } + + // Compare the name attribute + if (node->IsElement() && + node->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, + aAnchorName, eCaseMatters)) { + return node->AsElement(); + } + } + } + + // 3. Return null. + return nullptr; + }(); // 1. If there is no indicated part of the document, set the Document's // target element to null. // 2.1. Set the Document's target element to null. // 3.2. Set the Document's target element to target. esm->SetContentState(target, ElementState::URLTARGET); // TODO: Spec probably needs a section to account for this.
--- a/layout/base/nsRefreshDriver.cpp +++ b/layout/base/nsRefreshDriver.cpp @@ -1838,17 +1838,16 @@ uint32_t nsRefreshDriver::ObserverCount( sum += mStyleFlushObservers.Length(); sum += mLayoutFlushObservers.Length(); sum += mPendingFullscreenEvents.Length(); sum += mFrameRequestCallbackDocs.Length(); sum += mThrottledFrameRequestCallbackDocs.Length(); sum += mViewManagerFlushIsPending; sum += mEarlyRunners.Length(); sum += mTimerAdjustmentObservers.Length(); - sum += mAutoFocusFlushDocuments.Length(); return sum; } bool nsRefreshDriver::HasObservers() const { for (const ObserverArray& array : mObservers) { if (!array.IsEmpty()) { return true; } @@ -1859,17 +1858,17 @@ bool nsRefreshDriver::HasObservers() con // adjustment observers should not influence timer starting or stopping. return mViewManagerFlushIsPending || !mStyleFlushObservers.IsEmpty() || !mLayoutFlushObservers.IsEmpty() || !mAnimationEventFlushObservers.IsEmpty() || !mResizeEventFlushObservers.IsEmpty() || !mPendingFullscreenEvents.IsEmpty() || !mFrameRequestCallbackDocs.IsEmpty() || !mThrottledFrameRequestCallbackDocs.IsEmpty() || - !mAutoFocusFlushDocuments.IsEmpty() || !mEarlyRunners.IsEmpty(); + !mEarlyRunners.IsEmpty(); } void nsRefreshDriver::AppendObserverDescriptionsToString( nsACString& aStr) const { for (const ObserverArray& array : mObservers) { for (const auto& observer : array.EndLimitedRange()) { aStr.AppendPrintf("%s [%s], ", observer.mDescription, kFlushTypeNames[observer.mFlushType]); @@ -1901,20 +1900,16 @@ void nsRefreshDriver::AppendObserverDesc if (!mFrameRequestCallbackDocs.IsEmpty()) { aStr.AppendPrintf("%zux Frame request callback doc, ", mFrameRequestCallbackDocs.Length()); } if (!mThrottledFrameRequestCallbackDocs.IsEmpty()) { aStr.AppendPrintf("%zux Throttled frame request callback doc, ", mThrottledFrameRequestCallbackDocs.Length()); } - if (!mAutoFocusFlushDocuments.IsEmpty()) { - aStr.AppendPrintf("%zux AutoFocus flush doc, ", - mAutoFocusFlushDocuments.Length()); - } if (!mEarlyRunners.IsEmpty()) { aStr.AppendPrintf("%zux Early runner, ", mEarlyRunners.Length()); } // Remove last ", " aStr.Truncate(aStr.Length() - 2); } bool nsRefreshDriver::HasImageRequests() const { @@ -2134,34 +2129,16 @@ static void GetProfileTimelineSubDocShel } static void TakeFrameRequestCallbacksFrom( Document* aDocument, nsTArray<DocumentFrameCallbacks>& aTarget) { aTarget.AppendElement(aDocument); aDocument->TakeFrameRequestCallbacks(aTarget.LastElement().mCallbacks); } -void nsRefreshDriver::ScheduleAutoFocusFlush(Document* aDocument) { - MOZ_ASSERT(!mAutoFocusFlushDocuments.Contains(aDocument)); - mAutoFocusFlushDocuments.AppendElement(aDocument); - EnsureTimerStarted(); -} - -void nsRefreshDriver::FlushAutoFocusDocuments() { - nsTArray<RefPtr<Document>> docs(std::move(mAutoFocusFlushDocuments)); - - for (const auto& doc : docs) { - MOZ_KnownLive(doc)->FlushAutoFocusCandidates(); - } -} - -void nsRefreshDriver::CancelFlushAutoFocus(Document* aDocument) { - mAutoFocusFlushDocuments.RemoveElement(aDocument); -} - // https://fullscreen.spec.whatwg.org/#run-the-fullscreen-steps void nsRefreshDriver::RunFullscreenSteps() { // Swap out the current pending events nsTArray<UniquePtr<PendingFullscreenEvent>> pendings( std::move(mPendingFullscreenEvents)); for (UniquePtr<PendingFullscreenEvent>& event : pendings) { event->Dispatch(); } @@ -2592,17 +2569,16 @@ void nsRefreshDriver::Tick(VsyncId aId, if (i == 1 && (!mPresContext || !mPresContext->GetPresShell())) { StopTimer(); return; } if (i == 1) { // This is the FlushType::Style case. - FlushAutoFocusDocuments(); DispatchScrollEvents(); DispatchVisualViewportScrollEvents(); DispatchAnimationEvents(); RunFullscreenSteps(); RunFrameRequestCallbacks(aNowTime); if (mPresContext && mPresContext->GetPresShell()) { AutoTArray<PresShell*, 16> observers;
--- a/layout/base/nsRefreshDriver.h +++ b/layout/base/nsRefreshDriver.h @@ -341,18 +341,16 @@ class nsRefreshDriver final : public moz void ClearPendingTransactions() override; void ResetInitialTransactionId(TransactionId aTransactionId) override; mozilla::TimeStamp GetTransactionStart() override; mozilla::VsyncId GetVsyncId() override; mozilla::TimeStamp GetVsyncStart() override; bool IsWaitingForPaint(mozilla::TimeStamp aTime); - void ScheduleAutoFocusFlush(Document* aDocument); - // nsARefreshObserver NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override { return TransactionIdAllocator::AddRef(); } NS_IMETHOD_(MozExternalRefCountType) Release(void) override { return TransactionIdAllocator::Release(); } virtual void WillRefresh(mozilla::TimeStamp aTime) override; @@ -444,18 +442,16 @@ class nsRefreshDriver final : public moz void AddForceNotifyContentfulPaintPresContext(nsPresContext* aPresContext); void FlushForceNotifyContentfulPaintPresContext(); // Mark that we've just run a tick from vsync, used to throttle 'extra' // paints to one per vsync (see CanDoExtraTick). void FinishedVsyncTick() { mAttemptedExtraTickSinceLastVsync = false; } - void CancelFlushAutoFocus(Document* aDocument); - private: typedef nsTArray<RefPtr<VVPResizeEvent>> VisualViewportResizeEventArray; typedef nsTArray<RefPtr<mozilla::Runnable>> ScrollEventArray; typedef nsTArray<RefPtr<VVPScrollEvent>> VisualViewportScrollEventArray; using RequestTable = nsTHashSet<RefPtr<imgIRequest>>; struct ImageStartData { ImageStartData() = default; @@ -473,18 +469,16 @@ class nsRefreshDriver final : public moz mozilla::FlushType mFlushType; bool operator==(nsARefreshObserver* aObserver) const { return mObserver == aObserver; } operator RefPtr<nsARefreshObserver>() { return mObserver; } }; typedef nsTObserverArray<ObserverData> ObserverArray; - MOZ_CAN_RUN_SCRIPT - void FlushAutoFocusDocuments(); void RunFullscreenSteps(); void DispatchAnimationEvents(); MOZ_CAN_RUN_SCRIPT void RunFrameRequestCallbacks(mozilla::TimeStamp aNowTime); void UpdateIntersectionObservations(mozilla::TimeStamp aNowTime); void UpdateRelevancyOfContentVisibilityAutoFrames(); enum class IsExtraTick { @@ -670,17 +664,16 @@ class nsRefreshDriver final : public moz AutoTArray<mozilla::PresShell*, 16> mResizeEventFlushObservers; AutoTArray<mozilla::PresShell*, 16> mDelayedResizeEventFlushObservers; AutoTArray<mozilla::PresShell*, 16> mStyleFlushObservers; AutoTArray<mozilla::PresShell*, 16> mLayoutFlushObservers; // nsTArray on purpose, because we want to be able to swap. nsTArray<Document*> mFrameRequestCallbackDocs; nsTArray<Document*> mThrottledFrameRequestCallbackDocs; - nsTArray<RefPtr<Document>> mAutoFocusFlushDocuments; nsTObserverArray<nsAPostRefreshObserver*> mPostRefreshObservers; nsTArray<mozilla::UniquePtr<mozilla::PendingFullscreenEvent>> mPendingFullscreenEvents; AutoTArray<mozilla::AnimationEventDispatcher*, 16> mAnimationEventFlushObservers; // nsPresContexts which `NotifyContentfulPaint` have been called, // however the corresponding paint doesn't come from a regular
--- a/parser/html/nsHtml5TreeOpExecutor.h +++ b/parser/html/nsHtml5TreeOpExecutor.h @@ -126,18 +126,16 @@ class nsHtml5TreeOpExecutor final */ NS_IMETHOD WillInterrupt() override; /** * Unimplemented. For interface compat only. */ void WillResume() override; - virtual nsIContentSink* AsExecutor() override { return this; } - virtual void InitialTranslationCompleted() override; /** * Sets the parser. */ NS_IMETHOD SetParser(nsParserBase* aParser) override; /**
--- a/parser/htmlparser/nsIContentSink.h +++ b/parser/htmlparser/nsIContentSink.h @@ -85,22 +85,16 @@ class nsIContentSink : public nsISupport /** * This method gets called when the parser i/o gets unblocked, * and we're about to start dumping content again to the sink. */ virtual void WillResume() = 0; /** - * This method returns nullptr unless `this` can - * be cast as nsHtml5TreeOpExecutor. - */ - virtual nsIContentSink* AsExecutor() { return nullptr; } - - /** * This method gets called by the parser so that the content * sink can retain a reference to the parser. The expectation * is that the content sink will drop the reference when it * gets the DidBuildModel notification i.e. when parsing is done. */ NS_IMETHOD SetParser(nsParserBase* aParser) = 0; /**
--- a/parser/htmlparser/nsIHTMLContentSink.h +++ b/parser/htmlparser/nsIHTMLContentSink.h @@ -78,19 +78,13 @@ class nsIHTMLContentSink : public nsICon /** * This method gets called by the parser when a close * container tag has been consumed and needs to be closed. * * @param aTag - The tag to be closed. */ NS_IMETHOD CloseContainer(ElementType aTag) = 0; - - /** - * This method returns true if there are more than one - * pending style sheets, false otherwise. - */ - virtual bool WaitForPendingSheets() = 0; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsIHTMLContentSink, NS_IHTML_CONTENT_SINK_IID) #endif /* nsIHTMLContentSink_h___ */
new file mode 100644 --- /dev/null +++ b/testing/web-platform/meta/html/interaction/focus/the-autofocus-attribute/autofocus-dialog.html.ini @@ -0,0 +1,5 @@ +[autofocus-dialog.html] + expected: + if (os == "android") and fission: [OK, TIMEOUT] + [<dialog> can contain autofocus, without stopping page autofocus content from working] + expected: FAIL
new file mode 100644 --- /dev/null +++ b/testing/web-platform/meta/html/interaction/focus/the-autofocus-attribute/autofocus-in-not-fully-active-document.html.ini @@ -0,0 +1,3 @@ +[autofocus-in-not-fully-active-document.html] + expected: + if (os == "android") and fission: [OK, TIMEOUT]
new file mode 100644 --- /dev/null +++ b/testing/web-platform/meta/html/interaction/focus/the-autofocus-attribute/autofocus-on-stable-document.html.ini @@ -0,0 +1,5 @@ +[autofocus-on-stable-document.html] + expected: + if (os == "android") and fission: [OK, TIMEOUT] + [Autofocus should work if an element with autofocus is inserted into a document which was loaded some time ago.] + expected: FAIL
new file mode 100644 --- /dev/null +++ b/testing/web-platform/meta/html/interaction/focus/the-autofocus-attribute/document-with-fragment-empty.html.ini @@ -0,0 +1,8 @@ +[document-with-fragment-empty.html] + [Autofocus elements in iframed documents with empty fragments should work.] + expected: + if not debug and (os == "win") and (processor == "x86"): [FAIL, PASS] + if debug and (os == "android"): PASS + if debug and (os == "mac"): PASS + if debug and (os == "linux"): PASS + [PASS, FAIL]
new file mode 100644 --- /dev/null +++ b/testing/web-platform/meta/html/interaction/focus/the-autofocus-attribute/document-with-fragment-nonexistent.html.ini @@ -0,0 +1,6 @@ +[document-with-fragment-nonexistent.html] + [Autofocus elements in iframed documents with non-existent fragments should work.] + expected: + if debug and (os == "linux"): PASS + if debug and (os == "android"): PASS + [PASS, FAIL]
new file mode 100644 --- /dev/null +++ b/testing/web-platform/meta/html/interaction/focus/the-autofocus-attribute/document-with-fragment-top.html.ini @@ -0,0 +1,7 @@ +[document-with-fragment-top.html] + [Autofocus elements in iframed documents with "top" fragments should work.] + expected: + if debug and (os == "android"): PASS + if debug and (os == "linux"): PASS + if debug and (os == "mac"): PASS + [PASS, FAIL]
new file mode 100644 --- /dev/null +++ b/testing/web-platform/meta/html/interaction/focus/the-autofocus-attribute/first-reconnected.html.ini @@ -0,0 +1,5 @@ +[first-reconnected.html] + expected: + if (os == "android") and fission: [OK, TIMEOUT] + [The second autofocus element wins if the first autofocus element was disconnected and reconnected before flushing the autofocus candidates.] + expected: FAIL
new file mode 100644 --- /dev/null +++ b/testing/web-platform/meta/html/interaction/focus/the-autofocus-attribute/first-when-later-but-before.html.ini @@ -0,0 +1,3 @@ +[first-when-later-but-before.html] + [The temporally first autofocus in the document wins, even if an element is inserted later that is previous in the document tree.] + expected: [PASS, FAIL]
new file mode 100644 --- /dev/null +++ b/testing/web-platform/meta/html/interaction/focus/the-autofocus-attribute/first-when-later.html.ini @@ -0,0 +1,3 @@ +[first-when-later.html] + [The first autofocus in the document wins, even if elements are inserted later.] + expected: [PASS, FAIL]
new file mode 100644 --- /dev/null +++ b/testing/web-platform/meta/html/interaction/focus/the-autofocus-attribute/first.html.ini @@ -0,0 +1,3 @@ +[first.html] + [The first autofocus element in the document should win.] + expected: [PASS, FAIL]
new file mode 100644 --- /dev/null +++ b/testing/web-platform/meta/html/interaction/focus/the-autofocus-attribute/focusable-area-in-top-document.html.ini @@ -0,0 +1,5 @@ +[focusable-area-in-top-document.html] + [If topDocument's focused area is not topDocument, autofocus is not processed.] + expected: + if (processor == "x86") and (os == "linux"): [FAIL, PASS] + [PASS, FAIL]
new file mode 100644 --- /dev/null +++ b/testing/web-platform/meta/html/interaction/focus/the-autofocus-attribute/no-autofocus-on-changing-input-type.html.ini @@ -0,0 +1,3 @@ +[no-autofocus-on-changing-input-type.html] + [Changing input type should not refocus on the element.] + expected: [PASS, FAIL]
new file mode 100644 --- /dev/null +++ b/testing/web-platform/meta/html/interaction/focus/the-autofocus-attribute/no-cross-origin-autofocus.html.ini @@ -0,0 +1,3 @@ +[no-cross-origin-autofocus.html] + expected: + if (os == "android") and fission: [OK, TIMEOUT]
new file mode 100644 --- /dev/null +++ b/testing/web-platform/meta/html/interaction/focus/the-autofocus-attribute/no-sandboxed-automatic-features.html.ini @@ -0,0 +1,3 @@ +[no-sandboxed-automatic-features.html] + expected: + if (os == "android") and fission: [OK, TIMEOUT]
new file mode 100644 --- /dev/null +++ b/testing/web-platform/meta/html/interaction/focus/the-autofocus-attribute/not-on-first-task.html.ini @@ -0,0 +1,3 @@ +[not-on-first-task.html] + expected: + if (os == "android") and fission: [OK, TIMEOUT]
new file mode 100644 --- /dev/null +++ b/testing/web-platform/meta/html/interaction/focus/the-autofocus-attribute/queue-non-focusable.html.ini @@ -0,0 +1,8 @@ +[queue-non-focusable.html] + [If the first autofocus element is not focusable, but becomes focusable before a frame, it should be focused.] + expected: + if debug and (os == "win") and swgl: PASS + if debug and (os == "android"): PASS + if debug and (os == "linux"): PASS + if debug and (os == "mac"): PASS + [PASS, FAIL]
new file mode 100644 --- /dev/null +++ b/testing/web-platform/meta/html/interaction/focus/the-autofocus-attribute/same-origin-autofocus.html.ini @@ -0,0 +1,5 @@ +[same-origin-autofocus.html] + expected: + if (os == "android") and fission: [OK, TIMEOUT] + [Autofocus should not work in the same origin grand child iframe] + expected: FAIL
new file mode 100644 --- /dev/null +++ b/testing/web-platform/meta/html/interaction/focus/the-autofocus-attribute/skip-another-top-level-browsing-context.html.ini @@ -0,0 +1,3 @@ +[skip-another-top-level-browsing-context.html] + expected: + if (os == "android") and fission: [OK, TIMEOUT]
new file mode 100644 --- /dev/null +++ b/testing/web-platform/meta/html/interaction/focus/the-autofocus-attribute/skip-non-focusable.html.ini @@ -0,0 +1,5 @@ +[skip-non-focusable.html] + expected: + if (os == "android") and fission: [OK, TIMEOUT] + [Non-focusable autofocus element is skipped.] + expected: FAIL
new file mode 100644 --- /dev/null +++ b/testing/web-platform/meta/html/interaction/focus/the-autofocus-attribute/skip-not-fully-active.html.ini @@ -0,0 +1,3 @@ +[skip-not-fully-active.html] + expected: + if (os == "android") and fission: [OK, TIMEOUT]
new file mode 100644 --- /dev/null +++ b/testing/web-platform/meta/html/interaction/focus/the-autofocus-attribute/spin-by-blocking-style-sheet.html.ini @@ -0,0 +1,5 @@ +[spin-by-blocking-style-sheet.html] + expected: TIMEOUT + [Script-blocking style sheet should pause flushing autofocus candidates.] + expected: TIMEOUT +
new file mode 100644 --- /dev/null +++ b/testing/web-platform/meta/html/interaction/focus/the-autofocus-attribute/supported-elements.html.ini @@ -0,0 +1,17 @@ +[supported-elements.html] + expected: + if (os == "android") and fission: [OK, TIMEOUT] + [Contenteditable element should support autofocus] + expected: FAIL + + [Element with tabindex should support autofocus] + expected: FAIL + + [Host element with delegatesFocus including no focusable descendants should be skipped] + expected: FAIL + + [Area element should support autofocus] + expected: FAIL + + [Host element with delegatesFocus should support autofocus] + expected: FAIL
--- a/testing/web-platform/meta/html/interaction/focus/the-autofocus-attribute/update-the-rendering.html.ini +++ b/testing/web-platform/meta/html/interaction/focus/the-autofocus-attribute/update-the-rendering.html.ini @@ -1,5 +1,5 @@ [update-the-rendering.html] expected: if (os == "android") and fission: [OK, TIMEOUT] ["Flush autofocus candidates" should be happen before a scroll event and animation frame callbacks] - expected: [PASS, FAIL] + expected: FAIL
--- a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/document-with-fragment-top.html +++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/document-with-fragment-top.html @@ -5,22 +5,19 @@ <iframe src="resources/frame-with-autofocus-element.html#top"></iframe> <script> 'use strict'; promise_test(async () => { await waitForLoad(window); - const iframe = document.querySelector('iframe'); await waitUntilStableAutofocusState(); - assert_equals(document.activeElement, iframe, + assert_equals(document.activeElement, document.querySelector('iframe'), 'Autofocus elements in iframes should be focused.'); - const doc = iframe.contentDocument; - assert_true(!doc.querySelector(':target')); let input = document.createElement('input'); input.autofocus = true; document.body.appendChild(input); await waitUntilStableAutofocusState(); assert_not_equals(document.activeElement, input); }, 'Autofocus elements in iframed documents with "top" fragments should work.');
--- a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/document-with-fragment-valid.html +++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/document-with-fragment-valid.html @@ -16,33 +16,17 @@ promise_test(async () => { const doc = iframe.contentDocument; assert_true(!!doc.querySelector(':target')); let input = doc.createElement('input'); input.autofocus = true; doc.body.appendChild(input); await waitUntilStableAutofocusState(); assert_not_equals(doc.activeElement, input); - iframe.remove(); -}, 'Autofocus elements in iframed documents with URL fragments should be skipped. (id matches)'); - -promise_test(async () => { - let iframe = await waitForIframeLoad("resources/frame-with-a.html"); - iframe.contentWindow.location.hash = 'anchor1'; - await waitForEvent(iframe.contentWindow, 'hashchange'); - const doc = iframe.contentDocument; - assert_true(!!doc.querySelector(':target')); - - let input = doc.createElement('input'); - input.autofocus = true; - doc.body.appendChild(input); - await waitUntilStableAutofocusState(); - assert_not_equals(doc.activeElement, input); - iframe.remove(); -}, 'Autofocus elements in iframed documents with URL fragments should be skipped.(a element)'); +}, 'Autofocus elements in iframed documents with URL fragments should be skipped.'); promise_test(async () => { let w = window.open('resources/frame-with-anchor.html'); await waitForLoad(w); w.location.hash = 'anchor1'; await waitForEvent(w, 'hashchange'); const doc = w.document; assert_true(!!doc.querySelector(':target'));
deleted file mode 100644 --- a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/frame-with-a.html +++ /dev/null @@ -1,4 +0,0 @@ -<!DOCTYPE html> -<body> -<a name="anchor1"></a> -</body>
--- a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/utils.js +++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/resources/utils.js @@ -34,18 +34,8 @@ function timeOut(test, ms) { // function. // Exception: If the document has script-blocking style sheets, this function // doesn't work well. async function waitUntilStableAutofocusState(w) { let targetWindow = w || window; // Awaiting one animation frame is an easy way to determine autofocus state. await waitForAnimationFrame(targetWindow); } - -async function waitForIframeLoad(src, w = window) { - const iframe = w.document.createElement("iframe"); - let loadPromise = new Promise(resolve => { - iframe.addEventListener("load", () => resolve(iframe)); - }); - iframe.src = src; - w.document.body.appendChild(iframe); - return loadPromise; -}
--- a/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/spin-by-blocking-style-sheet.html +++ b/testing/web-platform/tests/html/interaction/focus/the-autofocus-attribute/spin-by-blocking-style-sheet.html @@ -1,19 +1,17 @@ <!DOCTYPE html> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> <script src="resources/utils.js"></script> +<link rel="stylesheet" href="resources/erase-first.css?pipe=trickle(d1)"> <input id="first" autofocus> <input id="second" autofocus> -<link rel="stylesheet" href="resources/erase-first.css?pipe=trickle(d1)"> - <script> 'use strict'; promise_test(async () => { - await waitForLoad(window); - await waitForAnimationFrame(); + await waitForEvent(document.body, 'focus', {capture:true}); assert_equals(document.activeElement.id, 'second'); }, 'Script-blocking style sheet should pause flushing autofocus candidates.'); </script>