677e62c460f08a4f4949021135221e03889cebde
[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 bool JSDOMWindow::getPropertyAttributes(ExecState* exec, const Identifier& propertyName, unsigned& attributes) const
476 {
477     // Only allow getting property attributes properties by frames in the same origin.
478     if (!allowsAccessFrom(exec))
479         return false;
480     return Base::getPropertyAttributes(exec, propertyName, attributes);
481 }
482
483 void JSDOMWindow::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction)
484 {
485     // Only allow defining getters by frames in the same origin.
486     if (!allowsAccessFrom(exec))
487         return;
488
489     // Don't allow shadowing location using defineGetter.
490     if (propertyName == "location")
491         return;
492
493     Base::defineGetter(exec, propertyName, getterFunction);
494 }
495
496 void JSDOMWindow::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction)
497 {
498     // Only allow defining setters by frames in the same origin.
499     if (!allowsAccessFrom(exec))
500         return;
501     Base::defineSetter(exec, propertyName, setterFunction);
502 }
503
504 JSValue JSDOMWindow::lookupGetter(ExecState* exec, const Identifier& propertyName)
505 {
506     // Only allow looking-up getters by frames in the same origin.
507     if (!allowsAccessFrom(exec))
508         return jsUndefined();
509     return Base::lookupGetter(exec, propertyName);
510 }
511
512 JSValue JSDOMWindow::lookupSetter(ExecState* exec, const Identifier& propertyName)
513 {
514     // Only allow looking-up setters by frames in the same origin.
515     if (!allowsAccessFrom(exec))
516         return jsUndefined();
517     return Base::lookupSetter(exec, propertyName);
518 }
519
520 // Custom Attributes
521
522 JSValue JSDOMWindow::history(ExecState* exec) const
523 {
524     History* history = impl()->history();
525     if (DOMObject* wrapper = getCachedDOMObjectWrapper(exec->globalData(), history))
526         return wrapper;
527
528     JSDOMWindow* window = const_cast<JSDOMWindow*>(this);
529     JSHistory* jsHistory = new (exec) JSHistory(getDOMStructure<JSHistory>(exec, window), window, history);
530     cacheDOMObjectWrapper(exec->globalData(), history, jsHistory);
531     return jsHistory;
532 }
533
534 JSValue JSDOMWindow::location(ExecState* exec) const
535 {
536     Location* location = impl()->location();
537     if (DOMObject* wrapper = getCachedDOMObjectWrapper(exec->globalData(), location))
538         return wrapper;
539
540     JSDOMWindow* window = const_cast<JSDOMWindow*>(this);
541     JSLocation* jsLocation = new (exec) JSLocation(getDOMStructure<JSLocation>(exec, window), window, location);
542     cacheDOMObjectWrapper(exec->globalData(), location, jsLocation);
543     return jsLocation;
544 }
545
546 void JSDOMWindow::setLocation(ExecState* exec, JSValue value)
547 {
548     Frame* lexicalFrame = toLexicalFrame(exec);
549     if (!lexicalFrame)
550         return;
551
552 #if ENABLE(DASHBOARD_SUPPORT)
553     // To avoid breaking old widgets, make "var location =" in a top-level frame create
554     // a property named "location" instead of performing a navigation (<rdar://problem/5688039>).
555     if (Settings* settings = lexicalFrame->settings()) {
556         if (settings->usesDashboardBackwardCompatibilityMode() && !lexicalFrame->tree()->parent()) {
557             if (allowsAccessFrom(exec))
558                 putDirect(Identifier(exec, "location"), value);
559             return;
560         }
561     }
562 #endif
563
564     Frame* frame = impl()->frame();
565     ASSERT(frame);
566
567     if (!shouldAllowNavigation(exec, frame))
568         return;
569
570     KURL url = completeURL(exec, value.toString(exec));
571     if (url.isNull())
572         return;
573
574     if (!protocolIsJavaScript(url) || allowsAccessFrom(exec)) {
575         // We want a new history item if this JS was called via a user gesture
576         frame->loader()->scheduleLocationChange(url, lexicalFrame->loader()->outgoingReferrer(), !lexicalFrame->script()->anyPageIsProcessingUserGesture(), false, processingUserGesture(exec));
577     }
578 }
579
580 JSValue JSDOMWindow::crypto(ExecState*) const
581 {
582     return jsUndefined();
583 }
584
585 JSValue JSDOMWindow::event(ExecState* exec) const
586 {
587     Event* event = currentEvent();
588     if (!event)
589         return jsUndefined();
590     return toJS(exec, event);
591 }
592
593 #if ENABLE(EVENTSOURCE)
594 JSValue JSDOMWindow::eventSource(ExecState* exec) const
595 {
596     return getDOMConstructor<JSEventSourceConstructor>(exec, this);
597 }
598 #endif
599
600 JSValue JSDOMWindow::image(ExecState* exec) const
601 {
602     return getDOMConstructor<JSImageConstructor>(exec, this);
603 }
604
605 JSValue JSDOMWindow::option(ExecState* exec) const
606 {
607     return getDOMConstructor<JSOptionConstructor>(exec, this);
608 }
609
610 #if ENABLE(VIDEO)
611 JSValue JSDOMWindow::audio(ExecState* exec) const
612 {
613     if (!MediaPlayer::isAvailable())
614         return jsUndefined();
615     return getDOMConstructor<JSAudioConstructor>(exec, this);
616 }
617 #endif
618
619 JSValue JSDOMWindow::webKitPoint(ExecState* exec) const
620 {
621     return getDOMConstructor<JSWebKitPointConstructor>(exec, this);
622 }
623
624 JSValue JSDOMWindow::webKitCSSMatrix(ExecState* exec) const
625 {
626     return getDOMConstructor<JSWebKitCSSMatrixConstructor>(exec, this);
627 }
628  
629 #if ENABLE(3D_CANVAS)
630 JSValue JSDOMWindow::canvasArrayBuffer(ExecState* exec) const
631 {
632     return getDOMConstructor<JSCanvasArrayBufferConstructor>(exec, this);
633 }
634  
635 JSValue JSDOMWindow::canvasByteArray(ExecState* exec) const
636 {
637     return getDOMConstructor<JSCanvasByteArrayConstructor>(exec, this);
638 }
639  
640 JSValue JSDOMWindow::canvasUnsignedByteArray(ExecState* exec) const
641 {
642     return getDOMConstructor<JSCanvasUnsignedByteArrayConstructor>(exec, this);
643 }
644  
645 JSValue JSDOMWindow::canvasIntArray(ExecState* exec) const
646 {
647     return getDOMConstructor<JSCanvasIntArrayConstructor>(exec, this);
648 }
649  
650 JSValue JSDOMWindow::canvasUnsignedIntArray(ExecState* exec) const
651 {
652     return getDOMConstructor<JSCanvasUnsignedIntArrayConstructor>(exec, this);
653 }
654  
655 JSValue JSDOMWindow::canvasShortArray(ExecState* exec) const
656 {
657     return getDOMConstructor<JSCanvasShortArrayConstructor>(exec, this);
658 }
659  
660 JSValue JSDOMWindow::canvasUnsignedShortArray(ExecState* exec) const
661 {
662     return getDOMConstructor<JSCanvasUnsignedShortArrayConstructor>(exec, this);
663 }
664  
665 JSValue JSDOMWindow::canvasFloatArray(ExecState* exec) const
666 {
667     return getDOMConstructor<JSCanvasFloatArrayConstructor>(exec, this);
668 }
669 #endif
670  
671 JSValue JSDOMWindow::xmlHttpRequest(ExecState* exec) const
672 {
673     return getDOMConstructor<JSXMLHttpRequestConstructor>(exec, this);
674 }
675
676 #if ENABLE(XSLT)
677 JSValue JSDOMWindow::xsltProcessor(ExecState* exec) const
678 {
679     return getDOMConstructor<JSXSLTProcessorConstructor>(exec, this);
680 }
681 #endif
682
683 #if ENABLE(CHANNEL_MESSAGING)
684 JSValue JSDOMWindow::messageChannel(ExecState* exec) const
685 {
686     return getDOMConstructor<JSMessageChannelConstructor>(exec, this);
687 }
688 #endif
689
690 #if ENABLE(WORKERS)
691 JSValue JSDOMWindow::worker(ExecState* exec) const
692 {
693     return getDOMConstructor<JSWorkerConstructor>(exec, this);
694 }
695 #endif
696
697 #if ENABLE(SHARED_WORKERS)
698 JSValue JSDOMWindow::sharedWorker(ExecState* exec) const
699 {
700     return getDOMConstructor<JSSharedWorkerConstructor>(exec, this);
701 }
702 #endif
703
704 #if ENABLE(WEB_SOCKETS)
705 JSValue JSDOMWindow::webSocket(ExecState* exec) const
706 {
707     return getDOMConstructor<JSWebSocketConstructor>(exec, this);
708 }
709 #endif
710
711 // Custom functions
712
713 // Helper for window.open() and window.showModalDialog()
714 static Frame* createWindow(ExecState* exec, Frame* lexicalFrame, Frame* dynamicFrame,
715                            Frame* openerFrame, const String& url, const String& frameName, 
716                            const WindowFeatures& windowFeatures, JSValue dialogArgs)
717 {
718     ASSERT(lexicalFrame);
719     ASSERT(dynamicFrame);
720
721     ResourceRequest request;
722
723     // For whatever reason, Firefox uses the dynamicGlobalObject to determine
724     // the outgoingReferrer.  We replicate that behavior here.
725     String referrer = dynamicFrame->loader()->outgoingReferrer();
726     request.setHTTPReferrer(referrer);
727     FrameLoader::addHTTPOriginIfNeeded(request, dynamicFrame->loader()->outgoingOrigin());
728     FrameLoadRequest frameRequest(request, frameName);
729
730     // FIXME: It's much better for client API if a new window starts with a URL, here where we
731     // know what URL we are going to open. Unfortunately, this code passes the empty string
732     // for the URL, but there's a reason for that. Before loading we have to set up the opener,
733     // openedByDOM, and dialogArguments values. Also, to decide whether to use the URL we currently
734     // do an allowsAccessFrom call using the window we create, which can't be done before creating it.
735     // We'd have to resolve all those issues to pass the URL instead of "".
736
737     bool created;
738     // We pass in the opener frame here so it can be used for looking up the frame name, in case the active frame
739     // is different from the opener frame, and the name references a frame relative to the opener frame, for example
740     // "_self" or "_parent".
741     Frame* newFrame = lexicalFrame->loader()->createWindow(openerFrame->loader(), frameRequest, windowFeatures, created);
742     if (!newFrame)
743         return 0;
744
745     newFrame->loader()->setOpener(openerFrame);
746     newFrame->loader()->setOpenedByDOM();
747
748     JSDOMWindow* newWindow = toJSDOMWindow(newFrame);
749
750     if (dialogArgs)
751         newWindow->putDirect(Identifier(exec, "dialogArguments"), dialogArgs);
752
753     if (!protocolIsJavaScript(url) || newWindow->allowsAccessFrom(exec)) {
754         KURL completedURL = url.isEmpty() ? KURL(ParsedURLString, "") : completeURL(exec, url);
755         bool userGesture = processingUserGesture(exec);
756
757         if (created)
758             newFrame->loader()->changeLocation(completedURL, referrer, false, false, userGesture);
759         else if (!url.isEmpty())
760             newFrame->loader()->scheduleLocationChange(completedURL.string(), referrer, !lexicalFrame->script()->anyPageIsProcessingUserGesture(), false, userGesture);
761     }
762
763     return newFrame;
764 }
765
766 JSValue JSDOMWindow::open(ExecState* exec, const ArgList& args)
767 {
768     Frame* frame = impl()->frame();
769     if (!frame)
770         return jsUndefined();
771     Frame* lexicalFrame = toLexicalFrame(exec);
772     if (!lexicalFrame)
773         return jsUndefined();
774     Frame* dynamicFrame = toDynamicFrame(exec);
775     if (!dynamicFrame)
776         return jsUndefined();
777
778     Page* page = frame->page();
779
780     String urlString = valueToStringWithUndefinedOrNullCheck(exec, args.at(0));
781     AtomicString frameName = args.at(1).isUndefinedOrNull() ? "_blank" : AtomicString(args.at(1).toString(exec));
782
783     // Because FrameTree::find() returns true for empty strings, we must check for empty framenames.
784     // Otherwise, illegitimate window.open() calls with no name will pass right through the popup blocker.
785     if (!DOMWindow::allowPopUp(dynamicFrame) && (frameName.isEmpty() || !frame->tree()->find(frameName)))
786         return jsUndefined();
787
788     // Get the target frame for the special cases of _top and _parent.  In those
789     // cases, we can schedule a location change right now and return early.
790     bool topOrParent = false;
791     if (frameName == "_top") {
792         frame = frame->tree()->top();
793         topOrParent = true;
794     } else if (frameName == "_parent") {
795         if (Frame* parent = frame->tree()->parent())
796             frame = parent;
797         topOrParent = true;
798     }
799     if (topOrParent) {
800         if (!shouldAllowNavigation(exec, frame))
801             return jsUndefined();
802
803         String completedURL;
804         if (!urlString.isEmpty())
805             completedURL = completeURL(exec, urlString).string();
806
807         const JSDOMWindow* targetedWindow = toJSDOMWindow(frame);
808         if (!completedURL.isEmpty() && (!protocolIsJavaScript(completedURL) || (targetedWindow && targetedWindow->allowsAccessFrom(exec)))) {
809             bool userGesture = processingUserGesture(exec);
810
811             // For whatever reason, Firefox uses the dynamicGlobalObject to
812             // determine the outgoingReferrer.  We replicate that behavior
813             // here.
814             String referrer = dynamicFrame->loader()->outgoingReferrer();
815
816             frame->loader()->scheduleLocationChange(completedURL, referrer, !lexicalFrame->script()->anyPageIsProcessingUserGesture(), false, userGesture);
817         }
818         return toJS(exec, frame->domWindow());
819     }
820
821     // In the case of a named frame or a new window, we'll use the createWindow() helper
822     WindowFeatures windowFeatures(valueToStringWithUndefinedOrNullCheck(exec, args.at(2)));
823     FloatRect windowRect(windowFeatures.xSet ? windowFeatures.x : 0, windowFeatures.ySet ? windowFeatures.y : 0,
824                          windowFeatures.widthSet ? windowFeatures.width : 0, windowFeatures.heightSet ? windowFeatures.height : 0);
825     DOMWindow::adjustWindowRect(screenAvailableRect(page ? page->mainFrame()->view() : 0), windowRect, windowRect);
826
827     windowFeatures.x = windowRect.x();
828     windowFeatures.y = windowRect.y();
829     windowFeatures.height = windowRect.height();
830     windowFeatures.width = windowRect.width();
831
832     frame = createWindow(exec, lexicalFrame, dynamicFrame, frame, urlString, frameName, windowFeatures, JSValue());
833
834     if (!frame)
835         return jsUndefined();
836
837     return toJS(exec, frame->domWindow());
838 }
839
840 JSValue JSDOMWindow::showModalDialog(ExecState* exec, const ArgList& args)
841 {
842     Frame* frame = impl()->frame();
843     if (!frame)
844         return jsUndefined();
845     Frame* lexicalFrame = toLexicalFrame(exec);
846     if (!lexicalFrame)
847         return jsUndefined();
848     Frame* dynamicFrame = toDynamicFrame(exec);
849     if (!dynamicFrame)
850         return jsUndefined();
851
852     if (!DOMWindow::canShowModalDialogNow(frame) || !DOMWindow::allowPopUp(dynamicFrame))
853         return jsUndefined();
854
855     String url = valueToStringWithUndefinedOrNullCheck(exec, args.at(0));
856     JSValue dialogArgs = args.at(1);
857     String featureArgs = valueToStringWithUndefinedOrNullCheck(exec, args.at(2));
858
859     HashMap<String, String> features;
860     DOMWindow::parseModalDialogFeatures(featureArgs, features);
861
862     const bool trusted = false;
863
864     // The following features from Microsoft's documentation are not implemented:
865     // - default font settings
866     // - width, height, left, and top specified in units other than "px"
867     // - edge (sunken or raised, default is raised)
868     // - dialogHide: trusted && boolFeature(features, "dialoghide"), makes dialog hide when you print
869     // - help: boolFeature(features, "help", true), makes help icon appear in dialog (what does it do on Windows?)
870     // - unadorned: trusted && boolFeature(features, "unadorned");
871
872     FloatRect screenRect = screenAvailableRect(frame->view());
873
874     WindowFeatures wargs;
875     wargs.width = WindowFeatures::floatFeature(features, "dialogwidth", 100, screenRect.width(), 620); // default here came from frame size of dialog in MacIE
876     wargs.widthSet = true;
877     wargs.height = WindowFeatures::floatFeature(features, "dialogheight", 100, screenRect.height(), 450); // default here came from frame size of dialog in MacIE
878     wargs.heightSet = true;
879
880     wargs.x = WindowFeatures::floatFeature(features, "dialogleft", screenRect.x(), screenRect.right() - wargs.width, -1);
881     wargs.xSet = wargs.x > 0;
882     wargs.y = WindowFeatures::floatFeature(features, "dialogtop", screenRect.y(), screenRect.bottom() - wargs.height, -1);
883     wargs.ySet = wargs.y > 0;
884
885     if (WindowFeatures::boolFeature(features, "center", true)) {
886         if (!wargs.xSet) {
887             wargs.x = screenRect.x() + (screenRect.width() - wargs.width) / 2;
888             wargs.xSet = true;
889         }
890         if (!wargs.ySet) {
891             wargs.y = screenRect.y() + (screenRect.height() - wargs.height) / 2;
892             wargs.ySet = true;
893         }
894     }
895
896     wargs.dialog = true;
897     wargs.resizable = WindowFeatures::boolFeature(features, "resizable");
898     wargs.scrollbarsVisible = WindowFeatures::boolFeature(features, "scroll", true);
899     wargs.statusBarVisible = WindowFeatures::boolFeature(features, "status", !trusted);
900     wargs.menuBarVisible = false;
901     wargs.toolBarVisible = false;
902     wargs.locationBarVisible = false;
903     wargs.fullscreen = false;
904
905     Frame* dialogFrame = createWindow(exec, lexicalFrame, dynamicFrame, frame, url, "", wargs, dialogArgs);
906     if (!dialogFrame)
907         return jsUndefined();
908
909     JSDOMWindow* dialogWindow = toJSDOMWindow(dialogFrame);
910     dialogFrame->page()->chrome()->runModal();
911
912     return dialogWindow->getDirect(Identifier(exec, "returnValue"));
913 }
914
915 JSValue JSDOMWindow::postMessage(ExecState* exec, const ArgList& args)
916 {
917     DOMWindow* window = impl();
918
919     DOMWindow* source = asJSDOMWindow(exec->lexicalGlobalObject())->impl();
920     String message = args.at(0).toString(exec);
921
922     if (exec->hadException())
923         return jsUndefined();
924
925     MessagePortArray messagePorts;
926     if (args.size() > 2)
927         fillMessagePortArray(exec, args.at(1), messagePorts);
928     if (exec->hadException())
929         return jsUndefined();
930
931     String targetOrigin = valueToStringWithUndefinedOrNullCheck(exec, args.at((args.size() == 2) ? 1 : 2));
932     if (exec->hadException())
933         return jsUndefined();
934
935     ExceptionCode ec = 0;
936     window->postMessage(message, &messagePorts, targetOrigin, source, ec);
937     setDOMException(exec, ec);
938
939     return jsUndefined();
940 }
941
942 JSValue JSDOMWindow::setTimeout(ExecState* exec, const ArgList& args)
943 {
944     ScheduledAction* action = ScheduledAction::create(exec, args);
945     if (exec->hadException())
946         return jsUndefined();
947     int delay = args.at(1).toInt32(exec);
948     return jsNumber(exec, impl()->setTimeout(action, delay));
949 }
950
951 JSValue JSDOMWindow::setInterval(ExecState* exec, const ArgList& args)
952 {
953     ScheduledAction* action = ScheduledAction::create(exec, args);
954     if (exec->hadException())
955         return jsUndefined();
956     int delay = args.at(1).toInt32(exec);
957     return jsNumber(exec, impl()->setInterval(action, delay));
958 }
959
960 JSValue JSDOMWindow::atob(ExecState* exec, const ArgList& args)
961 {
962     if (args.size() < 1)
963         return throwError(exec, SyntaxError, "Not enough arguments");
964
965     JSValue v = args.at(0);
966     if (v.isNull())
967         return jsEmptyString(exec);
968
969     UString s = v.toString(exec);
970     if (!s.is8Bit()) {
971         setDOMException(exec, INVALID_CHARACTER_ERR);
972         return jsUndefined();
973     }
974
975     Vector<char> in(s.size());
976     for (int i = 0; i < s.size(); ++i)
977         in[i] = static_cast<char>(s.data()[i]);
978     Vector<char> out;
979
980     if (!base64Decode(in, out))
981         return throwError(exec, GeneralError, "Cannot decode base64");
982
983     return jsString(exec, String(out.data(), out.size()));
984 }
985
986 JSValue JSDOMWindow::btoa(ExecState* exec, const ArgList& args)
987 {
988     if (args.size() < 1)
989         return throwError(exec, SyntaxError, "Not enough arguments");
990
991     JSValue v = args.at(0);
992     if (v.isNull())
993         return jsEmptyString(exec);
994
995     UString s = v.toString(exec);
996     if (!s.is8Bit()) {
997         setDOMException(exec, INVALID_CHARACTER_ERR);
998         return jsUndefined();
999     }
1000
1001     Vector<char> in(s.size());
1002     for (int i = 0; i < s.size(); ++i)
1003         in[i] = static_cast<char>(s.data()[i]);
1004     Vector<char> out;
1005
1006     base64Encode(in, out);
1007
1008     return jsString(exec, String(out.data(), out.size()));
1009 }
1010
1011 JSValue JSDOMWindow::addEventListener(ExecState* exec, const ArgList& args)
1012 {
1013     Frame* frame = impl()->frame();
1014     if (!frame)
1015         return jsUndefined();
1016
1017     if (RefPtr<JSEventListener> listener = findOrCreateJSEventListener(args.at(1)))
1018         impl()->addEventListener(AtomicString(args.at(0).toString(exec)), listener.release(), args.at(2).toBoolean(exec));
1019
1020     return jsUndefined();
1021 }
1022
1023 JSValue JSDOMWindow::removeEventListener(ExecState* exec, const ArgList& args)
1024 {
1025     Frame* frame = impl()->frame();
1026     if (!frame)
1027         return jsUndefined();
1028
1029     if (JSEventListener* listener = findJSEventListener(args.at(1)))
1030         impl()->removeEventListener(AtomicString(args.at(0).toString(exec)), listener, args.at(2).toBoolean(exec));
1031
1032     return jsUndefined();
1033 }
1034
1035 DOMWindow* toDOMWindow(JSValue value)
1036 {
1037     if (!value.isObject())
1038         return 0;
1039     JSObject* object = asObject(value);
1040     if (object->inherits(&JSDOMWindow::s_info))
1041         return static_cast<JSDOMWindow*>(object)->impl();
1042     if (object->inherits(&JSDOMWindowShell::s_info))
1043         return static_cast<JSDOMWindowShell*>(object)->impl();
1044     return 0;
1045 }
1046
1047 } // namespace WebCore