e3cc2f9c41110cdae8a54701145354f1d04f6419
[WebKit-https.git] / Source / WebCore / bindings / js / JSDOMWindowCustom.cpp
1 /*
2  * Copyright (C) 2007-2019 Apple Inc. All rights reserved.
3  * Copyright (C) 2011 Google Inc. All rights reserved.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 #include "config.h"
22 #include "JSDOMWindowCustom.h"
23
24 #include "DOMWindowIndexedDatabase.h"
25 #include "DOMWindowWebDatabase.h"
26 #include "Frame.h"
27 #include "HTMLCollection.h"
28 #include "HTMLDocument.h"
29 #include "HTMLFrameOwnerElement.h"
30 #include "HTTPParsers.h"
31 #include "JSDOMBindingSecurity.h"
32 #include "JSDOMConvertNullable.h"
33 #include "JSDOMConvertNumbers.h"
34 #include "JSDOMConvertStrings.h"
35 #include "JSDatabase.h"
36 #include "JSDatabaseCallback.h"
37 #include "JSEvent.h"
38 #include "JSEventListener.h"
39 #include "JSHTMLAudioElement.h"
40 #include "JSHTMLCollection.h"
41 #include "JSHTMLOptionElement.h"
42 #include "JSIDBFactory.h"
43 #include "JSRemoteDOMWindow.h"
44 #include "JSWindowProxy.h"
45 #include "JSWorker.h"
46 #include "Location.h"
47 #include "RuntimeEnabledFeatures.h"
48 #include "ScheduledAction.h"
49 #include "Settings.h"
50 #include "WebCoreJSClientData.h"
51 #include <JavaScriptCore/BuiltinNames.h>
52 #include <JavaScriptCore/HeapAnalyzer.h>
53 #include <JavaScriptCore/JSCInlines.h>
54 #include <JavaScriptCore/JSFunction.h>
55 #include <JavaScriptCore/JSMicrotask.h>
56 #include <JavaScriptCore/Lookup.h>
57 #include <JavaScriptCore/Structure.h>
58
59 #if ENABLE(USER_MESSAGE_HANDLERS)
60 #include "JSWebKitNamespace.h"
61 #endif
62
63
64 namespace WebCore {
65 using namespace JSC;
66
67 EncodedJSValue JSC_HOST_CALL jsDOMWindowInstanceFunctionShowModalDialog(JSGlobalObject*, CallFrame*);
68 EncodedJSValue JSC_HOST_CALL jsDOMWindowInstanceFunctionOpenDatabase(JSGlobalObject*, CallFrame*);
69
70 void JSDOMWindow::visitAdditionalChildren(SlotVisitor& visitor)
71 {
72     if (Frame* frame = wrapped().frame())
73         visitor.addOpaqueRoot(frame);
74
75     visitor.addOpaqueRoot(&wrapped());
76     
77     // Normally JSEventTargetCustom.cpp's JSEventTarget::visitAdditionalChildren() would call this. But
78     // even though DOMWindow is an EventTarget, JSDOMWindow does not subclass JSEventTarget, so we need
79     // to do this here.
80     wrapped().visitJSEventListeners(visitor);
81 }
82
83 #if ENABLE(USER_MESSAGE_HANDLERS)
84 static EncodedJSValue jsDOMWindowWebKit(ExecState* exec, EncodedJSValue thisValue, PropertyName)
85 {
86     VM& vm = exec->vm();
87     JSDOMWindow* castedThis = toJSDOMWindow(vm, JSValue::decode(thisValue));
88     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, castedThis->wrapped()))
89         return JSValue::encode(jsUndefined());
90     return JSValue::encode(toJS(exec, castedThis->globalObject(), castedThis->wrapped().webkitNamespace()));
91 }
92 #endif
93
94 template <DOMWindowType windowType>
95 bool jsDOMWindowGetOwnPropertySlotRestrictedAccess(JSDOMGlobalObject* thisObject, AbstractDOMWindow& window, ExecState& state, PropertyName propertyName, PropertySlot& slot, const String& errorMessage)
96 {
97     VM& vm = state.vm();
98     auto scope = DECLARE_THROW_SCOPE(vm);
99
100     auto& builtinNames = static_cast<JSVMClientData*>(vm.clientData)->builtinNames();
101
102     // https://html.spec.whatwg.org/#crossorigingetownpropertyhelper-(-o,-p-)
103
104     // These are the functions we allow access to cross-origin (DoNotCheckSecurity in IDL).
105     // Always provide the original function, on a fresh uncached function object.
106     if (propertyName == builtinNames.blurPublicName()) {
107         slot.setCustom(thisObject, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum), windowType == DOMWindowType::Remote ? nonCachingStaticFunctionGetter<jsRemoteDOMWindowInstanceFunctionBlur, 0> : nonCachingStaticFunctionGetter<jsDOMWindowInstanceFunctionBlur, 0>);
108         return true;
109     }
110     if (propertyName == builtinNames.closePublicName()) {
111         slot.setCustom(thisObject, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum), windowType == DOMWindowType::Remote ? nonCachingStaticFunctionGetter<jsRemoteDOMWindowInstanceFunctionClose, 0> : nonCachingStaticFunctionGetter<jsDOMWindowInstanceFunctionClose, 0>);
112         return true;
113     }
114     if (propertyName == builtinNames.focusPublicName()) {
115         slot.setCustom(thisObject, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum), windowType == DOMWindowType::Remote ? nonCachingStaticFunctionGetter<jsRemoteDOMWindowInstanceFunctionFocus, 0> : nonCachingStaticFunctionGetter<jsDOMWindowInstanceFunctionFocus, 0>);
116         return true;
117     }
118     if (propertyName == builtinNames.postMessagePublicName()) {
119         slot.setCustom(thisObject, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum), windowType == DOMWindowType::Remote ? nonCachingStaticFunctionGetter<jsRemoteDOMWindowInstanceFunctionPostMessage, 0> : nonCachingStaticFunctionGetter<jsDOMWindowInstanceFunctionPostMessage, 2>);
120         return true;
121     }
122
123     // When accessing cross-origin known Window properties, we always use the original property getter,
124     // even if the property was removed / redefined. As of early 2016, this matches Firefox and Chrome's
125     // behavior.
126     auto* classInfo = windowType == DOMWindowType::Remote ? JSRemoteDOMWindow::info() : JSDOMWindow::info();
127     if (auto* entry = classInfo->staticPropHashTable->entry(propertyName)) {
128         // Only allow access to these specific properties.
129         if (propertyName == builtinNames.locationPublicName()
130             || propertyName == builtinNames.closedPublicName()
131             || propertyName == vm.propertyNames->length
132             || propertyName == builtinNames.selfPublicName()
133             || propertyName == builtinNames.windowPublicName()
134             || propertyName == builtinNames.framesPublicName()
135             || propertyName == builtinNames.openerPublicName()
136             || propertyName == builtinNames.parentPublicName()
137             || propertyName == builtinNames.topPublicName()) {
138             bool shouldExposeSetter = propertyName == builtinNames.locationPublicName();
139             CustomGetterSetter* customGetterSetter = CustomGetterSetter::create(vm, entry->propertyGetter(), shouldExposeSetter ? entry->propertyPutter() : nullptr);
140             slot.setCustomGetterSetter(thisObject, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DontEnum), customGetterSetter);
141             return true;
142         }
143
144         // For any other entries in the static property table, deny access. (Early return also prevents
145         // named getter from returning frames with matching names - this seems a little questionable, see
146         // FIXME comment on prototype search below.)
147         throwSecurityError(state, scope, errorMessage);
148         slot.setUndefined();
149         return false;
150     }
151
152     // Check for child frames by name before built-in properties to match Mozilla. This does
153     // not match IE, but some sites end up naming frames things that conflict with window
154     // properties that are in Moz but not IE. Since we have some of these, we have to do it
155     // the Moz way.
156     // FIXME: Add support to named attributes on RemoteFrames.
157     auto* frame = window.frame();
158     if (frame && is<Frame>(*frame)) {
159         if (auto* scopedChild = downcast<Frame>(*frame).tree().scopedChild(propertyNameToAtomString(propertyName))) {
160             slot.setValue(thisObject, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::DontEnum, toJS(&state, scopedChild->document()->domWindow()));
161             return true;
162         }
163     }
164
165     if (handleCommonCrossOriginProperties(thisObject, vm, propertyName, slot))
166         return true;
167
168     throwSecurityError(state, scope, errorMessage);
169     slot.setUndefined();
170     return false;
171 }
172 template bool jsDOMWindowGetOwnPropertySlotRestrictedAccess<DOMWindowType::Local>(JSDOMGlobalObject*, AbstractDOMWindow&, ExecState&, PropertyName, PropertySlot&, const String&);
173 template bool jsDOMWindowGetOwnPropertySlotRestrictedAccess<DOMWindowType::Remote>(JSDOMGlobalObject*, AbstractDOMWindow&, ExecState&, PropertyName, PropertySlot&, const String&);
174
175 // https://html.spec.whatwg.org/#crossorigingetownpropertyhelper-(-o,-p-)
176 bool handleCommonCrossOriginProperties(JSObject* thisObject, VM& vm, PropertyName propertyName, PropertySlot& slot)
177 {
178     auto& propertyNames =  vm.propertyNames;
179     if (propertyName == propertyNames->builtinNames().thenPublicName() || propertyName == propertyNames->toStringTagSymbol || propertyName == propertyNames->hasInstanceSymbol || propertyName == propertyNames->isConcatSpreadableSymbol) {
180         slot.setValue(thisObject, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum, jsUndefined());
181         return true;
182     }
183     return false;
184 }
185
186 // Property access sequence is:
187 // (1) indexed properties,
188 // (2) regular own properties,
189 // (3) named properties (in fact, these shouldn't be on the window, should be on the NPO).
190 bool JSDOMWindow::getOwnPropertySlot(JSObject* object, ExecState* state, PropertyName propertyName, PropertySlot& slot)
191 {
192     // (1) First, indexed properties.
193     // Hand off all indexed access to getOwnPropertySlotByIndex, which supports the indexed getter.
194     if (Optional<unsigned> index = parseIndex(propertyName))
195         return getOwnPropertySlotByIndex(object, state, index.value(), slot);
196
197     auto* thisObject = jsCast<JSDOMWindow*>(object);
198
199     // Hand off all cross-domain access to jsDOMWindowGetOwnPropertySlotRestrictedAccess.
200     String errorMessage;
201     if (!BindingSecurity::shouldAllowAccessToDOMWindow(*state, thisObject->wrapped(), errorMessage))
202         return jsDOMWindowGetOwnPropertySlotRestrictedAccess<DOMWindowType::Local>(thisObject, thisObject->wrapped(), *state, propertyName, slot, errorMessage);
203
204     // FIXME: this needs more explanation.
205     // (Particularly, is it correct that this exists here but not in getOwnPropertySlotByIndex?)
206     slot.setWatchpointSet(thisObject->m_windowCloseWatchpoints);
207
208     // (2) Regular own properties.
209     PropertySlot slotCopy = slot;
210     if (Base::getOwnPropertySlot(thisObject, state, propertyName, slot)) {
211         auto* frame = thisObject->wrapped().frame();
212
213         // Detect when we're getting the property 'showModalDialog', this is disabled, and has its original value.
214         bool isShowModalDialogAndShouldHide = propertyName == static_cast<JSVMClientData*>(state->vm().clientData)->builtinNames().showModalDialogPublicName()
215             && (!frame || !DOMWindow::canShowModalDialog(*frame))
216             && slot.isValue() && isHostFunction(slot.getValue(state, propertyName), jsDOMWindowInstanceFunctionShowModalDialog);
217         // Unless we're in the showModalDialog special case, we're done.
218         if (!isShowModalDialogAndShouldHide)
219             return true;
220         slot = slotCopy;
221     }
222
223 #if ENABLE(USER_MESSAGE_HANDLERS)
224     if (propertyName == static_cast<JSVMClientData*>(state->vm().clientData)->builtinNames().webkitPublicName() && thisObject->wrapped().shouldHaveWebKitNamespaceForWorld(thisObject->world())) {
225         slot.setCacheableCustom(thisObject, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly, jsDOMWindowWebKit);
226         return true;
227     }
228 #endif
229
230     return false;
231 }
232
233 // Property access sequence is:
234 // (1) indexed properties,
235 // (2) regular own properties,
236 // (3) named properties (in fact, these shouldn't be on the window, should be on the NPO).
237 bool JSDOMWindow::getOwnPropertySlotByIndex(JSObject* object, ExecState* state, unsigned index, PropertySlot& slot)
238 {
239     VM& vm = state->vm();
240     auto* thisObject = jsCast<JSDOMWindow*>(object);
241     auto& window = thisObject->wrapped();
242     auto* frame = window.frame();
243
244     // Indexed getters take precendence over regular properties, so caching would be invalid.
245     slot.disableCaching();
246
247     String errorMessage;
248     Optional<bool> cachedIsCrossOriginAccess;
249     auto isCrossOriginAccess = [&] {
250         if (!cachedIsCrossOriginAccess)
251             cachedIsCrossOriginAccess = !BindingSecurity::shouldAllowAccessToDOMWindow(*state, window, errorMessage);
252         return *cachedIsCrossOriginAccess;
253     };
254
255     // (1) First, indexed properties.
256     // These are also allowed cross-origin, so come before the access check.
257     if (frame && index < frame->tree().scopedChildCount()) {
258         slot.setValue(thisObject, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly), toJS(state, frame->tree().scopedChild(index)->document()->domWindow()));
259         return true;
260     }
261
262     // Hand off all cross-domain/frameless access to jsDOMWindowGetOwnPropertySlotRestrictedAccess.
263     if (isCrossOriginAccess())
264         return jsDOMWindowGetOwnPropertySlotRestrictedAccess<DOMWindowType::Local>(thisObject, window, *state, Identifier::from(vm, index), slot, errorMessage);
265
266     // (2) Regular own properties.
267     return Base::getOwnPropertySlotByIndex(thisObject, state, index, slot);
268 }
269
270 void JSDOMWindow::doPutPropertySecurityCheck(JSObject* cell, ExecState* state, PropertyName propertyName, PutPropertySlot&)
271 {
272     VM& vm = state->vm();
273     auto scope = DECLARE_THROW_SCOPE(vm);
274
275     auto* thisObject = jsCast<JSDOMWindow*>(cell);
276     if (!thisObject->wrapped().frame())
277         return;
278
279     String errorMessage;
280     if (!BindingSecurity::shouldAllowAccessToDOMWindow(*state, thisObject->wrapped(), errorMessage)) {
281         // We only allow setting "location" attribute cross-origin.
282         if (propertyName == static_cast<JSVMClientData*>(vm.clientData)->builtinNames().locationPublicName())
283             return;
284         throwSecurityError(*state, scope, errorMessage);
285         return;
286     }
287 }
288
289 bool JSDOMWindow::put(JSCell* cell, ExecState* state, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
290 {
291     VM& vm = state->vm();
292     auto scope = DECLARE_THROW_SCOPE(vm);
293
294     auto* thisObject = jsCast<JSDOMWindow*>(cell);
295     if (!thisObject->wrapped().frame())
296         return false;
297
298     String errorMessage;
299     if (!BindingSecurity::shouldAllowAccessToDOMWindow(*state, thisObject->wrapped(), errorMessage)) {
300         // We only allow setting "location" attribute cross-origin.
301         if (propertyName == static_cast<JSVMClientData*>(vm.clientData)->builtinNames().locationPublicName()) {
302             bool putResult = false;
303             if (lookupPut(state, propertyName, thisObject, value, *s_info.staticPropHashTable, slot, putResult))
304                 return putResult;
305             return false;
306         }
307         throwSecurityError(*state, scope, errorMessage);
308         return false;
309     }
310
311     return Base::put(thisObject, state, propertyName, value, slot);
312 }
313
314 bool JSDOMWindow::putByIndex(JSCell* cell, ExecState* exec, unsigned index, JSValue value, bool shouldThrow)
315 {
316     auto* thisObject = jsCast<JSDOMWindow*>(cell);
317     if (!thisObject->wrapped().frame() || !BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped()))
318         return false;
319     
320     return Base::putByIndex(thisObject, exec, index, value, shouldThrow);
321 }
322
323 bool JSDOMWindow::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
324 {
325     JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(cell);
326     // Only allow deleting properties by frames in the same origin.
327     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped(), ThrowSecurityError))
328         return false;
329     return Base::deleteProperty(thisObject, exec, propertyName);
330 }
331
332 bool JSDOMWindow::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned propertyName)
333 {
334     JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(cell);
335     // Only allow deleting properties by frames in the same origin.
336     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped(), ThrowSecurityError))
337         return false;
338     return Base::deletePropertyByIndex(thisObject, exec, propertyName);
339 }
340
341 void JSDOMWindow::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer)
342 {
343     JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(cell);
344     auto& location = thisObject->wrapped().location();
345     analyzer.setLabelForCell(cell, location.href());
346
347     Base::analyzeHeap(cell, analyzer);
348 }
349
350 // https://html.spec.whatwg.org/#crossoriginproperties-(-o-)
351 template <CrossOriginObject objectType>
352 static void addCrossOriginPropertyNames(VM& vm, PropertyNameArray& propertyNames)
353 {
354     auto& builtinNames = static_cast<JSVMClientData*>(vm.clientData)->builtinNames();
355     switch (objectType) {
356     case CrossOriginObject::Location: {
357         static const Identifier* const properties[] = { &builtinNames.hrefPublicName(), &vm.propertyNames->replace };
358         for (auto* property : properties)
359             propertyNames.add(*property);
360         break;
361     }
362     case CrossOriginObject::Window: {
363         static const Identifier* const properties[] = {
364             &builtinNames.blurPublicName(), &builtinNames.closePublicName(), &builtinNames.closedPublicName(),
365             &builtinNames.focusPublicName(), &builtinNames.framesPublicName(), &vm.propertyNames->length,
366             &builtinNames.locationPublicName(), &builtinNames.openerPublicName(), &builtinNames.parentPublicName(),
367             &builtinNames.postMessagePublicName(), &builtinNames.selfPublicName(), &builtinNames.topPublicName(),
368             &builtinNames.windowPublicName()
369         };
370
371         for (auto* property : properties)
372             propertyNames.add(*property);
373         break;
374     }
375     }
376 }
377
378 // https://html.spec.whatwg.org/#crossoriginownpropertykeys-(-o-)
379 template <CrossOriginObject objectType>
380 void addCrossOriginOwnPropertyNames(JSC::ExecState& state, JSC::PropertyNameArray& propertyNames)
381 {
382     auto& vm = state.vm();
383     addCrossOriginPropertyNames<objectType>(vm, propertyNames);
384
385     static const Identifier* const properties[] = {
386         &vm.propertyNames->builtinNames().thenPublicName(), &vm.propertyNames->toStringTagSymbol, &vm.propertyNames->hasInstanceSymbol, &vm.propertyNames->isConcatSpreadableSymbol
387     };
388
389     for (auto* property : properties)
390         propertyNames.add(*property);
391
392 }
393 template void addCrossOriginOwnPropertyNames<CrossOriginObject::Window>(JSC::ExecState&, JSC::PropertyNameArray&);
394 template void addCrossOriginOwnPropertyNames<CrossOriginObject::Location>(JSC::ExecState&, JSC::PropertyNameArray&);
395
396 static void addScopedChildrenIndexes(ExecState& state, DOMWindow& window, PropertyNameArray& propertyNames)
397 {
398     auto* document = window.document();
399     if (!document)
400         return;
401
402     auto* frame = document->frame();
403     if (!frame)
404         return;
405
406     VM& vm = state.vm();
407     unsigned scopedChildCount = frame->tree().scopedChildCount();
408     for (unsigned i = 0; i < scopedChildCount; ++i)
409         propertyNames.add(Identifier::from(vm, i));
410 }
411
412 // https://html.spec.whatwg.org/#windowproxy-ownpropertykeys
413 void JSDOMWindow::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
414 {
415     JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object);
416
417     addScopedChildrenIndexes(*exec, thisObject->wrapped(), propertyNames);
418
419     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped(), DoNotReportSecurityError)) {
420         if (mode.includeDontEnumProperties())
421             addCrossOriginOwnPropertyNames<CrossOriginObject::Window>(*exec, propertyNames);
422         return;
423     }
424     Base::getOwnPropertyNames(thisObject, exec, propertyNames, mode);
425 }
426
427 bool JSDOMWindow::defineOwnProperty(JSC::JSObject* object, JSC::ExecState* exec, JSC::PropertyName propertyName, const JSC::PropertyDescriptor& descriptor, bool shouldThrow)
428 {
429     JSC::VM& vm = exec->vm();
430     JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object);
431     // Only allow defining properties in this way by frames in the same origin, as it allows setters to be introduced.
432     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped(), ThrowSecurityError))
433         return false;
434
435     // Don't allow shadowing location using accessor properties.
436     if (descriptor.isAccessorDescriptor() && propertyName == Identifier::fromString(vm, "location"))
437         return false;
438
439     return Base::defineOwnProperty(thisObject, exec, propertyName, descriptor, shouldThrow);
440 }
441
442 JSValue JSDOMWindow::getPrototype(JSObject* object, ExecState* exec)
443 {
444     JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object);
445     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped(), DoNotReportSecurityError))
446         return jsNull();
447
448     return Base::getPrototype(object, exec);
449 }
450
451 bool JSDOMWindow::preventExtensions(JSObject*, ExecState* exec)
452 {
453     auto scope = DECLARE_THROW_SCOPE(exec->vm());
454
455     throwTypeError(exec, scope, "Cannot prevent extensions on this object"_s);
456     return false;
457 }
458
459 String JSDOMWindow::toStringName(const JSObject* object, ExecState* exec)
460 {
461     auto* thisObject = jsCast<const JSDOMWindow*>(object);
462     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped(), DoNotReportSecurityError))
463         return "Object"_s;
464     return "Window"_s;
465 }
466
467 // Custom Attributes
468
469 JSValue JSDOMWindow::event(ExecState& state) const
470 {
471     Event* event = currentEvent();
472     if (!event)
473         return jsUndefined();
474     return toJS(&state, const_cast<JSDOMWindow*>(this), event);
475 }
476
477 // Custom functions
478
479 class DialogHandler {
480 public:
481     explicit DialogHandler(ExecState& exec)
482         : m_exec(exec)
483     {
484     }
485
486     void dialogCreated(DOMWindow&);
487     JSValue returnValue() const;
488
489 private:
490     ExecState& m_exec;
491     RefPtr<Frame> m_frame;
492 };
493
494 inline void DialogHandler::dialogCreated(DOMWindow& dialog)
495 {
496     VM& vm = m_exec.vm();
497     m_frame = dialog.frame();
498     
499     // FIXME: This looks like a leak between the normal world and an isolated
500     //        world if dialogArguments comes from an isolated world.
501     JSDOMWindow* globalObject = toJSDOMWindow(m_frame.get(), normalWorld(vm));
502     if (JSValue dialogArguments = m_exec.argument(1))
503         globalObject->putDirect(vm, Identifier::fromString(vm, "dialogArguments"), dialogArguments);
504 }
505
506 inline JSValue DialogHandler::returnValue() const
507 {
508     VM& vm = m_exec.vm();
509     JSDOMWindow* globalObject = toJSDOMWindow(m_frame.get(), normalWorld(vm));
510     if (!globalObject)
511         return jsUndefined();
512     Identifier identifier = Identifier::fromString(vm, "returnValue");
513     PropertySlot slot(globalObject, PropertySlot::InternalMethodType::Get);
514     if (!JSGlobalObject::getOwnPropertySlot(globalObject, &m_exec, identifier, slot))
515         return jsUndefined();
516     return slot.getValue(&m_exec, identifier);
517 }
518
519 JSValue JSDOMWindow::showModalDialog(ExecState& state)
520 {
521     VM& vm = state.vm();
522     auto scope = DECLARE_THROW_SCOPE(vm);
523
524     if (UNLIKELY(state.argumentCount() < 1))
525         return throwException(&state, scope, createNotEnoughArgumentsError(&state));
526
527     String urlString = convert<IDLNullable<IDLDOMString>>(state, state.argument(0));
528     RETURN_IF_EXCEPTION(scope, JSValue());
529     String dialogFeaturesString = convert<IDLNullable<IDLDOMString>>(state, state.argument(2));
530     RETURN_IF_EXCEPTION(scope, JSValue());
531
532     DialogHandler handler(state);
533
534     wrapped().showModalDialog(urlString, dialogFeaturesString, activeDOMWindow(state), firstDOMWindow(state), [&handler](DOMWindow& dialog) {
535         handler.dialogCreated(dialog);
536     });
537
538     return handler.returnValue();
539 }
540
541 JSValue JSDOMWindow::queueMicrotask(ExecState& state)
542 {
543     VM& vm = state.vm();
544     auto scope = DECLARE_THROW_SCOPE(vm);
545
546     if (UNLIKELY(state.argumentCount() < 1))
547         return throwException(&state, scope, createNotEnoughArgumentsError(&state));
548
549     JSValue functionValue = state.uncheckedArgument(0);
550     if (UNLIKELY(!functionValue.isFunction(vm)))
551         return JSValue::decode(throwArgumentMustBeFunctionError(state, scope, 0, "callback", "Window", "queueMicrotask"));
552
553     scope.release();
554     Base::queueMicrotask(JSC::createJSMicrotask(vm, functionValue));
555     return jsUndefined();
556 }
557
558 DOMWindow* JSDOMWindow::toWrapped(VM& vm, JSValue value)
559 {
560     if (!value.isObject())
561         return nullptr;
562     JSObject* object = asObject(value);
563     if (object->inherits<JSDOMWindow>(vm))
564         return &jsCast<JSDOMWindow*>(object)->wrapped();
565     if (object->inherits<JSWindowProxy>(vm)) {
566         if (auto* jsDOMWindow = jsDynamicCast<JSDOMWindow*>(vm, jsCast<JSWindowProxy*>(object)->window()))
567             return &jsDOMWindow->wrapped();
568     }
569     return nullptr;
570 }
571
572 void JSDOMWindow::setOpener(JSC::ExecState& state, JSC::JSValue value)
573 {
574     if (!BindingSecurity::shouldAllowAccessToDOMWindow(&state, wrapped(), ThrowSecurityError))
575         return;
576
577     if (value.isNull()) {
578         wrapped().disownOpener();
579         return;
580     }
581     VM& vm = state.vm();
582     replaceStaticPropertySlot(vm, this, Identifier::fromString(vm, "opener"), value);
583 }
584
585 JSValue JSDOMWindow::self(JSC::ExecState&) const
586 {
587     return globalThis();
588 }
589
590 JSValue JSDOMWindow::window(JSC::ExecState&) const
591 {
592     return globalThis();
593 }
594
595 JSValue JSDOMWindow::frames(JSC::ExecState&) const
596 {
597     return globalThis();
598 }
599
600 static inline JSC::EncodedJSValue jsDOMWindowInstanceFunctionOpenDatabaseBody(JSC::ExecState* state, typename IDLOperation<JSDOMWindow>::ClassParameter castedThis, JSC::ThrowScope& throwScope)
601 {
602     if (!BindingSecurity::shouldAllowAccessToDOMWindow(state, castedThis->wrapped(), ThrowSecurityError))
603         return JSValue::encode(jsUndefined());
604     auto& impl = castedThis->wrapped();
605     if (UNLIKELY(state->argumentCount() < 4))
606         return throwVMError(state, throwScope, createNotEnoughArgumentsError(state));
607     auto name = convert<IDLDOMString>(*state, state->uncheckedArgument(0));
608     RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
609     auto version = convert<IDLDOMString>(*state, state->uncheckedArgument(1));
610     RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
611     auto displayName = convert<IDLDOMString>(*state, state->uncheckedArgument(2));
612     RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
613     auto estimatedSize = convert<IDLUnsignedLong>(*state, state->uncheckedArgument(3));
614     RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
615
616     if (!RuntimeEnabledFeatures::sharedFeatures().webSQLEnabled()) {
617         if (name != "null" || version != "null" || displayName != "null" || estimatedSize)
618             propagateException(*state, throwScope, Exception(UnknownError, "Web SQL is deprecated"_s));
619         return JSValue::encode(constructEmptyObject(state, castedThis->globalObject()->objectPrototype()));
620     }
621
622     auto creationCallback = convert<IDLNullable<IDLCallbackFunction<JSDatabaseCallback>>>(*state, state->argument(4), *castedThis->globalObject(), [](JSC::ExecState& state, JSC::ThrowScope& scope) {
623         throwArgumentMustBeFunctionError(state, scope, 4, "creationCallback", "Window", "openDatabase");
624     });
625     RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
626     return JSValue::encode(toJS<IDLNullable<IDLInterface<Database>>>(*state, *castedThis->globalObject(), throwScope, WebCore::DOMWindowWebDatabase::openDatabase(impl, WTFMove(name), WTFMove(version), WTFMove(displayName), WTFMove(estimatedSize), WTFMove(creationCallback))));
627 }
628
629 template<> inline JSDOMWindow* IDLOperation<JSDOMWindow>::cast(ExecState& state)
630 {
631     return toJSDOMWindow(state.vm(), state.thisValue().toThis(&state, NotStrictMode));
632 }
633
634 EncodedJSValue JSC_HOST_CALL jsDOMWindowInstanceFunctionOpenDatabase(JSGlobalObject*, CallFrame* callFrame)
635 {
636     return IDLOperation<JSDOMWindow>::call<jsDOMWindowInstanceFunctionOpenDatabaseBody>(*callFrame, "openDatabase");
637 }
638
639 JSValue JSDOMWindow::openDatabase(JSC::ExecState& state) const
640 {
641     VM& vm = state.vm();
642     StringImpl* name = PropertyName(static_cast<JSVMClientData*>(vm.clientData)->builtinNames().openDatabasePublicName()).publicName();
643     if (RuntimeEnabledFeatures::sharedFeatures().webSQLEnabled())
644         return JSFunction::create(vm, state.lexicalGlobalObject(), 4, name, jsDOMWindowInstanceFunctionOpenDatabase, NoIntrinsic);
645
646     return JSFunction::createFunctionThatMasqueradesAsUndefined(vm, state.lexicalGlobalObject(), 4, name, jsDOMWindowInstanceFunctionOpenDatabase, NoIntrinsic);
647 }
648
649 void JSDOMWindow::setOpenDatabase(JSC::ExecState& state, JSC::JSValue value)
650 {
651     if (!BindingSecurity::shouldAllowAccessToDOMWindow(&state, wrapped(), ThrowSecurityError))
652         return;
653
654     VM& vm = state.vm();
655     replaceStaticPropertySlot(vm, this, Identifier::fromString(vm, "openDatabase"), value);
656 }
657
658 } // namespace WebCore