• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "config.h"
27 #include "core/html/HTMLDialogElement.h"
28 
29 #include "bindings/core/v8/ExceptionState.h"
30 #include "core/accessibility/AXObjectCache.h"
31 #include "core/dom/ExceptionCode.h"
32 #include "core/dom/NodeTraversal.h"
33 #include "core/events/Event.h"
34 #include "core/frame/UseCounter.h"
35 #include "core/html/HTMLFormControlElement.h"
36 #include "core/frame/FrameView.h"
37 #include "core/rendering/RenderBlock.h"
38 #include "core/rendering/style/RenderStyle.h"
39 
40 namespace blink {
41 
42 using namespace HTMLNames;
43 
44 // This function chooses the focused element when showModal() is invoked, as described in the spec for showModal().
setFocusForModalDialog(HTMLDialogElement * dialog)45 static void setFocusForModalDialog(HTMLDialogElement* dialog)
46 {
47     Element* focusableDescendant = 0;
48     Node* next = 0;
49     for (Node* node = dialog->firstChild(); node; node = next) {
50         if (isHTMLDialogElement(*node))
51             next = NodeTraversal::nextSkippingChildren(*node, dialog);
52         else
53             next = NodeTraversal::next(*node, dialog);
54 
55         if (!node->isElementNode())
56             continue;
57         Element* element = toElement(node);
58         if (element->isFormControlElement()) {
59             HTMLFormControlElement* control = toHTMLFormControlElement(node);
60             if (control->isAutofocusable()) {
61                 control->focus();
62                 return;
63             }
64         }
65         if (!focusableDescendant && element->isFocusable())
66             focusableDescendant = element;
67     }
68 
69     if (focusableDescendant) {
70         focusableDescendant->focus();
71         return;
72     }
73 
74     if (dialog->isFocusable()) {
75         dialog->focus();
76         return;
77     }
78 
79     dialog->document().setFocusedElement(nullptr);
80 }
81 
inertSubtreesChanged(Document & document)82 static void inertSubtreesChanged(Document& document)
83 {
84     // When a modal dialog opens or closes, nodes all over the accessibility
85     // tree can change inertness which means they must be added or removed from
86     // the tree. The most foolproof way is to clear the entire tree and rebuild
87     // it, though a more clever way is probably possible.
88     Document& topDocument = document.topDocument();
89     topDocument.clearAXObjectCache();
90     if (AXObjectCache* cache = topDocument.axObjectCache())
91         cache->childrenChanged(cache->getOrCreate(&topDocument));
92 }
93 
HTMLDialogElement(Document & document)94 inline HTMLDialogElement::HTMLDialogElement(Document& document)
95     : HTMLElement(dialogTag, document)
96     , m_centeringMode(NotCentered)
97     , m_centeredPosition(0)
98     , m_returnValue("")
99 {
100     UseCounter::count(document, UseCounter::DialogElement);
101 }
102 
DEFINE_NODE_FACTORY(HTMLDialogElement)103 DEFINE_NODE_FACTORY(HTMLDialogElement)
104 
105 void HTMLDialogElement::close(const String& returnValue, ExceptionState& exceptionState)
106 {
107     if (!fastHasAttribute(openAttr)) {
108         exceptionState.throwDOMException(InvalidStateError, "The element does not have an 'open' attribute, and therefore cannot be closed.");
109         return;
110     }
111     closeDialog(returnValue);
112 }
113 
closeDialog(const String & returnValue)114 void HTMLDialogElement::closeDialog(const String& returnValue)
115 {
116     if (!fastHasAttribute(openAttr))
117         return;
118     setBooleanAttribute(openAttr, false);
119 
120     HTMLDialogElement* activeModalDialog = document().activeModalDialog();
121     document().removeFromTopLayer(this);
122     if (activeModalDialog == this)
123         inertSubtreesChanged(document());
124 
125     if (!returnValue.isNull())
126         m_returnValue = returnValue;
127 
128     dispatchScopedEvent(Event::create(EventTypeNames::close));
129 }
130 
forceLayoutForCentering()131 void HTMLDialogElement::forceLayoutForCentering()
132 {
133     m_centeringMode = NeedsCentering;
134     document().updateLayoutIgnorePendingStylesheets();
135     if (m_centeringMode == NeedsCentering)
136         setNotCentered();
137 }
138 
show()139 void HTMLDialogElement::show()
140 {
141     if (fastHasAttribute(openAttr))
142         return;
143     setBooleanAttribute(openAttr, true);
144 }
145 
showModal(ExceptionState & exceptionState)146 void HTMLDialogElement::showModal(ExceptionState& exceptionState)
147 {
148     if (fastHasAttribute(openAttr)) {
149         exceptionState.throwDOMException(InvalidStateError, "The element already has an 'open' attribute, and therefore cannot be opened modally.");
150         return;
151     }
152     if (!inDocument()) {
153         exceptionState.throwDOMException(InvalidStateError, "The element is not in a Document.");
154         return;
155     }
156 
157     document().addToTopLayer(this);
158     setBooleanAttribute(openAttr, true);
159 
160     // Throw away the AX cache first, so the subsequent steps don't have a chance of queuing up
161     // AX events on objects that would be invalidated when the cache is thrown away.
162     inertSubtreesChanged(document());
163 
164     forceLayoutForCentering();
165     setFocusForModalDialog(this);
166 }
167 
removedFrom(ContainerNode * insertionPoint)168 void HTMLDialogElement::removedFrom(ContainerNode* insertionPoint)
169 {
170     HTMLElement::removedFrom(insertionPoint);
171     setNotCentered();
172     // FIXME: We should call inertSubtreesChanged() here.
173 }
174 
setCentered(LayoutUnit centeredPosition)175 void HTMLDialogElement::setCentered(LayoutUnit centeredPosition)
176 {
177     ASSERT(m_centeringMode == NeedsCentering);
178     m_centeredPosition = centeredPosition;
179     m_centeringMode = Centered;
180 }
181 
setNotCentered()182 void HTMLDialogElement::setNotCentered()
183 {
184     m_centeringMode = NotCentered;
185 }
186 
isPresentationAttribute(const QualifiedName & name) const187 bool HTMLDialogElement::isPresentationAttribute(const QualifiedName& name) const
188 {
189     // FIXME: Workaround for <https://bugs.webkit.org/show_bug.cgi?id=91058>: modifying an attribute for which there is an attribute selector
190     // in html.css sometimes does not trigger a style recalc.
191     if (name == openAttr)
192         return true;
193 
194     return HTMLElement::isPresentationAttribute(name);
195 }
196 
defaultEventHandler(Event * event)197 void HTMLDialogElement::defaultEventHandler(Event* event)
198 {
199     if (event->type() == EventTypeNames::cancel) {
200         closeDialog();
201         event->setDefaultHandled();
202         return;
203     }
204     HTMLElement::defaultEventHandler(event);
205 }
206 
207 } // namespace blink
208