diff --git a/devtools/shared/css/generated/properties-db.js b/devtools/shared/css/generated/properties-db.js index fb95ffd1f1..587f5704d1 100644 --- a/devtools/shared/css/generated/properties-db.js +++ b/devtools/shared/css/generated/properties-db.js @@ -2208,10 +2208,10 @@ exports.CSS_PROPERTIES = { "values": [ "-moz-available", "-moz-calc", - "-moz-fit-content", "auto", "calc", "content", + "fit-content", "inherit", "initial", "max-content", @@ -2231,10 +2231,10 @@ exports.CSS_PROPERTIES = { "values": [ "-moz-available", "-moz-calc", - "-moz-fit-content", "auto", "calc", "content", + "fit-content", "inherit", "initial", "max-content", @@ -3282,7 +3282,6 @@ exports.CSS_PROPERTIES = { "-moz-crisp-edges", "-moz-deck", "-moz-element", - "-moz-fit-content", "-moz-grid", "-moz-grid-group", "-moz-grid-line", @@ -3424,6 +3423,7 @@ exports.CSS_PROPERTIES = { "extra-expanded", "fill", "fill-box", + "fit-content", "fixed", "flat", "flex", @@ -6000,10 +6000,10 @@ exports.CSS_PROPERTIES = { "values": [ "-moz-available", "-moz-calc", - "-moz-fit-content", "auto", "calc", "content", + "fit-content", "inherit", "initial", "max-content", @@ -6023,10 +6023,10 @@ exports.CSS_PROPERTIES = { "values": [ "-moz-available", "-moz-calc", - "-moz-fit-content", "auto", "calc", "content", + "fit-content", "inherit", "initial", "max-content", @@ -6816,9 +6816,9 @@ exports.CSS_PROPERTIES = { "values": [ "-moz-available", "-moz-calc", - "-moz-fit-content", "auto", "calc", + "fit-content", "inherit", "initial", "max-content", @@ -6903,9 +6903,9 @@ exports.CSS_PROPERTIES = { "values": [ "-moz-available", "-moz-calc", - "-moz-fit-content", "auto", "calc", + "fit-content", "inherit", "initial", "max-content", @@ -7782,8 +7782,8 @@ exports.CSS_PROPERTIES = { "values": [ "-moz-available", "-moz-calc", - "-moz-fit-content", "calc", + "fit-content", "inherit", "initial", "max-content", @@ -7804,8 +7804,8 @@ exports.CSS_PROPERTIES = { "values": [ "-moz-available", "-moz-calc", - "-moz-fit-content", "calc", + "fit-content", "inherit", "initial", "max-content", @@ -7826,8 +7826,8 @@ exports.CSS_PROPERTIES = { "values": [ "-moz-available", "-moz-calc", - "-moz-fit-content", "calc", + "fit-content", "inherit", "initial", "max-content", @@ -7866,9 +7866,9 @@ exports.CSS_PROPERTIES = { "values": [ "-moz-available", "-moz-calc", - "-moz-fit-content", "auto", "calc", + "fit-content", "inherit", "initial", "max-content", @@ -7888,9 +7888,9 @@ exports.CSS_PROPERTIES = { "values": [ "-moz-available", "-moz-calc", - "-moz-fit-content", "auto", "calc", + "fit-content", "inherit", "initial", "max-content", @@ -7910,9 +7910,9 @@ exports.CSS_PROPERTIES = { "values": [ "-moz-available", "-moz-calc", - "-moz-fit-content", "auto", "calc", + "fit-content", "inherit", "initial", "max-content", @@ -9710,9 +9710,9 @@ exports.CSS_PROPERTIES = { "values": [ "-moz-available", "-moz-calc", - "-moz-fit-content", "auto", "calc", + "fit-content", "inherit", "initial", "max-content", @@ -9826,6 +9826,7 @@ exports.CSS_PROPERTIES = { exports.PSEUDO_ELEMENTS = [ ":after", ":before", + ":slotted", ":backdrop", ":first-letter", ":first-line", diff --git a/dom/base/DocumentOrShadowRoot.cpp b/dom/base/DocumentOrShadowRoot.cpp index f420ff6a28..2850bdb2d6 100644 --- a/dom/base/DocumentOrShadowRoot.cpp +++ b/dom/base/DocumentOrShadowRoot.cpp @@ -169,7 +169,7 @@ DocumentOrShadowRoot::GetFullscreenElement() return nullptr; } - Element* element = AsNode().OwnerDoc()->FullScreenStackTop(); + Element* element = AsNode().OwnerDoc()->GetUnretargetedFullScreenElement(); NS_ASSERTION(!element || element->State().HasState(NS_EVENT_STATE_FULL_SCREEN), "Fullscreen element should have fullscreen styles applied"); diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index da3207e9c9..624f9c502f 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -738,20 +738,40 @@ nsContentUtils::InitializeModifierStrings() sModifierSeparator = new nsString(modifierSeparator); } +mozilla::EventClassID +nsContentUtils::GetEventClassIDFromMessage(EventMessage aEventMessage) +{ + switch (aEventMessage) { +#define MESSAGE_TO_EVENT(name_, message_, type_, struct_) \ + case message_: return struct_; +#include "mozilla/EventNameList.h" +#undef MESSAGE_TO_EVENT + default: + MOZ_ASSERT_UNREACHABLE("Invalid event message?"); + return eBasicEventClass; + } +} + +static nsIAtom* +GetEventTypeFromMessage(EventMessage aEventMessage) +{ + switch (aEventMessage) { +#define MESSAGE_TO_EVENT(name_, message_, type_, struct_) \ + case message_: return nsGkAtoms::on##name_; +#include "mozilla/EventNameList.h" +#undef MESSAGE_TO_EVENT + default: + return nullptr; + } +} + // Because of SVG/SMIL we have several atoms mapped to the same // id, but we can rely on MESSAGE_TO_EVENT to map id to only one atom. static bool ShouldAddEventToStringEventTable(const EventNameMapping& aMapping) { - switch(aMapping.mMessage) { -#define MESSAGE_TO_EVENT(name_, message_, type_, struct_) \ - case message_: return nsGkAtoms::on##name_ == aMapping.mAtom; -#include "mozilla/EventNameList.h" -#undef MESSAGE_TO_EVENT - default: - break; - } - return false; + MOZ_ASSERT(aMapping.mAtom); + return GetEventTypeFromMessage(aMapping.mMessage) == aMapping.mAtom; } bool @@ -4149,10 +4169,12 @@ nsContentUtils::DispatchEventForPreloadURI(nsIDOMNode* aNode, // static nsresult -nsContentUtils::DispatchTrustedEvent(nsIDocument* aDoc, nsISupports* aTarget, +nsContentUtils::DispatchTrustedEvent(nsIDocument* aDoc, + nsISupports* aTarget, const nsAString& aEventName, - bool aCanBubble, bool aCancelable, - bool *aDefaultAction) + bool aCanBubble, + bool aCancelable, + bool* aDefaultAction) { return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable, true, aDefaultAction); @@ -4160,10 +4182,12 @@ nsContentUtils::DispatchTrustedEvent(nsIDocument* aDoc, nsISupports* aTarget, // static nsresult -nsContentUtils::DispatchUntrustedEvent(nsIDocument* aDoc, nsISupports* aTarget, +nsContentUtils::DispatchUntrustedEvent(nsIDocument* aDoc, + nsISupports* aTarget, const nsAString& aEventName, - bool aCanBubble, bool aCancelable, - bool *aDefaultAction) + bool aCanBubble, + bool aCancelable, + bool* aDefaultAction) { return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable, false, aDefaultAction); @@ -4171,10 +4195,13 @@ nsContentUtils::DispatchUntrustedEvent(nsIDocument* aDoc, nsISupports* aTarget, // static nsresult -nsContentUtils::DispatchEvent(nsIDocument* aDoc, nsISupports* aTarget, +nsContentUtils::DispatchEvent(nsIDocument* aDoc, + nsISupports* aTarget, const nsAString& aEventName, - bool aCanBubble, bool aCancelable, - bool aTrusted, bool *aDefaultAction, + bool aCanBubble, + bool aCancelable, + bool aTrusted, + bool* aDefaultAction, bool aOnlyChromeDispatch) { nsCOMPtr event; @@ -4189,12 +4216,50 @@ nsContentUtils::DispatchEvent(nsIDocument* aDoc, nsISupports* aTarget, return target->DispatchEvent(event, aDefaultAction ? aDefaultAction : &dummy); } +// static +nsresult +nsContentUtils::DispatchEvent(nsIDocument* aDoc, + nsISupports* aTarget, + WidgetEvent& aEvent, + EventMessage aEventMessage, + bool aCanBubble, + bool aCancelable, + bool aTrusted, + bool* aDefaultAction, + bool aOnlyChromeDispatch) +{ + MOZ_ASSERT_IF(aOnlyChromeDispatch, aTrusted); + + nsCOMPtr target(do_QueryInterface(aTarget)); + + aEvent.mTime = PR_Now(); + + aEvent.mSpecifiedEventType = GetEventTypeFromMessage(aEventMessage); + aEvent.SetDefaultComposed(); + aEvent.SetDefaultComposedInNativeAnonymousContent(); + + aEvent.mFlags.mBubbles = aCanBubble; + aEvent.mFlags.mCancelable = aCancelable; + aEvent.mFlags.mOnlyChromeDispatch = aOnlyChromeDispatch; + + aEvent.mTarget = target; + + nsEventStatus status = nsEventStatus_eIgnore; + nsresult rv = EventDispatcher::DispatchDOMEvent(target, &aEvent, nullptr, + nullptr, &status); + if (aDefaultAction) { + *aDefaultAction = (status != nsEventStatus_eConsumeNoDefault); + } + return rv; +} + nsresult nsContentUtils::DispatchChromeEvent(nsIDocument *aDoc, nsISupports *aTarget, const nsAString& aEventName, - bool aCanBubble, bool aCancelable, - bool *aDefaultAction) + bool aCanBubble, + bool aCancelable, + bool* aDefaultAction) { nsCOMPtr event; diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index 3d5476d7aa..37866aaf64 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -19,6 +19,7 @@ #include "js/TypeDecls.h" #include "js/Value.h" #include "js/RootingAPI.h" +#include "mozilla/BasicEvents.h" #include "mozilla/EventForwards.h" #include "mozilla/GuardObjects.h" #include "mozilla/TimeStamp.h" @@ -1208,6 +1209,33 @@ public: bool aCancelable, bool *aDefaultAction = nullptr); + /** + * This method creates and dispatches a trusted event using an event message. + * @param aDoc The document which will be used to create the event. + * @param aTarget The target of the event, should be QIable to + * EventTarget. + * @param aEventMessage The event message. + * @param aCanBubble Whether the event can bubble. + * @param aCancelable Is the event cancelable. + * @param aDefaultAction Set to true if default action should be taken, + * see nsIDOMEventTarget::DispatchEvent. + */ + template + static nsresult DispatchTrustedEvent(nsIDocument* aDoc, + nsISupports* aTarget, + mozilla::EventMessage aEventMessage, + bool aCanBubble, + bool aCancelable, + bool *aDefaultAction = nullptr, + bool aOnlyChromeDispatch = false) + { + WidgetEventType event(true, aEventMessage); + MOZ_ASSERT(GetEventClassIDFromMessage(aEventMessage) == event.mClass); + return DispatchEvent(aDoc, aTarget, event, aEventMessage, + aCanBubble, aCancelable, true, + aDefaultAction, aOnlyChromeDispatch); + } + /** * This method creates and dispatches a untrusted event. * Works only with events which can be created by calling @@ -1228,6 +1256,33 @@ public: bool aCancelable, bool *aDefaultAction = nullptr); + /** + * This method creates and dispatches a untrusted event using an event message. + * @param aDoc The document which will be used to create the event. + * @param aTarget The target of the event, should be QIable to + * EventTarget. + * @param aEventMessage The event message. + * @param aCanBubble Whether the event can bubble. + * @param aCancelable Is the event cancelable. + * @param aDefaultAction Set to true if default action should be taken, + * see nsIDOMEventTarget::DispatchEvent. + */ + template + static nsresult DispatchUntrustedEvent(nsIDocument* aDoc, + nsISupports* aTarget, + mozilla::EventMessage aEventMessage, + bool aCanBubble, + bool aCancelable, + bool *aDefaultAction = nullptr, + bool aOnlyChromeDispatch = false) + { + WidgetEventType event(false, aEventMessage); + MOZ_ASSERT(GetEventClassIDFromMessage(aEventMessage) == event.mClass); + return DispatchEvent(aDoc, aTarget, event, aEventMessage, + aCanBubble, aCancelable, false, + aDefaultAction, aOnlyChromeDispatch); + } + /** * This method creates and dispatches a trusted event to the chrome * event handler (the parent object of the DOM Window in the event target @@ -2883,6 +2938,16 @@ private: bool *aDefaultAction = nullptr, bool aOnlyChromeDispatch = false); + static nsresult DispatchEvent(nsIDocument* aDoc, + nsISupports* aTarget, + mozilla::WidgetEvent& aWidgetEvent, + mozilla::EventMessage aEventMessage, + bool aCanBubble, + bool aCancelable, + bool aTrusted, + bool *aDefaultAction = nullptr, + bool aOnlyChromeDispatch = false); + static void InitializeModifierStrings(); static void DropFragmentParsers(); @@ -2894,6 +2959,9 @@ private: static void* AllocClassMatchingInfo(nsINode* aRootNode, const nsString* aClasses); + static mozilla::EventClassID + GetEventClassIDFromMessage(mozilla::EventMessage aEventMessage); + // Fills in aInfo with the tokens from the supplied autocomplete attribute. static AutocompleteAttrState InternalSerializeAutocompleteAttribute(const nsAttrValue* aAttrVal, mozilla::dom::AutocompleteInfo& aInfo); diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index 0c6950e422..c13e775a94 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -159,6 +159,7 @@ #include "nsEscape.h" #include "nsObjectLoadingContent.h" #include "nsHtml5TreeOpExecutor.h" +#include "mozilla/dom/HTMLDialogElement.h" #include "mozilla/dom/HTMLLinkElement.h" #include "mozilla/dom/HTMLMediaElement.h" #include "mozilla/dom/HTMLIFrameElement.h" @@ -8481,7 +8482,7 @@ nsDocument::OnPageHide(bool aPersisted, EnumerateActivityObservers(NotifyActivityChanged, nullptr); ClearPendingFullscreenRequests(this); - if (FullScreenStackTop()) { + if (GetUnretargetedFullScreenElement()) { // If this document was fullscreen, we should exit fullscreen in this // doctree branch. This ensures that if the user navigates while in // fullscreen mode we don't leave its still visible ancestor documents @@ -10112,6 +10113,19 @@ nsDocument::GetFullscreenRoot() return root; } +size_t +nsDocument::CountFullscreenElements() const { + size_t count = 0; + for (const nsWeakPtr& ptr : mTopLayer) { + if (nsCOMPtr elem = do_QueryReferent(ptr)) { + if (elem->State().HasState(NS_EVENT_STATE_FULL_SCREEN)) { + count++; + } + } + } + return count; +} + void nsDocument::SetFullscreenRoot(nsIDocument* aRoot) { @@ -10125,6 +10139,18 @@ nsDocument::MozCancelFullScreen() return NS_OK; } +void nsDocument::TryCancelDialog() { + // Check if the document is blocked by modal dialog + for (const nsWeakPtr& weakPtr : Reversed(mTopLayer)) { + nsCOMPtr element(do_QueryReferent(weakPtr)); + if (HTMLDialogElement* dialog = + HTMLDialogElement::FromContentOrNull(element)) { + dialog->CancelDialog(); + break; + } + } +} + void nsIDocument::ExitFullscreen() { @@ -10175,7 +10201,7 @@ nsIDocument::AsyncExitFullscreen(nsIDocument* aDoc) static bool CountFullscreenSubDocuments(nsIDocument* aDoc, void* aData) { - if (aDoc->FullScreenStackTop()) { + if (aDoc->GetUnretargetedFullScreenElement()) { uint32_t* count = static_cast(aData); (*count)++; } @@ -10195,7 +10221,7 @@ nsDocument::IsFullscreenLeaf() { // A fullscreen leaf document is fullscreen, and has no fullscreen // subdocuments. - if (!FullScreenStackTop()) { + if (!GetUnretargetedFullScreenElement()) { return false; } return CountFullscreenSubDocuments(this) == 0; @@ -10204,11 +10230,11 @@ nsDocument::IsFullscreenLeaf() static bool ResetFullScreen(nsIDocument* aDocument, void* aData) { - if (aDocument->FullScreenStackTop()) { + if (aDocument->GetUnretargetedFullScreenElement()) { NS_ASSERTION(CountFullscreenSubDocuments(aDocument) <= 1, "Should have at most 1 fullscreen subdocument."); static_cast(aDocument)->CleanupFullscreenState(); - NS_ASSERTION(!aDocument->FullScreenStackTop(), + NS_ASSERTION(!aDocument->GetUnretargetedFullScreenElement(), "Should reset full-screen"); auto changed = reinterpret_cast*>(aData); changed->AppendElement(aDocument); @@ -10256,7 +10282,7 @@ nsIDocument::ExitFullscreenInDocTree(nsIDocument* aMaybeNotARootDoc) UnlockPointer(); nsCOMPtr root = aMaybeNotARootDoc->GetFullscreenRoot(); - if (!root || !root->FullScreenStackTop()) { + if (!root || !root->GetUnretargetedFullScreenElement()) { // If a document was detached before exiting from fullscreen, it is // possible that the root had left fullscreen state. In this case, // we would not get anything from the ResetFullScreen() call. Root's @@ -10285,7 +10311,7 @@ nsIDocument::ExitFullscreenInDocTree(nsIDocument* aMaybeNotARootDoc) DispatchFullScreenChange(changed[changed.Length() - i - 1]); } - NS_ASSERTION(!root->FullScreenStackTop(), + NS_ASSERTION(!root->GetUnretargetedFullScreenElement(), "Fullscreen root should no longer be a fullscreen doc..."); // Move the top-level window out of fullscreen mode. @@ -10302,7 +10328,7 @@ GetFullscreenLeaf(nsIDocument* aDoc, void* aData) nsIDocument** result = static_cast(aData); *result = aDoc; return false; - } else if (aDoc->FullScreenStackTop()) { + } else if (aDoc->GetUnretargetedFullScreenElement()) { aDoc->EnumerateSubDocuments(GetFullscreenLeaf, aData); } return true; @@ -10321,7 +10347,7 @@ GetFullscreenLeaf(nsIDocument* aDoc) nsIDocument* root = nsContentUtils::GetRootDocument(aDoc); // Check that the root is actually fullscreen so we don't waste time walking // around its descendants. - if (!root->FullScreenStackTop()) { + if (!root->GetUnretargetedFullScreenElement()) { return nullptr; } GetFullscreenLeaf(root, &leaf); @@ -10331,10 +10357,10 @@ GetFullscreenLeaf(nsIDocument* aDoc) void nsDocument::RestorePreviousFullScreenState() { - NS_ASSERTION(!FullScreenStackTop() || !FullscreenRoots::IsEmpty(), + NS_ASSERTION(!GetUnretargetedFullScreenElement() || !FullscreenRoots::IsEmpty(), "Should have at least 1 fullscreen root when fullscreen!"); - if (!FullScreenStackTop() || !GetWindow() || FullscreenRoots::IsEmpty()) { + if (!GetUnretargetedFullScreenElement() || !GetWindow() || FullscreenRoots::IsEmpty()) { return; } @@ -10350,10 +10376,10 @@ nsDocument::RestorePreviousFullScreenState() // Collect all ancestor documents which we are going to change. for (; doc; doc = doc->GetParentDocument()) { nsDocument* theDoc = static_cast(doc); - MOZ_ASSERT(!theDoc->mFullScreenStack.IsEmpty(), + Element* top = theDoc->GetUnretargetedFullScreenElement(); + MOZ_ASSERT(!top, "Ancestor of fullscreen document must also be in fullscreen"); if (doc != this) { - Element* top = theDoc->FullScreenStackTop(); if (top->IsHTMLElement(nsGkAtoms::iframe)) { if (static_cast(top)->FullscreenFlag()) { // If this is an iframe, and it explicitly requested @@ -10363,14 +10389,14 @@ nsDocument::RestorePreviousFullScreenState() } } exitDocs.AppendElement(theDoc); - if (theDoc->mFullScreenStack.Length() > 1) { + if (theDoc->CountFullscreenElements() > 1) { break; } } nsDocument* lastDoc = exitDocs.LastElement(); - if (!lastDoc->GetParentDocument() && - lastDoc->mFullScreenStack.Length() == 1) { + size_t fullscreenCount = lastDoc->CountFullscreenElements(); + if (!lastDoc->GetParentDocument() && fullscreenCount == 1) { // If we are fully exiting fullscreen, don't touch anything here, // just wait for the window to get out from fullscreen first. AskWindowToExitFullscreen(this); @@ -10387,8 +10413,8 @@ nsDocument::RestorePreviousFullScreenState() // The last document will either rollback one fullscreen element, or // completely exit from the fullscreen state as well. nsIDocument* newFullscreenDoc; - if (lastDoc->mFullScreenStack.Length() > 1) { - lastDoc->FullScreenStackPop(); + if (fullscreenCount > 1) { + lastDoc->UnsetFullscreenElement(); newFullscreenDoc = lastDoc; } else { lastDoc->CleanupFullscreenState(); @@ -10482,93 +10508,131 @@ ClearFullscreenStateOnElement(Element* aElement) void nsDocument::CleanupFullscreenState() { - // Iterate the fullscreen stack and clear the fullscreen states. + // Iterate the top layer and clear the fullscreen states. // Since we also need to clear the fullscreen-ancestor state, and // currently fullscreen elements can only be placed in hierarchy // order in the stack, reversely iterating the stack could be more // efficient. NOTE that fullscreen-ancestor state would be removed // in bug 1199529, and the elements may not in hierarchy order // after bug 1195213. - for (nsWeakPtr& weakPtr : Reversed(mFullScreenStack)) { - if (nsCOMPtr element = do_QueryReferent(weakPtr)) { - ClearFullscreenStateOnElement(element); + mTopLayer.RemoveElementsBy([&](const nsWeakPtr& weakPtr) { + nsCOMPtr element(do_QueryReferent(weakPtr)); + if (!element || !element->IsInComposedDoc() || + element->OwnerDoc() != this) { + return true; } - } - mFullScreenStack.Clear(); + + if (element->State().HasState(NS_EVENT_STATE_FULL_SCREEN)) { + ClearFullscreenStateOnElement(element); + return true; + } + return false; + }); + mFullscreenRoot = nullptr; UpdateViewportScrollbarOverrideForFullscreen(this); } -bool -nsDocument::FullScreenStackPush(Element* aElement) -{ - NS_ASSERTION(aElement, "Must pass non-null to FullScreenStackPush()"); - Element* top = FullScreenStackTop(); - if (top == aElement || !aElement) { - return false; - } - EventStateManager::SetFullScreenState(aElement, true); - mFullScreenStack.AppendElement(do_GetWeakReference(aElement)); - NS_ASSERTION(FullScreenStackTop() == aElement, "Should match"); +void +nsDocument::UnsetFullscreenElement() { + Element* removedElement = TopLayerPop([](Element* element) -> bool { + return element->State().HasState(NS_EVENT_STATE_FULL_SCREEN); + }); + + MOZ_ASSERT(removedElement->State().HasState(NS_EVENT_STATE_FULL_SCREEN)); + ClearFullscreenStateOnElement(removedElement); UpdateViewportScrollbarOverrideForFullscreen(this); +} + +bool +nsDocument::SetFullscreenElement(Element* aElement) { + if (TopLayerPush(aElement)) { + EventStateManager::SetFullScreenState(aElement, true); + UpdateViewportScrollbarOverrideForFullscreen(this); + return true; + } + return false; +} + +bool +nsDocument::TopLayerPush(Element* aElement) { + NS_ASSERTION(aElement, "Must pass non-null to TopLayerPush()"); + auto predictFunc = [&aElement](Element* element) { + return element == aElement; + }; + TopLayerPop(predictFunc); + + mTopLayer.AppendElement(do_GetWeakReference(aElement)); + NS_ASSERTION(GetTopLayerTop() == aElement, "Should match"); return true; } -void -nsDocument::FullScreenStackPop() -{ - if (mFullScreenStack.IsEmpty()) { - return; +Element* +nsDocument::TopLayerPop(FunctionRef aPredicateFunc) { + if (mTopLayer.IsEmpty()) { + return nullptr; } - ClearFullscreenStateOnElement(FullScreenStackTop()); - - // Remove top element. Note the remaining top element in the stack - // will not have full-screen style bits set, so we will need to restore - // them on the new top element before returning. - uint32_t last = mFullScreenStack.Length() - 1; - mFullScreenStack.RemoveElementAt(last); + // Remove the topmost element that qualifies aPredicate; This + // is required is because the top layer contains not only + // fullscreen elements, but also dialog elements. + Element* removedElement; + for (auto i : Reversed(MakeRange(mTopLayer.Length()))) { + nsCOMPtr element(do_QueryReferent(mTopLayer[i])); + if (element && aPredicateFunc(element)) { + removedElement = element; + mTopLayer.RemoveElementAt(i); + break; + } + } // Pop from the stack null elements (references to elements which have // been GC'd since they were added to the stack) and elements which are // no longer in this document. - while (!mFullScreenStack.IsEmpty()) { - Element* element = FullScreenStackTop(); + while (!mTopLayer.IsEmpty()) { + Element* element = GetTopLayerTop(); if (!element || !element->IsInUncomposedDoc() || element->OwnerDoc() != this) { - NS_ASSERTION(!element->State().HasState(NS_EVENT_STATE_FULL_SCREEN), - "Should have already removed full-screen styles"); - uint32_t last = mFullScreenStack.Length() - 1; - mFullScreenStack.RemoveElementAt(last); + mTopLayer.RemoveElementAt(mTopLayer.Length() - 1); } else { // The top element of the stack is now an in-doc element. Return here. break; } } - UpdateViewportScrollbarOverrideForFullscreen(this); + return removedElement; } Element* -nsDocument::FullScreenStackTop() -{ - if (mFullScreenStack.IsEmpty()) { +nsDocument::GetTopLayerTop() { + if (mTopLayer.IsEmpty()) { return nullptr; } - uint32_t last = mFullScreenStack.Length() - 1; - nsCOMPtr element(do_QueryReferent(mFullScreenStack[last])); - NS_ASSERTION(element, "Should have full-screen element!"); - NS_ASSERTION(element->IsInComposedDoc(), "Full-screen element should be in doc"); - NS_ASSERTION(element->OwnerDoc() == this, "Full-screen element should be in this doc"); + uint32_t last = mTopLayer.Length() - 1; + nsCOMPtr element(do_QueryReferent(mTopLayer[last])); + NS_ASSERTION(element, "Should have a top layer element!"); + NS_ASSERTION(element->IsInComposedDoc(), "Top layer element should be in doc"); + NS_ASSERTION(element->OwnerDoc() == this, "Top layer element should be in this doc"); return element; } -/* virtual */ nsTArray -nsDocument::GetFullscreenStack() const -{ +Element* +nsDocument::GetUnretargetedFullScreenElement() { + for (const nsWeakPtr& weakPtr : Reversed(mTopLayer)) { + nsCOMPtr element(do_QueryReferent(weakPtr)); + // Per spec, the fullscreen element is the topmost element in the document's + // top layer of which the fullscreen flag is set, if any, and null otherwise. + if (element && element->State().HasState(NS_EVENT_STATE_FULL_SCREEN)) { + return element; + } + } + return nullptr; +} + +nsTArray +nsDocument::GetTopLayer() const { nsTArray elements; - for (const nsWeakPtr& ptr : mFullScreenStack) { + for (const nsWeakPtr& ptr : mTopLayer) { if (nsCOMPtr elem = do_QueryReferent(ptr)) { - MOZ_ASSERT(elem->State().HasState(NS_EVENT_STATE_FULL_SCREEN)); elements.AppendElement(elem); } } @@ -10683,7 +10747,8 @@ nsDocument::FullscreenElementReadyCheck(Element* aElement, { NS_ASSERTION(aElement, "Must pass non-null element to nsDocument::RequestFullScreen"); - if (!aElement || aElement == FullScreenStackTop()) { + Element* fullscreenElement = GetUnretargetedFullScreenElement(); + if (!aElement || aElement == fullscreenElement) { return false; } if (!aElement->IsInComposedDoc()) { @@ -10712,9 +10777,9 @@ nsDocument::FullscreenElementReadyCheck(Element* aElement, } //XXXsmaug Note, we don't follow the latest fullscreen spec here. // This whole check could be probably removed. - if (FullScreenStackTop() && + if (fullscreenElement && !nsContentUtils::ContentIsHostIncludingDescendantOf(aElement, - FullScreenStackTop())) { + fullscreenElement)) { // If this document is full-screen, only grant full-screen requests from // a descendant of the current full-screen element. DispatchFullscreenError("FullscreenDeniedNotDescendant"); @@ -10871,7 +10936,7 @@ ShouldApplyFullscreenDirectly(nsIDocument* aDoc, // If we are in the content process, we can apply the fullscreen // state directly only if we have been in DOM fullscreen, because // otherwise we always need to notify the chrome. - return !!nsContentUtils::GetRootDocument(aDoc)->GetFullscreenElement(); + return !!nsContentUtils::GetRootDocument(aDoc)->GetUnretargetedFullScreenElement(); } else { // If we are in the chrome process, and the window has not been in // fullscreen, we certainly need to make that fullscreen first. @@ -10994,8 +11059,8 @@ nsDocument::ApplyFullscreen(const FullscreenRequest& aRequest) // Set the full-screen element. This sets the full-screen style on the // element, and the full-screen-ancestor styles on ancestors of the element // in this document. - DebugOnly x = FullScreenStackPush(elem); - NS_ASSERTION(x, "Full-screen state of requesting doc should always change!"); + DebugOnly x = SetFullscreenElement(elem); + MOZ_ASSERT(x, "Full-screen state of requesting doc should always change!"); // Set the iframe fullscreen flag. if (elem->IsHTMLElement(nsGkAtoms::iframe)) { static_cast(elem)->SetFullscreenFlag(true); @@ -11017,14 +11082,14 @@ nsDocument::ApplyFullscreen(const FullscreenRequest& aRequest) } nsIDocument* parent = child->GetParentDocument(); Element* element = parent->FindContentForSubDocument(child)->AsElement(); - if (static_cast(parent)->FullScreenStackPush(element)) { + if (static_cast(parent)->SetFullscreenElement(element)) { changed.AppendElement(parent); child = parent; } else { // We've reached either the root, or a point in the doctree where the // new full-screen element container is the same as the previous // full-screen element's container. No more changes need to be made - // to the full-screen stacks of documents further up the tree. + // to the top layer of documents further up the tree. break; } } @@ -11294,7 +11359,7 @@ PointerLockRequest::Run() } // If it is neither user input initiated, nor requested in fullscreen, // it should be rejected. - if (!error && !mUserInputOrChromeCaller && !doc->GetFullscreenElement()) { + if (!error && !mUserInputOrChromeCaller && !doc->GetUnretargetedFullScreenElement()) { error = "PointerLockDeniedNotInputDriven"; } if (!error && !d->SetPointerLock(e, NS_STYLE_CURSOR_NONE)) { diff --git a/dom/base/nsDocument.h b/dom/base/nsDocument.h index 22157500a0..70bda75fed 100644 --- a/dom/base/nsDocument.h +++ b/dom/base/nsDocument.h @@ -56,6 +56,7 @@ #include "imgIRequest.h" #include "mozilla/EventListenerManager.h" #include "mozilla/EventStates.h" +#include "mozilla/FunctionRef.h" #include "mozilla/MemoryReporting.h" #include "mozilla/PendingAnimationTracker.h" #include "mozilla/dom/DOMImplementation.h" @@ -77,6 +78,7 @@ #define XML_DECLARATION_BITS_STANDALONE_EXISTS (1 << 2) #define XML_DECLARATION_BITS_STANDALONE_YES (1 << 3) +using namespace mozilla; class nsDOMStyleSheetSetList; class nsDocument; @@ -638,7 +640,7 @@ public: using mozilla::dom::DocumentOrShadowRoot::GetElementsByTagName; using mozilla::dom::DocumentOrShadowRoot::GetElementsByTagNameNS; using mozilla::dom::DocumentOrShadowRoot::GetElementsByClassName; - + // nsIDOMEventTarget virtual nsresult GetEventTargetParent( mozilla::EventChainPreVisitor& aVisitor) override; @@ -850,7 +852,7 @@ public: virtual Element* FindImageMap(const nsAString& aNormalizedMapName) override; - virtual nsTArray GetFullscreenStack() const override; + virtual nsTArray GetTopLayer() const override; virtual void AsyncRequestFullScreen( mozilla::UniquePtr&& aRequest) override; virtual void RestorePreviousFullScreenState() override; @@ -860,6 +862,7 @@ public: virtual nsresult RemoteFrameFullscreenReverted() override; virtual nsIDocument* GetFullscreenRoot() override; + virtual size_t CountFullscreenElements() const override; virtual void SetFullscreenRoot(nsIDocument* aRoot) override; // Returns the size of the mBlockedTrackingNodes array. (nsIDocument.h) @@ -896,23 +899,32 @@ public: // to move this document into full-screen mode if allowed. void RequestFullScreen(mozilla::UniquePtr&& aRequest); - // Removes all elements from the full-screen stack, removing full-scren - // styles from the top element in the stack. + // Removes all elements with the full-screen flag set from the top layer, and + // clears their full-screen flag. void CleanupFullscreenState(); - // Pushes aElement onto the full-screen stack, and removes full-screen styles - // from the former full-screen stack top, and its ancestors, and applies the - // styles to aElement. aElement becomes the new "full-screen element". - bool FullScreenStackPush(Element* aElement); + // Pushes aElement onto the top layer + bool TopLayerPush(Element* aElement) override; - // Remove the top element from the full-screen stack. Removes the full-screen - // styles from the former top element, and applies them to the new top - // element, if there is one. - void FullScreenStackPop(); + // Removes the topmost element which have aPredicate return true from the top + // layer. The removed element, if any, is returned. + Element* TopLayerPop(FunctionRef aPredicateFunc) override; + + // Pops the fullscreen element from the top layer and clears its + // fullscreen flag. + void UnsetFullscreenElement(); + + // Pushes the given element into the top of top layer and set fullscreen + // flag. + bool SetFullscreenElement(Element* aElement); + + // Cancel the dialog element if the document is blocked by the dialog + void TryCancelDialog(); // Returns the top element from the full-screen stack. - Element* FullScreenStackTop() override; - + Element* GetTopLayerTop() override; + // Return the fullscreen element in the top layer + Element* GetUnretargetedFullScreenElement() override; // DOM-exposed fullscreen API bool FullscreenEnabled() override; @@ -1193,10 +1205,8 @@ protected: // is a weak reference to avoid leaks due to circular references. nsWeakPtr mScopeObject; - // Stack of full-screen elements. When we request full-screen we push the - // full-screen element onto this stack, and when we cancel full-screen we - // pop one off this stack, restoring the previous full-screen state - nsTArray mFullScreenStack; + // Stack of top layer elements. + nsTArray mTopLayer; // The root of the doc tree in which this document is in. This is only // non-null when this document is in fullscreen mode. diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h index 90c0668e2e..bf4b053f5e 100644 --- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -33,6 +33,7 @@ #include "mozilla/UniquePtr.h" #include "mozilla/CORSMode.h" #include "mozilla/dom/DocumentOrShadowRoot.h" +#include "mozilla/FunctionRef.h" #include "mozilla/LinkedList.h" #include "mozilla/StyleSheet.h" #include "mozilla/TimeStamp.h" @@ -99,6 +100,8 @@ class nsIGlobalObject; class nsStyleSet; struct nsCSSSelectorList; +using mozilla::FunctionRef; + namespace mozilla { class CSSStyleSheet; class ErrorResult; @@ -1273,9 +1276,9 @@ public: virtual void RemoveFromNameTable(Element* aElement, nsIAtom* aName) = 0; /** - * Returns all elements in the fullscreen stack in the insertion order. + * Returns all elements in the top layer in the insertion order. */ - virtual nsTArray GetFullscreenStack() const = 0; + virtual nsTArray GetTopLayer() const = 0; /** * Asynchronously requests that the document make aElement the fullscreen @@ -1331,12 +1334,26 @@ public: */ virtual nsIDocument* GetFullscreenRoot() = 0; + virtual size_t CountFullscreenElements() const = 0; + /** * Sets the fullscreen root to aRoot. This stores a weak reference to aRoot * in this document. */ virtual void SetFullscreenRoot(nsIDocument* aRoot) = 0; + + /** + * Push elements to and pop elements from the top layer. + * Currently in use for fullscreen and modal version of . + */ + virtual bool TopLayerPush(Element* aElement) = 0; + virtual Element* TopLayerPop(FunctionRef aPredicateFunc) = 0; + /** + * Cancel the dialog element if the document is blocked by the dialog. + */ + virtual void TryCancelDialog() = 0; + /** * Synchronously cleans up the fullscreen state on the given document. * @@ -2607,7 +2624,8 @@ public: nsIURI* GetDocumentURIObject() const; // Not const because all the full-screen goop is not const virtual bool FullscreenEnabled() = 0; - virtual Element* FullScreenStackTop() = 0; + virtual Element* GetTopLayerTop() = 0; + virtual Element* GetUnretargetedFullScreenElement() = 0; bool Fullscreen() { return !!GetFullscreenElement(); 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/events/TextComposition.cpp b/dom/events/TextComposition.cpp index bd7ebbc468..3b3dc6505b 100644 --- a/dom/events/TextComposition.cpp +++ b/dom/events/TextComposition.cpp @@ -21,21 +21,8 @@ #include "mozilla/Unused.h" #include "mozilla/dom/TabParent.h" -#ifdef XP_MACOSX -// Some defiens will be conflict with OSX SDK -#define TextRange _TextRange -#define TextRangeArray _TextRangeArray -#define Comment _Comment -#endif - #include "nsPluginInstanceOwner.h" -#ifdef XP_MACOSX -#undef TextRange -#undef TextRangeArray -#undef Comment -#endif - using namespace mozilla::widget; namespace mozilla { diff --git a/dom/html/HTMLDialogElement.cpp b/dom/html/HTMLDialogElement.cpp index a178e5afea..ef6d40b832 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,11 +102,36 @@ 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(); } +void HTMLDialogElement::CancelDialog() { + // 1) Let close be the result of firing an event named cancel at dialog, with + // the cancelable attribute initialized to true. + bool defaultAction = true; + nsContentUtils::DispatchTrustedEvent( + OwnerDoc(), + static_cast(this), + NS_LITERAL_STRING("cancel"), + false, /* can bubble */ + true, /* can cancel */ + &defaultAction); + + // 2) If close is true and dialog has an open attribute, then close the dialog + // with no return value. + if (defaultAction) { + Optional retValue; + Close(retValue); + } +} + + JSObject* HTMLDialogElement::WrapNode(JSContext* aCx, JS::Handle aGivenProto) { diff --git a/dom/html/HTMLDialogElement.h b/dom/html/HTMLDialogElement.h index 0520ef56b0..4bc9ab879f 100644 --- a/dom/html/HTMLDialogElement.h +++ b/dom/html/HTMLDialogElement.h @@ -41,10 +41,15 @@ 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; + void CancelDialog(); nsString mReturnValue; @@ -52,6 +57,9 @@ protected: virtual ~HTMLDialogElement(); JSObject* WrapNode(JSContext* aCx, JS::Handle aGivenProto) override; + +private: + void RemoveFromTopLayerIfNeeded(); }; } // namespace dom diff --git a/dom/plugins/base/npapi.h b/dom/plugins/base/npapi.h index aa6a66b2bd..996df80844 100644 --- a/dom/plugins/base/npapi.h +++ b/dom/plugins/base/npapi.h @@ -34,7 +34,14 @@ #endif #if defined(XP_MACOSX) +// Some defines will conflict with OSX SDK +#define TextRange _TextRange +#define TextRangeArray _TextRangeArray +#define Comment _Comment #include +#undef TextRange +#undef TextRangeArray +#undef Comment #include #ifndef NP_NO_CARBON #include diff --git a/dom/plugins/base/nsPluginInstanceOwner.h b/dom/plugins/base/nsPluginInstanceOwner.h index 03977b047f..0eba706cb4 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.h +++ b/dom/plugins/base/nsPluginInstanceOwner.h @@ -20,7 +20,14 @@ #ifdef XP_MACOSX #include "mozilla/gfx/QuartzSupport.h" +// Some defines will conflict with OSX SDK +#define TextRange _TextRange +#define TextRangeArray _TextRangeArray +#define Comment _Comment #include +#undef TextRange +#undef TextRangeArray +#undef Comment #endif class nsIInputStream; diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index e156e432d3..fb7390753d 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -4726,8 +4726,8 @@ GetDefiniteSizeTakenByBoxSizing(StyleBoxSizing aBoxSizing, } // Handles only max-content and min-content, and -// -moz-fit-content for min-width and max-width, since the others -// (-moz-fit-content for width, and -moz-available) have no effect on +// fit-content for min-width and max-width, since the others +// (fit-content for width, and -moz-available) have no effect on // intrinsic widths. enum eWidthProperty { PROP_WIDTH, PROP_MAX_WIDTH, PROP_MIN_WIDTH }; static bool @@ -5038,7 +5038,7 @@ nsLayoutUtils::IntrinsicForAxis(PhysicalAxis aAxis, if (styleISize.GetUnit() == eStyleUnit_Enumerated && (styleISize.GetIntValue() == NS_STYLE_WIDTH_MAX_CONTENT || styleISize.GetIntValue() == NS_STYLE_WIDTH_MIN_CONTENT)) { - // -moz-fit-content and -moz-available enumerated widths compute intrinsic + // fit-content and -moz-available enumerated widths compute intrinsic // widths just like auto. // For max-content and min-content, we handle them like // specified widths, but ignore box-sizing. @@ -5054,7 +5054,7 @@ nsLayoutUtils::IntrinsicForAxis(PhysicalAxis aAxis, ++gNoiseIndent; #endif if (aType != MIN_ISIZE) { - // At this point, |styleISize| is auto/-moz-fit-content/-moz-available or + // At this point, |styleISize| is auto/fit-content/-moz-available or // has a percentage. The intrinisic size for those under a max-content // constraint is the max-content contribution which we shouldn't clamp. aMarginBoxMinSizeClamp = NS_MAXSIZE; diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 93314440b5..666d4ff740 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -8063,6 +8063,12 @@ PresShell::HandleEventInternal(WidgetEvent* aEvent, aEvent->mFlags.mDefaultPreventedByChrome) { mIsLastChromeOnlyEscapeKeyConsumed = true; } + if (aEvent->mMessage == eKeyDown && + !aEvent->mFlags.mDefaultPrevented) { + if (nsIDocument* doc = GetDocument()) { + doc->TryCancelDialog(); + } + } } } break; diff --git a/layout/generic/nsViewportFrame.cpp b/layout/generic/nsViewportFrame.cpp index ec933b84e3..ab3bfc88c3 100644 --- a/layout/generic/nsViewportFrame.cpp +++ b/layout/generic/nsViewportFrame.cpp @@ -123,8 +123,8 @@ ViewportFrame::BuildDisplayListForTopLayer(nsDisplayListBuilder* aBuilder, nsDisplayList* aList) { nsIDocument* doc = PresContext()->Document(); - nsTArray fullscreenStack = doc->GetFullscreenStack(); - for (Element* elem : fullscreenStack) { + nsTArray toplayer = doc->GetTopLayer(); + for (Element* elem : toplayer) { if (nsIFrame* frame = elem->GetPrimaryFrame()) { // There are two cases where an element in fullscreen is not in // the top layer: diff --git a/layout/style/nsCSSKeywordList.h b/layout/style/nsCSSKeywordList.h index bbe25893dd..033ca5db17 100644 --- a/layout/style/nsCSSKeywordList.h +++ b/layout/style/nsCSSKeywordList.h @@ -63,7 +63,6 @@ CSS_KEY(-moz-element, _moz_element) CSS_KEY(-moz-eventreerow, _moz_eventreerow) CSS_KEY(-moz-field, _moz_field) CSS_KEY(-moz-fieldtext, _moz_fieldtext) -CSS_KEY(-moz-fit-content, _moz_fit_content) CSS_KEY(-moz-fixed, _moz_fixed) CSS_KEY(-moz-grabbing, _moz_grabbing) CSS_KEY(-moz-grab, _moz_grab) diff --git a/layout/style/nsCSSProps.cpp b/layout/style/nsCSSProps.cpp index 7f1fe2c18f..9acc69daf1 100644 --- a/layout/style/nsCSSProps.cpp +++ b/layout/style/nsCSSProps.cpp @@ -2260,7 +2260,7 @@ const KTableEntry nsCSSProps::kWhitespaceKTable[] = { const KTableEntry nsCSSProps::kWidthKTable[] = { { eCSSKeyword_max_content, NS_STYLE_WIDTH_MAX_CONTENT }, { eCSSKeyword_min_content, NS_STYLE_WIDTH_MIN_CONTENT }, - { eCSSKeyword__moz_fit_content, NS_STYLE_WIDTH_FIT_CONTENT }, + { eCSSKeyword_fit_content, NS_STYLE_WIDTH_FIT_CONTENT }, { eCSSKeyword__moz_available, NS_STYLE_WIDTH_AVAILABLE }, { eCSSKeyword_UNKNOWN, -1 } }; @@ -2269,7 +2269,7 @@ const KTableEntry nsCSSProps::kWidthKTable[] = { const KTableEntry nsCSSProps::kFlexBasisKTable[] = { { eCSSKeyword_max_content, NS_STYLE_WIDTH_MAX_CONTENT }, { eCSSKeyword_min_content, NS_STYLE_WIDTH_MIN_CONTENT }, - { eCSSKeyword__moz_fit_content, NS_STYLE_WIDTH_FIT_CONTENT }, + { eCSSKeyword_fit_content, NS_STYLE_WIDTH_FIT_CONTENT }, { eCSSKeyword__moz_available, NS_STYLE_WIDTH_AVAILABLE }, { eCSSKeyword_content, NS_STYLE_FLEX_BASIS_CONTENT }, { eCSSKeyword_UNKNOWN, -1 } 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/forms.css b/layout/style/res/forms.css index fcf5874829..6d0a5503ae 100644 --- a/layout/style/res/forms.css +++ b/layout/style/res/forms.css @@ -55,7 +55,7 @@ fieldset > legend { padding-inline-start: 2px; padding-inline-end: 2px; - inline-size: -moz-fit-content; + inline-size: fit-content; } legend { diff --git a/layout/style/res/html.css b/layout/style/res/html.css index 93539c0890..38814a8354 100644 --- a/layout/style/res/html.css +++ b/layout/style/res/html.css @@ -811,21 +811,31 @@ 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; - width: -moz-fit-content; + color: #222; /*CanvasText*/ + background: snow; /*Canvas*/ + width: fit-content; } 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; diff --git a/layout/tables/BasicTableLayoutStrategy.cpp b/layout/tables/BasicTableLayoutStrategy.cpp index 8ece42fcb3..c599142252 100644 --- a/layout/tables/BasicTableLayoutStrategy.cpp +++ b/layout/tables/BasicTableLayoutStrategy.cpp @@ -179,7 +179,7 @@ GetISizeInfo(nsRenderingContext *aRenderingContext, if (!aIsCell || maxISize.GetIntValue() == NS_STYLE_WIDTH_AVAILABLE) { maxISize.SetNoneValue(); } else if (maxISize.GetIntValue() == NS_STYLE_WIDTH_FIT_CONTENT) { - // for 'max-inline-size', '-moz-fit-content' is like + // for 'max-inline-size', 'fit-content' is like // 'max-content' maxISize.SetIntValue(NS_STYLE_WIDTH_MAX_CONTENT, eStyleUnit_Enumerated); @@ -206,7 +206,7 @@ GetISizeInfo(nsRenderingContext *aRenderingContext, if (!aIsCell || minISize.GetIntValue() == NS_STYLE_WIDTH_AVAILABLE) { minISize.SetCoordValue(0); } else if (minISize.GetIntValue() == NS_STYLE_WIDTH_FIT_CONTENT) { - // for 'min-inline-size', '-moz-fit-content' is like + // for 'min-inline-size', 'fit-content' is like // 'min-content' minISize.SetIntValue(NS_STYLE_WIDTH_MIN_CONTENT, eStyleUnit_Enumerated); diff --git a/layout/tables/FixedTableLayoutStrategy.cpp b/layout/tables/FixedTableLayoutStrategy.cpp index 072be701a8..d1b30b9059 100644 --- a/layout/tables/FixedTableLayoutStrategy.cpp +++ b/layout/tables/FixedTableLayoutStrategy.cpp @@ -111,7 +111,7 @@ FixedTableLayoutStrategy::GetMinISize(nsRenderingContext* aRenderingContext) result -= spacing * (colSpan - 1); } } - // else, for 'auto', '-moz-available', '-moz-fit-content', + // else, for 'auto', '-moz-available', 'fit-content', // and 'calc()' with percentages, do nothing } } @@ -266,7 +266,7 @@ FixedTableLayoutStrategy::ComputeColumnISizes(const ReflowInput& aReflowInput) colFrame->AddPrefPercent(pct); pctTotal += pct; } else { - // 'auto', '-moz-available', '-moz-fit-content', and 'calc()' + // 'auto', '-moz-available', 'fit-content', and 'calc()' // with percentages colISize = unassignedMarker; } diff --git a/mfbt/FunctionRef.h b/mfbt/FunctionRef.h new file mode 100644 index 0000000000..874e2d413f --- /dev/null +++ b/mfbt/FunctionRef.h @@ -0,0 +1,220 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +/* + * A generic callable type that can be initialized from any compatible callable, + * suitable for use as a function argument for the duration of the function + * call (and no longer). + */ + +#ifndef mozilla_FunctionRef_h +#define mozilla_FunctionRef_h + +#include "mozilla/OperatorNewExtensions.h" // mozilla::NotNull, ::operator new + +#include // std::nullptr_t +#include // std::{declval,integral_constant}, std::is_{convertible,same,void}_v, std::{enable_if,remove_reference,remove_cv}_t + +// This concept and its implementation are substantially inspired by foonathan's prior art: +// +// https://foonathan.net/2017/01/function-ref-implementation/ +// https://github.com/foonathan/type_safe/blob/2017851053f8dd268372f1612865792c5c621570/include/type_safe/reference.hpp +// +// Note: this is C++ code voodoo. Be careful if you have to make any changes. + +namespace mozilla { + +namespace detail { + +// Template helper to determine if |Returned| is a return type compatible with +// |Required|: if the former converts to the latter, or if |Required| is |void| +// and nothing is returned. +template +using CompatibleReturnType = + std::integral_constant || + std::is_convertible_v>; + +// Template helper to check if |Func| called with |Params| arguments returns +// a type compatible with |Ret|. +template +using EnableMatchingFunction = std::enable_if_t< + CompatibleReturnType< + decltype(std::declval()(std::declval()...)), Ret>::value, + int>; + +struct MatchingFunctionPointerTag {}; +struct MatchingFunctorTag {}; +struct InvalidFunctorTag {}; + +// Template helper to determine the proper way to store |Callable|: as function +// pointer, as pointer to object, or unstorable. +template +struct GetCallableTag { + // Match the case where |Callable| is a compatible function pointer or + // converts to one. (|+obj| invokes such a conversion.) + template + static MatchingFunctionPointerTag test( + int, T& obj, EnableMatchingFunction = 0); + + // Match the case where |Callable| is callable but can't be converted to a + // function pointer. (|short| is a worse match for 0 than |int|, causing the + // function pointer match to be preferred if both apply.) + template + static MatchingFunctorTag test(short, T& obj, + EnableMatchingFunction = 0); + + // Match all remaining cases. (Any other match is preferred to an ellipsis + // match.) + static InvalidFunctorTag test(...); + + using Type = decltype(test(0, std::declval())); +}; + +// If the callable is |nullptr|, |std::declval()| will be an +// error. Provide a specialization for |nullptr| that will fail substitution. +template +struct GetCallableTag {}; + +template +using EnableFunctionTag = std::enable_if_t< + std::is_same_v::Type, + Result>, + int>; + +} // namespace detail + +/** + * An efficient, type-erasing, non-owning reference to a callable. It is + * intended for use as the type of a function parameter that is not used after + * the function in question returns. + * + * This class does not own the callable, so in general it is unsafe to store a + * FunctionRef. + */ +template +class FunctionRef; + +template +class FunctionRef { + union Payload; + + // |FunctionRef| stores an adaptor function pointer, determined by the + // arguments passed to the constructor. That adaptor will perform the steps + // needed to invoke the callable passed at construction time. + using Adaptor = Ret (*)(const Payload& aPayload, Params... aParams); + + // If |FunctionRef|'s callable can be stored as a function pointer, that + // function pointer is stored after being cast to this *different* function + // pointer type. |mAdaptor| then casts back to the original type to call it. + // ([expr.reinterpret.cast]p6 guarantees that A->B->A function pointer casts + // produce the original function pointer value.) An outlandish signature is + // used to emphasize that the exact function pointer type doesn't matter. + using FuncPtr = Payload***** (*)(Payload*****); + + /** + * An adaptor function (used by this class's function call operator) that + * invokes the callable in |mPayload|, forwarding arguments and converting + * return type as needed. + */ + const Adaptor mAdaptor; + + /** Storage for the wrapped callable value. */ + union Payload { + // This arm is used if |FunctionRef| is passed a compatible function pointer + // or a lambda/callable that converts to a compatible function pointer. + FuncPtr mFuncPtr; + + // This arm is used if |FunctionRef| is passed some other callable or + // |nullptr|. + void* mObject; + } mPayload; + + template + static Ret CallFunctionPointer(const Payload& aPayload, + Params... aParams) noexcept { + auto func = reinterpret_cast(aPayload.mFuncPtr); + return static_cast(func(static_cast(aParams)...)); + } + + template + FunctionRef(detail::MatchingFunctionPointerTag, Ret2 (*aFuncPtr)(Params2...)) + : mAdaptor(&CallFunctionPointer) { + ::new (KnownNotNull, &mPayload.mFuncPtr) + FuncPtr(reinterpret_cast(aFuncPtr)); + } + + public: + /** + * Construct a |FunctionRef| that's like a null function pointer that can't be + * called. + */ + MOZ_IMPLICIT FunctionRef(std::nullptr_t) noexcept : mAdaptor(nullptr) { + // This is technically unnecessary, but it seems best to always initialize + // a union arm. + ::new (KnownNotNull, &mPayload.mObject) void*(nullptr); + } + + FunctionRef() : FunctionRef(nullptr) {} + + /** + * Constructs a |FunctionRef| from an object callable with |Params| arguments, + * that returns a type convertible to |Ret|, where the callable isn't + * convertible to function pointer (often because it contains some internal + * state). For example: + * + * int x = 5; + * auto doSideEffect = [&x]{ x++; }; // state is captured reference to |x| + * FunctionRef f(doSideEffect); + */ + template < + typename Callable, + typename = detail::EnableFunctionTag, + typename std::enable_if_t>, + FunctionRef>>* = nullptr> + MOZ_IMPLICIT FunctionRef(Callable& aCallable) noexcept + : mAdaptor([](const Payload& aPayload, Params... aParams) { + auto& func = *static_cast(aPayload.mObject); + return static_cast(func(static_cast(aParams)...)); + }) { + ::new (KnownNotNull, &mPayload.mObject) void*(&aCallable); + } + + /** + * Constructs a |FunctionRef| from an value callable with |Params| arguments, + * that returns a type convertible to |Ret|, where the callable is stateless + * and is (or is convertible to) a function pointer. For example: + * + * // Exact match + * double twice(double d) { return d * 2; } + * FunctionRef func1(&twice); + * + * // Compatible match + * float thrice(long double d) { return static_cast(d) * 3; } + * FunctionRef func2(&thrice); + * + * // Non-generic lambdas that don't capture anything have a conversion + * // function to the appropriate function pointer type. + * FunctionRef f([](long double){ return 'c'; }); + */ + template > + MOZ_IMPLICIT FunctionRef(const Callable& aCallable) noexcept + : FunctionRef(detail::MatchingFunctionPointerTag{}, +aCallable) {} + + /** Call the callable stored in this with the given arguments. */ + Ret operator()(Params... params) const { + return mAdaptor(mPayload, static_cast(params)...); + } + + /** Return true iff this wasn't created from |nullptr|. */ + explicit operator bool() const noexcept { return mAdaptor != nullptr; } +}; + +} /* namespace mozilla */ + +#endif /* mozilla_FunctionRef_h */ diff --git a/mfbt/moz.build b/mfbt/moz.build index 54b13f21b9..5dc890cdbc 100644 --- a/mfbt/moz.build +++ b/mfbt/moz.build @@ -42,6 +42,7 @@ EXPORTS.mozilla = [ 'FastBernoulliTrial.h', 'FloatingPoint.h', 'Function.h', + 'FunctionRef.h', 'GuardObjects.h', 'HashFunctions.h', 'IndexSequence.h',