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