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