2008-09-01 Dean Jackson <dino@apple.com>
[WebKit-https.git] / WebCore / bindings / js / JSDOMWindowBase.cpp
1 /*
2  *  Copyright (C) 2000 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2006 Jon Shier (jshier@iastate.edu)
4  *  Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reseved.
5  *  Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org)
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Lesser General Public
9  *  License as published by the Free Software Foundation; either
10  *  version 2 of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this library; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
20  *  USA
21  */
22
23 #include "config.h"
24 #include "JSDOMWindowBase.h"
25
26 #include "Base64.h"
27 #include "CString.h"
28 #include "Console.h"
29 #include "DOMWindow.h"
30 #include "Element.h"
31 #include "EventListener.h"
32 #include "EventNames.h"
33 #include "ExceptionCode.h"
34 #include "FloatRect.h"
35 #include "Frame.h"
36 #include "FrameLoadRequest.h"
37 #include "FrameLoader.h"
38 #include "FrameTree.h"
39 #include "GCController.h"
40 #include "HTMLDocument.h"
41 #include "JSAudioConstructor.h"
42 #include "JSDOMWindowCustom.h"
43 #include "JSEvent.h"
44 #include "JSEventListener.h"
45 #include "JSHTMLCollection.h"
46 #include "JSHTMLOptionElementConstructor.h"
47 #include "JSImageConstructor.h"
48 #include "JSNode.h"
49 #include "JSXMLHttpRequestConstructor.h"
50 #include "Logging.h"
51 #include "MediaPlayer.h"
52 #include "Page.h"
53 #include "PausedTimeouts.h"
54 #include "PlatformScreen.h"
55 #include "PluginInfoStore.h"
56 #include "RenderView.h"
57 #include "ScheduledAction.h"
58 #include "ScriptController.h"
59 #include "SecurityOrigin.h"
60 #include "Settings.h"
61 #include "WindowFeatures.h"
62 #include "htmlediting.h"
63 #include <kjs/Error.h>
64 #include <kjs/JSLock.h>
65 #include <wtf/AlwaysInline.h>
66 #include <wtf/MathExtras.h>
67
68 #if ENABLE(XSLT)
69 #include "JSXSLTProcessorConstructor.h"
70 #endif
71
72 using namespace KJS;
73
74 namespace WebCore {
75
76 static JSValue* windowProtoFuncAToB(ExecState*, JSObject*, JSValue*, const ArgList&);
77 static JSValue* windowProtoFuncBToA(ExecState*, JSObject*, JSValue*, const ArgList&);
78 static JSValue* windowProtoFuncOpen(ExecState*, JSObject*, JSValue*, const ArgList&);
79 static JSValue* windowProtoFuncSetTimeout(ExecState*, JSObject*, JSValue*, const ArgList&);
80 static JSValue* windowProtoFuncClearTimeoutOrInterval(ExecState*, JSObject*, JSValue*, const ArgList&);
81 static JSValue* windowProtoFuncSetInterval(ExecState*, JSObject*, JSValue*, const ArgList&);
82 static JSValue* windowProtoFuncAddEventListener(ExecState*, JSObject*, JSValue*, const ArgList&);
83 static JSValue* windowProtoFuncRemoveEventListener(ExecState*, JSObject*, JSValue*, const ArgList&);
84 static JSValue* windowProtoFuncShowModalDialog(ExecState*, JSObject*, JSValue*, const ArgList&);
85 static JSValue* windowProtoFuncNotImplemented(ExecState*, JSObject*, JSValue*, const ArgList&);
86
87 }
88
89 #include "JSDOMWindowBase.lut.h"
90
91 namespace WebCore {
92
93 using namespace EventNames;
94
95 static int lastUsedTimeoutId;
96
97 static int timerNestingLevel = 0;
98 const int cMaxTimerNestingLevel = 5;
99 const double cMinimumTimerInterval = 0.010;
100
101 class DOMWindowTimer : public TimerBase {
102 public:
103     DOMWindowTimer(int timeoutId, int nestingLevel, JSDOMWindowBase* object, ScheduledAction* action)
104         : m_timeoutId(timeoutId)
105         , m_nestingLevel(nestingLevel)
106         , m_object(object)
107         , m_action(action)
108     {
109     }
110
111     virtual ~DOMWindowTimer()
112     {
113         JSLock lock(false);
114         delete m_action;
115     }
116
117     int timeoutId() const { return m_timeoutId; }
118
119     int nestingLevel() const { return m_nestingLevel; }
120     void setNestingLevel(int n) { m_nestingLevel = n; }
121
122     ScheduledAction* action() const { return m_action; }
123     ScheduledAction* takeAction() { ScheduledAction* a = m_action; m_action = 0; return a; }
124
125 private:
126     virtual void fired();
127
128     int m_timeoutId;
129     int m_nestingLevel;
130     JSDOMWindowBase* m_object;
131     ScheduledAction* m_action;
132 };
133
134 ////////////////////// JSDOMWindowBase Object ////////////////////////
135
136 const ClassInfo JSDOMWindowBase::s_info = { "Window", 0, &JSDOMWindowBaseTable, 0 };
137
138 /*
139 @begin JSDOMWindowBaseTable
140 # -- Functions --
141   atob                  WebCore::windowProtoFuncAToB                 DontDelete|Function 1
142   btoa                  WebCore::windowProtoFuncBToA                 DontDelete|Function 1
143   open                  WebCore::windowProtoFuncOpen                 DontDelete|Function 3
144   setTimeout            WebCore::windowProtoFuncSetTimeout           DontDelete|Function 2
145   clearTimeout          WebCore::windowProtoFuncClearTimeoutOrInterval DontDelete|Function 1
146   setInterval           WebCore::windowProtoFuncSetInterval          DontDelete|Function 2
147   clearInterval         WebCore::windowProtoFuncClearTimeoutOrInterval DontDelete|Function 1
148   addEventListener      WebCore::windowProtoFuncAddEventListener     DontDelete|Function 3
149   removeEventListener   WebCore::windowProtoFuncRemoveEventListener  DontDelete|Function 3
150   showModalDialog       WebCore::windowProtoFuncShowModalDialog      DontDelete|Function 1
151 # Not implemented
152   captureEvents         WebCore::windowProtoFuncNotImplemented       DontDelete|Function 0
153   releaseEvents         WebCore::windowProtoFuncNotImplemented       DontDelete|Function 0
154
155 # -- Attributes --
156   crypto                WebCore::JSDOMWindowBase::Crypto             DontDelete|ReadOnly
157   event                 WebCore::JSDOMWindowBase::Event_             DontDelete
158 # -- Event Listeners --
159   onabort               WebCore::JSDOMWindowBase::Onabort            DontDelete
160   onblur                WebCore::JSDOMWindowBase::Onblur             DontDelete
161   onchange              WebCore::JSDOMWindowBase::Onchange           DontDelete
162   onclick               WebCore::JSDOMWindowBase::Onclick            DontDelete
163   ondblclick            WebCore::JSDOMWindowBase::Ondblclick         DontDelete
164   onerror               WebCore::JSDOMWindowBase::Onerror            DontDelete
165   onfocus               WebCore::JSDOMWindowBase::Onfocus            DontDelete
166   onkeydown             WebCore::JSDOMWindowBase::Onkeydown          DontDelete
167   onkeypress            WebCore::JSDOMWindowBase::Onkeypress         DontDelete
168   onkeyup               WebCore::JSDOMWindowBase::Onkeyup            DontDelete
169   onload                WebCore::JSDOMWindowBase::Onload             DontDelete
170   onmousedown           WebCore::JSDOMWindowBase::Onmousedown        DontDelete
171   onmousemove           WebCore::JSDOMWindowBase::Onmousemove        DontDelete
172   onmouseout            WebCore::JSDOMWindowBase::Onmouseout         DontDelete
173   onmouseover           WebCore::JSDOMWindowBase::Onmouseover        DontDelete
174   onmouseup             WebCore::JSDOMWindowBase::Onmouseup          DontDelete
175   onmousewheel          WebCore::JSDOMWindowBase::OnWindowMouseWheel DontDelete
176   onreset               WebCore::JSDOMWindowBase::Onreset            DontDelete
177   onresize              WebCore::JSDOMWindowBase::Onresize           DontDelete
178   onscroll              WebCore::JSDOMWindowBase::Onscroll           DontDelete
179   onsearch              WebCore::JSDOMWindowBase::Onsearch           DontDelete
180   onselect              WebCore::JSDOMWindowBase::Onselect           DontDelete
181   onsubmit              WebCore::JSDOMWindowBase::Onsubmit           DontDelete
182   onunload              WebCore::JSDOMWindowBase::Onunload           DontDelete
183   onbeforeunload        WebCore::JSDOMWindowBase::Onbeforeunload     DontDelete
184   onwebkitanimationstart    WebCore::JSDOMWindowBase::OnWebKitAnimationStart  DontDelete
185   onwebkitanimationiteration     WebCore::JSDOMWindowBase::OnWebKitAnimationIteration   DontDelete
186   onwebkitanimationend      WebCore::JSDOMWindowBase::OnWebKitAnimationEnd    DontDelete
187   onwebkittransitionend     WebCore::JSDOMWindowBase::OnWebKitTransitionEnd   DontDelete
188 # -- Constructors --
189   Audio                 WebCore::JSDOMWindowBase::Audio              DontDelete
190   Image                 WebCore::JSDOMWindowBase::Image              DontDelete
191   Option                WebCore::JSDOMWindowBase::Option             DontDelete
192   XMLHttpRequest        WebCore::JSDOMWindowBase::XMLHttpRequest     DontDelete
193   XSLTProcessor         WebCore::JSDOMWindowBase::XSLTProcessor      DontDelete
194 @end
195 */
196
197 JSDOMWindowBase::JSDOMWindowBaseData::JSDOMWindowBaseData(PassRefPtr<DOMWindow> window_, JSDOMWindowBase* jsWindow_, JSDOMWindowShell* shell_)
198     : JSGlobalObjectData(jsWindow_, shell_)
199     , impl(window_)
200     , evt(0)
201     , returnValueSlot(0)
202     , shell(shell_)
203 {
204 }
205
206 JSDOMWindowBase::JSDOMWindowBase(JSObject* prototype, DOMWindow* window, JSDOMWindowShell* shell)
207     : JSGlobalObject(prototype, new JSDOMWindowBaseData(window, this, shell), shell)
208 {
209     // Time in milliseconds before the script timeout handler kicks in.
210     setTimeoutTime(10000);
211
212     GlobalPropertyInfo staticGlobals[] = {
213         GlobalPropertyInfo(Identifier(globalExec(), "document"), jsNull(), DontDelete | ReadOnly),
214         GlobalPropertyInfo(Identifier(globalExec(), "window"), d()->shell, DontDelete | ReadOnly)
215     };
216     
217     addStaticGlobals(staticGlobals, sizeof(staticGlobals) / sizeof(GlobalPropertyInfo));
218 }
219
220 void JSDOMWindowBase::updateDocument()
221 {
222     ASSERT(d()->impl->document());
223     ExecState* exec = globalExec();
224     symbolTablePutWithAttributes(Identifier(exec, "document"), toJS(exec, d()->impl->document()), DontDelete | ReadOnly);
225 }
226
227 JSDOMWindowBase::~JSDOMWindowBase()
228 {
229     if (d()->impl->frame())
230         d()->impl->frame()->script()->clearFormerWindow(asJSDOMWindow(this));
231
232     clearAllTimeouts();
233
234     // Clear any backpointers to the window
235
236     ListenersMap::iterator i2 = d()->jsEventListeners.begin();
237     ListenersMap::iterator e2 = d()->jsEventListeners.end();
238     for (; i2 != e2; ++i2)
239         i2->second->clearWindow();
240     i2 = d()->jsHTMLEventListeners.begin();
241     e2 = d()->jsHTMLEventListeners.end();
242     for (; i2 != e2; ++i2)
243         i2->second->clearWindow();
244
245     UnprotectedListenersMap::iterator i1 = d()->jsUnprotectedEventListeners.begin();
246     UnprotectedListenersMap::iterator e1 = d()->jsUnprotectedEventListeners.end();
247     for (; i1 != e1; ++i1)
248         i1->second->clearWindow();
249     i1 = d()->jsUnprotectedHTMLEventListeners.begin();
250     e1 = d()->jsUnprotectedHTMLEventListeners.end();
251     for (; i1 != e1; ++i1)
252         i1->second->clearWindow();
253 }
254
255 static bool allowPopUp(ExecState* exec)
256 {
257     Frame* frame = asJSDOMWindow(exec->dynamicGlobalObject())->impl()->frame();
258
259     ASSERT(frame);
260     if (frame->script()->processingUserGesture())
261         return true;
262     Settings* settings = frame->settings();
263     return settings && settings->JavaScriptCanOpenWindowsAutomatically();
264 }
265
266 static HashMap<String, String> parseModalDialogFeatures(const String& featuresArg)
267 {
268     HashMap<String, String> map;
269
270     Vector<String> features;
271     featuresArg.split(';', features);
272     Vector<String>::const_iterator end = features.end();
273     for (Vector<String>::const_iterator it = features.begin(); it != end; ++it) {
274         String s = *it;
275         int pos = s.find('=');
276         int colonPos = s.find(':');
277         if (pos >= 0 && colonPos >= 0)
278             continue; // ignore any strings that have both = and :
279         if (pos < 0)
280             pos = colonPos;
281         if (pos < 0) {
282             // null string for value means key without value
283             map.set(s.stripWhiteSpace().lower(), String());
284         } else {
285             String key = s.left(pos).stripWhiteSpace().lower();
286             String val = s.substring(pos + 1).stripWhiteSpace().lower();
287             int spacePos = val.find(' ');
288             if (spacePos != -1)
289                 val = val.left(spacePos);
290             map.set(key, val);
291         }
292     }
293
294     return map;
295 }
296
297 static Frame* createWindow(ExecState* exec, Frame* openerFrame, const String& url,
298     const String& frameName, const WindowFeatures& windowFeatures, JSValue* dialogArgs)
299 {
300     Frame* activeFrame = asJSDOMWindow(exec->dynamicGlobalObject())->impl()->frame();
301     ASSERT(activeFrame);
302
303     ResourceRequest request;
304
305     request.setHTTPReferrer(activeFrame->loader()->outgoingReferrer());
306     FrameLoadRequest frameRequest(request, frameName);
307
308     // FIXME: It's much better for client API if a new window starts with a URL, here where we
309     // know what URL we are going to open. Unfortunately, this code passes the empty string
310     // for the URL, but there's a reason for that. Before loading we have to set up the opener,
311     // openedByDOM, and dialogArguments values. Also, to decide whether to use the URL we currently
312     // do an allowsAccessFrom call using the window we create, which can't be done before creating it.
313     // We'd have to resolve all those issues to pass the URL instead of "".
314
315     bool created;
316     // We pass in the opener frame here so it can be used for looking up the frame name, in case the active frame
317     // is different from the opener frame, and the name references a frame relative to the opener frame, for example
318     // "_self" or "_parent".
319     Frame* newFrame = activeFrame->loader()->createWindow(openerFrame->loader(), frameRequest, windowFeatures, created);
320     if (!newFrame)
321         return 0;
322
323     newFrame->loader()->setOpener(openerFrame);
324     newFrame->loader()->setOpenedByDOM();
325
326     JSDOMWindow* newWindow = toJSDOMWindow(newFrame);
327
328     if (dialogArgs)
329         newWindow->putDirect(Identifier(exec, "dialogArguments"), dialogArgs);
330
331     if (!protocolIs(url, "javascript") || newWindow->allowsAccessFrom(exec)) {
332         KURL completedURL = url.isEmpty() ? KURL("") : activeFrame->document()->completeURL(url);
333         bool userGesture = activeFrame->script()->processingUserGesture();
334
335         if (created)
336             newFrame->loader()->changeLocation(completedURL, activeFrame->loader()->outgoingReferrer(), false, userGesture);
337         else if (!url.isEmpty())
338             newFrame->loader()->scheduleLocationChange(completedURL.string(), activeFrame->loader()->outgoingReferrer(), false, userGesture);
339     }
340
341     return newFrame;
342 }
343
344 static bool canShowModalDialog(const Frame* frame)
345 {
346     if (!frame)
347         return false;
348     return frame->page()->chrome()->canRunModal();
349 }
350
351 static bool canShowModalDialogNow(const Frame* frame)
352 {
353     if (!frame)
354         return false;
355     return frame->page()->chrome()->canRunModalNow();
356 }
357
358 static JSValue* showModalDialog(ExecState* exec, Frame* frame, const String& url, JSValue* dialogArgs, const String& featureArgs)
359 {
360     if (!canShowModalDialogNow(frame) || !allowPopUp(exec))
361         return jsUndefined();
362
363     const HashMap<String, String> features = parseModalDialogFeatures(featureArgs);
364
365     const bool trusted = false;
366
367     // The following features from Microsoft's documentation are not implemented:
368     // - default font settings
369     // - width, height, left, and top specified in units other than "px"
370     // - edge (sunken or raised, default is raised)
371     // - dialogHide: trusted && boolFeature(features, "dialoghide"), makes dialog hide when you print
372     // - help: boolFeature(features, "help", true), makes help icon appear in dialog (what does it do on Windows?)
373     // - unadorned: trusted && boolFeature(features, "unadorned");
374
375     if (!frame)
376         return jsUndefined();
377
378     FloatRect screenRect = screenAvailableRect(frame->view());
379
380     WindowFeatures wargs;
381     wargs.width = WindowFeatures::floatFeature(features, "dialogwidth", 100, screenRect.width(), 620); // default here came from frame size of dialog in MacIE
382     wargs.widthSet = true;
383     wargs.height = WindowFeatures::floatFeature(features, "dialogheight", 100, screenRect.height(), 450); // default here came from frame size of dialog in MacIE
384     wargs.heightSet = true;
385
386     wargs.x = WindowFeatures::floatFeature(features, "dialogleft", screenRect.x(), screenRect.right() - wargs.width, -1);
387     wargs.xSet = wargs.x > 0;
388     wargs.y = WindowFeatures::floatFeature(features, "dialogtop", screenRect.y(), screenRect.bottom() - wargs.height, -1);
389     wargs.ySet = wargs.y > 0;
390
391     if (WindowFeatures::boolFeature(features, "center", true)) {
392         if (!wargs.xSet) {
393             wargs.x = screenRect.x() + (screenRect.width() - wargs.width) / 2;
394             wargs.xSet = true;
395         }
396         if (!wargs.ySet) {
397             wargs.y = screenRect.y() + (screenRect.height() - wargs.height) / 2;
398             wargs.ySet = true;
399         }
400     }
401
402     wargs.dialog = true;
403     wargs.resizable = WindowFeatures::boolFeature(features, "resizable");
404     wargs.scrollbarsVisible = WindowFeatures::boolFeature(features, "scroll", true);
405     wargs.statusBarVisible = WindowFeatures::boolFeature(features, "status", !trusted);
406     wargs.menuBarVisible = false;
407     wargs.toolBarVisible = false;
408     wargs.locationBarVisible = false;
409     wargs.fullscreen = false;
410
411     Frame* dialogFrame = createWindow(exec, frame, url, "", wargs, dialogArgs);
412     if (!dialogFrame)
413         return jsUndefined();
414
415     JSDOMWindow* dialogWindow = toJSDOMWindow(dialogFrame);
416
417     // Get the return value either just before clearing the dialog window's
418     // properties (in JSDOMWindowBase::clear), or when on return from runModal.
419     JSValue* returnValue = 0;
420     dialogWindow->setReturnValueSlot(&returnValue);
421     dialogFrame->page()->chrome()->runModal();
422     dialogWindow->setReturnValueSlot(0);
423
424     // If we don't have a return value, get it now.
425     // Either JSDOMWindowBase::clear was not called yet, or there was no return value,
426     // and in that case, there's no harm in trying again (no benefit either).
427     if (!returnValue)
428         returnValue = dialogWindow->getDirect(Identifier(exec, "returnValue"));
429
430     return returnValue ? returnValue : jsUndefined();
431 }
432
433 JSValue *JSDOMWindowBase::getValueProperty(ExecState *exec, int token) const
434 {
435     ASSERT(impl()->frame());
436
437     switch (token) {
438     case Crypto:
439         return jsUndefined(); // FIXME: implement this
440     case Event_:
441         if (!allowsAccessFrom(exec))
442             return jsUndefined();
443         if (!d()->evt)
444             return jsUndefined();
445         return toJS(exec, d()->evt);
446     case Image:
447         if (!allowsAccessFrom(exec))
448             return jsUndefined();
449         // FIXME: this property (and the few below) probably shouldn't create a new object every
450         // time
451         return new (exec) JSImageConstructor(exec, impl()->frame()->document());
452     case Option:
453         if (!allowsAccessFrom(exec))
454             return jsUndefined();
455         return new (exec) JSHTMLOptionElementConstructor(exec, impl()->frame()->document());
456     case XMLHttpRequest:
457         if (!allowsAccessFrom(exec))
458             return jsUndefined();
459         return new (exec) JSXMLHttpRequestConstructor(exec, impl()->frame()->document());
460     case Audio:
461 #if ENABLE(VIDEO)
462         if (!allowsAccessFrom(exec))
463             return jsUndefined();
464         if (!MediaPlayer::isAvailable())
465             return jsUndefined();
466         return new (exec) JSAudioConstructor(exec, impl()->frame()->document());
467 #else
468         return jsUndefined();
469 #endif
470     case XSLTProcessor:
471 #if ENABLE(XSLT)
472         if (!allowsAccessFrom(exec))
473             return jsUndefined();
474         return new (exec) JSXSLTProcessorConstructor(exec);
475 #else
476         return jsUndefined();
477 #endif
478     }
479
480     if (!allowsAccessFrom(exec))
481         return jsUndefined();
482
483     switch (token) {
484     case Onabort:
485         return getListener(exec, abortEvent);
486     case Onblur:
487         return getListener(exec, blurEvent);
488     case Onchange:
489         return getListener(exec, changeEvent);
490     case Onclick:
491         return getListener(exec, clickEvent);
492     case Ondblclick:
493         return getListener(exec, dblclickEvent);
494     case Onerror:
495         return getListener(exec, errorEvent);
496     case Onfocus:
497         return getListener(exec, focusEvent);
498     case Onkeydown:
499         return getListener(exec, keydownEvent);
500     case Onkeypress:
501         return getListener(exec, keypressEvent);
502     case Onkeyup:
503         return getListener(exec, keyupEvent);
504     case Onload:
505         return getListener(exec, loadEvent);
506     case Onmousedown:
507         return getListener(exec, mousedownEvent);
508     case Onmousemove:
509         return getListener(exec, mousemoveEvent);
510     case Onmouseout:
511         return getListener(exec, mouseoutEvent);
512     case Onmouseover:
513         return getListener(exec, mouseoverEvent);
514     case Onmouseup:
515         return getListener(exec, mouseupEvent);
516     case OnWindowMouseWheel:
517         return getListener(exec, mousewheelEvent);
518     case Onreset:
519         return getListener(exec, resetEvent);
520     case Onresize:
521         return getListener(exec,resizeEvent);
522     case Onscroll:
523         return getListener(exec,scrollEvent);
524     case Onsearch:
525         return getListener(exec,searchEvent);
526     case Onselect:
527         return getListener(exec,selectEvent);
528     case Onsubmit:
529         return getListener(exec,submitEvent);
530     case Onbeforeunload:
531         return getListener(exec, beforeunloadEvent);
532     case Onunload:
533         return getListener(exec, unloadEvent);
534     case OnWebKitAnimationStart:
535         return getListener(exec, webkitAnimationStartEvent);
536     case OnWebKitAnimationIteration:
537         return getListener(exec, webkitAnimationIterationEvent);
538     case OnWebKitAnimationEnd:
539         return getListener(exec, webkitAnimationEndEvent);
540     case OnWebKitTransitionEnd:
541         return getListener(exec, webkitTransitionEndEvent);
542     }
543     ASSERT_NOT_REACHED();
544     return jsUndefined();
545 }
546
547 JSValue* JSDOMWindowBase::childFrameGetter(ExecState* exec, const Identifier& propertyName, const PropertySlot& slot)
548 {
549     return toJS(exec, static_cast<JSDOMWindowBase*>(slot.slotBase())->impl()->frame()->tree()->child(AtomicString(propertyName))->domWindow());
550 }
551
552 JSValue* JSDOMWindowBase::indexGetter(ExecState* exec, const Identifier&, const PropertySlot& slot)
553 {
554     return toJS(exec, static_cast<JSDOMWindowBase*>(slot.slotBase())->impl()->frame()->tree()->child(slot.index())->domWindow());
555 }
556
557 JSValue* JSDOMWindowBase::namedItemGetter(ExecState* exec, const Identifier& propertyName, const PropertySlot& slot)
558 {
559     JSDOMWindowBase* thisObj = static_cast<JSDOMWindowBase*>(slot.slotBase());
560     Document* doc = thisObj->impl()->frame()->document();
561     ASSERT(thisObj->allowsAccessFrom(exec));
562     ASSERT(doc);
563     ASSERT(doc->isHTMLDocument());
564
565     RefPtr<HTMLCollection> collection = doc->windowNamedItems(propertyName);
566     if (collection->length() == 1)
567         return toJS(exec, collection->firstItem());
568     return toJS(exec, collection.get());
569 }
570
571 bool JSDOMWindowBase::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
572 {
573     // Check for child frames by name before built-in properties to
574     // match Mozilla. This does not match IE, but some sites end up
575     // naming frames things that conflict with window properties that
576     // are in Moz but not IE. Since we have some of these, we have to do
577     // it the Moz way.
578     if (impl()->frame()->tree()->child(propertyName)) {
579         slot.setCustom(this, childFrameGetter);
580         return true;
581     }
582
583     const HashEntry* entry = JSDOMWindowBaseTable.entry(exec, propertyName);
584     if (entry) {
585         if (entry->attributes & Function) {
586             if (entry->functionValue == windowProtoFuncShowModalDialog) {
587                 if (!canShowModalDialog(impl()->frame()))
588                     return false;
589             }
590             if (allowsAccessFrom(exec))
591                 slot.setStaticEntry(this, entry, staticFunctionGetter);
592             else
593                 slot.setUndefined();
594         } else
595             slot.setStaticEntry(this, entry, staticValueGetter<JSDOMWindowBase>);
596         return true;
597     }
598
599     // Do prototype lookup early so that functions and attributes in the prototype can have
600     // precedence over the index and name getters.  
601     JSValue* proto = prototype();
602     if (proto->isObject()) {
603         if (static_cast<JSObject*>(proto)->getPropertySlot(exec, propertyName, slot)) {
604             if (!allowsAccessFrom(exec))
605                 slot.setUndefined();
606             return true;
607         }
608     }
609
610     // FIXME: Search the whole frame hierachy somewhere around here.
611     // We need to test the correct priority order.
612
613     // allow window[1] or parent[1] etc. (#56983)
614     bool ok;
615     unsigned i = propertyName.toArrayIndex(&ok);
616     if (ok && i < impl()->frame()->tree()->childCount()) {
617         slot.setCustomIndex(this, i, indexGetter);
618         return true;
619     }
620
621     if (!allowsAccessFrom(exec)) {
622         slot.setUndefined();
623         return true;
624     }
625
626     // Allow shortcuts like 'Image1' instead of document.images.Image1
627     Document* document = impl()->frame()->document();
628     if (document && document->isHTMLDocument()) {
629         AtomicStringImpl* atomicPropertyName = AtomicString::find(propertyName);
630         if (atomicPropertyName && (static_cast<HTMLDocument*>(document)->hasNamedItem(atomicPropertyName) || document->hasElementWithId(atomicPropertyName))) {
631             slot.setCustom(this, namedItemGetter);
632             return true;
633         }
634     }
635
636     return Base::getOwnPropertySlot(exec, propertyName, slot);
637 }
638
639 void JSDOMWindowBase::put(ExecState* exec, const Identifier& propertyName, JSValue* value, PutPropertySlot& slot)
640 {
641     const HashEntry* entry = JSDOMWindowBaseTable.entry(exec, propertyName);
642     if (entry) {
643         if (entry->attributes & Function) {
644             if (allowsAccessFrom(exec))
645                 Base::put(exec, propertyName, value, slot);
646             return;
647         }
648         if (entry->attributes & ReadOnly)
649             return;
650
651         switch (entry->integerValue) {
652         case Onabort:
653             if (allowsAccessFrom(exec))
654                 setListener(exec, abortEvent, value);
655             return;
656         case Onblur:
657             if (allowsAccessFrom(exec))
658                 setListener(exec, blurEvent, value);
659             return;
660         case Onchange:
661             if (allowsAccessFrom(exec))
662                 setListener(exec, changeEvent, value);
663             return;
664         case Onclick:
665             if (allowsAccessFrom(exec))
666                 setListener(exec, clickEvent, value);
667             return;
668         case Ondblclick:
669             if (allowsAccessFrom(exec))
670                 setListener(exec, dblclickEvent, value);
671             return;
672         case Onerror:
673             if (allowsAccessFrom(exec))
674                 setListener(exec, errorEvent, value);
675             return;
676         case Onfocus:
677             if (allowsAccessFrom(exec))
678                 setListener(exec, focusEvent, value);
679             return;
680         case Onkeydown:
681             if (allowsAccessFrom(exec))
682                 setListener(exec, keydownEvent, value);
683             return;
684         case Onkeypress:
685             if (allowsAccessFrom(exec))
686                 setListener(exec, keypressEvent, value);
687             return;
688         case Onkeyup:
689             if (allowsAccessFrom(exec))
690                 setListener(exec, keyupEvent, value);
691             return;
692         case Onload:
693             if (allowsAccessFrom(exec))
694                 setListener(exec, loadEvent, value);
695             return;
696         case Onmousedown:
697             if (allowsAccessFrom(exec))
698                 setListener(exec, mousedownEvent, value);
699             return;
700         case Onmousemove:
701             if (allowsAccessFrom(exec))
702                 setListener(exec, mousemoveEvent, value);
703             return;
704         case Onmouseout:
705             if (allowsAccessFrom(exec))
706                 setListener(exec, mouseoutEvent, value);
707             return;
708         case Onmouseover:
709             if (allowsAccessFrom(exec))
710                 setListener(exec, mouseoverEvent, value);
711             return;
712         case Onmouseup:
713             if (allowsAccessFrom(exec))
714                 setListener(exec, mouseupEvent, value);
715             return;
716         case OnWindowMouseWheel:
717             if (allowsAccessFrom(exec))
718                 setListener(exec, mousewheelEvent, value);
719             return;
720         case Onreset:
721             if (allowsAccessFrom(exec))
722                 setListener(exec, resetEvent, value);
723             return;
724         case Onresize:
725             if (allowsAccessFrom(exec))
726                 setListener(exec, resizeEvent, value);
727             return;
728         case Onscroll:
729             if (allowsAccessFrom(exec))
730                 setListener(exec, scrollEvent, value);
731             return;
732         case Onsearch:
733             if (allowsAccessFrom(exec))
734                 setListener(exec, searchEvent, value);
735             return;
736         case Onselect:
737             if (allowsAccessFrom(exec))
738                 setListener(exec, selectEvent, value);
739             return;
740         case Onsubmit:
741             if (allowsAccessFrom(exec))
742                 setListener(exec, submitEvent, value);
743             return;
744         case Onbeforeunload:
745             if (allowsAccessFrom(exec))
746                 setListener(exec, beforeunloadEvent, value);
747             return;
748         case Onunload:
749             if (allowsAccessFrom(exec))
750                 setListener(exec, unloadEvent, value);
751             return;
752         case OnWebKitAnimationStart:
753             if (allowsAccessFrom(exec))
754                 setListener(exec, webkitAnimationStartEvent, value);
755             return;
756         case OnWebKitAnimationIteration:
757             if (allowsAccessFrom(exec))
758                 setListener(exec, webkitAnimationIterationEvent, value);
759             return;
760         case OnWebKitAnimationEnd:
761             if (allowsAccessFrom(exec))
762                 setListener(exec, webkitAnimationEndEvent, value);
763             return;
764         case OnWebKitTransitionEnd:
765             if (allowsAccessFrom(exec))
766                 setListener(exec, webkitTransitionEndEvent, value);
767             return;
768         default:
769             break;
770         }
771     }
772
773     if (allowsAccessFrom(exec))
774         Base::put(exec, propertyName, value, slot);
775 }
776
777 String JSDOMWindowBase::crossDomainAccessErrorMessage(const JSGlobalObject* other) const
778 {
779     KURL originURL = asJSDOMWindow(other)->impl()->url();
780     KURL targetURL = impl()->frame()->document()->url();
781     if (originURL.isNull() || targetURL.isNull())
782         return String();
783
784     // FIXME: this error message should contain more specifics of why the same origin check has failed.
785     return String::format("Unsafe JavaScript attempt to access frame with URL %s from frame with URL %s. Domains, protocols and ports must match.\n",
786         targetURL.string().utf8().data(), originURL.string().utf8().data());
787 }
788
789 void JSDOMWindowBase::printErrorMessage(const String& message) const
790 {
791     if (message.isEmpty())
792         return;
793
794     Frame* frame = impl()->frame();
795     if (!frame)
796         return;
797
798     Settings* settings = frame->settings();
799     if (!settings)
800         return;
801     
802     if (settings->privateBrowsingEnabled())
803         return;
804
805     impl()->console()->addMessage(JSMessageSource, ErrorMessageLevel, message, 1, String()); // FIXME: provide a real line number and source URL.
806 }
807
808 ExecState* JSDOMWindowBase::globalExec()
809 {
810     // We need to make sure that any script execution happening in this
811     // frame does not destroy it
812     ASSERT(impl()->frame());
813     impl()->frame()->keepAlive();
814     return Base::globalExec();
815 }
816
817 bool JSDOMWindowBase::shouldInterruptScript() const
818 {
819     ASSERT(impl()->frame());
820     Page* page = impl()->frame()->page();
821
822     // See <rdar://problem/5479443>. We don't think that page can ever be NULL
823     // in this case, but if it is, we've gotten into a state where we may have
824     // hung the UI, with no way to ask the client whether to cancel execution.
825     // For now, our solution is just to cancel execution no matter what,
826     // ensuring that we never hang. We might want to consider other solutions
827     // if we discover problems with this one.
828     ASSERT(page);
829     if (!page)
830         return true;
831
832     return page->chrome()->shouldInterruptJavaScript();
833 }
834
835 void JSDOMWindowBase::setListener(ExecState* exec, const AtomicString& eventType, JSValue* func)
836 {
837     ASSERT(impl()->frame());
838     Document* doc = impl()->frame()->document();
839     if (!doc)
840         return;
841
842     doc->setHTMLWindowEventListener(eventType, findOrCreateJSEventListener(exec, func, true));
843 }
844
845 JSValue* JSDOMWindowBase::getListener(ExecState* exec, const AtomicString& eventType) const
846 {
847     ASSERT(impl()->frame());
848     Document* doc = impl()->frame()->document();
849     if (!doc)
850         return jsUndefined();
851
852     EventListener* listener = doc->getHTMLWindowEventListener(eventType);
853     if (listener && static_cast<JSEventListener*>(listener)->listenerObj())
854         return static_cast<JSEventListener*>(listener)->listenerObj();
855     return jsNull();
856 }
857
858 JSEventListener* JSDOMWindowBase::findJSEventListener(JSValue* val, bool html)
859 {
860     if (!val->isObject())
861         return 0;
862     JSObject* object = static_cast<JSObject*>(val);
863     ListenersMap& listeners = html ? d()->jsHTMLEventListeners : d()->jsEventListeners;
864     return listeners.get(object);
865 }
866
867 PassRefPtr<JSEventListener> JSDOMWindowBase::findOrCreateJSEventListener(ExecState* exec, JSValue* val, bool html)
868 {
869     JSEventListener* listener = findJSEventListener(val, html);
870     if (listener)
871         return listener;
872
873     if (!val->isObject())
874         return 0;
875     JSObject* object = static_cast<JSObject*>(val);
876
877     // Note that the JSEventListener constructor adds it to our jsEventListeners list
878     return JSEventListener::create(object, static_cast<JSDOMWindow*>(this), html).get();
879 }
880
881 JSUnprotectedEventListener* JSDOMWindowBase::findJSUnprotectedEventListener(ExecState* exec, JSValue* val, bool html)
882 {
883     if (!val->isObject())
884         return 0;
885     JSObject* object = static_cast<JSObject*>(val);
886     UnprotectedListenersMap& listeners = html ? d()->jsUnprotectedHTMLEventListeners : d()->jsUnprotectedEventListeners;
887     return listeners.get(object);
888 }
889
890 PassRefPtr<JSUnprotectedEventListener> JSDOMWindowBase::findOrCreateJSUnprotectedEventListener(ExecState* exec, JSValue* val, bool html)
891 {
892     JSUnprotectedEventListener* listener = findJSUnprotectedEventListener(exec, val, html);
893     if (listener)
894         return listener;
895     if (!val->isObject())
896         return 0;
897     JSObject* object = static_cast<JSObject*>(val);
898
899     // The JSUnprotectedEventListener constructor adds it to our jsUnprotectedEventListeners map.
900     return JSUnprotectedEventListener::create(object, static_cast<JSDOMWindow*>(this), html).get();
901 }
902
903 void JSDOMWindowBase::clearHelperObjectProperties()
904 {
905     d()->evt = 0;
906 }
907
908 void JSDOMWindowBase::clear()
909 {
910     JSLock lock(false);
911
912     if (d()->returnValueSlot && !*d()->returnValueSlot)
913         *d()->returnValueSlot = getDirect(Identifier(globalExec(), "returnValue"));
914
915     clearAllTimeouts();
916     clearHelperObjectProperties();
917 }
918
919 void JSDOMWindowBase::setCurrentEvent(Event* evt)
920 {
921     d()->evt = evt;
922 }
923
924 Event* JSDOMWindowBase::currentEvent()
925 {
926     return d()->evt;
927 }
928
929 JSObject* JSDOMWindowBase::toThisObject(ExecState*) const
930 {
931     return shell();
932 }
933
934 JSDOMWindowShell* JSDOMWindowBase::shell() const
935 {
936     return d()->shell;
937 }
938
939 JSGlobalData* JSDOMWindowBase::commonJSGlobalData()
940 {
941     static JSGlobalData* globalData = JSGlobalData::create().releaseRef();
942     return globalData;
943 }
944
945 JSValue* windowProtoFuncAToB(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
946 {
947     JSDOMWindow* window = toJSDOMWindow(thisValue);
948     if (!window)
949         return throwError(exec, TypeError);
950     if (!window->allowsAccessFrom(exec))
951         return jsUndefined();
952
953     if (args.size() < 1)
954         return throwError(exec, SyntaxError, "Not enough arguments");
955
956     JSValue* v = args.at(exec, 0);
957     if (v->isNull())
958         return jsEmptyString(exec);
959
960     UString s = v->toString(exec);
961     if (!s.is8Bit()) {
962         setDOMException(exec, INVALID_CHARACTER_ERR);
963         return jsUndefined();
964     }
965
966     Vector<char> in(s.size());
967     for (int i = 0; i < s.size(); ++i)
968         in[i] = static_cast<char>(s.data()[i]);
969     Vector<char> out;
970
971     if (!base64Decode(in, out))
972         return throwError(exec, GeneralError, "Cannot decode base64");
973
974     return jsString(exec, String(out.data(), out.size()));
975 }
976
977 JSValue* windowProtoFuncBToA(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
978 {
979     JSDOMWindow* window = toJSDOMWindow(thisValue);
980     if (!window)
981         return throwError(exec, TypeError);
982     if (!window->allowsAccessFrom(exec))
983         return jsUndefined();
984
985     if (args.size() < 1)
986         return throwError(exec, SyntaxError, "Not enough arguments");
987
988     JSValue* v = args.at(exec, 0);
989     if (v->isNull())
990         return jsEmptyString(exec);
991
992     UString s = v->toString(exec);
993     if (!s.is8Bit()) {
994         setDOMException(exec, INVALID_CHARACTER_ERR);
995         return jsUndefined();
996     }
997
998     Vector<char> in(s.size());
999     for (int i = 0; i < s.size(); ++i)
1000         in[i] = static_cast<char>(s.data()[i]);
1001     Vector<char> out;
1002
1003     base64Encode(in, out);
1004
1005     return jsString(exec, String(out.data(), out.size()));
1006 }
1007
1008 JSValue* windowProtoFuncOpen(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
1009 {
1010     JSDOMWindow* window = toJSDOMWindow(thisValue);
1011     if (!window)
1012         return throwError(exec, TypeError);
1013     if (!window->allowsAccessFrom(exec))
1014         return jsUndefined();
1015
1016     Frame* frame = window->impl()->frame();
1017     if (!frame)
1018         return jsUndefined();
1019     Frame* activeFrame = asJSDOMWindow(exec->dynamicGlobalObject())->impl()->frame();
1020     if (!activeFrame)
1021         return  jsUndefined();
1022
1023     Page* page = frame->page();
1024
1025     String urlString = valueToStringWithUndefinedOrNullCheck(exec, args.at(exec, 0));
1026     AtomicString frameName = args.at(exec, 1)->isUndefinedOrNull() ? "_blank" : AtomicString(args.at(exec, 1)->toString(exec));
1027
1028     // Because FrameTree::find() returns true for empty strings, we must check for empty framenames.
1029     // Otherwise, illegitimate window.open() calls with no name will pass right through the popup blocker.
1030     if (!allowPopUp(exec) && (frameName.isEmpty() || !frame->tree()->find(frameName)))
1031         return jsUndefined();
1032
1033     // Get the target frame for the special cases of _top and _parent.  In those
1034     // cases, we can schedule a location change right now and return early.
1035     bool topOrParent = false;
1036     if (frameName == "_top") {
1037         frame = frame->tree()->top();
1038         topOrParent = true;
1039     } else if (frameName == "_parent") {
1040         if (Frame* parent = frame->tree()->parent())
1041             frame = parent;
1042         topOrParent = true;
1043     }
1044     if (topOrParent) {
1045         if (!activeFrame->loader()->shouldAllowNavigation(frame))
1046             return jsUndefined();
1047
1048         String completedURL;
1049         if (!urlString.isEmpty())
1050             completedURL = activeFrame->document()->completeURL(urlString).string();
1051
1052         const JSDOMWindow* targetedWindow = toJSDOMWindow(frame);
1053         if (!completedURL.isEmpty() && (!protocolIs(completedURL, "javascript") || (targetedWindow && targetedWindow->allowsAccessFrom(exec)))) {
1054             bool userGesture = activeFrame->script()->processingUserGesture();
1055             frame->loader()->scheduleLocationChange(completedURL, activeFrame->loader()->outgoingReferrer(), false, userGesture);
1056         }
1057         return toJS(exec, frame->domWindow());
1058     }
1059
1060     // In the case of a named frame or a new window, we'll use the createWindow() helper
1061     WindowFeatures windowFeatures(valueToStringWithUndefinedOrNullCheck(exec, args.at(exec, 2)));
1062     FloatRect windowRect(windowFeatures.x, windowFeatures.y, windowFeatures.width, windowFeatures.height);
1063     DOMWindow::adjustWindowRect(screenAvailableRect(page ? page->mainFrame()->view() : 0), windowRect, windowRect);
1064
1065     windowFeatures.x = windowRect.x();
1066     windowFeatures.y = windowRect.y();
1067     windowFeatures.height = windowRect.height();
1068     windowFeatures.width = windowRect.width();
1069
1070     frame = createWindow(exec, frame, urlString, frameName, windowFeatures, 0);
1071
1072     if (!frame)
1073         return jsUndefined();
1074
1075     return toJS(exec, frame->domWindow()); // global object
1076 }
1077
1078 static JSValue* setTimeoutOrInterval(ExecState* exec, JSValue* thisValue, const ArgList& args, bool timeout)
1079 {
1080     JSDOMWindow* window = toJSDOMWindow(thisValue);
1081     if (!window)
1082         return throwError(exec, TypeError);
1083     if (!window->allowsAccessFrom(exec))
1084         return jsUndefined();
1085
1086     JSValue* v = args.at(exec, 0);
1087     int delay = args.at(exec, 1)->toInt32(exec);
1088     if (v->isString())
1089         return jsNumber(exec, window->installTimeout(static_cast<JSString*>(v)->value(), delay, timeout));
1090     CallData callData;
1091     if (v->getCallData(callData) == CallTypeNone)
1092         return jsUndefined();
1093     ArgList argsTail;
1094     args.getSlice(2, argsTail);
1095     return jsNumber(exec, window->installTimeout(exec, v, argsTail, delay, timeout));
1096 }
1097
1098 JSValue* windowProtoFuncClearTimeoutOrInterval(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
1099 {
1100     JSDOMWindow* window = toJSDOMWindow(thisValue);
1101     if (!window)
1102         return throwError(exec, TypeError);
1103     if (!window->allowsAccessFrom(exec))
1104         return jsUndefined();
1105
1106     window->clearTimeout(args.at(exec, 0)->toInt32(exec));
1107     return jsUndefined();
1108 }
1109
1110 JSValue* windowProtoFuncSetTimeout(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
1111 {
1112     return setTimeoutOrInterval(exec, thisValue, args, true);
1113 }
1114
1115 JSValue* windowProtoFuncSetInterval(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
1116 {
1117     return setTimeoutOrInterval(exec, thisValue, args, false);
1118 }
1119
1120 JSValue* windowProtoFuncAddEventListener(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
1121 {
1122     JSDOMWindow* window = toJSDOMWindow(thisValue);
1123     if (!window)
1124         return throwError(exec, TypeError);
1125     if (!window->allowsAccessFrom(exec))
1126         return jsUndefined();
1127
1128     Frame* frame = window->impl()->frame();
1129     if (!frame)
1130         return jsUndefined();
1131
1132     if (RefPtr<JSEventListener> listener = window->findOrCreateJSEventListener(exec, args.at(exec, 1))) {
1133         if (Document* doc = frame->document())
1134             doc->addWindowEventListener(AtomicString(args.at(exec, 0)->toString(exec)), listener.release(), args.at(exec, 2)->toBoolean(exec));
1135     }
1136
1137     return jsUndefined();
1138 }
1139
1140 JSValue* windowProtoFuncRemoveEventListener(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
1141 {
1142     JSDOMWindow* window = toJSDOMWindow(thisValue);
1143     if (!window)
1144         return throwError(exec, TypeError);
1145     if (!window->allowsAccessFrom(exec))
1146         return jsUndefined();
1147
1148     Frame* frame = window->impl()->frame();
1149     if (!frame)
1150         return jsUndefined();
1151
1152     if (JSEventListener* listener = window->findJSEventListener(args.at(exec, 1))) {
1153         if (Document* doc = frame->document())
1154             doc->removeWindowEventListener(AtomicString(args.at(exec, 0)->toString(exec)), listener, args.at(exec, 2)->toBoolean(exec));
1155     }
1156
1157     return jsUndefined();
1158 }
1159
1160 JSValue* windowProtoFuncShowModalDialog(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
1161 {
1162     JSDOMWindow* window = toJSDOMWindow(thisValue);
1163     if (!window)
1164         return throwError(exec, TypeError);
1165     if (!window->allowsAccessFrom(exec))
1166         return jsUndefined();
1167
1168     Frame* frame = window->impl()->frame();
1169     if (!frame)
1170         return jsUndefined();
1171
1172     return showModalDialog(exec, frame, valueToStringWithUndefinedOrNullCheck(exec, args.at(exec, 0)), args.at(exec, 1), valueToStringWithUndefinedOrNullCheck(exec, args.at(exec, 2)));
1173 }
1174
1175 JSValue* windowProtoFuncNotImplemented(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList&)
1176 {
1177     if (!toJSDOMWindow(thisValue))
1178         return throwError(exec, TypeError);
1179     return jsUndefined();
1180 }
1181
1182 void JSDOMWindowBase::setReturnValueSlot(JSValue** slot)
1183 {
1184     d()->returnValueSlot = slot;
1185 }
1186
1187 ////////////////////// timeouts ////////////////////////
1188
1189 void JSDOMWindowBase::clearAllTimeouts()
1190 {
1191     deleteAllValues(d()->timeouts);
1192     d()->timeouts.clear();
1193 }
1194
1195 int JSDOMWindowBase::installTimeout(ScheduledAction* a, int t, bool singleShot)
1196 {
1197     int timeoutId = ++lastUsedTimeoutId;
1198
1199     // avoid wraparound going negative on us
1200     if (timeoutId <= 0)
1201         timeoutId = 1;
1202
1203     int nestLevel = timerNestingLevel + 1;
1204     DOMWindowTimer* timer = new DOMWindowTimer(timeoutId, nestLevel, this, a);
1205     ASSERT(!d()->timeouts.get(timeoutId));
1206     d()->timeouts.set(timeoutId, timer);
1207     // Use a minimum interval of 10 ms to match other browsers, but only once we've
1208     // nested enough to notice that we're repeating.
1209     // Faster timers might be "better", but they're incompatible.
1210     double interval = max(0.001, t * 0.001);
1211     if (interval < cMinimumTimerInterval && nestLevel >= cMaxTimerNestingLevel)
1212         interval = cMinimumTimerInterval;
1213     if (singleShot)
1214         timer->startOneShot(interval);
1215     else
1216         timer->startRepeating(interval);
1217     return timeoutId;
1218 }
1219
1220 int JSDOMWindowBase::installTimeout(const UString& handler, int t, bool singleShot)
1221 {
1222     return installTimeout(new ScheduledAction(handler), t, singleShot);
1223 }
1224
1225 int JSDOMWindowBase::installTimeout(ExecState* exec, JSValue* func, const ArgList& args, int t, bool singleShot)
1226 {
1227     return installTimeout(new ScheduledAction(exec, func, args), t, singleShot);
1228 }
1229
1230 void JSDOMWindowBase::pauseTimeouts(OwnPtr<PausedTimeouts>& result)
1231 {
1232     size_t timeoutsCount = d()->timeouts.size();
1233     if (!timeoutsCount) {
1234         result.clear();
1235         return;
1236     }
1237
1238     PausedTimeout* t = new PausedTimeout[timeoutsCount];
1239     result.set(new PausedTimeouts(t, timeoutsCount));
1240
1241     JSDOMWindowBaseData::TimeoutsMap::iterator it = d()->timeouts.begin();
1242     for (size_t i = 0; i != timeoutsCount; ++i, ++it) {
1243         int timeoutId = it->first;
1244         DOMWindowTimer* timer = it->second;
1245         t[i].timeoutId = timeoutId;
1246         t[i].nestingLevel = timer->nestingLevel();
1247         t[i].nextFireInterval = timer->nextFireInterval();
1248         t[i].repeatInterval = timer->repeatInterval();
1249         t[i].action = timer->takeAction();
1250     }
1251     ASSERT(it == d()->timeouts.end());
1252
1253     deleteAllValues(d()->timeouts);
1254     d()->timeouts.clear();
1255 }
1256
1257 void JSDOMWindowBase::resumeTimeouts(OwnPtr<PausedTimeouts>& timeouts)
1258 {
1259     if (!timeouts)
1260         return;
1261     size_t count = timeouts->numTimeouts();
1262     PausedTimeout* array = timeouts->takeTimeouts();
1263     for (size_t i = 0; i != count; ++i) {
1264         int timeoutId = array[i].timeoutId;
1265         DOMWindowTimer* timer = new DOMWindowTimer(timeoutId, array[i].nestingLevel, this, array[i].action);
1266         d()->timeouts.set(timeoutId, timer);
1267         timer->start(array[i].nextFireInterval, array[i].repeatInterval);
1268     }
1269     delete [] array;
1270     timeouts.clear();
1271 }
1272
1273 void JSDOMWindowBase::clearTimeout(int timeoutId, bool delAction)
1274 {
1275     // timeout IDs have to be positive, and 0 and -1 are unsafe to
1276     // even look up since they are the empty and deleted value
1277     // respectively
1278     if (timeoutId <= 0)
1279         return;
1280
1281     delete d()->timeouts.take(timeoutId);
1282 }
1283
1284 void JSDOMWindowBase::timerFired(DOMWindowTimer* timer)
1285 {
1286     // Simple case for non-one-shot timers.
1287     if (timer->isActive()) {
1288         int timeoutId = timer->timeoutId();
1289
1290         timer->action()->execute(shell());
1291         // The DOMWindowTimer object may have been deleted or replaced during execution,
1292         // so we re-fetch it.
1293         timer = d()->timeouts.get(timeoutId);
1294         if (!timer)
1295             return;
1296
1297         if (timer->repeatInterval() && timer->repeatInterval() < cMinimumTimerInterval) {
1298             timer->setNestingLevel(timer->nestingLevel() + 1);
1299             if (timer->nestingLevel() >= cMaxTimerNestingLevel)
1300                 timer->augmentRepeatInterval(cMinimumTimerInterval - timer->repeatInterval());
1301         }
1302         return;
1303     }
1304
1305     // Delete timer before executing the action for one-shot timers.
1306     ScheduledAction* action = timer->takeAction();
1307     d()->timeouts.remove(timer->timeoutId());
1308     delete timer;
1309     action->execute(shell());
1310
1311     JSLock lock(false);
1312     delete action;
1313 }
1314
1315 void JSDOMWindowBase::disconnectFrame()
1316 {
1317     clearAllTimeouts();
1318 }
1319
1320 JSDOMWindowBase::ListenersMap& JSDOMWindowBase::jsEventListeners()
1321 {
1322     return d()->jsEventListeners;
1323 }
1324
1325 JSDOMWindowBase::ListenersMap& JSDOMWindowBase::jsHTMLEventListeners()
1326 {
1327     return d()->jsHTMLEventListeners;
1328 }
1329
1330 JSDOMWindowBase::UnprotectedListenersMap& JSDOMWindowBase::jsUnprotectedEventListeners()
1331 {
1332     return d()->jsUnprotectedEventListeners;
1333 }
1334
1335 JSDOMWindowBase::UnprotectedListenersMap& JSDOMWindowBase::jsUnprotectedHTMLEventListeners()
1336 {
1337     return d()->jsUnprotectedHTMLEventListeners;
1338 }
1339
1340 void DOMWindowTimer::fired()
1341 {
1342     timerNestingLevel = m_nestingLevel;
1343     m_object->timerFired(this);
1344     timerNestingLevel = 0;
1345 }
1346
1347 JSValue* toJS(ExecState*, DOMWindow* domWindow)
1348 {
1349     if (!domWindow)
1350         return jsNull();
1351     Frame* frame = domWindow->frame();
1352     if (!frame)
1353         return jsNull();
1354     return frame->script()->windowShell();
1355 }
1356
1357 JSDOMWindow* toJSDOMWindow(Frame* frame)
1358 {
1359     if (!frame)
1360         return 0;
1361     return frame->script()->windowShell()->window();
1362 }
1363
1364 JSDOMWindow* toJSDOMWindow(JSValue* value)
1365 {
1366     if (!value->isObject())
1367         return 0;
1368     const ClassInfo* classInfo = static_cast<JSObject*>(value)->classInfo();
1369     if (classInfo == &JSDOMWindow::s_info)
1370         return static_cast<JSDOMWindow*>(value);
1371     if (classInfo == &JSDOMWindowShell::s_info)
1372         return static_cast<JSDOMWindowShell*>(value)->window();
1373     return 0;
1374 }
1375
1376 } // namespace WebCore