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