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