WebCore: Adding support for the optional creation callback that could be
[WebKit-https.git] / WebCore / bindings / js / JSDOMWindowCustom.cpp
1 /*
2  * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #include "config.h"
21 #include "JSDOMWindowCustom.h"
22
23 #include "AtomicString.h"
24 #include "Chrome.h"
25 #include "Database.h"
26 #include "DOMWindow.h"
27 #include "Document.h"
28 #include "ExceptionCode.h"
29 #include "FloatRect.h"
30 #include "Frame.h"
31 #include "FrameLoadRequest.h"
32 #include "FrameLoader.h"
33 #include "FrameTree.h"
34 #include "FrameView.h"
35 #include "HTMLCollection.h"
36 #include "HTMLDocument.h"
37 #include "History.h"
38 #include "JSAudioConstructor.h"
39 #include "JSDatabase.h"
40 #include "JSDatabaseCallback.h"
41 #include "JSDOMWindowShell.h"
42 #include "JSEvent.h"
43 #include "JSEventListener.h"
44 #include "JSEventSourceConstructor.h"
45 #include "JSHTMLCollection.h"
46 #include "JSHistory.h"
47 #include "JSImageConstructor.h"
48 #include "JSLocation.h"
49 #include "JSMessageChannelConstructor.h"
50 #include "JSMessagePort.h"
51 #include "JSMessagePortCustom.h"
52 #include "JSOptionConstructor.h"
53
54 #if ENABLE(SHARED_WORKERS)
55 #include "JSSharedWorkerConstructor.h"
56 #endif
57
58 #if ENABLE(3D_CANVAS)
59 #include "JSWebGLArrayBufferConstructor.h"
60 #include "JSWebGLByteArrayConstructor.h"
61 #include "JSWebGLUnsignedByteArrayConstructor.h"
62 #include "JSWebGLIntArrayConstructor.h"
63 #include "JSWebGLUnsignedIntArrayConstructor.h"
64 #include "JSWebGLShortArrayConstructor.h"
65 #include "JSWebGLUnsignedShortArrayConstructor.h"
66 #include "JSWebGLFloatArrayConstructor.h"
67 #endif
68 #include "JSWebKitCSSMatrixConstructor.h"
69 #include "JSWebKitPointConstructor.h"
70 #if ENABLE(WEB_SOCKETS)
71 #include "JSWebSocketConstructor.h"
72 #endif
73 #include "JSWorkerConstructor.h"
74 #include "JSXMLHttpRequestConstructor.h"
75 #include "JSXSLTProcessorConstructor.h"
76 #include "Location.h"
77 #include "MediaPlayer.h"
78 #include "MessagePort.h"
79 #include "NotificationCenter.h"
80 #include "Page.h"
81 #include "PlatformScreen.h"
82 #include "RegisteredEventListener.h"
83 #include "ScheduledAction.h"
84 #include "ScriptController.h"
85 #include "SerializedScriptValue.h"
86 #include "Settings.h"
87 #include "SharedWorkerRepository.h"
88 #include "WindowFeatures.h"
89 #include <runtime/Error.h>
90 #include <runtime/JSFunction.h>
91 #include <runtime/JSObject.h>
92 #include <runtime/PrototypeFunction.h>
93
94 using namespace JSC;
95
96 namespace WebCore {
97
98 void JSDOMWindow::markChildren(MarkStack& markStack)
99 {
100     Base::markChildren(markStack);
101
102     impl()->markJSEventListeners(markStack);
103
104     JSGlobalData& globalData = *Heap::heap(this)->globalData();
105
106     markDOMObjectWrapper(markStack, globalData, impl()->optionalConsole());
107     markDOMObjectWrapper(markStack, globalData, impl()->optionalHistory());
108     markDOMObjectWrapper(markStack, globalData, impl()->optionalLocationbar());
109     markDOMObjectWrapper(markStack, globalData, impl()->optionalMenubar());
110     markDOMObjectWrapper(markStack, globalData, impl()->optionalNavigator());
111     markDOMObjectWrapper(markStack, globalData, impl()->optionalPersonalbar());
112     markDOMObjectWrapper(markStack, globalData, impl()->optionalScreen());
113     markDOMObjectWrapper(markStack, globalData, impl()->optionalScrollbars());
114     markDOMObjectWrapper(markStack, globalData, impl()->optionalSelection());
115     markDOMObjectWrapper(markStack, globalData, impl()->optionalStatusbar());
116     markDOMObjectWrapper(markStack, globalData, impl()->optionalToolbar());
117     markDOMObjectWrapper(markStack, globalData, impl()->optionalLocation());
118     markDOMObjectWrapper(markStack, globalData, impl()->optionalMedia());
119 #if ENABLE(DOM_STORAGE)
120     markDOMObjectWrapper(markStack, globalData, impl()->optionalSessionStorage());
121     markDOMObjectWrapper(markStack, globalData, impl()->optionalLocalStorage());
122 #endif
123 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
124     markDOMObjectWrapper(markStack, globalData, impl()->optionalApplicationCache());
125 #endif
126 }
127
128 template<NativeFunction nativeFunction, int length>
129 JSValue nonCachingStaticFunctionGetter(ExecState* exec, JSValue, const Identifier& propertyName)
130 {
131     return new (exec) NativeFunctionWrapper(exec, exec->lexicalGlobalObject()->prototypeFunctionStructure(), length, propertyName, nativeFunction);
132 }
133
134 static JSValue childFrameGetter(ExecState* exec, JSValue slotBase, const Identifier& propertyName)
135 {
136     return toJS(exec, static_cast<JSDOMWindow*>(asObject(slotBase))->impl()->frame()->tree()->child(AtomicString(propertyName))->domWindow());
137 }
138
139 static JSValue indexGetter(ExecState* exec, JSValue slotBase, unsigned index)
140 {
141     return toJS(exec, static_cast<JSDOMWindow*>(asObject(slotBase))->impl()->frame()->tree()->child(index)->domWindow());
142 }
143
144 static JSValue namedItemGetter(ExecState* exec, JSValue slotBase, const Identifier& propertyName)
145 {
146     JSDOMWindowBase* thisObj = static_cast<JSDOMWindow*>(asObject(slotBase));
147     Document* document = thisObj->impl()->frame()->document();
148
149     ASSERT(thisObj->allowsAccessFrom(exec));
150     ASSERT(document);
151     ASSERT(document->isHTMLDocument());
152
153     RefPtr<HTMLCollection> collection = document->windowNamedItems(propertyName);
154     if (collection->length() == 1)
155         return toJS(exec, collection->firstItem());
156     return toJS(exec, collection.get());
157 }
158
159 bool JSDOMWindow::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
160 {
161     // When accessing a Window cross-domain, functions are always the native built-in ones, and they
162     // are not affected by properties changed on the Window or anything in its prototype chain.
163     // This is consistent with the behavior of Firefox.
164
165     const HashEntry* entry;
166
167     // We don't want any properties other than "close" and "closed" on a closed window.
168     if (!impl()->frame()) {
169         // The following code is safe for cross-domain and same domain use.
170         // It ignores any custom properties that might be set on the DOMWindow (including a custom prototype).
171         entry = s_info.propHashTable(exec)->entry(exec, propertyName);
172         if (entry && !(entry->attributes() & Function) && entry->propertyGetter() == jsDOMWindowClosed) {
173             slot.setCustom(this, entry->propertyGetter());
174             return true;
175         }
176         entry = JSDOMWindowPrototype::s_info.propHashTable(exec)->entry(exec, propertyName);
177         if (entry && (entry->attributes() & Function) && entry->function() == jsDOMWindowPrototypeFunctionClose) {
178             slot.setCustom(this, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionClose, 0>);
179             return true;
180         }
181
182         // FIXME: We should have a message here that explains why the property access/function call was
183         // not allowed. 
184         slot.setUndefined();
185         return true;
186     }
187
188     // We need to check for cross-domain access here without printing the generic warning message
189     // because we always allow access to some function, just different ones depending whether access
190     // is allowed.
191     String errorMessage;
192     bool allowsAccess = allowsAccessFrom(exec, errorMessage);
193
194     // Look for overrides before looking at any of our own properties, but ignore overrides completely
195     // if this is cross-domain access.
196     if (allowsAccess && JSGlobalObject::getOwnPropertySlot(exec, propertyName, slot))
197         return true;
198
199     // We need this code here because otherwise JSDOMWindowBase will stop the search before we even get to the
200     // prototype due to the blanket same origin (allowsAccessFrom) check at the end of getOwnPropertySlot.
201     // Also, it's important to get the implementation straight out of the DOMWindow prototype regardless of
202     // what prototype is actually set on this object.
203     entry = JSDOMWindowPrototype::s_info.propHashTable(exec)->entry(exec, propertyName);
204     if (entry) {
205         if (entry->attributes() & Function) {
206             if (entry->function() == jsDOMWindowPrototypeFunctionBlur) {
207                 if (!allowsAccess) {
208                     slot.setCustom(this, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionBlur, 0>);
209                     return true;
210                 }
211             } else if (entry->function() == jsDOMWindowPrototypeFunctionClose) {
212                 if (!allowsAccess) {
213                     slot.setCustom(this, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionClose, 0>);
214                     return true;
215                 }
216             } else if (entry->function() == jsDOMWindowPrototypeFunctionFocus) {
217                 if (!allowsAccess) {
218                     slot.setCustom(this, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionFocus, 0>);
219                     return true;
220                 }
221             } else if (entry->function() == jsDOMWindowPrototypeFunctionPostMessage) {
222                 if (!allowsAccess) {
223                     slot.setCustom(this, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionPostMessage, 2>);
224                     return true;
225                 }
226             } else if (entry->function() == jsDOMWindowPrototypeFunctionShowModalDialog) {
227                 if (!DOMWindow::canShowModalDialog(impl()->frame())) {
228                     slot.setUndefined();
229                     return true;
230                 }
231             }
232         }
233     } else {
234         // Allow access to toString() cross-domain, but always Object.prototype.toString.
235         if (propertyName == exec->propertyNames().toString) {
236             if (!allowsAccess) {
237                 slot.setCustom(this, objectToStringFunctionGetter);
238                 return true;
239             }
240         }
241     }
242
243     entry = JSDOMWindow::s_info.propHashTable(exec)->entry(exec, propertyName);
244     if (entry) {
245         slot.setCustom(this, entry->propertyGetter());
246         return true;
247     }
248
249     // Check for child frames by name before built-in properties to
250     // match Mozilla. This does not match IE, but some sites end up
251     // naming frames things that conflict with window properties that
252     // are in Moz but not IE. Since we have some of these, we have to do
253     // it the Moz way.
254     if (impl()->frame()->tree()->child(propertyName)) {
255         slot.setCustom(this, childFrameGetter);
256         return true;
257     }
258
259     // Do prototype lookup early so that functions and attributes in the prototype can have
260     // precedence over the index and name getters.  
261     JSValue proto = prototype();
262     if (proto.isObject()) {
263         if (asObject(proto)->getPropertySlot(exec, propertyName, slot)) {
264             if (!allowsAccess) {
265                 printErrorMessage(errorMessage);
266                 slot.setUndefined();
267             }
268             return true;
269         }
270     }
271
272     // FIXME: Search the whole frame hierarchy somewhere around here.
273     // We need to test the correct priority order.
274
275     // allow window[1] or parent[1] etc. (#56983)
276     bool ok;
277     unsigned i = propertyName.toArrayIndex(&ok);
278     if (ok && i < impl()->frame()->tree()->childCount()) {
279         slot.setCustomIndex(this, i, indexGetter);
280         return true;
281     }
282
283     if (!allowsAccess) {
284         printErrorMessage(errorMessage);
285         slot.setUndefined();
286         return true;
287     }
288
289     // Allow shortcuts like 'Image1' instead of document.images.Image1
290     Document* document = impl()->frame()->document();
291     if (document->isHTMLDocument()) {
292         AtomicStringImpl* atomicPropertyName = AtomicString::find(propertyName);
293         if (atomicPropertyName && (static_cast<HTMLDocument*>(document)->hasNamedItem(atomicPropertyName) || document->hasElementWithId(atomicPropertyName))) {
294             slot.setCustom(this, namedItemGetter);
295             return true;
296         }
297     }
298
299     return Base::getOwnPropertySlot(exec, propertyName, slot);
300 }
301
302 bool JSDOMWindow::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
303 {
304     // Never allow cross-domain getOwnPropertyDescriptor
305     if (!allowsAccessFrom(exec))
306         return false;
307
308     const HashEntry* entry;
309     
310     // We don't want any properties other than "close" and "closed" on a closed window.
311     if (!impl()->frame()) {
312         // The following code is safe for cross-domain and same domain use.
313         // It ignores any custom properties that might be set on the DOMWindow (including a custom prototype).
314         entry = s_info.propHashTable(exec)->entry(exec, propertyName);
315         if (entry && !(entry->attributes() & Function) && entry->propertyGetter() == jsDOMWindowClosed) {
316             descriptor.setDescriptor(jsBoolean(true), ReadOnly | DontDelete | DontEnum);
317             return true;
318         }
319         entry = JSDOMWindowPrototype::s_info.propHashTable(exec)->entry(exec, propertyName);
320         if (entry && (entry->attributes() & Function) && entry->function() == jsDOMWindowPrototypeFunctionClose) {
321             PropertySlot slot;
322             slot.setCustom(this, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionClose, 0>);
323             descriptor.setDescriptor(slot.getValue(exec, propertyName), ReadOnly | DontDelete | DontEnum);
324             return true;
325         }
326         descriptor.setUndefined();
327         return true;
328     }
329
330     entry = JSDOMWindow::s_info.propHashTable(exec)->entry(exec, propertyName);
331     if (entry) {
332         PropertySlot slot;
333         slot.setCustom(this, entry->propertyGetter());
334         descriptor.setDescriptor(slot.getValue(exec, propertyName), entry->attributes());
335         return true;
336     }
337     
338     // Check for child frames by name before built-in properties to
339     // match Mozilla. This does not match IE, but some sites end up
340     // naming frames things that conflict with window properties that
341     // are in Moz but not IE. Since we have some of these, we have to do
342     // it the Moz way.
343     if (impl()->frame()->tree()->child(propertyName)) {
344         PropertySlot slot;
345         slot.setCustom(this, childFrameGetter);
346         descriptor.setDescriptor(slot.getValue(exec, propertyName), ReadOnly | DontDelete | DontEnum);
347         return true;
348     }
349     
350     bool ok;
351     unsigned i = propertyName.toArrayIndex(&ok);
352     if (ok && i < impl()->frame()->tree()->childCount()) {
353         PropertySlot slot;
354         slot.setCustomIndex(this, i, indexGetter);
355         descriptor.setDescriptor(slot.getValue(exec, propertyName), ReadOnly | DontDelete | DontEnum);
356         return true;
357     }
358
359     // Allow shortcuts like 'Image1' instead of document.images.Image1
360     Document* document = impl()->frame()->document();
361     if (document->isHTMLDocument()) {
362         AtomicStringImpl* atomicPropertyName = AtomicString::find(propertyName);
363         if (atomicPropertyName && (static_cast<HTMLDocument*>(document)->hasNamedItem(atomicPropertyName) || document->hasElementWithId(atomicPropertyName))) {
364             PropertySlot slot;
365             slot.setCustom(this, namedItemGetter);
366             descriptor.setDescriptor(slot.getValue(exec, propertyName), ReadOnly | DontDelete | DontEnum);
367             return true;
368         }
369     }
370     
371     return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor);
372 }
373
374 void JSDOMWindow::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
375 {
376     if (!impl()->frame())
377         return;
378
379     // Optimization: access JavaScript global variables directly before involving the DOM.
380     if (JSGlobalObject::hasOwnPropertyForWrite(exec, propertyName)) {
381         if (allowsAccessFrom(exec))
382             JSGlobalObject::put(exec, propertyName, value, slot);
383         return;
384     }
385
386     if (lookupPut<JSDOMWindow>(exec, propertyName, value, s_info.propHashTable(exec), this))
387         return;
388
389     if (allowsAccessFrom(exec))
390         Base::put(exec, propertyName, value, slot);
391 }
392
393 bool JSDOMWindow::deleteProperty(ExecState* exec, const Identifier& propertyName)
394 {
395     // Only allow deleting properties by frames in the same origin.
396     if (!allowsAccessFrom(exec))
397         return false;
398     return Base::deleteProperty(exec, propertyName);
399 }
400
401 void JSDOMWindow::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
402 {
403     // Only allow the window to enumerated by frames in the same origin.
404     if (!allowsAccessFrom(exec))
405         return;
406     Base::getPropertyNames(exec, propertyNames, mode);
407 }
408
409 void JSDOMWindow::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
410 {
411     // Only allow the window to enumerated by frames in the same origin.
412     if (!allowsAccessFrom(exec))
413         return;
414     Base::getOwnPropertyNames(exec, propertyNames, mode);
415 }
416
417 void JSDOMWindow::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes)
418 {
419     // Only allow defining getters by frames in the same origin.
420     if (!allowsAccessFrom(exec))
421         return;
422
423     // Don't allow shadowing location using defineGetter.
424     if (propertyName == "location")
425         return;
426
427     Base::defineGetter(exec, propertyName, getterFunction, attributes);
428 }
429
430 void JSDOMWindow::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes)
431 {
432     // Only allow defining setters by frames in the same origin.
433     if (!allowsAccessFrom(exec))
434         return;
435     Base::defineSetter(exec, propertyName, setterFunction, attributes);
436 }
437
438 bool JSDOMWindow::defineOwnProperty(JSC::ExecState* exec, const JSC::Identifier& propertyName, JSC::PropertyDescriptor& descriptor, bool shouldThrow)
439 {
440     // Only allow defining properties in this way by frames in the same origin, as it allows setters to be introduced.
441     if (!allowsAccessFrom(exec))
442         return false;
443     return Base::defineOwnProperty(exec, propertyName, descriptor, shouldThrow);
444 }
445
446 JSValue JSDOMWindow::lookupGetter(ExecState* exec, const Identifier& propertyName)
447 {
448     // Only allow looking-up getters by frames in the same origin.
449     if (!allowsAccessFrom(exec))
450         return jsUndefined();
451     return Base::lookupGetter(exec, propertyName);
452 }
453
454 JSValue JSDOMWindow::lookupSetter(ExecState* exec, const Identifier& propertyName)
455 {
456     // Only allow looking-up setters by frames in the same origin.
457     if (!allowsAccessFrom(exec))
458         return jsUndefined();
459     return Base::lookupSetter(exec, propertyName);
460 }
461
462 // Custom Attributes
463
464 JSValue JSDOMWindow::history(ExecState* exec) const
465 {
466     History* history = impl()->history();
467     if (DOMObject* wrapper = getCachedDOMObjectWrapper(exec, history))
468         return wrapper;
469
470     JSDOMWindow* window = const_cast<JSDOMWindow*>(this);
471     JSHistory* jsHistory = new (exec) JSHistory(getDOMStructure<JSHistory>(exec, window), window, history);
472     cacheDOMObjectWrapper(exec, history, jsHistory);
473     return jsHistory;
474 }
475
476 JSValue JSDOMWindow::location(ExecState* exec) const
477 {
478     Location* location = impl()->location();
479     if (DOMObject* wrapper = getCachedDOMObjectWrapper(exec, location))
480         return wrapper;
481
482     JSDOMWindow* window = const_cast<JSDOMWindow*>(this);
483     JSLocation* jsLocation = new (exec) JSLocation(getDOMStructure<JSLocation>(exec, window), window, location);
484     cacheDOMObjectWrapper(exec, location, jsLocation);
485     return jsLocation;
486 }
487
488 void JSDOMWindow::setLocation(ExecState* exec, JSValue value)
489 {
490     Frame* lexicalFrame = toLexicalFrame(exec);
491     if (!lexicalFrame)
492         return;
493
494 #if ENABLE(DASHBOARD_SUPPORT)
495     // To avoid breaking old widgets, make "var location =" in a top-level frame create
496     // a property named "location" instead of performing a navigation (<rdar://problem/5688039>).
497     if (Settings* settings = lexicalFrame->settings()) {
498         if (settings->usesDashboardBackwardCompatibilityMode() && !lexicalFrame->tree()->parent()) {
499             if (allowsAccessFrom(exec))
500                 putDirect(Identifier(exec, "location"), value);
501             return;
502         }
503     }
504 #endif
505
506     Frame* frame = impl()->frame();
507     ASSERT(frame);
508
509     KURL url = completeURL(exec, value.toString(exec));
510     if (url.isNull())
511         return;
512
513     if (!shouldAllowNavigation(exec, frame))
514         return;
515
516     if (!protocolIsJavaScript(url) || allowsAccessFrom(exec)) {
517         // We want a new history item if this JS was called via a user gesture
518         frame->redirectScheduler()->scheduleLocationChange(url, lexicalFrame->loader()->outgoingReferrer(), !lexicalFrame->script()->anyPageIsProcessingUserGesture(), false, processingUserGesture(exec));
519     }
520 }
521
522 JSValue JSDOMWindow::crypto(ExecState*) const
523 {
524     return jsUndefined();
525 }
526
527 JSValue JSDOMWindow::event(ExecState* exec) const
528 {
529     Event* event = currentEvent();
530     if (!event)
531         return jsUndefined();
532     return toJS(exec, event);
533 }
534
535 #if ENABLE(EVENTSOURCE)
536 JSValue JSDOMWindow::eventSource(ExecState* exec) const
537 {
538     return getDOMConstructor<JSEventSourceConstructor>(exec, this);
539 }
540 #endif
541
542 JSValue JSDOMWindow::image(ExecState* exec) const
543 {
544     return getDOMConstructor<JSImageConstructor>(exec, this);
545 }
546
547 JSValue JSDOMWindow::option(ExecState* exec) const
548 {
549     return getDOMConstructor<JSOptionConstructor>(exec, this);
550 }
551
552 #if ENABLE(VIDEO)
553 JSValue JSDOMWindow::audio(ExecState* exec) const
554 {
555     if (!MediaPlayer::isAvailable())
556         return jsUndefined();
557     return getDOMConstructor<JSAudioConstructor>(exec, this);
558 }
559 #endif
560
561 JSValue JSDOMWindow::webKitPoint(ExecState* exec) const
562 {
563     return getDOMConstructor<JSWebKitPointConstructor>(exec, this);
564 }
565
566 JSValue JSDOMWindow::webKitCSSMatrix(ExecState* exec) const
567 {
568     return getDOMConstructor<JSWebKitCSSMatrixConstructor>(exec, this);
569 }
570  
571 #if ENABLE(3D_CANVAS)
572 JSValue JSDOMWindow::webGLArrayBuffer(ExecState* exec) const
573 {
574     return getDOMConstructor<JSWebGLArrayBufferConstructor>(exec, this);
575 }
576  
577 JSValue JSDOMWindow::webGLByteArray(ExecState* exec) const
578 {
579     return getDOMConstructor<JSWebGLByteArrayConstructor>(exec, this);
580 }
581  
582 JSValue JSDOMWindow::webGLUnsignedByteArray(ExecState* exec) const
583 {
584     return getDOMConstructor<JSWebGLUnsignedByteArrayConstructor>(exec, this);
585 }
586  
587 JSValue JSDOMWindow::webGLIntArray(ExecState* exec) const
588 {
589     return getDOMConstructor<JSWebGLIntArrayConstructor>(exec, this);
590 }
591  
592 JSValue JSDOMWindow::webGLUnsignedIntArray(ExecState* exec) const
593 {
594     return getDOMConstructor<JSWebGLUnsignedIntArrayConstructor>(exec, this);
595 }
596  
597 JSValue JSDOMWindow::webGLShortArray(ExecState* exec) const
598 {
599     return getDOMConstructor<JSWebGLShortArrayConstructor>(exec, this);
600 }
601  
602 JSValue JSDOMWindow::webGLUnsignedShortArray(ExecState* exec) const
603 {
604     return getDOMConstructor<JSWebGLUnsignedShortArrayConstructor>(exec, this);
605 }
606  
607 JSValue JSDOMWindow::webGLFloatArray(ExecState* exec) const
608 {
609     return getDOMConstructor<JSWebGLFloatArrayConstructor>(exec, this);
610 }
611 #endif
612  
613 JSValue JSDOMWindow::xmlHttpRequest(ExecState* exec) const
614 {
615     return getDOMConstructor<JSXMLHttpRequestConstructor>(exec, this);
616 }
617
618 #if ENABLE(XSLT)
619 JSValue JSDOMWindow::xsltProcessor(ExecState* exec) const
620 {
621     return getDOMConstructor<JSXSLTProcessorConstructor>(exec, this);
622 }
623 #endif
624
625 #if ENABLE(CHANNEL_MESSAGING)
626 JSValue JSDOMWindow::messageChannel(ExecState* exec) const
627 {
628     return getDOMConstructor<JSMessageChannelConstructor>(exec, this);
629 }
630 #endif
631
632 #if ENABLE(WORKERS)
633 JSValue JSDOMWindow::worker(ExecState* exec) const
634 {
635     return getDOMConstructor<JSWorkerConstructor>(exec, this);
636 }
637 #endif
638
639 #if ENABLE(SHARED_WORKERS)
640 JSValue JSDOMWindow::sharedWorker(ExecState* exec) const
641 {
642     if (SharedWorkerRepository::isAvailable())
643         return getDOMConstructor<JSSharedWorkerConstructor>(exec, this);
644     return jsUndefined();
645 }
646 #endif
647
648 #if ENABLE(WEB_SOCKETS)
649 JSValue JSDOMWindow::webSocket(ExecState* exec) const
650 {
651     Frame* frame = impl()->frame();
652     if (!frame)
653         return jsUndefined();
654     Settings* settings = frame->settings();
655     if (!settings)
656         return jsUndefined();
657     return getDOMConstructor<JSWebSocketConstructor>(exec, this);
658 }
659 #endif
660
661 // Custom functions
662
663 // Helper for window.open() and window.showModalDialog()
664 static Frame* createWindow(ExecState* exec, Frame* lexicalFrame, Frame* dynamicFrame,
665                            Frame* openerFrame, const String& url, const String& frameName, 
666                            const WindowFeatures& windowFeatures, JSValue dialogArgs)
667 {
668     ASSERT(lexicalFrame);
669     ASSERT(dynamicFrame);
670
671     if (Document* lexicalDocument = lexicalFrame->document()) {
672         // Sandboxed iframes cannot open new auxiliary browsing contexts.
673         if (lexicalDocument->securityOrigin()->isSandboxed(SandboxNavigation))
674             return 0;
675     }
676
677     ResourceRequest request;
678
679     // For whatever reason, Firefox uses the dynamicGlobalObject to determine
680     // the outgoingReferrer.  We replicate that behavior here.
681     String referrer = dynamicFrame->loader()->outgoingReferrer();
682     request.setHTTPReferrer(referrer);
683     FrameLoader::addHTTPOriginIfNeeded(request, dynamicFrame->loader()->outgoingOrigin());
684     FrameLoadRequest frameRequest(request, frameName);
685
686     // FIXME: It's much better for client API if a new window starts with a URL, here where we
687     // know what URL we are going to open. Unfortunately, this code passes the empty string
688     // for the URL, but there's a reason for that. Before loading we have to set up the opener,
689     // openedByDOM, and dialogArguments values. Also, to decide whether to use the URL we currently
690     // do an allowsAccessFrom call using the window we create, which can't be done before creating it.
691     // We'd have to resolve all those issues to pass the URL instead of "".
692
693     bool created;
694     // We pass in the opener frame here so it can be used for looking up the frame name, in case the active frame
695     // is different from the opener frame, and the name references a frame relative to the opener frame, for example
696     // "_self" or "_parent".
697     Frame* newFrame = lexicalFrame->loader()->createWindow(openerFrame->loader(), frameRequest, windowFeatures, created);
698     if (!newFrame)
699         return 0;
700
701     newFrame->loader()->setOpener(openerFrame);
702     newFrame->page()->setOpenedByDOM();
703
704     // FIXME: If a window is created from an isolated world, what are the consequences of this? 'dialogArguments' only appears back in the normal world?
705     JSDOMWindow* newWindow = toJSDOMWindow(newFrame, normalWorld(exec->globalData()));
706
707     if (dialogArgs)
708         newWindow->putDirect(Identifier(exec, "dialogArguments"), dialogArgs);
709
710     if (!protocolIsJavaScript(url) || newWindow->allowsAccessFrom(exec)) {
711         KURL completedURL = url.isEmpty() ? KURL(ParsedURLString, "") : completeURL(exec, url);
712         bool userGesture = processingUserGesture(exec);
713
714         if (created)
715             newFrame->loader()->changeLocation(completedURL, referrer, false, false, userGesture);
716         else if (!url.isEmpty())
717             newFrame->redirectScheduler()->scheduleLocationChange(completedURL.string(), referrer, !lexicalFrame->script()->anyPageIsProcessingUserGesture(), false, userGesture);
718     }
719
720     return newFrame;
721 }
722
723 static bool domWindowAllowPopUp(Frame* activeFrame, ExecState* exec)
724 {
725     ASSERT(activeFrame);
726     if (activeFrame->script()->processingUserGesture(currentWorld(exec)))
727         return true;
728     return DOMWindow::allowPopUp(activeFrame);
729 }
730
731 JSValue JSDOMWindow::open(ExecState* exec, const ArgList& args)
732 {
733     String urlString = valueToStringWithUndefinedOrNullCheck(exec, args.at(0));
734     AtomicString frameName = args.at(1).isUndefinedOrNull() ? "_blank" : AtomicString(args.at(1).toString(exec));
735     WindowFeatures windowFeatures(valueToStringWithUndefinedOrNullCheck(exec, args.at(2)));
736
737     Frame* frame = impl()->frame();
738     if (!frame)
739         return jsUndefined();
740     Frame* lexicalFrame = toLexicalFrame(exec);
741     if (!lexicalFrame)
742         return jsUndefined();
743     Frame* dynamicFrame = toDynamicFrame(exec);
744     if (!dynamicFrame)
745         return jsUndefined();
746
747     Page* page = frame->page();
748
749     // Because FrameTree::find() returns true for empty strings, we must check for empty framenames.
750     // Otherwise, illegitimate window.open() calls with no name will pass right through the popup blocker.
751     if (!domWindowAllowPopUp(dynamicFrame, exec) && (frameName.isEmpty() || !frame->tree()->find(frameName)))
752         return jsUndefined();
753
754     // Get the target frame for the special cases of _top and _parent.  In those
755     // cases, we can schedule a location change right now and return early.
756     bool topOrParent = false;
757     if (frameName == "_top") {
758         frame = frame->tree()->top();
759         topOrParent = true;
760     } else if (frameName == "_parent") {
761         if (Frame* parent = frame->tree()->parent())
762             frame = parent;
763         topOrParent = true;
764     }
765     if (topOrParent) {
766         String completedURL;
767         if (!urlString.isEmpty())
768             completedURL = completeURL(exec, urlString).string();
769
770         if (!shouldAllowNavigation(exec, frame))
771             return jsUndefined();
772
773         const JSDOMWindow* targetedWindow = toJSDOMWindow(frame, currentWorld(exec));
774         if (!completedURL.isEmpty() && (!protocolIsJavaScript(completedURL) || (targetedWindow && targetedWindow->allowsAccessFrom(exec)))) {
775             bool userGesture = processingUserGesture(exec);
776
777             // For whatever reason, Firefox uses the dynamicGlobalObject to
778             // determine the outgoingReferrer.  We replicate that behavior
779             // here.
780             String referrer = dynamicFrame->loader()->outgoingReferrer();
781
782             frame->redirectScheduler()->scheduleLocationChange(completedURL, referrer, !lexicalFrame->script()->anyPageIsProcessingUserGesture(), false, userGesture);
783         }
784         return toJS(exec, frame->domWindow());
785     }
786
787     // In the case of a named frame or a new window, we'll use the createWindow() helper
788     FloatRect windowRect(windowFeatures.xSet ? windowFeatures.x : 0, windowFeatures.ySet ? windowFeatures.y : 0,
789                          windowFeatures.widthSet ? windowFeatures.width : 0, windowFeatures.heightSet ? windowFeatures.height : 0);
790     DOMWindow::adjustWindowRect(screenAvailableRect(page ? page->mainFrame()->view() : 0), windowRect, windowRect);
791
792     windowFeatures.x = windowRect.x();
793     windowFeatures.y = windowRect.y();
794     windowFeatures.height = windowRect.height();
795     windowFeatures.width = windowRect.width();
796
797     frame = createWindow(exec, lexicalFrame, dynamicFrame, frame, urlString, frameName, windowFeatures, JSValue());
798
799     if (!frame)
800         return jsUndefined();
801
802     return toJS(exec, frame->domWindow());
803 }
804
805 JSValue JSDOMWindow::showModalDialog(ExecState* exec, const ArgList& args)
806 {
807     String url = valueToStringWithUndefinedOrNullCheck(exec, args.at(0));
808     JSValue dialogArgs = args.at(1);
809     String featureArgs = valueToStringWithUndefinedOrNullCheck(exec, args.at(2));
810
811     Frame* frame = impl()->frame();
812     if (!frame)
813         return jsUndefined();
814     Frame* lexicalFrame = toLexicalFrame(exec);
815     if (!lexicalFrame)
816         return jsUndefined();
817     Frame* dynamicFrame = toDynamicFrame(exec);
818     if (!dynamicFrame)
819         return jsUndefined();
820
821     if (!DOMWindow::canShowModalDialogNow(frame) || !domWindowAllowPopUp(dynamicFrame, exec))
822         return jsUndefined();
823
824     HashMap<String, String> features;
825     DOMWindow::parseModalDialogFeatures(featureArgs, features);
826
827     const bool trusted = false;
828
829     // The following features from Microsoft's documentation are not implemented:
830     // - default font settings
831     // - width, height, left, and top specified in units other than "px"
832     // - edge (sunken or raised, default is raised)
833     // - dialogHide: trusted && boolFeature(features, "dialoghide"), makes dialog hide when you print
834     // - help: boolFeature(features, "help", true), makes help icon appear in dialog (what does it do on Windows?)
835     // - unadorned: trusted && boolFeature(features, "unadorned");
836
837     FloatRect screenRect = screenAvailableRect(frame->view());
838
839     WindowFeatures wargs;
840     wargs.width = WindowFeatures::floatFeature(features, "dialogwidth", 100, screenRect.width(), 620); // default here came from frame size of dialog in MacIE
841     wargs.widthSet = true;
842     wargs.height = WindowFeatures::floatFeature(features, "dialogheight", 100, screenRect.height(), 450); // default here came from frame size of dialog in MacIE
843     wargs.heightSet = true;
844
845     wargs.x = WindowFeatures::floatFeature(features, "dialogleft", screenRect.x(), screenRect.right() - wargs.width, -1);
846     wargs.xSet = wargs.x > 0;
847     wargs.y = WindowFeatures::floatFeature(features, "dialogtop", screenRect.y(), screenRect.bottom() - wargs.height, -1);
848     wargs.ySet = wargs.y > 0;
849
850     if (WindowFeatures::boolFeature(features, "center", true)) {
851         if (!wargs.xSet) {
852             wargs.x = screenRect.x() + (screenRect.width() - wargs.width) / 2;
853             wargs.xSet = true;
854         }
855         if (!wargs.ySet) {
856             wargs.y = screenRect.y() + (screenRect.height() - wargs.height) / 2;
857             wargs.ySet = true;
858         }
859     }
860
861     wargs.dialog = true;
862     wargs.resizable = WindowFeatures::boolFeature(features, "resizable");
863     wargs.scrollbarsVisible = WindowFeatures::boolFeature(features, "scroll", true);
864     wargs.statusBarVisible = WindowFeatures::boolFeature(features, "status", !trusted);
865     wargs.menuBarVisible = false;
866     wargs.toolBarVisible = false;
867     wargs.locationBarVisible = false;
868     wargs.fullscreen = false;
869
870     Frame* dialogFrame = createWindow(exec, lexicalFrame, dynamicFrame, frame, url, "", wargs, dialogArgs);
871     if (!dialogFrame)
872         return jsUndefined();
873
874     JSDOMWindow* dialogWindow = toJSDOMWindow(dialogFrame, currentWorld(exec));
875     dialogFrame->page()->chrome()->runModal();
876
877     Identifier returnValue(exec, "returnValue");
878     if (dialogWindow->allowsAccessFromNoErrorMessage(exec)) {
879         PropertySlot slot;
880         // This is safe, we have already performed the origin security check and we are
881         // not interested in any of the DOM properties of the window.
882         if (dialogWindow->JSGlobalObject::getOwnPropertySlot(exec, returnValue, slot))
883             return slot.getValue(exec, returnValue);
884     }
885     return jsUndefined();
886 }
887
888 JSValue JSDOMWindow::postMessage(ExecState* exec, const ArgList& args)
889 {
890     DOMWindow* window = impl();
891
892     DOMWindow* source = asJSDOMWindow(exec->lexicalGlobalObject())->impl();
893     PassRefPtr<SerializedScriptValue> message = SerializedScriptValue::create(exec, args.at(0));
894
895     if (exec->hadException())
896         return jsUndefined();
897
898     MessagePortArray messagePorts;
899     if (args.size() > 2)
900         fillMessagePortArray(exec, args.at(1), messagePorts);
901     if (exec->hadException())
902         return jsUndefined();
903
904     String targetOrigin = valueToStringWithUndefinedOrNullCheck(exec, args.at((args.size() == 2) ? 1 : 2));
905     if (exec->hadException())
906         return jsUndefined();
907
908     ExceptionCode ec = 0;
909     window->postMessage(message, &messagePorts, targetOrigin, source, ec);
910     setDOMException(exec, ec);
911
912     return jsUndefined();
913 }
914
915 JSValue JSDOMWindow::setTimeout(ExecState* exec, const ArgList& args)
916 {
917     OwnPtr<ScheduledAction> action = ScheduledAction::create(exec, args, currentWorld(exec));
918     if (exec->hadException())
919         return jsUndefined();
920     int delay = args.at(1).toInt32(exec);
921
922     ExceptionCode ec = 0;
923     int result = impl()->setTimeout(action.release(), delay, ec);
924     setDOMException(exec, ec);
925
926     return jsNumber(exec, result);
927 }
928
929 JSValue JSDOMWindow::setInterval(ExecState* exec, const ArgList& args)
930 {
931     OwnPtr<ScheduledAction> action = ScheduledAction::create(exec, args, currentWorld(exec));
932     if (exec->hadException())
933         return jsUndefined();
934     int delay = args.at(1).toInt32(exec);
935
936     ExceptionCode ec = 0;
937     int result = impl()->setInterval(action.release(), delay, ec);
938     setDOMException(exec, ec);
939
940     return jsNumber(exec, result);
941 }
942
943 JSValue JSDOMWindow::addEventListener(ExecState* exec, const ArgList& args)
944 {
945     Frame* frame = impl()->frame();
946     if (!frame)
947         return jsUndefined();
948
949     JSValue listener = args.at(1);
950     if (!listener.isObject())
951         return jsUndefined();
952
953     impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), this, false, currentWorld(exec)), args.at(2).toBoolean(exec));
954     return jsUndefined();
955 }
956
957 JSValue JSDOMWindow::removeEventListener(ExecState* exec, const ArgList& args)
958 {
959     Frame* frame = impl()->frame();
960     if (!frame)
961         return jsUndefined();
962
963     JSValue listener = args.at(1);
964     if (!listener.isObject())
965         return jsUndefined();
966
967     impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), this, false, currentWorld(exec)).get(), args.at(2).toBoolean(exec));
968     return jsUndefined();
969 }
970
971 JSValue JSDOMWindow::openDatabase(ExecState* exec, const ArgList& args)
972 {
973     if (!allowsAccessFrom(exec) || (args.size() < 4))
974         return jsUndefined();
975     ExceptionCode ec = 0;
976     const UString& name = args.at(0).toString(exec);
977     const UString& version = args.at(1).toString(exec);
978     const UString& displayName = args.at(2).toString(exec);
979     unsigned long estimatedSize = args.at(3).toInt32(exec);
980     RefPtr<DatabaseCallback> creationCallback;
981     if ((args.size() >= 5) && args.at(4).isObject())
982         creationCallback = JSDatabaseCallback::create(asObject(args.at(4)), globalObject());
983
984     JSValue result = toJS(exec, globalObject(), WTF::getPtr(impl()->openDatabase(name, version, displayName, estimatedSize, creationCallback.release(), ec)));
985     setDOMException(exec, ec);
986     return result;
987 }
988
989 DOMWindow* toDOMWindow(JSValue value)
990 {
991     if (!value.isObject())
992         return 0;
993     JSObject* object = asObject(value);
994     if (object->inherits(&JSDOMWindow::s_info))
995         return static_cast<JSDOMWindow*>(object)->impl();
996     if (object->inherits(&JSDOMWindowShell::s_info))
997         return static_cast<JSDOMWindowShell*>(object)->impl();
998     return 0;
999 }
1000
1001 } // namespace WebCore