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