Replace WTF::move with WTFMove
[WebKit-https.git] / Source / WebCore / bindings / js / JSDOMWindowCustom.cpp
1 /*
2  * Copyright (C) 2007, 2008, 2009, 2010 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 static EncodedJSValue childFrameGetter(ExecState* exec, JSObject* slotBase, EncodedJSValue, PropertyName propertyName)
70 {
71     return JSValue::encode(toJS(exec, jsCast<JSDOMWindow*>(slotBase)->wrapped().frame()->tree().scopedChild(propertyNameToAtomicString(propertyName))->document()->domWindow()));
72 }
73
74 static EncodedJSValue namedItemGetter(ExecState* exec, JSObject* slotBase, EncodedJSValue, PropertyName propertyName)
75 {
76     JSDOMWindowBase* thisObj = jsCast<JSDOMWindow*>(slotBase);
77     Document* document = thisObj->wrapped().frame()->document();
78
79     ASSERT(BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObj->wrapped()));
80     ASSERT(is<HTMLDocument>(document));
81
82     AtomicStringImpl* atomicPropertyName = propertyName.publicName();
83     if (!atomicPropertyName || !downcast<HTMLDocument>(*document).hasWindowNamedItem(*atomicPropertyName))
84         return JSValue::encode(jsUndefined());
85
86     if (UNLIKELY(downcast<HTMLDocument>(*document).windowNamedItemContainsMultipleElements(*atomicPropertyName))) {
87         Ref<HTMLCollection> collection = document->windowNamedItems(atomicPropertyName);
88         ASSERT(collection->length() > 1);
89         return JSValue::encode(toJS(exec, thisObj->globalObject(), WTF::getPtr(collection)));
90     }
91
92     return JSValue::encode(toJS(exec, thisObj->globalObject(), downcast<HTMLDocument>(*document).windowNamedItem(*atomicPropertyName)));
93 }
94
95 #if ENABLE(USER_MESSAGE_HANDLERS)
96 static EncodedJSValue jsDOMWindowWebKit(ExecState* exec, JSObject*, EncodedJSValue thisValue, PropertyName)
97 {
98     JSDOMWindow* castedThis = toJSDOMWindow(JSValue::decode(thisValue));
99     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, castedThis->wrapped()))
100         return JSValue::encode(jsUndefined());
101     return JSValue::encode(toJS(exec, castedThis->globalObject(), castedThis->wrapped().webkitNamespace()));
102 }
103 #endif
104
105 #if ENABLE(INDEXED_DATABASE)
106 static EncodedJSValue jsDOMWindowIndexedDB(ExecState* exec, JSObject* slotBase, EncodedJSValue thisValue, PropertyName)
107 {
108     UNUSED_PARAM(exec);
109     UNUSED_PARAM(slotBase);
110     UNUSED_PARAM(thisValue);
111     auto* castedThis = toJSDOMWindow(JSValue::decode(thisValue));
112     if (!RuntimeEnabledFeatures::sharedFeatures().indexedDBEnabled())
113         return JSValue::encode(jsUndefined());
114     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, castedThis->wrapped()))
115         return JSValue::encode(jsUndefined());
116     auto& impl = castedThis->wrapped();
117     JSValue result = toJS(exec, castedThis->globalObject(), WTF::getPtr(DOMWindowIndexedDatabase::indexedDB(&impl)));
118     return JSValue::encode(result);
119 }
120 #endif
121
122 bool JSDOMWindow::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
123 {
124     JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object);
125     // When accessing a Window cross-domain, functions are always the native built-in ones, and they
126     // are not affected by properties changed on the Window or anything in its prototype chain.
127     // This is consistent with the behavior of Firefox.
128
129     // We don't want any properties other than "close" and "closed" on a frameless window (i.e. one whose page got closed,
130     // or whose iframe got removed).
131     // FIXME: This doesn't fully match Firefox, which allows at least toString in addition to those.
132     if (!thisObject->wrapped().frame()) {
133         // The following code is safe for cross-domain and same domain use.
134         // It ignores any custom properties that might be set on the DOMWindow (including a custom prototype).
135         if (propertyName == exec->propertyNames().closed) {
136             slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, jsDOMWindowClosed);
137             return true;
138         }
139         if (propertyName == exec->propertyNames().close) {
140             slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionClose, 0>);
141             return true;
142         }
143
144         // FIXME: We should have a message here that explains why the property access/function call was
145         // not allowed. 
146         slot.setUndefined();
147         return true;
148     }
149
150     slot.setWatchpointSet(thisObject->m_windowCloseWatchpoints);
151
152     // We need to check for cross-domain access here without printing the generic warning message
153     // because we always allow access to some function, just different ones depending whether access
154     // is allowed.
155     String errorMessage;
156     bool allowsAccess = shouldAllowAccessToDOMWindow(exec, thisObject->wrapped(), errorMessage);
157     
158     // Look for overrides before looking at any of our own properties, but ignore overrides completely
159     // if this is cross-domain access.
160     if (allowsAccess && JSGlobalObject::getOwnPropertySlot(thisObject, exec, propertyName, slot))
161         return true;
162     
163     // We need this code here because otherwise JSDOMWindowBase will stop the search before we even get to the
164     // prototype due to the blanket same origin (shouldAllowAccessToDOMWindow) check at the end of getOwnPropertySlot.
165     // Also, it's important to get the implementation straight out of the DOMWindow prototype regardless of
166     // what prototype is actually set on this object.
167     if (propertyName == exec->propertyNames().blur) {
168         slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionBlur, 0>);
169         return true;
170     } else if (propertyName == exec->propertyNames().close) {
171         slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionClose, 0>);
172         return true;
173     } else if (propertyName == exec->propertyNames().focus) {
174         slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionFocus, 0>);
175         return true;
176     } else if (propertyName == exec->propertyNames().postMessage) {
177         slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionPostMessage, 2>);
178         return true;
179     } else if (propertyName == exec->propertyNames().showModalDialog) {
180         if (!DOMWindow::canShowModalDialog(thisObject->wrapped().frame())) {
181             slot.setUndefined();
182             return true;
183         }
184     } else if (propertyName == exec->propertyNames().toString) {
185         // Allow access to toString() cross-domain, but always Object.prototype.toString.
186         if (!allowsAccess) {
187             slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, objectToStringFunctionGetter);
188             return true;
189         }
190     }
191
192 #if ENABLE(INDEXED_DATABASE)
193     // FIXME: With generated JS bindings built on static property tables there is no way to
194     // completely remove a generated property at runtime.
195     // So to completely disable IndexedDB at runtime we have to not generate these accessors
196     // and have to handle them specially here.
197     // Once https://webkit.org/b/145669 is resolved, they can once again be auto generated.
198     if (RuntimeEnabledFeatures::sharedFeatures().indexedDBEnabled() && (propertyName == exec->propertyNames().indexedDB || propertyName == exec->propertyNames().webkitIndexedDB)) {
199         slot.setCustom(thisObject, allowsAccess ? DontDelete | ReadOnly | CustomAccessor : ReadOnly | DontDelete | DontEnum, jsDOMWindowIndexedDB);
200         return true;
201     }
202 #endif
203
204     const HashTableValue* entry = JSDOMWindow::info()->staticPropHashTable->entry(propertyName);
205     if (entry) {
206         slot.setCacheableCustom(thisObject, allowsAccess ? entry->attributes() : ReadOnly | DontDelete | DontEnum, entry->propertyGetter());
207         return true;
208     }
209
210 #if ENABLE(USER_MESSAGE_HANDLERS)
211     if (propertyName == exec->propertyNames().webkit && thisObject->wrapped().shouldHaveWebKitNamespaceForWorld(thisObject->world())) {
212         slot.setCacheableCustom(thisObject, allowsAccess ? DontDelete | ReadOnly : ReadOnly | DontDelete | DontEnum, jsDOMWindowWebKit);
213         return true;
214     }
215 #endif
216
217     // Do prototype lookup early so that functions and attributes in the prototype can have
218     // precedence over the index and name getters.  
219     JSValue proto = thisObject->prototype();
220     if (proto.isObject()) {
221         if (asObject(proto)->getPropertySlot(exec, propertyName, slot)) {
222             if (!allowsAccess) {
223                 thisObject->printErrorMessage(errorMessage);
224                 slot.setUndefined();
225             }
226             return true;
227         }
228     }
229
230     // After this point it is no longer valid to cache any results because of
231     // the impure nature of the property accesses which follow. We can move this 
232     // statement further down when we add ways to mitigate these impurities with, 
233     // for example, watchpoints.
234     slot.disableCaching();
235
236     // Check for child frames by name before built-in properties to
237     // match Mozilla. This does not match IE, but some sites end up
238     // naming frames things that conflict with window properties that
239     // are in Moz but not IE. Since we have some of these, we have to do
240     // it the Moz way.
241     if (thisObject->wrapped().frame()->tree().scopedChild(propertyNameToAtomicString(propertyName))) {
242         slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, childFrameGetter);
243         return true;
244     }
245
246     // FIXME: Search the whole frame hierarchy somewhere around here.
247     // We need to test the correct priority order.
248
249     // allow window[1] or parent[1] etc. (#56983)
250     Optional<uint32_t> index = parseIndex(propertyName);
251     if (index && index.value() < thisObject->wrapped().frame()->tree().scopedChildCount()) {
252         slot.setValue(thisObject, ReadOnly | DontDelete | DontEnum,
253             toJS(exec, thisObject->wrapped().frame()->tree().scopedChild(index.value())->document()->domWindow()));
254         return true;
255     }
256
257     if (!allowsAccess) {
258         thisObject->printErrorMessage(errorMessage);
259         slot.setUndefined();
260         return true;
261     }
262
263     // Allow shortcuts like 'Image1' instead of document.images.Image1
264     Document* document = thisObject->wrapped().frame()->document();
265     if (is<HTMLDocument>(*document)) {
266         AtomicStringImpl* atomicPropertyName = propertyName.publicName();
267         if (atomicPropertyName && downcast<HTMLDocument>(*document).hasWindowNamedItem(*atomicPropertyName)) {
268             slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, namedItemGetter);
269             return true;
270         }
271     }
272
273     return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
274 }
275
276 bool JSDOMWindow::getOwnPropertySlotByIndex(JSObject* object, ExecState* exec, unsigned index, PropertySlot& slot)
277 {
278     JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object);
279     
280     if (!thisObject->wrapped().frame()) {
281         // FIXME: We should have a message here that explains why the property access/function call was
282         // not allowed. 
283         slot.setUndefined();
284         return true;
285     }
286
287     // We need to check for cross-domain access here without printing the generic warning message
288     // because we always allow access to some function, just different ones depending whether access
289     // is allowed.
290     String errorMessage;
291     bool allowsAccess = shouldAllowAccessToDOMWindow(exec, thisObject->wrapped(), errorMessage);
292
293     // Look for overrides before looking at any of our own properties, but ignore overrides completely
294     // if this is cross-domain access.
295     if (allowsAccess && JSGlobalObject::getOwnPropertySlotByIndex(thisObject, exec, index, slot))
296         return true;
297     
298     Identifier propertyName = Identifier::from(exec, index);
299     
300     // Check for child frames by name before built-in properties to
301     // match Mozilla. This does not match IE, but some sites end up
302     // naming frames things that conflict with window properties that
303     // are in Moz but not IE. Since we have some of these, we have to do
304     // it the Moz way.
305     if (thisObject->wrapped().frame()->tree().scopedChild(propertyNameToAtomicString(propertyName))) {
306         slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, childFrameGetter);
307         return true;
308     }
309     
310     // Do prototype lookup early so that functions and attributes in the prototype can have
311     // precedence over the index and name getters.  
312     JSValue proto = thisObject->prototype();
313     if (proto.isObject()) {
314         if (asObject(proto)->getPropertySlot(exec, index, slot)) {
315             if (!allowsAccess) {
316                 thisObject->printErrorMessage(errorMessage);
317                 slot.setUndefined();
318             }
319             return true;
320         }
321     }
322
323     // FIXME: Search the whole frame hierarchy somewhere around here.
324     // We need to test the correct priority order.
325
326     // allow window[1] or parent[1] etc. (#56983)
327     if (index < thisObject->wrapped().frame()->tree().scopedChildCount()) {
328         slot.setValue(thisObject, ReadOnly | DontDelete | DontEnum,
329             toJS(exec, thisObject->wrapped().frame()->tree().scopedChild(index)->document()->domWindow()));
330         return true;
331     }
332
333     if (!allowsAccess) {
334         thisObject->printErrorMessage(errorMessage);
335         slot.setUndefined();
336         return true;
337     }
338
339     // Allow shortcuts like 'Image1' instead of document.images.Image1
340     Document* document = thisObject->wrapped().frame()->document();
341     if (is<HTMLDocument>(*document)) {
342         // This propertyName is generated by Identifier::from.
343         // This function generates Identifier from String and always returns the non-symbol Identifier.
344         ASSERT(!propertyName.isSymbol());
345         auto atomicPropertyName = static_cast<AtomicStringImpl*>(propertyName.impl());
346         if (atomicPropertyName && downcast<HTMLDocument>(*document).hasWindowNamedItem(*atomicPropertyName)) {
347             slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, namedItemGetter);
348             return true;
349         }
350     }
351
352     return Base::getOwnPropertySlotByIndex(thisObject, exec, index, slot);
353 }
354
355 void JSDOMWindow::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
356 {
357     JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(cell);
358     if (!thisObject->wrapped().frame())
359         return;
360
361     // Optimization: access JavaScript global variables directly before involving the DOM.
362     if (thisObject->JSGlobalObject::hasOwnPropertyForWrite(exec, propertyName)) {
363         if (BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped()))
364             JSGlobalObject::put(thisObject, exec, propertyName, value, slot);
365         return;
366     }
367
368     if (lookupPut(exec, propertyName, thisObject, value, *s_info.staticPropHashTable, slot))
369         return;
370
371     if (BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped()))
372         Base::put(thisObject, exec, propertyName, value, slot);
373 }
374
375 void JSDOMWindow::putByIndex(JSCell* cell, ExecState* exec, unsigned index, JSValue value, bool shouldThrow)
376 {
377     JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(cell);
378     if (!thisObject->wrapped().frame())
379         return;
380     
381     Identifier propertyName = Identifier::from(exec, index);
382
383     // Optimization: access JavaScript global variables directly before involving the DOM.
384     if (thisObject->JSGlobalObject::hasOwnPropertyForWrite(exec, propertyName)) {
385         if (BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped()))
386             JSGlobalObject::putByIndex(thisObject, exec, index, value, shouldThrow);
387         return;
388     }
389     
390     if (BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped()))
391         Base::putByIndex(thisObject, exec, index, value, shouldThrow);
392 }
393
394 bool JSDOMWindow::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
395 {
396     JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(cell);
397     // Only allow deleting properties by frames in the same origin.
398     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped()))
399         return false;
400     return Base::deleteProperty(thisObject, exec, propertyName);
401 }
402
403 bool JSDOMWindow::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned propertyName)
404 {
405     JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(cell);
406     // Only allow deleting properties by frames in the same origin.
407     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped()))
408         return false;
409     return Base::deletePropertyByIndex(thisObject, exec, propertyName);
410 }
411
412 uint32_t JSDOMWindow::getEnumerableLength(ExecState* exec, JSObject* object)
413 {
414     JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object);
415     // Only allow the window to enumerated by frames in the same origin.
416     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped()))
417         return 0;
418     return Base::getEnumerableLength(exec, thisObject);
419 }
420
421 void JSDOMWindow::getStructurePropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
422 {
423     JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object);
424     // Only allow the window to enumerated by frames in the same origin.
425     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped()))
426         return;
427     Base::getStructurePropertyNames(thisObject, exec, propertyNames, mode);
428 }
429
430 void JSDOMWindow::getGenericPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
431 {
432     JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object);
433     // Only allow the window to enumerated by frames in the same origin.
434     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped()))
435         return;
436     Base::getGenericPropertyNames(thisObject, exec, propertyNames, mode);
437 }
438
439 void JSDOMWindow::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
440 {
441     JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object);
442     // Only allow the window to enumerated by frames in the same origin.
443     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped()))
444         return;
445     Base::getPropertyNames(thisObject, exec, propertyNames, mode);
446 }
447
448 void JSDOMWindow::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
449 {
450     JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object);
451     // Only allow the window to enumerated by frames in the same origin.
452     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped()))
453         return;
454     Base::getOwnPropertyNames(thisObject, exec, propertyNames, mode);
455 }
456
457 bool JSDOMWindow::defineOwnProperty(JSC::JSObject* object, JSC::ExecState* exec, JSC::PropertyName propertyName, const JSC::PropertyDescriptor& descriptor, bool shouldThrow)
458 {
459     JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object);
460     // Only allow defining properties in this way by frames in the same origin, as it allows setters to be introduced.
461     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped()))
462         return false;
463
464     // Don't allow shadowing location using accessor properties.
465     if (descriptor.isAccessorDescriptor() && propertyName == Identifier::fromString(exec, "location"))
466         return false;
467
468     return Base::defineOwnProperty(thisObject, exec, propertyName, descriptor, shouldThrow);
469 }
470
471 // Custom Attributes
472
473 void JSDOMWindow::setLocation(ExecState& state, JSValue value)
474 {
475 #if ENABLE(DASHBOARD_SUPPORT)
476     // To avoid breaking old widgets, make "var location =" in a top-level frame create
477     // a property named "location" instead of performing a navigation (<rdar://problem/5688039>).
478     if (Frame* activeFrame = activeDOMWindow(&state).frame()) {
479         if (activeFrame->settings().usesDashboardBackwardCompatibilityMode() && !activeFrame->tree().parent()) {
480             if (BindingSecurity::shouldAllowAccessToDOMWindow(&state, wrapped()))
481                 putDirect(state.vm(), Identifier::fromString(&state, "location"), value);
482             return;
483         }
484     }
485 #endif
486
487     String locationString = value.toString(&state)->value(&state);
488     if (state.hadException())
489         return;
490
491     if (Location* location = wrapped().location())
492         location->setHref(activeDOMWindow(&state), firstDOMWindow(&state), locationString);
493 }
494
495 JSValue JSDOMWindow::event(ExecState& state) const
496 {
497     Event* event = currentEvent();
498     if (!event)
499         return jsUndefined();
500     return toJS(&state, const_cast<JSDOMWindow*>(this), event);
501 }
502
503 JSValue JSDOMWindow::image(ExecState& state) const
504 {
505     return createImageConstructor(state.vm(), *this);
506 }
507
508 #if ENABLE(IOS_TOUCH_EVENTS)
509 JSValue JSDOMWindow::touch(ExecState& state) const
510 {
511     return getDOMConstructor<JSTouchConstructor>(state.vm(), *this);
512 }
513
514 JSValue JSDOMWindow::touchList(ExecState& state) const
515 {
516     return getDOMConstructor<JSTouchListConstructor>(state.vm(), *this);
517 }
518 #endif
519
520 // Custom functions
521
522 JSValue JSDOMWindow::open(ExecState& state)
523 {
524     String urlString = valueToStringWithUndefinedOrNullCheck(&state, state.argument(0));
525     if (state.hadException())
526         return jsUndefined();
527     AtomicString frameName = state.argument(1).isUndefinedOrNull() ? "_blank" : state.argument(1).toString(&state)->value(&state);
528     if (state.hadException())
529         return jsUndefined();
530     String windowFeaturesString = valueToStringWithUndefinedOrNullCheck(&state, state.argument(2));
531     if (state.hadException())
532         return jsUndefined();
533
534     RefPtr<DOMWindow> openedWindow = wrapped().open(urlString, frameName, windowFeaturesString, activeDOMWindow(&state), firstDOMWindow(&state));
535     if (!openedWindow)
536         return jsUndefined();
537     return toJS(&state, openedWindow.get());
538 }
539
540 class DialogHandler {
541 public:
542     explicit DialogHandler(ExecState& exec)
543         : m_exec(exec)
544     {
545     }
546
547     void dialogCreated(DOMWindow&);
548     JSValue returnValue() const;
549
550 private:
551     ExecState& m_exec;
552     RefPtr<Frame> m_frame;
553 };
554
555 inline void DialogHandler::dialogCreated(DOMWindow& dialog)
556 {
557     m_frame = dialog.frame();
558     
559     // FIXME: This looks like a leak between the normal world and an isolated
560     //        world if dialogArguments comes from an isolated world.
561     JSDOMWindow* globalObject = toJSDOMWindow(m_frame.get(), normalWorld(m_exec.vm()));
562     if (JSValue dialogArguments = m_exec.argument(1))
563         globalObject->putDirect(m_exec.vm(), Identifier::fromString(&m_exec, "dialogArguments"), dialogArguments);
564 }
565
566 inline JSValue DialogHandler::returnValue() const
567 {
568     JSDOMWindow* globalObject = toJSDOMWindow(m_frame.get(), normalWorld(m_exec.vm()));
569     if (!globalObject)
570         return jsUndefined();
571     Identifier identifier = Identifier::fromString(&m_exec, "returnValue");
572     PropertySlot slot(globalObject);
573     if (!JSGlobalObject::getOwnPropertySlot(globalObject, &m_exec, identifier, slot))
574         return jsUndefined();
575     return slot.getValue(&m_exec, identifier);
576 }
577
578 JSValue JSDOMWindow::showModalDialog(ExecState& state)
579 {
580     String urlString = valueToStringWithUndefinedOrNullCheck(&state, state.argument(0));
581     if (state.hadException())
582         return jsUndefined();
583     String dialogFeaturesString = valueToStringWithUndefinedOrNullCheck(&state, state.argument(2));
584     if (state.hadException())
585         return jsUndefined();
586
587     DialogHandler handler(state);
588
589     wrapped().showModalDialog(urlString, dialogFeaturesString, activeDOMWindow(&state), firstDOMWindow(&state), [&handler](DOMWindow& dialog) {
590         handler.dialogCreated(dialog);
591     });
592
593     return handler.returnValue();
594 }
595
596 static JSValue handlePostMessage(DOMWindow& impl, ExecState& state)
597 {
598     MessagePortArray messagePorts;
599     ArrayBufferArray arrayBuffers;
600
601     // This function has variable arguments and can be:
602     // Per current spec:
603     //   postMessage(message, targetOrigin)
604     //   postMessage(message, targetOrigin, {sequence of transferrables})
605     // Legacy non-standard implementations in webkit allowed:
606     //   postMessage(message, {sequence of transferrables}, targetOrigin);
607     int targetOriginArgIndex = 1;
608     if (state.argumentCount() > 2) {
609         int transferablesArgIndex = 2;
610         if (state.argument(2).isString()) {
611             targetOriginArgIndex = 2;
612             transferablesArgIndex = 1;
613         }
614         fillMessagePortArray(state, state.argument(transferablesArgIndex), messagePorts, arrayBuffers);
615     }
616     if (state.hadException())
617         return jsUndefined();
618
619     RefPtr<SerializedScriptValue> message = SerializedScriptValue::create(&state, state.argument(0),
620                                                                          &messagePorts,
621                                                                          &arrayBuffers);
622
623     if (state.hadException())
624         return jsUndefined();
625
626     String targetOrigin = valueToStringWithUndefinedOrNullCheck(&state, state.argument(targetOriginArgIndex));
627     if (state.hadException())
628         return jsUndefined();
629
630     ExceptionCode ec = 0;
631     impl.postMessage(message.release(), &messagePorts, targetOrigin, activeDOMWindow(&state), ec);
632     setDOMException(&state, ec);
633
634     return jsUndefined();
635 }
636
637 JSValue JSDOMWindow::postMessage(ExecState& state)
638 {
639     return handlePostMessage(wrapped(), state);
640 }
641
642 JSValue JSDOMWindow::setTimeout(ExecState& state)
643 {
644     ContentSecurityPolicy* contentSecurityPolicy = wrapped().document() ? wrapped().document()->contentSecurityPolicy() : nullptr;
645     std::unique_ptr<ScheduledAction> action = ScheduledAction::create(&state, globalObject()->world(), contentSecurityPolicy);
646     if (state.hadException())
647         return jsUndefined();
648
649     if (!action)
650         return jsNumber(0);
651
652     int delay = state.argument(1).toInt32(&state);
653
654     ExceptionCode ec = 0;
655     int result = wrapped().setTimeout(WTFMove(action), delay, ec);
656     setDOMException(&state, ec);
657
658     return jsNumber(result);
659 }
660
661 JSValue JSDOMWindow::setInterval(ExecState& state)
662 {
663     ContentSecurityPolicy* contentSecurityPolicy = wrapped().document() ? wrapped().document()->contentSecurityPolicy() : nullptr;
664     std::unique_ptr<ScheduledAction> action = ScheduledAction::create(&state, globalObject()->world(), contentSecurityPolicy);
665     if (state.hadException())
666         return jsUndefined();
667     int delay = state.argument(1).toInt32(&state);
668
669     if (!action)
670         return jsNumber(0);
671
672     ExceptionCode ec = 0;
673     int result = wrapped().setInterval(WTFMove(action), delay, ec);
674     setDOMException(&state, ec);
675
676     return jsNumber(result);
677 }
678
679 JSValue JSDOMWindow::addEventListener(ExecState& state)
680 {
681     Frame* frame = wrapped().frame();
682     if (!frame)
683         return jsUndefined();
684
685     JSValue listener = state.argument(1);
686     if (!listener.isObject())
687         return jsUndefined();
688
689     wrapped().addEventListener(state.argument(0).toString(&state)->toAtomicString(&state), JSEventListener::create(asObject(listener), this, false, globalObject()->world()), state.argument(2).toBoolean(&state));
690     return jsUndefined();
691 }
692
693 JSValue JSDOMWindow::removeEventListener(ExecState& state)
694 {
695     Frame* frame = wrapped().frame();
696     if (!frame)
697         return jsUndefined();
698
699     JSValue listener = state.argument(1);
700     if (!listener.isObject())
701         return jsUndefined();
702
703     wrapped().removeEventListener(state.argument(0).toString(&state)->toAtomicString(&state), JSEventListener::create(asObject(listener), this, false, globalObject()->world()).ptr(), state.argument(2).toBoolean(&state));
704     return jsUndefined();
705 }
706
707 DOMWindow* JSDOMWindow::toWrapped(JSValue value)
708 {
709     if (!value.isObject())
710         return 0;
711     JSObject* object = asObject(value);
712     if (object->inherits(JSDOMWindow::info()))
713         return &jsCast<JSDOMWindow*>(object)->wrapped();
714     if (object->inherits(JSDOMWindowShell::info()))
715         return &jsCast<JSDOMWindowShell*>(object)->wrapped();
716     return 0;
717 }
718
719 } // namespace WebCore