diff --git a/dom/events/EventStates.h b/dom/events/EventStates.h index de0f651bd4..eef0ca1002 100644 --- a/dom/events/EventStates.h +++ b/dom/events/EventStates.h @@ -291,6 +291,8 @@ private: #define NS_EVENT_STATE_PLACEHOLDERSHOWN NS_DEFINE_EVENT_STATE_MACRO(52) // Element has focus-within. #define NS_EVENT_STATE_FOCUS_WITHIN NS_DEFINE_EVENT_STATE_MACRO(53) +// Modal element +#define NS_EVENT_STATE_MODAL_DIALOG NS_DEFINE_EVENT_STATE_MACRO(54) #define DIR_ATTR_STATES (NS_EVENT_STATE_HAS_DIR_ATTR | \ NS_EVENT_STATE_DIR_ATTR_LTR | \ @@ -309,7 +311,8 @@ private: #define ESM_MANAGED_STATES (DIR_ATTR_STATES | NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_FOCUS | \ NS_EVENT_STATE_HOVER | NS_EVENT_STATE_DRAGOVER | \ NS_EVENT_STATE_URLTARGET | NS_EVENT_STATE_FOCUSRING | \ - NS_EVENT_STATE_FULL_SCREEN | NS_EVENT_STATE_FOCUS_WITHIN) + NS_EVENT_STATE_FULL_SCREEN | NS_EVENT_STATE_FOCUS_WITHIN | \ + NS_EVENT_STATE_MODAL_DIALOG) #define INTRINSIC_STATES (~ESM_MANAGED_STATES) diff --git a/dom/html/HTMLDialogElement.cpp b/dom/html/HTMLDialogElement.cpp index a178e5afea..795b44dc1a 100644 --- a/dom/html/HTMLDialogElement.cpp +++ b/dom/html/HTMLDialogElement.cpp @@ -56,6 +56,9 @@ HTMLDialogElement::Close(const mozilla::dom::Optional& aReturnValue) ErrorResult ignored; SetOpen(false, ignored); ignored.SuppressException(); + + RemoveFromTopLayerIfNeeded(); + RefPtr eventDispatcher = new AsyncEventDispatcher(this, NS_LITERAL_STRING("close"), false); eventDispatcher->PostDOMEvent(); @@ -72,6 +75,26 @@ HTMLDialogElement::Show() ignored.SuppressException(); } +bool HTMLDialogElement::IsInTopLayer() const { + return State().HasState(NS_EVENT_STATE_MODAL_DIALOG); +} + +void HTMLDialogElement::RemoveFromTopLayerIfNeeded() { + if (!IsInTopLayer()) { + return; + } + auto predictFunc = [&](Element* element) { return element == this; }; + + DebugOnly removedElement = OwnerDoc()->TopLayerPop(predictFunc); + MOZ_ASSERT(removedElement == this); + RemoveStates(NS_EVENT_STATE_MODAL_DIALOG); +} + +void HTMLDialogElement::UnbindFromTree(bool aNullParent) { + RemoveFromTopLayerIfNeeded(); + nsGenericHTMLElement::UnbindFromTree(aNullParent); +} + void HTMLDialogElement::ShowModal(ErrorResult& aError) { @@ -79,6 +102,10 @@ HTMLDialogElement::ShowModal(ErrorResult& aError) aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; } + + if (!IsInTopLayer() && OwnerDoc()->TopLayerPush(this)) { + AddStates(NS_EVENT_STATE_MODAL_DIALOG); + } SetOpen(true, aError); aError.SuppressException(); diff --git a/dom/html/HTMLDialogElement.h b/dom/html/HTMLDialogElement.h index 0520ef56b0..6386452d91 100644 --- a/dom/html/HTMLDialogElement.h +++ b/dom/html/HTMLDialogElement.h @@ -41,10 +41,14 @@ public: { mReturnValue = aReturnValue; } + + void UnbindFromTree(bool aNullParent = true) /*override*/; void Close(const mozilla::dom::Optional& aReturnValue); void Show(); void ShowModal(ErrorResult& aError); + + bool IsInTopLayer() const; nsString mReturnValue; @@ -52,6 +56,9 @@ protected: virtual ~HTMLDialogElement(); JSObject* WrapNode(JSContext* aCx, JS::Handle aGivenProto) override; + +private: + void RemoveFromTopLayerIfNeeded(); }; } // namespace dom diff --git a/layout/style/nsCSSPseudoClassList.h b/layout/style/nsCSSPseudoClassList.h index 7bdb244c0a..9b1b4355e6 100644 --- a/layout/style/nsCSSPseudoClassList.h +++ b/layout/style/nsCSSPseudoClassList.h @@ -190,6 +190,12 @@ CSS_STATE_PSEUDO_CLASS(fullscreen, ":fullscreen", NS_EVENT_STATE_FULL_SCREEN) CSS_STATE_PSEUDO_CLASS(mozFullScreen, ":-moz-full-screen", 0, "", NS_EVENT_STATE_FULL_SCREEN) +// Matches if a element is modal +CSS_STATE_PSEUDO_CLASS(mozModalDialog, ":-moz-modal-dialog", + CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS, + "", + NS_EVENT_STATE_MODAL_DIALOG) + // Matches if the element is focused and should show a focus ring CSS_STATE_PSEUDO_CLASS(mozFocusRing, ":-moz-focusring", 0, "", NS_EVENT_STATE_FOCUSRING) diff --git a/layout/style/res/html.css b/layout/style/res/html.css index 93539c0890..7f792a06bc 100644 --- a/layout/style/res/html.css +++ b/layout/style/res/html.css @@ -811,14 +811,15 @@ dialog { position: absolute; inset-inline-start: 0; inset-inline-end: 0; - color: black; margin: auto; border-width: initial; border-style: solid; - border-color: initial; + border-color: dimgrey; border-image: initial; + border-radius: 5px; padding: 1em; - background: white; + color: #222; /*CanvasText*/ + background: snow; /*Canvas*/ width: -moz-fit-content; } @@ -826,6 +827,15 @@ dialog:not([open]) { display: none; } +dialog:-moz-modal-dialog { + -moz-top-layer: top !important; +} + +/* https://html.spec.whatwg.org/#flow-content-3 */ +dialog::backdrop { + background: rgba(0, 0, 0, 0.1); +} + /* emulation of non-standard HTML tag */ marquee { inline-size: -moz-available;