f7ea017a684f7f15d9e9e1f3f5d9d7c47274d0d5
[WebKit.git] / Source / WebCore / bindings / js / JSDOMWindowCustom.cpp
1 /*
2  * Copyright (C) 2007-2010, 2016 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 "JSEvent.h"
29 #include "JSEventListener.h"
30 #include "JSHTMLAudioElement.h"
31 #include "JSHTMLCollection.h"
32 #include "JSHTMLOptionElement.h"
33 #include "JSIDBFactory.h"
34 #include "JSImageConstructor.h"
35 #include "JSMessagePortCustom.h"
36 #include "JSWorker.h"
37 #include "Location.h"
38 #include "RuntimeEnabledFeatures.h"
39 #include "ScheduledAction.h"
40 #include "Settings.h"
41
42 #if ENABLE(IOS_TOUCH_EVENTS)
43 #include "JSTouchConstructorIOS.h"
44 #include "JSTouchListConstructorIOS.h"
45 #endif
46
47 #if ENABLE(WEB_AUDIO)
48 #include "JSAudioContext.h"
49 #endif
50
51 #if ENABLE(WEB_SOCKETS)
52 #include "JSWebSocket.h"
53 #endif
54
55 #if ENABLE(USER_MESSAGE_HANDLERS)
56 #include "JSWebKitNamespace.h"
57 #endif
58
59 using namespace JSC;
60
61 namespace WebCore {
62
63 void JSDOMWindow::visitAdditionalChildren(SlotVisitor& visitor)
64 {
65     if (Frame* frame = wrapped().frame())
66         visitor.addOpaqueRoot(frame);
67 }
68
69 #if ENABLE(USER_MESSAGE_HANDLERS)
70 static EncodedJSValue jsDOMWindowWebKit(ExecState* exec, EncodedJSValue thisValue, PropertyName)
71 {
72     JSDOMWindow* castedThis = toJSDOMWindow(JSValue::decode(thisValue));
73     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, castedThis->wrapped()))
74         return JSValue::encode(jsUndefined());
75     return JSValue::encode(toJS(exec, castedThis->globalObject(), castedThis->wrapped().webkitNamespace()));
76 }
77 #endif
78
79 static bool jsDOMWindowGetOwnPropertySlotRestrictedAccess(JSDOMWindow* thisObject, Frame* frame, ExecState* exec, PropertyName propertyName, PropertySlot& slot, String& errorMessage)
80 {
81     // Allow access to toString() cross-domain, but always Object.prototype.toString.
82     if (propertyName == exec->propertyNames().toString) {
83         slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<objectProtoFuncToString, 0>);
84         return true;
85     }
86
87     // We don't want any properties other than "close" and "closed" on a frameless window
88     // (i.e. one whose page got closed, or whose iframe got removed).
89     // FIXME: This handling for frameless windows duplicates similar behaviour for cross-origin
90     // access below; we should try to find a way to merge the two.
91     if (!frame) {
92         if (propertyName == exec->propertyNames().closed) {
93             slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, jsDOMWindowClosed);
94             return true;
95         }
96         if (propertyName == exec->propertyNames().close) {
97             slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowInstanceFunctionClose, 0>);
98             return true;
99         }
100
101         // FIXME: We should have a message here that explains why the property access/function call was
102         // not allowed. 
103         slot.setUndefined();
104         return true;
105     }
106
107     // These are the functions we allow access to cross-origin (DoNotCheckSecurity in IDL).
108     // Always provide the original function, on a fresh uncached function object.
109     if (propertyName == exec->propertyNames().blur) {
110         slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowInstanceFunctionBlur, 0>);
111         return true;
112     }
113     if (propertyName == exec->propertyNames().close) {
114         slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowInstanceFunctionClose, 0>);
115         return true;
116     }
117     if (propertyName == exec->propertyNames().focus) {
118         slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowInstanceFunctionFocus, 0>);
119         return true;
120     }
121     if (propertyName == exec->propertyNames().postMessage) {
122         slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowInstanceFunctionPostMessage, 2>);
123         return true;
124     }
125
126     // When accessing cross-origin known Window properties, we always use the original property getter,
127     // even if the property was removed / redefined. As of early 2016, this matches Firefox and Chrome's
128     // behavior.
129     if (auto* entry = JSDOMWindow::info()->staticPropHashTable->entry(propertyName)) {
130         // Only allow access to these specific properties.
131         if (propertyName == exec->propertyNames().location
132             || propertyName == exec->propertyNames().closed
133             || propertyName == exec->propertyNames().length
134             || propertyName == exec->propertyNames().self
135             || propertyName == exec->propertyNames().window
136             || propertyName == exec->propertyNames().frames
137             || propertyName == exec->propertyNames().opener
138             || propertyName == exec->propertyNames().parent
139             || propertyName == exec->propertyNames().top) {
140             slot.setCacheableCustom(thisObject, ReadOnly | DontDelete | DontEnum, entry->propertyGetter());
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         thisObject->printErrorMessage(errorMessage);
148         slot.setUndefined();
149         return true;
150     }
151
152     // Do prototype lookup early so that functions and attributes in the prototype can have
153     // precedence over the index and name getters.
154     // FIXME: This seems like a silly idea. It only serves to suppress named property access
155     // to frames that happen to have names corresponding to properties on the prototype.
156     // This seems to only serve to leak some information cross-origin.
157     JSValue proto = thisObject->getPrototypeDirect();
158     if (proto.isObject() && asObject(proto)->getPropertySlot(exec, propertyName, slot)) {
159         thisObject->printErrorMessage(errorMessage);
160         slot.setUndefined();
161         return true;
162     }
163
164     // Check for child frames by name before built-in properties to match Mozilla. This does
165     // not match IE, but some sites end up naming frames things that conflict with window
166     // properties that are in Moz but not IE. Since we have some of these, we have to do it
167     // the Moz way.
168     if (auto* scopedChild = frame->tree().scopedChild(propertyNameToAtomicString(propertyName))) {
169         slot.setValue(thisObject, ReadOnly | DontDelete | DontEnum, toJS(exec, scopedChild->document()->domWindow()));
170         return true;
171     }
172
173     thisObject->printErrorMessage(errorMessage);
174     slot.setUndefined();
175     return true;
176 }
177
178 static bool jsDOMWindowGetOwnPropertySlotNamedItemGetter(JSDOMWindow* thisObject, Frame& frame, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
179 {
180     JSValue proto = thisObject->getPrototypeDirect();
181     if (proto.isObject() && asObject(proto)->hasProperty(exec, propertyName))
182         return false;
183
184     // Check for child frames by name before built-in properties to match Mozilla. This does
185     // not match IE, but some sites end up naming frames things that conflict with window
186     // properties that are in Moz but not IE. Since we have some of these, we have to do it
187     // the Moz way.
188     if (auto* scopedChild = frame.tree().scopedChild(propertyNameToAtomicString(propertyName))) {
189         slot.setValue(thisObject, ReadOnly | DontDelete | DontEnum, toJS(exec, scopedChild->document()->domWindow()));
190         return true;
191     }
192
193     // FIXME: Search the whole frame hierarchy somewhere around here.
194     // We need to test the correct priority order.
195
196     // Allow shortcuts like 'Image1' instead of document.images.Image1
197     Document* document = frame.document();
198     if (is<HTMLDocument>(*document)) {
199         auto& htmlDocument = downcast<HTMLDocument>(*document);
200         auto* atomicPropertyName = propertyName.publicName();
201         if (atomicPropertyName && htmlDocument.hasWindowNamedItem(*atomicPropertyName)) {
202             JSValue namedItem;
203             if (UNLIKELY(htmlDocument.windowNamedItemContainsMultipleElements(*atomicPropertyName))) {
204                 Ref<HTMLCollection> collection = document->windowNamedItems(atomicPropertyName);
205                 ASSERT(collection->length() > 1);
206                 namedItem = toJS(exec, thisObject->globalObject(), collection.ptr());
207             } else
208                 namedItem = toJS(exec, thisObject->globalObject(), htmlDocument.windowNamedItem(*atomicPropertyName));
209             slot.setValue(thisObject, ReadOnly | DontDelete | DontEnum, namedItem);
210             return true;
211         }
212     }
213
214     return false;
215 }
216
217 // Property access sequence is:
218 // (1) indexed properties,
219 // (2) regular own properties,
220 // (3) named properties (in fact, these shouldn't be on the window, should be on the NPO).
221 bool JSDOMWindow::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
222 {
223     // (1) First, indexed properties.
224     // Hand off all indexed access to getOwnPropertySlotByIndex, which supports the indexed getter.
225     if (Optional<unsigned> index = parseIndex(propertyName))
226         return getOwnPropertySlotByIndex(object, exec, index.value(), slot);
227
228     auto* thisObject = jsCast<JSDOMWindow*>(object);
229     auto* frame = thisObject->wrapped().frame();
230
231     // Hand off all cross-domain/frameless access to jsDOMWindowGetOwnPropertySlotRestrictedAccess.
232     String errorMessage;
233     if (!frame || !shouldAllowAccessToDOMWindow(exec, thisObject->wrapped(), errorMessage))
234         return jsDOMWindowGetOwnPropertySlotRestrictedAccess(thisObject, frame, exec, propertyName, slot, errorMessage);
235     
236     // FIXME: this need more explanation.
237     // (Particularly, is it correct that this exists here but not in getOwnPropertySlotByIndex?)
238     slot.setWatchpointSet(thisObject->m_windowCloseWatchpoints);
239
240     if (propertyName == exec->propertyNames().showModalDialog) {
241         if (Base::getOwnPropertySlot(thisObject, exec, propertyName, slot))
242             return true;
243         if (!DOMWindow::canShowModalDialog(frame))
244             return jsDOMWindowGetOwnPropertySlotNamedItemGetter(thisObject, *frame, exec, propertyName, slot);
245     }
246
247     // (2) Regular own properties.
248     if (getStaticPropertySlot<JSDOMWindow, Base>(exec, *JSDOMWindow::info()->staticPropHashTable, thisObject, propertyName, slot))
249         return true;
250
251 #if ENABLE(USER_MESSAGE_HANDLERS)
252     if (propertyName == exec->propertyNames().webkit && thisObject->wrapped().shouldHaveWebKitNamespaceForWorld(thisObject->world())) {
253         slot.setCacheableCustom(thisObject, DontDelete | ReadOnly, jsDOMWindowWebKit);
254         return true;
255     }
256 #endif
257
258     // (3) Finally, named properties.
259     // Really, this should just be 'return false;' - these should all be on the NPO.
260     return jsDOMWindowGetOwnPropertySlotNamedItemGetter(thisObject, *frame, exec, propertyName, slot);
261 }
262
263 // Property access sequence is:
264 // (1) indexed properties,
265 // (2) regular own properties,
266 // (3) named properties (in fact, these shouldn't be on the window, should be on the NPO).
267 bool JSDOMWindow::getOwnPropertySlotByIndex(JSObject* object, ExecState* exec, unsigned index, PropertySlot& slot)
268 {
269     auto* thisObject = jsCast<JSDOMWindow*>(object);
270     auto* frame = thisObject->wrapped().frame();
271
272     // Indexed getters take precendence over regular properties, so caching would be invalid.
273     slot.disableCaching();
274
275     // (1) First, indexed properties.
276     // These are also allowed cross-orgin, so come before the access check.
277     if (frame && index < frame->tree().scopedChildCount()) {
278         slot.setValue(thisObject, ReadOnly | DontDelete | DontEnum, toJS(exec, frame->tree().scopedChild(index)->document()->domWindow()));
279         return true;
280     }
281
282     // Hand off all cross-domain/frameless access to jsDOMWindowGetOwnPropertySlotRestrictedAccess.
283     String errorMessage;
284     if (!frame || !shouldAllowAccessToDOMWindow(exec, thisObject->wrapped(), errorMessage))
285         return jsDOMWindowGetOwnPropertySlotRestrictedAccess(thisObject, frame, exec, Identifier::from(exec, index), slot, errorMessage);
286
287     // (2) Regular own properties.
288     if (Base::getOwnPropertySlotByIndex(thisObject, exec, index, slot))
289         return true;
290
291     // (3) Finally, named properties.
292     // Really, this should just be 'return false;' - these should all be on the NPO.
293     return jsDOMWindowGetOwnPropertySlotNamedItemGetter(thisObject, *frame, exec, Identifier::from(exec, index), slot);
294 }
295
296 bool JSDOMWindow::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
297 {
298     auto* thisObject = jsCast<JSDOMWindow*>(cell);
299     if (!thisObject->wrapped().frame())
300         return false;
301
302     String errorMessage;
303     if (!shouldAllowAccessToDOMWindow(exec, thisObject->wrapped(), errorMessage)) {
304         // We only allow setting "location" attribute cross-origin.
305         if (propertyName == exec->propertyNames().location) {
306             bool putResult = false;
307             if (lookupPut(exec, propertyName, thisObject, value, *s_info.staticPropHashTable, slot, putResult))
308                 return putResult;
309             return false;
310         }
311         thisObject->printErrorMessage(errorMessage);
312         return false;
313     }
314
315     return Base::put(thisObject, exec, propertyName, value, slot);
316 }
317
318 bool JSDOMWindow::putByIndex(JSCell* cell, ExecState* exec, unsigned index, JSValue value, bool shouldThrow)
319 {
320     auto* thisObject = jsCast<JSDOMWindow*>(cell);
321     if (!thisObject->wrapped().frame() || !BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped()))
322         return false;
323     
324     return Base::putByIndex(thisObject, exec, index, value, shouldThrow);
325 }
326
327 bool JSDOMWindow::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
328 {
329     JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(cell);
330     // Only allow deleting properties by frames in the same origin.
331     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped()))
332         return false;
333     return Base::deleteProperty(thisObject, exec, propertyName);
334 }
335
336 bool JSDOMWindow::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned propertyName)
337 {
338     JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(cell);
339     // Only allow deleting properties by frames in the same origin.
340     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped()))
341         return false;
342     return Base::deletePropertyByIndex(thisObject, exec, propertyName);
343 }
344
345 uint32_t JSDOMWindow::getEnumerableLength(ExecState* exec, JSObject* object)
346 {
347     JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object);
348     // Only allow the window to enumerated by frames in the same origin.
349     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped()))
350         return 0;
351     return Base::getEnumerableLength(exec, thisObject);
352 }
353
354 void JSDOMWindow::getStructurePropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
355 {
356     JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object);
357     // Only allow the window to enumerated by frames in the same origin.
358     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped()))
359         return;
360     Base::getStructurePropertyNames(thisObject, exec, propertyNames, mode);
361 }
362
363 void JSDOMWindow::getGenericPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
364 {
365     JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object);
366     // Only allow the window to enumerated by frames in the same origin.
367     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped()))
368         return;
369     Base::getGenericPropertyNames(thisObject, exec, propertyNames, mode);
370 }
371
372 void JSDOMWindow::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
373 {
374     JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object);
375     // Only allow the window to enumerated by frames in the same origin.
376     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped()))
377         return;
378     Base::getPropertyNames(thisObject, exec, propertyNames, mode);
379 }
380
381 void JSDOMWindow::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
382 {
383     JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object);
384     // Only allow the window to enumerated by frames in the same origin.
385     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped()))
386         return;
387     Base::getOwnPropertyNames(thisObject, exec, propertyNames, mode);
388 }
389
390 bool JSDOMWindow::defineOwnProperty(JSC::JSObject* object, JSC::ExecState* exec, JSC::PropertyName propertyName, const JSC::PropertyDescriptor& descriptor, bool shouldThrow)
391 {
392     JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object);
393     // Only allow defining properties in this way by frames in the same origin, as it allows setters to be introduced.
394     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped()))
395         return false;
396
397     // Don't allow shadowing location using accessor properties.
398     if (descriptor.isAccessorDescriptor() && propertyName == Identifier::fromString(exec, "location"))
399         return false;
400
401     return Base::defineOwnProperty(thisObject, exec, propertyName, descriptor, shouldThrow);
402 }
403
404 // Custom Attributes
405
406 void JSDOMWindow::setLocation(ExecState& state, JSValue value)
407 {
408 #if ENABLE(DASHBOARD_SUPPORT)
409     // To avoid breaking old widgets, make "var location =" in a top-level frame create
410     // a property named "location" instead of performing a navigation (<rdar://problem/5688039>).
411     if (Frame* activeFrame = activeDOMWindow(&state).frame()) {
412         if (activeFrame->settings().usesDashboardBackwardCompatibilityMode() && !activeFrame->tree().parent()) {
413             if (BindingSecurity::shouldAllowAccessToDOMWindow(&state, wrapped()))
414                 putDirect(state.vm(), Identifier::fromString(&state, "location"), value);
415             return;
416         }
417     }
418 #endif
419
420     String locationString = value.toString(&state)->value(&state);
421     if (state.hadException())
422         return;
423
424     if (Location* location = wrapped().location())
425         location->setHref(activeDOMWindow(&state), firstDOMWindow(&state), locationString);
426 }
427
428 JSValue JSDOMWindow::event(ExecState& state) const
429 {
430     Event* event = currentEvent();
431     if (!event)
432         return jsUndefined();
433     return toJS(&state, const_cast<JSDOMWindow*>(this), event);
434 }
435
436 JSValue JSDOMWindow::image(ExecState& state) const
437 {
438     return createImageConstructor(state.vm(), *this);
439 }
440
441 #if ENABLE(IOS_TOUCH_EVENTS)
442 JSValue JSDOMWindow::touch(ExecState& state) const
443 {
444     return getDOMConstructor<JSTouchConstructor>(state.vm(), *this);
445 }
446
447 JSValue JSDOMWindow::touchList(ExecState& state) const
448 {
449     return getDOMConstructor<JSTouchListConstructor>(state.vm(), *this);
450 }
451 #endif
452
453 // Custom functions
454
455 JSValue JSDOMWindow::open(ExecState& state)
456 {
457     String urlString = valueToStringWithUndefinedOrNullCheck(&state, state.argument(0));
458     if (state.hadException())
459         return jsUndefined();
460     AtomicString frameName = state.argument(1).isUndefinedOrNull() ? "_blank" : state.argument(1).toString(&state)->value(&state);
461     if (state.hadException())
462         return jsUndefined();
463     String windowFeaturesString = valueToStringWithUndefinedOrNullCheck(&state, state.argument(2));
464     if (state.hadException())
465         return jsUndefined();
466
467     RefPtr<DOMWindow> openedWindow = wrapped().open(urlString, frameName, windowFeaturesString, activeDOMWindow(&state), firstDOMWindow(&state));
468     if (!openedWindow)
469         return jsUndefined();
470     return toJS(&state, openedWindow.get());
471 }
472
473 class DialogHandler {
474 public:
475     explicit DialogHandler(ExecState& exec)
476         : m_exec(exec)
477     {
478     }
479
480     void dialogCreated(DOMWindow&);
481     JSValue returnValue() const;
482
483 private:
484     ExecState& m_exec;
485     RefPtr<Frame> m_frame;
486 };
487
488 inline void DialogHandler::dialogCreated(DOMWindow& dialog)
489 {
490     m_frame = dialog.frame();
491     
492     // FIXME: This looks like a leak between the normal world and an isolated
493     //        world if dialogArguments comes from an isolated world.
494     JSDOMWindow* globalObject = toJSDOMWindow(m_frame.get(), normalWorld(m_exec.vm()));
495     if (JSValue dialogArguments = m_exec.argument(1))
496         globalObject->putDirect(m_exec.vm(), Identifier::fromString(&m_exec, "dialogArguments"), dialogArguments);
497 }
498
499 inline JSValue DialogHandler::returnValue() const
500 {
501     JSDOMWindow* globalObject = toJSDOMWindow(m_frame.get(), normalWorld(m_exec.vm()));
502     if (!globalObject)
503         return jsUndefined();
504     Identifier identifier = Identifier::fromString(&m_exec, "returnValue");
505     PropertySlot slot(globalObject, PropertySlot::InternalMethodType::Get);
506     if (!JSGlobalObject::getOwnPropertySlot(globalObject, &m_exec, identifier, slot))
507         return jsUndefined();
508     return slot.getValue(&m_exec, identifier);
509 }
510
511 JSValue JSDOMWindow::showModalDialog(ExecState& state)
512 {
513     if (UNLIKELY(state.argumentCount() < 1))
514         return state.vm().throwException(&state, createNotEnoughArgumentsError(&state));
515
516     String urlString = valueToStringWithUndefinedOrNullCheck(&state, state.argument(0));
517     if (state.hadException())
518         return jsUndefined();
519     String dialogFeaturesString = valueToStringWithUndefinedOrNullCheck(&state, state.argument(2));
520     if (state.hadException())
521         return jsUndefined();
522
523     DialogHandler handler(state);
524
525     wrapped().showModalDialog(urlString, dialogFeaturesString, activeDOMWindow(&state), firstDOMWindow(&state), [&handler](DOMWindow& dialog) {
526         handler.dialogCreated(dialog);
527     });
528
529     return handler.returnValue();
530 }
531
532 static JSValue handlePostMessage(DOMWindow& impl, ExecState& state)
533 {
534     MessagePortArray messagePorts;
535     ArrayBufferArray arrayBuffers;
536
537     // This function has variable arguments and can be:
538     // Per current spec:
539     //   postMessage(message, targetOrigin)
540     //   postMessage(message, targetOrigin, {sequence of transferrables})
541     // Legacy non-standard implementations in webkit allowed:
542     //   postMessage(message, {sequence of transferrables}, targetOrigin);
543     int targetOriginArgIndex = 1;
544     if (state.argumentCount() > 2) {
545         int transferablesArgIndex = 2;
546         if (state.argument(2).isString()) {
547             targetOriginArgIndex = 2;
548             transferablesArgIndex = 1;
549         }
550         fillMessagePortArray(state, state.argument(transferablesArgIndex), messagePorts, arrayBuffers);
551     }
552     if (state.hadException())
553         return jsUndefined();
554
555     RefPtr<SerializedScriptValue> message = SerializedScriptValue::create(&state, state.argument(0),
556                                                                          &messagePorts,
557                                                                          &arrayBuffers);
558
559     if (state.hadException())
560         return jsUndefined();
561
562     String targetOrigin = valueToStringWithUndefinedOrNullCheck(&state, state.argument(targetOriginArgIndex));
563     if (state.hadException())
564         return jsUndefined();
565
566     ExceptionCode ec = 0;
567     impl.postMessage(message.release(), &messagePorts, targetOrigin, callerDOMWindow(&state), ec);
568     setDOMException(&state, ec);
569
570     return jsUndefined();
571 }
572
573 JSValue JSDOMWindow::postMessage(ExecState& state)
574 {
575     return handlePostMessage(wrapped(), state);
576 }
577
578 JSValue JSDOMWindow::setTimeout(ExecState& state)
579 {
580     ContentSecurityPolicy* contentSecurityPolicy = wrapped().document() ? wrapped().document()->contentSecurityPolicy() : nullptr;
581     std::unique_ptr<ScheduledAction> action = ScheduledAction::create(&state, globalObject()->world(), contentSecurityPolicy);
582     if (state.hadException())
583         return jsUndefined();
584
585     if (!action)
586         return jsNumber(0);
587
588     int delay = state.argument(1).toInt32(&state);
589
590     ExceptionCode ec = 0;
591     int result = wrapped().setTimeout(WTFMove(action), delay, ec);
592     setDOMException(&state, ec);
593
594     return jsNumber(result);
595 }
596
597 JSValue JSDOMWindow::setInterval(ExecState& state)
598 {
599     ContentSecurityPolicy* contentSecurityPolicy = wrapped().document() ? wrapped().document()->contentSecurityPolicy() : nullptr;
600     std::unique_ptr<ScheduledAction> action = ScheduledAction::create(&state, globalObject()->world(), contentSecurityPolicy);
601     if (state.hadException())
602         return jsUndefined();
603     int delay = state.argument(1).toInt32(&state);
604
605     if (!action)
606         return jsNumber(0);
607
608     ExceptionCode ec = 0;
609     int result = wrapped().setInterval(WTFMove(action), delay, ec);
610     setDOMException(&state, ec);
611
612     return jsNumber(result);
613 }
614
615 DOMWindow* JSDOMWindow::toWrapped(ExecState&, JSValue value)
616 {
617     if (!value.isObject())
618         return nullptr;
619     JSObject* object = asObject(value);
620     if (object->inherits(JSDOMWindow::info()))
621         return &jsCast<JSDOMWindow*>(object)->wrapped();
622     if (object->inherits(JSDOMWindowShell::info()))
623         return &jsCast<JSDOMWindowShell*>(object)->wrapped();
624     return nullptr;
625 }
626
627 } // namespace WebCore