8b2f20db36c65429990f413e1bba2439b80f49bf
[WebKit-https.git] / Source / WebCore / Modules / indexeddb / legacy / LegacyRequest.cpp
1 /*
2  * Copyright (C) 2010 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  * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "LegacyRequest.h"
31
32 #if ENABLE(INDEXED_DATABASE)
33
34 #include "EventListener.h"
35 #include "EventNames.h"
36 #include "EventQueue.h"
37 #include "ExceptionCodePlaceholder.h"
38 #include "IDBBindingUtilities.h"
39 #include "IDBCursorBackend.h"
40 #include "IDBCursorWithValue.h"
41 #include "IDBDatabase.h"
42 #include "IDBEventDispatcher.h"
43 #include "IDBIndex.h"
44 #include "LegacyCursor.h"
45 #include "LegacyCursorWithValue.h"
46 #include "LegacyTransaction.h"
47 #include "Logging.h"
48 #include "ScriptExecutionContext.h"
49 #include "SharedBuffer.h"
50
51 namespace WebCore {
52
53 Ref<LegacyRequest> LegacyRequest::create(ScriptExecutionContext* context, PassRefPtr<LegacyAny> source, LegacyTransaction* transaction)
54 {
55     Ref<LegacyRequest> request(adoptRef(*new LegacyRequest(context, source, IDBDatabaseBackend::NormalTask, transaction)));
56     request->suspendIfNeeded();
57     // Requests associated with IDBFactory (open/deleteDatabase/getDatabaseNames) are not associated with transactions.
58     if (transaction)
59         transaction->registerRequest(request.ptr());
60     return request;
61 }
62
63 Ref<LegacyRequest> LegacyRequest::create(ScriptExecutionContext* context, PassRefPtr<LegacyAny> source, IDBDatabaseBackend::TaskType taskType, LegacyTransaction* transaction)
64 {
65     Ref<LegacyRequest> request(adoptRef(*new LegacyRequest(context, source, taskType, transaction)));
66     request->suspendIfNeeded();
67     // Requests associated with IDBFactory (open/deleteDatabase/getDatabaseNames) are not associated with transactions.
68     if (transaction)
69         transaction->registerRequest(request.ptr());
70     return request;
71 }
72
73 LegacyRequest::LegacyRequest(ScriptExecutionContext* context, PassRefPtr<LegacyAny> source, IDBDatabaseBackend::TaskType taskType, LegacyTransaction* transaction)
74     : IDBOpenDBRequest(context)
75     , m_result(nullptr)
76     , m_errorCode(0)
77     , m_contextStopped(false)
78     , m_transaction(transaction)
79     , m_readyState(IDBRequestReadyState::Pending)
80     , m_requestAborted(false)
81     , m_source(source)
82     , m_taskType(taskType)
83     , m_hasPendingActivity(true)
84     , m_cursorType(IndexedDB::CursorType::KeyAndValue)
85     , m_cursorDirection(IndexedDB::CursorDirection::Next)
86     , m_cursorFinished(false)
87     , m_pendingCursor(nullptr)
88     , m_didFireUpgradeNeededEvent(false)
89     , m_preventPropagation(false)
90     , m_requestState(context)
91 {
92 }
93
94 LegacyRequest::~LegacyRequest()
95 {
96 }
97
98 RefPtr<IDBAny> LegacyRequest::result(ExceptionCodeWithMessage& ec) const
99 {
100     if (m_readyState != IDBRequestReadyState::Done) {
101         ec.code = IDBDatabaseException::InvalidStateError;
102         return 0;
103     }
104     return m_result;
105 }
106
107 RefPtr<DOMError> LegacyRequest::error(ExceptionCodeWithMessage& ec) const
108 {
109     if (m_readyState != IDBRequestReadyState::Done) {
110         ec.code = IDBDatabaseException::InvalidStateError;
111         return 0;
112     }
113     return m_error;
114 }
115
116 unsigned short LegacyRequest::errorCode(ExceptionCode& ec) const
117 {
118     if (m_readyState != IDBRequestReadyState::Done) {
119         ec = IDBDatabaseException::InvalidStateError;
120         return 0;
121     }
122     return m_errorCode;
123 }
124
125 RefPtr<IDBAny> LegacyRequest::source() const
126 {
127     return m_source;
128 }
129
130 RefPtr<IDBTransaction> LegacyRequest::transaction() const
131 {
132     return m_transaction;
133 }
134
135 const String& LegacyRequest::readyState() const
136 {
137     ASSERT(m_readyState == IDBRequestReadyState::Pending || m_readyState == IDBRequestReadyState::Done);
138     DEPRECATED_DEFINE_STATIC_LOCAL(AtomicString, pending, ("pending", AtomicString::ConstructFromLiteral));
139     DEPRECATED_DEFINE_STATIC_LOCAL(AtomicString, done, ("done", AtomicString::ConstructFromLiteral));
140
141     if (m_readyState == IDBRequestReadyState::Pending)
142         return pending;
143
144     return done;
145 }
146
147 void LegacyRequest::markEarlyDeath()
148 {
149     ASSERT(m_readyState == IDBRequestReadyState::Pending);
150     m_readyState = IDBRequestReadyState::DeprecatedEarlyDeath;
151     if (m_transaction)
152         m_transaction->unregisterRequest(this);
153 }
154
155 void LegacyRequest::abort()
156 {
157     ASSERT(!m_requestAborted);
158     if (m_contextStopped || !scriptExecutionContext())
159         return;
160     ASSERT(m_readyState == IDBRequestReadyState::Pending || m_readyState == IDBRequestReadyState::Done);
161     if (m_readyState == IDBRequestReadyState::Done)
162         return;
163
164     // Enqueued events may be the only reference to this object.
165     RefPtr<LegacyRequest> self(this);
166
167     EventQueue& eventQueue = scriptExecutionContext()->eventQueue();
168     for (auto& event : m_enqueuedEvents) {
169         bool removed = eventQueue.cancelEvent(event);
170         ASSERT_UNUSED(removed, removed);
171     }
172     m_enqueuedEvents.clear();
173
174     m_errorCode = 0;
175     m_error = nullptr;
176     m_errorMessage = String();
177     m_result = nullptr;
178     onError(IDBDatabaseError::create(IDBDatabaseException::AbortError));
179     m_requestAborted = true;
180 }
181
182 void LegacyRequest::setCursorDetails(IndexedDB::CursorType cursorType, IndexedDB::CursorDirection direction)
183 {
184     ASSERT(m_readyState == IDBRequestReadyState::Pending);
185     ASSERT(!m_pendingCursor);
186     m_cursorType = cursorType;
187     m_cursorDirection = direction;
188 }
189
190 void LegacyRequest::setPendingCursor(PassRefPtr<LegacyCursor> cursor)
191 {
192     ASSERT(m_readyState == IDBRequestReadyState::Done);
193     ASSERT(scriptExecutionContext());
194     ASSERT(m_transaction);
195     ASSERT(!m_pendingCursor);
196     ASSERT(cursor == getResultCursor());
197
198     m_pendingCursor = cursor;
199     m_result = nullptr;
200     m_readyState = IDBRequestReadyState::Pending;
201     m_errorCode = 0;
202     m_error = nullptr;
203     m_errorMessage = String();
204     m_transaction->registerRequest(this);
205 }
206
207 RefPtr<LegacyCursor> LegacyRequest::getResultCursor()
208 {
209     if (!m_result)
210         return nullptr;
211     if (m_result->type() == IDBAny::Type::IDBCursor)
212         return m_result->legacyCursor();
213     if (m_result->type() == IDBAny::Type::IDBCursorWithValue)
214         return m_result->legacyCursorWithValue();
215     return nullptr;
216 }
217
218 void LegacyRequest::setResultCursor(PassRefPtr<LegacyCursor> cursor, PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, const Deprecated::ScriptValue& value)
219 {
220     ASSERT(m_readyState == IDBRequestReadyState::Pending);
221     m_cursorKey = key;
222     m_cursorPrimaryKey = primaryKey;
223     m_cursorValue = value;
224
225     if (m_cursorType == IndexedDB::CursorType::KeyOnly) {
226         m_result = LegacyAny::create(cursor);
227         return;
228     }
229
230     m_result = LegacyAny::create(LegacyCursorWithValue::fromCursor(cursor));
231 }
232
233 void LegacyRequest::finishCursor()
234 {
235     m_cursorFinished = true;
236     if (m_readyState != IDBRequestReadyState::Pending)
237         m_hasPendingActivity = false;
238 }
239
240 bool LegacyRequest::shouldEnqueueEvent() const
241 {
242     if (m_contextStopped || !scriptExecutionContext())
243         return false;
244     ASSERT(m_readyState == IDBRequestReadyState::Pending || m_readyState == IDBRequestReadyState::Done);
245     if (m_requestAborted)
246         return false;
247     ASSERT(m_readyState == IDBRequestReadyState::Pending);
248     ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_error && !m_result);
249     return true;
250 }
251
252 void LegacyRequest::onError(PassRefPtr<IDBDatabaseError> error)
253 {
254     LOG(StorageAPI, "LegacyRequest::onError() (%s) '%s'", error->name().utf8().data(), error->message().utf8().data());
255     if (!shouldEnqueueEvent())
256         return;
257
258     m_errorCode = error->code();
259     m_errorMessage = error->message();
260     m_error = DOMError::create(IDBDatabaseException::getErrorName(error->idbCode()));
261     m_pendingCursor = nullptr;
262     enqueueEvent(Event::create(eventNames().errorEvent, true, true));
263 }
264
265 static Ref<Event> createSuccessEvent()
266 {
267     return Event::create(eventNames().successEvent, false, false);
268 }
269
270 void LegacyRequest::onSuccess(PassRefPtr<DOMStringList> domStringList)
271 {
272     LOG(StorageAPI, "LegacyRequest::onSuccess(DOMStringList)");
273     if (!shouldEnqueueEvent())
274         return;
275
276     m_result = LegacyAny::create(domStringList);
277     enqueueEvent(createSuccessEvent());
278 }
279
280 void LegacyRequest::onSuccess(PassRefPtr<IDBCursorBackend> prpBackend)
281 {
282     LOG(StorageAPI, "LegacyRequest::onSuccess(IDBCursor)");
283     if (!shouldEnqueueEvent())
284         return;
285
286     DOMRequestState::Scope scope(m_requestState);
287
288     RefPtr<IDBCursorBackend> backend = prpBackend;
289     RefPtr<IDBKey> key = backend->key();
290     RefPtr<IDBKey> primaryKey = backend->primaryKey();
291
292     Deprecated::ScriptValue value = deserializeIDBValueBuffer(requestState(), backend->valueBuffer(), !!key);
293
294     ASSERT(!m_pendingCursor);
295     RefPtr<LegacyCursor> cursor;
296     switch (m_cursorType) {
297     case IndexedDB::CursorType::KeyOnly:
298         cursor = LegacyCursor::create(backend.get(), m_cursorDirection, this, m_source.get(), m_transaction.get());
299         break;
300     case IndexedDB::CursorType::KeyAndValue:
301         cursor = LegacyCursorWithValue::create(backend.release(), m_cursorDirection, this, m_source.get(), m_transaction.get());
302         break;
303     default:
304         ASSERT_NOT_REACHED();
305     }
306     setResultCursor(cursor, key.release(), primaryKey.release(), value);
307
308     enqueueEvent(createSuccessEvent());
309 }
310
311 void LegacyRequest::onSuccess(PassRefPtr<IDBKey> idbKey)
312 {
313     LOG(StorageAPI, "LegacyRequest::onSuccess(IDBKey)");
314     if (!shouldEnqueueEvent())
315         return;
316
317     if (idbKey && idbKey->isValid()) {
318         DOMRequestState::Scope scope(m_requestState);
319         m_result = LegacyAny::create(idbKeyToScriptValue(requestState(), idbKey));
320     } else
321         m_result = LegacyAny::createInvalid();
322     enqueueEvent(createSuccessEvent());
323 }
324
325 void LegacyRequest::onSuccess(PassRefPtr<SharedBuffer> valueBuffer)
326 {
327     LOG(StorageAPI, "LegacyRequest::onSuccess(SharedBuffer)");
328     if (!shouldEnqueueEvent())
329         return;
330
331     DOMRequestState::Scope scope(m_requestState);
332
333     // FIXME: By not knowing whether or not the key is defined here, we don't know
334     // if a null valueBuffer means the value is null or the value is undefined.
335     Deprecated::ScriptValue value = deserializeIDBValueBuffer(requestState(), valueBuffer, true);
336     onSuccessInternal(value);
337 }
338
339 #ifndef NDEBUG
340 static PassRefPtr<IDBObjectStore> effectiveObjectStore(LegacyAny* source)
341 {
342     if (source->type() == IDBAny::Type::IDBObjectStore)
343         return source->idbObjectStore();
344     if (source->type() == IDBAny::Type::IDBIndex)
345         return source->idbIndex()->objectStore();
346
347     ASSERT_NOT_REACHED();
348     return 0;
349 }
350 #endif
351
352 void LegacyRequest::onSuccess(PassRefPtr<SharedBuffer> valueBuffer, PassRefPtr<IDBKey> prpPrimaryKey, const IDBKeyPath& keyPath)
353 {
354     LOG(StorageAPI, "LegacyRequest::onSuccess(SharedBuffer, IDBKey, IDBKeyPath)");
355     if (!shouldEnqueueEvent())
356         return;
357
358 #ifndef NDEBUG
359     ASSERT(keyPath == effectiveObjectStore(m_source.get())->keyPath());
360 #endif
361     DOMRequestState::Scope scope(m_requestState);
362
363     // FIXME: By not knowing whether or not the key is defined here, we don't know
364     // if a null valueBuffer means the value is null or the value is undefined.
365     Deprecated::ScriptValue value = deserializeIDBValueBuffer(requestState(), valueBuffer, true);
366
367     RefPtr<IDBKey> primaryKey = prpPrimaryKey;
368
369     if (!keyPath.isNull()) {
370 #ifndef NDEBUG
371         RefPtr<IDBKey> expectedKey = createIDBKeyFromScriptValueAndKeyPath(requestState()->exec(), value, keyPath);
372         ASSERT(!expectedKey || expectedKey->isEqual(primaryKey.get()));
373 #endif
374         bool injected = injectIDBKeyIntoScriptValue(requestState(), primaryKey, value, keyPath);
375         ASSERT_UNUSED(injected, injected);
376     }
377
378     onSuccessInternal(value);
379 }
380
381 void LegacyRequest::onSuccess(int64_t value)
382 {
383     LOG(StorageAPI, "LegacyRequest::onSuccess(int64_t)");
384     if (!shouldEnqueueEvent())
385         return;
386     return onSuccessInternal(SerializedScriptValue::numberValue(value));
387 }
388
389 void LegacyRequest::onSuccess()
390 {
391     LOG(StorageAPI, "LegacyRequest::onSuccess()");
392     if (!shouldEnqueueEvent())
393         return;
394     return onSuccessInternal(SerializedScriptValue::undefinedValue());
395 }
396
397 void LegacyRequest::onSuccessInternal(PassRefPtr<SerializedScriptValue> value)
398 {
399     ASSERT(!m_contextStopped);
400     DOMRequestState::Scope scope(m_requestState);
401     return onSuccessInternal(deserializeIDBValue(requestState(), value));
402 }
403
404 void LegacyRequest::onSuccessInternal(const Deprecated::ScriptValue& value)
405 {
406     m_result = LegacyAny::create(value);
407     if (m_pendingCursor) {
408         m_pendingCursor->close();
409         m_pendingCursor = nullptr;
410     }
411     enqueueEvent(createSuccessEvent());
412 }
413
414 void LegacyRequest::onSuccess(PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> buffer)
415 {
416     LOG(StorageAPI, "LegacyRequest::onSuccess(key, primaryKey, valueBuffer)");
417     if (!shouldEnqueueEvent())
418         return;
419
420     DOMRequestState::Scope scope(m_requestState);
421
422     Deprecated::ScriptValue value = deserializeIDBValueBuffer(requestState(), buffer, !!key);
423
424     ASSERT(m_pendingCursor);
425     setResultCursor(m_pendingCursor.release(), key, primaryKey, value);
426     enqueueEvent(createSuccessEvent());
427 }
428
429 void LegacyRequest::onSuccess(PassRefPtr<IDBDatabaseBackend>, const IDBDatabaseMetadata&)
430 {
431     // Only the LegacyOpenDBRequest version of this should ever be called;
432     ASSERT_NOT_REACHED();
433 }
434
435 bool LegacyRequest::hasPendingActivity() const
436 {
437     // FIXME: In an ideal world, we should return true as long as anyone has a or can
438     //        get a handle to us and we have event listeners. This is order to handle
439     //        user generated events properly.
440     return m_hasPendingActivity && !m_contextStopped;
441 }
442
443 void LegacyRequest::stop()
444 {
445     if (m_contextStopped)
446         return;
447
448     m_contextStopped = true;
449     m_requestState.clear();
450     if (m_readyState == IDBRequestReadyState::Pending)
451         markEarlyDeath();
452 }
453
454 bool LegacyRequest::canSuspendForDocumentSuspension() const
455 {
456     return !m_hasPendingActivity;
457 }
458
459 const char* LegacyRequest::activeDOMObjectName() const
460 {
461     return "LegacyRequest";
462 }
463
464 EventTargetInterface LegacyRequest::eventTargetInterface() const
465 {
466     return IDBRequestEventTargetInterfaceType;
467 }
468
469 bool LegacyRequest::dispatchEvent(Event& event)
470 {
471     LOG(StorageAPI, "LegacyRequest::dispatchEvent");
472     ASSERT(m_readyState == IDBRequestReadyState::Pending);
473     ASSERT(!m_contextStopped);
474     ASSERT(m_hasPendingActivity);
475     ASSERT(m_enqueuedEvents.size());
476     ASSERT(scriptExecutionContext());
477     ASSERT(event.target() == this);
478     ASSERT_WITH_MESSAGE(m_readyState < IDBRequestReadyState::Done, "When dispatching event %s, m_readyState < DONE(%d), was %d", event.type().string().utf8().data(), static_cast<int>(IDBRequestReadyState::Done), static_cast<int>(m_readyState));
479
480     DOMRequestState::Scope scope(m_requestState);
481
482     if (event.type() != eventNames().blockedEvent)
483         m_readyState = IDBRequestReadyState::Done;
484
485     for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) {
486         if (m_enqueuedEvents[i].ptr() == &event)
487             m_enqueuedEvents.remove(i);
488     }
489
490     Vector<RefPtr<EventTarget>> targets;
491     targets.append(this);
492     if (m_transaction && !m_preventPropagation) {
493         targets.append(m_transaction);
494         // If there ever are events that are associated with a database but
495         // that do not have a transaction, then this will not work and we need
496         // this object to actually hold a reference to the database (to ensure
497         // it stays alive).
498         targets.append(m_transaction->db());
499     }
500
501     // Cursor properties should not updated until the success event is being dispatched.
502     RefPtr<LegacyCursor> cursorToNotify;
503     if (event.type() == eventNames().successEvent) {
504         cursorToNotify = getResultCursor();
505         if (cursorToNotify) {
506             cursorToNotify->setValueReady(requestState(), m_cursorKey.release(), m_cursorPrimaryKey.release(), m_cursorValue);
507             m_cursorValue.clear();
508         }
509     }
510
511     if (event.type() == eventNames().upgradeneededEvent) {
512         ASSERT(!m_didFireUpgradeNeededEvent);
513         m_didFireUpgradeNeededEvent = true;
514     }
515
516     // FIXME: When we allow custom event dispatching, this will probably need to change.
517     ASSERT_WITH_MESSAGE(event.type() == eventNames().successEvent || event.type() == eventNames().errorEvent || event.type() == eventNames().blockedEvent || event.type() == eventNames().upgradeneededEvent, "event type was %s", event.type().string().utf8().data());
518     const bool setTransactionActive = m_transaction && (event.type() == eventNames().successEvent || event.type() == eventNames().upgradeneededEvent || (event.type() == eventNames().errorEvent && m_errorCode != IDBDatabaseException::AbortError));
519
520     if (setTransactionActive)
521         m_transaction->setActive(true);
522
523     bool dontPreventDefault = IDBEventDispatcher::dispatch(event, targets);
524
525     if (m_transaction) {
526         if (m_readyState == IDBRequestReadyState::Done)
527             m_transaction->unregisterRequest(this);
528
529         // Possibly abort the transaction. This must occur after unregistering (so this request
530         // doesn't receive a second error) and before deactivating (which might trigger commit).
531         if (event.type() == eventNames().errorEvent && dontPreventDefault && !m_requestAborted) {
532             m_transaction->setError(m_error, m_errorMessage);
533             ExceptionCodeWithMessage ec;
534             m_transaction->abort(ec);
535         }
536
537         // If this was the last request in the transaction's list, it may commit here.
538         if (setTransactionActive)
539             m_transaction->setActive(false);
540     }
541
542     if (cursorToNotify)
543         cursorToNotify->postSuccessHandlerCallback();
544
545     if (m_readyState == IDBRequestReadyState::Done && (!cursorToNotify || m_cursorFinished) && event.type() != eventNames().upgradeneededEvent)
546         m_hasPendingActivity = false;
547
548     return dontPreventDefault;
549 }
550
551 void LegacyRequest::uncaughtExceptionInEventHandler()
552 {
553     if (m_transaction && !m_requestAborted) {
554         m_transaction->setError(DOMError::create(IDBDatabaseException::getErrorName(IDBDatabaseException::AbortError)), "Uncaught exception in event handler.");
555         ExceptionCodeWithMessage ec;
556         m_transaction->abort(ec);
557     }
558 }
559
560 void LegacyRequest::transactionDidFinishAndDispatch()
561 {
562     ASSERT(m_transaction);
563     ASSERT(m_transaction->isVersionChange());
564     ASSERT(m_readyState == IDBRequestReadyState::Done);
565     ASSERT(scriptExecutionContext());
566     m_transaction = nullptr;
567     m_readyState = IDBRequestReadyState::Pending;
568 }
569
570 void LegacyRequest::enqueueEvent(Ref<Event>&& event)
571 {
572     ASSERT(m_readyState == IDBRequestReadyState::Pending || m_readyState == IDBRequestReadyState::Done);
573
574     if (m_contextStopped || !scriptExecutionContext())
575         return;
576
577     ASSERT_WITH_MESSAGE(m_readyState == IDBRequestReadyState::Pending || m_didFireUpgradeNeededEvent, "When queueing event %s, m_readyState was %d", event->type().string().utf8().data(), static_cast<int>(m_readyState));
578
579     event->setTarget(this);
580
581     if (scriptExecutionContext()->eventQueue().enqueueEvent(event.copyRef()))
582         m_enqueuedEvents.append(WTFMove(event));
583 }
584
585 } // namespace WebCore
586
587 #endif