Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / dom / html / HTMLDialogElement.cpp
blobcd36201182a3d80dac28843d0ad281cc66455678
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/HTMLDialogElement.h"
8 #include "mozilla/dom/ElementBinding.h"
9 #include "mozilla/dom/HTMLDialogElementBinding.h"
11 #include "nsContentUtils.h"
12 #include "nsFocusManager.h"
13 #include "nsIFrame.h"
15 NS_IMPL_NS_NEW_HTML_ELEMENT(Dialog)
17 namespace mozilla::dom {
19 HTMLDialogElement::~HTMLDialogElement() = default;
21 NS_IMPL_ELEMENT_CLONE(HTMLDialogElement)
23 void HTMLDialogElement::Close(
24 const mozilla::dom::Optional<nsAString>& aReturnValue) {
25 if (!Open()) {
26 return;
28 if (aReturnValue.WasPassed()) {
29 SetReturnValue(aReturnValue.Value());
32 SetOpen(false, IgnoreErrors());
34 RemoveFromTopLayerIfNeeded();
36 RefPtr<Element> previouslyFocusedElement =
37 do_QueryReferent(mPreviouslyFocusedElement);
39 if (previouslyFocusedElement) {
40 mPreviouslyFocusedElement = nullptr;
42 FocusOptions options;
43 options.mPreventScroll = true;
44 previouslyFocusedElement->Focus(options, CallerType::NonSystem,
45 IgnoredErrorResult());
48 RefPtr<AsyncEventDispatcher> eventDispatcher =
49 new AsyncEventDispatcher(this, u"close"_ns, CanBubble::eNo);
50 eventDispatcher->PostDOMEvent();
53 void HTMLDialogElement::Show(ErrorResult& aError) {
54 if (Open()) {
55 if (!IsInTopLayer()) {
56 return;
58 return aError.ThrowInvalidStateError(
59 "Cannot call show() on an open modal dialog.");
62 SetOpen(true, IgnoreErrors());
64 StorePreviouslyFocusedElement();
66 RefPtr<nsINode> hideUntil = GetTopmostPopoverAncestor(nullptr, false);
67 if (!hideUntil) {
68 hideUntil = OwnerDoc();
71 OwnerDoc()->HideAllPopoversUntil(*hideUntil, false, true);
72 FocusDialog();
75 bool HTMLDialogElement::IsInTopLayer() const {
76 return State().HasState(ElementState::MODAL);
79 void HTMLDialogElement::AddToTopLayerIfNeeded() {
80 MOZ_ASSERT(IsInComposedDoc());
81 if (IsInTopLayer()) {
82 return;
85 OwnerDoc()->AddModalDialog(*this);
88 void HTMLDialogElement::RemoveFromTopLayerIfNeeded() {
89 if (!IsInTopLayer()) {
90 return;
92 OwnerDoc()->RemoveModalDialog(*this);
95 void HTMLDialogElement::StorePreviouslyFocusedElement() {
96 if (Element* element = nsFocusManager::GetFocusedElementStatic()) {
97 if (NS_SUCCEEDED(nsContentUtils::CheckSameOrigin(this, element))) {
98 mPreviouslyFocusedElement = do_GetWeakReference(element);
100 } else if (Document* doc = GetComposedDoc()) {
101 // Looks like there's a discrepancy sometimes when focus is moved
102 // to a different in-process window.
103 if (nsIContent* unretargetedFocus = doc->GetUnretargetedFocusedContent()) {
104 mPreviouslyFocusedElement = do_GetWeakReference(unretargetedFocus);
109 void HTMLDialogElement::UnbindFromTree(UnbindContext& aContext) {
110 RemoveFromTopLayerIfNeeded();
111 nsGenericHTMLElement::UnbindFromTree(aContext);
114 void HTMLDialogElement::ShowModal(ErrorResult& aError) {
115 if (Open()) {
116 if (IsInTopLayer()) {
117 return;
119 return aError.ThrowInvalidStateError(
120 "Cannot call showModal() on an open non-modal dialog.");
123 if (!IsInComposedDoc()) {
124 return aError.ThrowInvalidStateError("Dialog element is not connected");
127 if (IsPopoverOpen()) {
128 return aError.ThrowInvalidStateError(
129 "Dialog element is already an open popover.");
132 AddToTopLayerIfNeeded();
134 SetOpen(true, aError);
136 StorePreviouslyFocusedElement();
138 RefPtr<nsINode> hideUntil = GetTopmostPopoverAncestor(nullptr, false);
139 if (!hideUntil) {
140 hideUntil = OwnerDoc();
143 OwnerDoc()->HideAllPopoversUntil(*hideUntil, false, true);
144 FocusDialog();
146 aError.SuppressException();
149 void HTMLDialogElement::FocusDialog() {
150 // 1) If subject is inert, return.
151 // 2) Let control be the first descendant element of subject, in tree
152 // order, that is not inert and has the autofocus attribute specified.
153 RefPtr<Document> doc = OwnerDoc();
154 if (IsInComposedDoc()) {
155 doc->FlushPendingNotifications(FlushType::Frames);
158 RefPtr<Element> control = HasAttr(nsGkAtoms::autofocus)
159 ? this
160 : GetFocusDelegate(false /* aWithMouse */);
162 // If there isn't one of those either, then let control be subject.
163 if (!control) {
164 control = this;
167 FocusCandidate(control, IsInTopLayer());
170 int32_t HTMLDialogElement::TabIndexDefault() { return 0; }
172 void HTMLDialogElement::QueueCancelDialog() {
173 // queues an element task on the user interaction task source
174 OwnerDoc()->Dispatch(
175 NewRunnableMethod("HTMLDialogElement::RunCancelDialogSteps", this,
176 &HTMLDialogElement::RunCancelDialogSteps));
179 void HTMLDialogElement::RunCancelDialogSteps() {
180 // 1) Let close be the result of firing an event named cancel at dialog, with
181 // the cancelable attribute initialized to true.
182 bool defaultAction = true;
183 nsContentUtils::DispatchTrustedEvent(OwnerDoc(), this, u"cancel"_ns,
184 CanBubble::eNo, Cancelable::eYes,
185 &defaultAction);
187 // 2) If close is true and dialog has an open attribute, then close the dialog
188 // with no return value.
189 if (defaultAction) {
190 Optional<nsAString> retValue;
191 Close(retValue);
195 JSObject* HTMLDialogElement::WrapNode(JSContext* aCx,
196 JS::Handle<JSObject*> aGivenProto) {
197 return HTMLDialogElement_Binding::Wrap(aCx, this, aGivenProto);
200 } // namespace mozilla::dom