1/*
2 * Copyright (C) 2010 Google Inc. All Rights Reserved.
3 * Copyright (C) 2013 Apple Inc. All Rights Reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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 INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 */
27
28#include "config.h"
29#include "DocumentEventQueue.h"
30
31#include "DOMWindow.h"
32#include "Document.h"
33#include "Event.h"
34#include "EventNames.h"
35#include "SuspendableTimer.h"
36#include <wtf/Ref.h>
37
38namespace WebCore {
39
40class DocumentEventQueue::Timer final : public SuspendableTimer {
41public:
42 Timer(DocumentEventQueue& eventQueue)
43 : SuspendableTimer(eventQueue.m_document)
44 , m_eventQueue(eventQueue)
45 {
46 }
47
48private:
49 void fired() override
50 {
51 ASSERT(!isSuspended());
52 m_eventQueue.pendingEventTimerFired();
53 }
54
55 const char* activeDOMObjectName() const override { return "DocumentEventQueueTimer"; }
56
57 DocumentEventQueue& m_eventQueue;
58};
59
60DocumentEventQueue::DocumentEventQueue(Document& document)
61 : m_document(document)
62 , m_pendingEventTimer(std::make_unique<Timer>(*this))
63 , m_isClosed(false)
64{
65 m_pendingEventTimer->suspendIfNeeded();
66}
67
68DocumentEventQueue::~DocumentEventQueue() = default;
69
70bool DocumentEventQueue::enqueueEvent(Ref<Event>&& event)
71{
72 ASSERT(event->target());
73 ASSERT(!m_queuedEvents.contains(event.ptr()));
74
75 if (m_isClosed)
76 return false;
77
78 m_queuedEvents.add(event.ptr());
79 if (!m_pendingEventTimer->isActive())
80 m_pendingEventTimer->startOneShot(0_s);
81 return true;
82}
83
84void DocumentEventQueue::enqueueOrDispatchScrollEvent(Node& target)
85{
86 ASSERT(&target.document() == &m_document);
87
88 // Per the W3C CSSOM View Module, scroll events fired at the document should bubble, others should not.
89 enqueueScrollEvent(target, target.isDocumentNode() ? Event::CanBubble::Yes : Event::CanBubble::No, Event::IsCancelable::No);
90}
91
92void DocumentEventQueue::enqueueScrollEvent(EventTarget& target, Event::CanBubble canBubble, Event::IsCancelable cancelable)
93{
94 if (m_isClosed)
95 return;
96
97 if (!m_document.hasListenerType(Document::SCROLL_LISTENER))
98 return;
99
100 if (!m_targetsWithQueuedScrollEvents.add(&target).isNewEntry)
101 return;
102
103 Ref<Event> scrollEvent = Event::create(eventNames().scrollEvent, canBubble, cancelable);
104 scrollEvent->setTarget(&target);
105 enqueueEvent(WTFMove(scrollEvent));
106}
107
108void DocumentEventQueue::enqueueResizeEvent(EventTarget& target, Event::CanBubble canBubble, Event::IsCancelable cancelable)
109{
110 if (m_isClosed)
111 return;
112
113 if (!m_document.hasListenerType(Document::RESIZE_LISTENER))
114 return;
115
116 if (!m_targetsWithQueuedResizeEvents.add(&target).isNewEntry)
117 return;
118
119 Ref<Event> resizeEvent = Event::create(eventNames().resizeEvent, canBubble, cancelable);
120 resizeEvent->setTarget(&target);
121 enqueueEvent(WTFMove(resizeEvent));
122}
123
124bool DocumentEventQueue::cancelEvent(Event& event)
125{
126 bool found = m_queuedEvents.remove(&event);
127 if (m_queuedEvents.isEmpty())
128 m_pendingEventTimer->cancel();
129 return found;
130}
131
132void DocumentEventQueue::close()
133{
134 m_isClosed = true;
135 m_pendingEventTimer->cancel();
136 m_queuedEvents.clear();
137}
138
139void DocumentEventQueue::pendingEventTimerFired()
140{
141 ASSERT(!m_pendingEventTimer->isActive());
142 ASSERT(!m_queuedEvents.isEmpty());
143
144 m_targetsWithQueuedScrollEvents.clear();
145 m_targetsWithQueuedResizeEvents.clear();
146
147 // Insert a marker for where we should stop.
148 ASSERT(!m_queuedEvents.contains(nullptr));
149 m_queuedEvents.add(nullptr);
150
151 Ref<Document> protect(m_document);
152
153 while (!m_queuedEvents.isEmpty()) {
154 RefPtr<Event> event = m_queuedEvents.takeFirst();
155 if (!event)
156 break;
157 dispatchEvent(*event);
158 }
159}
160
161void DocumentEventQueue::dispatchEvent(Event& event)
162{
163 // FIXME: Why do we have this special case here instead of a virtual function?
164 // If it's not safe to call EventTarget::dispatchEvent on a DOMWindow, then we
165 // likely have problems elsewhere.
166 auto& eventTarget = *event.target();
167 if (is<DOMWindow>(eventTarget))
168 downcast<DOMWindow>(eventTarget).dispatchEvent(event, nullptr);
169 else
170 eventTarget.dispatchEvent(event);
171}
172
173}
174