author | decltype <mozilla@decltype.org> |
Mon, 08 Jan 2018 22:35:00 +0100 | |
changeset 404032 | a87c09c4434a670d51bc949b3906899177bdce76 |
parent 404031 | 0208069d01216e1166810214585d2f0e9f59216b |
child 404033 | dc9ba6649af7290170dc76be598ea669a169c6a2 |
push id | 99924 |
push user | ebalazs@mozilla.com |
push date | Thu, 15 Feb 2018 20:43:51 +0000 |
treeherder | mozilla-inbound@a7d2a49f46fb [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | bz |
bugs | 712130 |
milestone | 60.0a1 |
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/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -1577,16 +1577,17 @@ nsDocument::nsDocument(const char* aCont , mNeedsReleaseAfterStackRefCntRelease(false) , mMaybeServiceWorkerControlled(false) #ifdef DEBUG , mWillReparent(false) #endif , mDOMLoadingSet(false) , mDOMInteractiveSet(false) , mDOMCompleteSet(false) + , mAutoFocusFired(false) { SetContentTypeInternal(nsDependentCString(aContentType)); MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p created", this)); // Start out mLastStyleSheetSet as null, per spec SetDOMStringToNull(mLastStyleSheetSet); @@ -9439,16 +9440,143 @@ nsDocument::GetTemplateContentsOwner() // |doc| is the template contents owner of template elements created // by |doc|. doc->mTemplateContentsOwner = doc; } return mTemplateContentsOwner; } +static already_AddRefed<nsPIDOMWindowOuter> +FindTopWindowForElement(Element* element) +{ + nsIDocument* 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->GetTop()) { + window = top.forget(); + } + 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(already_AddRefed<Element>&& aElement, + already_AddRefed<nsPIDOMWindowOuter>&& aTopWindow) + : mozilla::Runnable("nsAutoFocusEvent") + , mElement(aElement) + , mTopWindow(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; + } + + nsFocusManager* fm = nsFocusManager::GetFocusManager(); + if (!fm) { + return NS_ERROR_NULL_POINTER; + } + + nsIDocument* document = mElement->OwnerDoc(); + + // Don't steal focus from the user. + if (mTopWindow->GetFocusedNode()) { + return NS_OK; + } + + // If something is focused in the same document, ignore autofocus. + if (!fm->GetFocusedContent() || + fm->GetFocusedContent()->OwnerDoc() != document) { + mozilla::ErrorResult rv; + mElement->Focus(rv); + return rv.StealNSResult(); + } + + return NS_OK; + } +private: + nsCOMPtr<Element> mElement; + nsCOMPtr<nsPIDOMWindowOuter> mTopWindow; +}; + +void +nsDocument::SetAutoFocusElement(Element* aAutoFocusElement) +{ + if (mAutoFocusFired) { + // 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 +nsDocument::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) { + mAutoFocusFired = true; + + nsCOMPtr<nsPIDOMWindowOuter> topWindow = + FindTopWindowForElement(autoFocusElement); + if (!topWindow) { + return; + } + + // NOTE: This may be removed in the future since the spec technically + // allows autofocus after load. + nsCOMPtr<nsIDocument> topDoc = topWindow->GetExtantDoc(); + if (topDoc && topDoc->GetReadyStateEnum() == nsIDocument::READYSTATE_COMPLETE) { + return; + } + + nsCOMPtr<nsIRunnable> event = + new nsAutoFocusEvent(autoFocusElement.forget(), topWindow.forget()); + nsresult rv = NS_DispatchToCurrentThread(event.forget()); + NS_ENSURE_SUCCESS_VOID(rv); + } +} + void nsDocument::SetScrollToRef(nsIURI *aDocumentURI) { if (!aDocumentURI) { return; } nsAutoCString ref;
--- a/dom/base/nsDocument.h +++ b/dom/base/nsDocument.h @@ -799,16 +799,19 @@ public: virtual nsresult LoadChromeSheetSync(nsIURI* uri, bool isAgentSheet, RefPtr<mozilla::StyleSheet>* aSheet) override; virtual nsISupports* GetCurrentContentSink() override; // Only BlockOnload should call this! void AsyncBlockOnload(); + virtual void SetAutoFocusElement(Element* aAutoFocusElement) override; + virtual void TriggerAutoFocus() override; + virtual void SetScrollToRef(nsIURI *aDocumentURI) override; virtual void ScrollToRef() override; virtual void ResetScrolledToRefAlready() override; virtual void SetChangeScrollPosWhenScrollingToRef(bool aValue) override; virtual Element* LookupImageElement(const nsAString& aElementId) override; virtual void MozSetImageElement(const nsAString& aImageElementId, Element* aElement) override; @@ -1301,16 +1304,18 @@ private: // Set if we've found a URL for the current picture nsString mPreloadPictureFoundSource; RefPtr<mozilla::dom::DOMImplementation> mDOMImplementation; RefPtr<nsContentList> mImageMaps; + nsWeakPtr mAutoFocusElement; + nsCString mScrollToRef; uint8_t mScrolledToRefAlready : 1; uint8_t mChangeScrollPosWhenScrollingToRef : 1; // Tracking for plugins in the document. nsTHashtable< nsPtrHashKey<nsIObjectLoadingContent> > mPlugins; RefPtr<mozilla::dom::DocumentTimeline> mDocumentTimeline; @@ -1351,16 +1356,17 @@ public: bool mWillReparent; #endif private: void RecordNavigationTiming(ReadyState aReadyState); bool mDOMLoadingSet : 1; bool mDOMInteractiveSet : 1; bool mDOMCompleteSet : 1; + bool mAutoFocusFired : 1; }; class nsDocumentOnStack { public: explicit nsDocumentOnStack(nsDocument* aDoc) : mDoc(aDoc) { mDoc->IncreaseStackRefCnt();
--- a/dom/base/nsFocusManager.cpp +++ b/dom/base/nsFocusManager.cpp @@ -1598,17 +1598,17 @@ nsFocusManager::CheckIfFocusable(nsICont nsCOMPtr<nsIDocument> doc = aContent->GetComposedDoc(); // can't focus elements that are not in documents if (!doc) { LOGCONTENT("Cannot focus %s because content not in document", aContent) return nullptr; } // Make sure that our frames are up to date while ensuring the presshell is - // also initialized in case we come from an autofocus event. + // also initialized in case we come from a script calling focus() early. mEventHandlingNeedsFlush = false; doc->FlushPendingNotifications(FlushType::EnsurePresShellInitAndFrames); nsIPresShell *shell = doc->GetShell(); if (!shell) return nullptr; // the root content can always be focused,
--- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -2632,16 +2632,19 @@ public: */ mozilla::EventStates GetDocumentState() const { return mDocumentState; } virtual nsISupports* GetCurrentContentSink() = 0; + virtual void SetAutoFocusElement(Element* aAutoFocusElement) = 0; + virtual void TriggerAutoFocus() = 0; + virtual void SetScrollToRef(nsIURI *aDocumentURI) = 0; virtual void ScrollToRef() = 0; virtual void ResetScrolledToRefAlready() = 0; virtual void SetChangeScrollPosWhenScrollingToRef(bool aValue) = 0; using mozilla::dom::DocumentOrShadowRoot::GetElementById; using mozilla::dom::DocumentOrShadowRoot::GetElementsByTagName; using mozilla::dom::DocumentOrShadowRoot::GetElementsByTagNameNS;
--- a/dom/html/nsGenericHTMLElement.cpp +++ b/dom/html/nsGenericHTMLElement.cpp @@ -106,74 +106,16 @@ #include "mozilla/StyleSetHandleInlines.h" #include "ReferrerPolicy.h" #include "mozilla/dom/HTMLLabelElement.h" #include "mozilla/dom/HTMLInputElement.h" using namespace mozilla; using namespace mozilla::dom; -/** - * nsAutoFocusEvent is used to dispatch a focus event when a - * nsGenericHTMLFormElement is binded to the tree with the autofocus attribute - * enabled. - */ -class nsAutoFocusEvent : public Runnable -{ -public: - explicit nsAutoFocusEvent(nsGenericHTMLFormElement* aElement) - : mozilla::Runnable("nsAutoFocusEvent") - , mElement(aElement) - { - } - - NS_IMETHOD Run() override { - nsFocusManager* fm = nsFocusManager::GetFocusManager(); - if (!fm) { - return NS_ERROR_NULL_POINTER; - } - - nsIDocument* document = mElement->OwnerDoc(); - - nsPIDOMWindowOuter* window = document->GetWindow(); - if (!window) { - return NS_OK; - } - - // Trying to found the top window (equivalent to window.top). - if (nsCOMPtr<nsPIDOMWindowOuter> top = window->GetTop()) { - window = top; - } - - if (window->GetFocusedNode()) { - return NS_OK; - } - - nsCOMPtr<nsIDocument> topDoc = window->GetExtantDoc(); - if (topDoc && topDoc->GetReadyStateEnum() == nsIDocument::READYSTATE_COMPLETE) { - return NS_OK; - } - - // If something is focused in the same document, ignore autofocus. - if (!fm->GetFocusedContent() || - fm->GetFocusedContent()->OwnerDoc() != document) { - mozilla::ErrorResult rv; - mElement->Focus(rv); - return rv.StealNSResult(); - } - - return NS_OK; - } -private: - // NOTE: nsGenericHTMLFormElement is saved as a nsGenericHTMLElement - // because AddRef/Release are ambiguous with nsGenericHTMLFormElement - // and Focus() is declared (and defined) in nsGenericHTMLElement class. - RefPtr<nsGenericHTMLElement> mElement; -}; - NS_IMPL_ADDREF_INHERITED(nsGenericHTMLElement, nsGenericHTMLElementBase) NS_IMPL_RELEASE_INHERITED(nsGenericHTMLElement, nsGenericHTMLElementBase) NS_INTERFACE_MAP_BEGIN(nsGenericHTMLElement) NS_INTERFACE_MAP_ENTRY(nsIDOMElement) NS_INTERFACE_MAP_ENTRY(nsIDOMNode) NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElementBase) @@ -1877,20 +1819,18 @@ nsGenericHTMLFormElement::BindToTree(nsI aCompileEventHandlers); NS_ENSURE_SUCCESS(rv, rv); // An autofocus event has to be launched if the autofocus attribute is // specified and the element accept the autofocus attribute. In addition, // the document should not be already loaded and the "browser.autofocus" // preference should be 'true'. if (IsAutofocusable() && HasAttr(kNameSpaceID_None, nsGkAtoms::autofocus) && - nsContentUtils::AutoFocusEnabled()) { - nsCOMPtr<nsIRunnable> event = new nsAutoFocusEvent(this); - rv = NS_DispatchToCurrentThread(event); - NS_ENSURE_SUCCESS(rv, rv); + nsContentUtils::AutoFocusEnabled() && aDocument) { + aDocument->SetAutoFocusElement(this); } // If @form is set, the element *has* to be in a document, otherwise it // wouldn't be possible to find an element with the corresponding id. // If @form isn't set, the element *has* to have a parent, otherwise it // wouldn't be possible to find a form ancestor. // We should not call UpdateFormOwner if none of these conditions are // fulfilled.
--- a/layout/base/PresShell.cpp +++ b/layout/base/PresShell.cpp @@ -1812,16 +1812,18 @@ PresShell::Initialize(nscoord aWidth, ns // (Do this in a script runner, since our caller might have a script // blocker on the stack.) nsContentUtils::AddScriptRunner(new XBLConstructorRunner(mDocument)); // XBLConstructorRunner might destroy us. NS_ENSURE_STATE(!mHaveShutDown); } + 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->GetStateBits() & 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);