16677d970b72316da8af424368afe2b5b39a3b6c
[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         default:
753             break;
754         }
755     }
756
757     if (allowsAccessFrom(exec))
758         Base::put(exec, propertyName, value, slot);
759 }
760
761 String JSDOMWindowBase::crossDomainAccessErrorMessage(const JSGlobalObject* other) const
762 {
763     KURL originURL = asJSDOMWindow(other)->impl()->url();
764     KURL targetURL = impl()->frame()->document()->url();
765     if (originURL.isNull() || targetURL.isNull())
766         return String();
767
768     // FIXME: this error message should contain more specifics of why the same origin check has failed.
769     return String::format("Unsafe JavaScript attempt to access frame with URL %s from frame with URL %s. Domains, protocols and ports must match.\n",
770         targetURL.string().utf8().data(), originURL.string().utf8().data());
771 }
772
773 void JSDOMWindowBase::printErrorMessage(const String& message) const
774 {
775     if (message.isEmpty())
776         return;
777
778     Frame* frame = impl()->frame();
779     if (!frame)
780         return;
781
782     Settings* settings = frame->settings();
783     if (!settings)
784         return;
785     
786     if (settings->privateBrowsingEnabled())
787         return;
788
789     impl()->console()->addMessage(JSMessageSource, ErrorMessageLevel, message, 1, String()); // FIXME: provide a real line number and source URL.
790 }
791
792 ExecState* JSDOMWindowBase::globalExec()
793 {
794     // We need to make sure that any script execution happening in this
795     // frame does not destroy it
796     ASSERT(impl()->frame());
797     impl()->frame()->keepAlive();
798     return Base::globalExec();
799 }
800
801 bool JSDOMWindowBase::shouldInterruptScript() const
802 {
803     ASSERT(impl()->frame());
804     Page* page = impl()->frame()->page();
805
806     // See <rdar://problem/5479443>. We don't think that page can ever be NULL
807     // in this case, but if it is, we've gotten into a state where we may have
808     // hung the UI, with no way to ask the client whether to cancel execution.
809     // For now, our solution is just to cancel execution no matter what,
810     // ensuring that we never hang. We might want to consider other solutions
811     // if we discover problems with this one.
812     ASSERT(page);
813     if (!page)
814         return true;
815
816     return page->chrome()->shouldInterruptJavaScript();
817 }
818
819 void JSDOMWindowBase::setListener(ExecState* exec, const AtomicString& eventType, JSValue* func)
820 {
821     ASSERT(impl()->frame());
822     Document* doc = impl()->frame()->document();
823     if (!doc)
824         return;
825
826     doc->setHTMLWindowEventListener(eventType, findOrCreateJSEventListener(exec, func, true));
827 }
828
829 JSValue* JSDOMWindowBase::getListener(ExecState* exec, const AtomicString& eventType) const
830 {
831     ASSERT(impl()->frame());
832     Document* doc = impl()->frame()->document();
833     if (!doc)
834         return jsUndefined();
835
836     EventListener* listener = doc->getHTMLWindowEventListener(eventType);
837     if (listener && static_cast<JSEventListener*>(listener)->listenerObj())
838         return static_cast<JSEventListener*>(listener)->listenerObj();
839     return jsNull();
840 }
841
842 JSEventListener* JSDOMWindowBase::findJSEventListener(JSValue* val, bool html)
843 {
844     if (!val->isObject())
845         return 0;
846     JSObject* object = static_cast<JSObject*>(val);
847     ListenersMap& listeners = html ? d()->jsHTMLEventListeners : d()->jsEventListeners;
848     return listeners.get(object);
849 }
850
851 PassRefPtr<JSEventListener> JSDOMWindowBase::findOrCreateJSEventListener(ExecState* exec, JSValue* val, bool html)
852 {
853     JSEventListener* listener = findJSEventListener(val, html);
854     if (listener)
855         return listener;
856
857     if (!val->isObject())
858         return 0;
859     JSObject* object = static_cast<JSObject*>(val);
860
861     // Note that the JSEventListener constructor adds it to our jsEventListeners list
862     return JSEventListener::create(object, static_cast<JSDOMWindow*>(this), html).get();
863 }
864
865 JSUnprotectedEventListener* JSDOMWindowBase::findJSUnprotectedEventListener(ExecState* exec, JSValue* val, bool html)
866 {
867     if (!val->isObject())
868         return 0;
869     JSObject* object = static_cast<JSObject*>(val);
870     UnprotectedListenersMap& listeners = html ? d()->jsUnprotectedHTMLEventListeners : d()->jsUnprotectedEventListeners;
871     return listeners.get(object);
872 }
873
874 PassRefPtr<JSUnprotectedEventListener> JSDOMWindowBase::findOrCreateJSUnprotectedEventListener(ExecState* exec, JSValue* val, bool html)
875 {
876     JSUnprotectedEventListener* listener = findJSUnprotectedEventListener(exec, val, html);
877     if (listener)
878         return listener;
879     if (!val->isObject())
880         return 0;
881     JSObject* object = static_cast<JSObject*>(val);
882
883     // The JSUnprotectedEventListener constructor adds it to our jsUnprotectedEventListeners map.
884     return JSUnprotectedEventListener::create(object, static_cast<JSDOMWindow*>(this), html).get();
885 }
886
887 void JSDOMWindowBase::clearHelperObjectProperties()
888 {
889     d()->evt = 0;
890 }
891
892 void JSDOMWindowBase::clear()
893 {
894     JSLock lock(false);
895
896     if (d()->returnValueSlot && !*d()->returnValueSlot)
897         *d()->returnValueSlot = getDirect(Identifier(globalExec(), "returnValue"));
898
899     clearAllTimeouts();
900     clearHelperObjectProperties();
901 }
902
903 void JSDOMWindowBase::setCurrentEvent(Event* evt)
904 {
905     d()->evt = evt;
906 }
907
908 Event* JSDOMWindowBase::currentEvent()
909 {
910     return d()->evt;
911 }
912
913 JSObject* JSDOMWindowBase::toThisObject(ExecState*) const
914 {
915     return shell();
916 }
917
918 JSDOMWindowShell* JSDOMWindowBase::shell() const
919 {
920     return d()->shell;
921 }
922
923 JSGlobalData* JSDOMWindowBase::commonJSGlobalData()
924 {
925     static JSGlobalData* globalData = JSGlobalData::create().releaseRef();
926     return globalData;
927 }
928
929 JSValue* windowProtoFuncAToB(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
930 {
931     JSDOMWindow* window = toJSDOMWindow(thisValue);
932     if (!window)
933         return throwError(exec, TypeError);
934     if (!window->allowsAccessFrom(exec))
935         return jsUndefined();
936
937     if (args.size() < 1)
938         return throwError(exec, SyntaxError, "Not enough arguments");
939
940     JSValue* v = args.at(exec, 0);
941     if (v->isNull())
942         return jsEmptyString(exec);
943
944     UString s = v->toString(exec);
945     if (!s.is8Bit()) {
946         setDOMException(exec, INVALID_CHARACTER_ERR);
947         return jsUndefined();
948     }
949
950     Vector<char> in(s.size());
951     for (int i = 0; i < s.size(); ++i)
952         in[i] = static_cast<char>(s.data()[i]);
953     Vector<char> out;
954
955     if (!base64Decode(in, out))
956         return throwError(exec, GeneralError, "Cannot decode base64");
957
958     return jsString(exec, String(out.data(), out.size()));
959 }
960
961 JSValue* windowProtoFuncBToA(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
962 {
963     JSDOMWindow* window = toJSDOMWindow(thisValue);
964     if (!window)
965         return throwError(exec, TypeError);
966     if (!window->allowsAccessFrom(exec))
967         return jsUndefined();
968
969     if (args.size() < 1)
970         return throwError(exec, SyntaxError, "Not enough arguments");
971
972     JSValue* v = args.at(exec, 0);
973     if (v->isNull())
974         return jsEmptyString(exec);
975
976     UString s = v->toString(exec);
977     if (!s.is8Bit()) {
978         setDOMException(exec, INVALID_CHARACTER_ERR);
979         return jsUndefined();
980     }
981
982     Vector<char> in(s.size());
983     for (int i = 0; i < s.size(); ++i)
984         in[i] = static_cast<char>(s.data()[i]);
985     Vector<char> out;
986
987     base64Encode(in, out);
988
989     return jsString(exec, String(out.data(), out.size()));
990 }
991
992 JSValue* windowProtoFuncOpen(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
993 {
994     JSDOMWindow* window = toJSDOMWindow(thisValue);
995     if (!window)
996         return throwError(exec, TypeError);
997     if (!window->allowsAccessFrom(exec))
998         return jsUndefined();
999
1000     Frame* frame = window->impl()->frame();
1001     if (!frame)
1002         return jsUndefined();
1003     Frame* activeFrame = asJSDOMWindow(exec->dynamicGlobalObject())->impl()->frame();
1004     if (!activeFrame)
1005         return  jsUndefined();
1006
1007     Page* page = frame->page();
1008
1009     String urlString = valueToStringWithUndefinedOrNullCheck(exec, args.at(exec, 0));
1010     AtomicString frameName = args.at(exec, 1)->isUndefinedOrNull() ? "_blank" : AtomicString(args.at(exec, 1)->toString(exec));
1011
1012     // Because FrameTree::find() returns true for empty strings, we must check for empty framenames.
1013     // Otherwise, illegitimate window.open() calls with no name will pass right through the popup blocker.
1014     if (!allowPopUp(exec) && (frameName.isEmpty() || !frame->tree()->find(frameName)))
1015         return jsUndefined();
1016
1017     // Get the target frame for the special cases of _top and _parent.  In those
1018     // cases, we can schedule a location change right now and return early.
1019     bool topOrParent = false;
1020     if (frameName == "_top") {
1021         frame = frame->tree()->top();
1022         topOrParent = true;
1023     } else if (frameName == "_parent") {
1024         if (Frame* parent = frame->tree()->parent())
1025             frame = parent;
1026         topOrParent = true;
1027     }
1028     if (topOrParent) {
1029         if (!activeFrame->loader()->shouldAllowNavigation(frame))
1030             return jsUndefined();
1031
1032         String completedURL;
1033         if (!urlString.isEmpty())
1034             completedURL = activeFrame->document()->completeURL(urlString).string();
1035
1036         const JSDOMWindow* targetedWindow = toJSDOMWindow(frame);
1037         if (!completedURL.isEmpty() && (!protocolIs(completedURL, "javascript") || (targetedWindow && targetedWindow->allowsAccessFrom(exec)))) {
1038             bool userGesture = activeFrame->script()->processingUserGesture();
1039             frame->loader()->scheduleLocationChange(completedURL, activeFrame->loader()->outgoingReferrer(), false, userGesture);
1040         }
1041         return toJS(exec, frame->domWindow());
1042     }
1043
1044     // In the case of a named frame or a new window, we'll use the createWindow() helper
1045     WindowFeatures windowFeatures(valueToStringWithUndefinedOrNullCheck(exec, args.at(exec, 2)));
1046     FloatRect windowRect(windowFeatures.x, windowFeatures.y, windowFeatures.width, windowFeatures.height);
1047     DOMWindow::adjustWindowRect(screenAvailableRect(page ? page->mainFrame()->view() : 0), windowRect, windowRect);
1048
1049     windowFeatures.x = windowRect.x();
1050     windowFeatures.y = windowRect.y();
1051     windowFeatures.height = windowRect.height();
1052     windowFeatures.width = windowRect.width();
1053
1054     frame = createWindow(exec, frame, urlString, frameName, windowFeatures, 0);
1055
1056     if (!frame)
1057         return jsUndefined();
1058
1059     return toJS(exec, frame->domWindow()); // global object
1060 }
1061
1062 static JSValue* setTimeoutOrInterval(ExecState* exec, JSValue* thisValue, const ArgList& args, bool timeout)
1063 {
1064     JSDOMWindow* window = toJSDOMWindow(thisValue);
1065     if (!window)
1066         return throwError(exec, TypeError);
1067     if (!window->allowsAccessFrom(exec))
1068         return jsUndefined();
1069
1070     JSValue* v = args.at(exec, 0);
1071     int delay = args.at(exec, 1)->toInt32(exec);
1072     if (v->isString())
1073         return jsNumber(exec, window->installTimeout(static_cast<JSString*>(v)->value(), delay, timeout));
1074     CallData callData;
1075     if (v->getCallData(callData) == CallTypeNone)
1076         return jsUndefined();
1077     ArgList argsTail;
1078     args.getSlice(2, argsTail);
1079     return jsNumber(exec, window->installTimeout(exec, v, argsTail, delay, timeout));
1080 }
1081
1082 JSValue* windowProtoFuncClearTimeoutOrInterval(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
1083 {
1084     JSDOMWindow* window = toJSDOMWindow(thisValue);
1085     if (!window)
1086         return throwError(exec, TypeError);
1087     if (!window->allowsAccessFrom(exec))
1088         return jsUndefined();
1089
1090     window->clearTimeout(args.at(exec, 0)->toInt32(exec));
1091     return jsUndefined();
1092 }
1093
1094 JSValue* windowProtoFuncSetTimeout(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
1095 {
1096     return setTimeoutOrInterval(exec, thisValue, args, true);
1097 }
1098
1099 JSValue* windowProtoFuncSetInterval(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
1100 {
1101     return setTimeoutOrInterval(exec, thisValue, args, false);
1102 }
1103
1104 JSValue* windowProtoFuncAddEventListener(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
1105 {
1106     JSDOMWindow* window = toJSDOMWindow(thisValue);
1107     if (!window)
1108         return throwError(exec, TypeError);
1109     if (!window->allowsAccessFrom(exec))
1110         return jsUndefined();
1111
1112     Frame* frame = window->impl()->frame();
1113     if (!frame)
1114         return jsUndefined();
1115
1116     if (RefPtr<JSEventListener> listener = window->findOrCreateJSEventListener(exec, args.at(exec, 1))) {
1117         if (Document* doc = frame->document())
1118             doc->addWindowEventListener(AtomicString(args.at(exec, 0)->toString(exec)), listener.release(), args.at(exec, 2)->toBoolean(exec));
1119     }
1120
1121     return jsUndefined();
1122 }
1123
1124 JSValue* windowProtoFuncRemoveEventListener(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
1125 {
1126     JSDOMWindow* window = toJSDOMWindow(thisValue);
1127     if (!window)
1128         return throwError(exec, TypeError);
1129     if (!window->allowsAccessFrom(exec))
1130         return jsUndefined();
1131
1132     Frame* frame = window->impl()->frame();
1133     if (!frame)
1134         return jsUndefined();
1135
1136     if (JSEventListener* listener = window->findJSEventListener(args.at(exec, 1))) {
1137         if (Document* doc = frame->document())
1138             doc->removeWindowEventListener(AtomicString(args.at(exec, 0)->toString(exec)), listener, args.at(exec, 2)->toBoolean(exec));
1139     }
1140
1141     return jsUndefined();
1142 }
1143
1144 JSValue* windowProtoFuncShowModalDialog(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
1145 {
1146     JSDOMWindow* window = toJSDOMWindow(thisValue);
1147     if (!window)
1148         return throwError(exec, TypeError);
1149     if (!window->allowsAccessFrom(exec))
1150         return jsUndefined();
1151
1152     Frame* frame = window->impl()->frame();
1153     if (!frame)
1154         return jsUndefined();
1155
1156     return showModalDialog(exec, frame, valueToStringWithUndefinedOrNullCheck(exec, args.at(exec, 0)), args.at(exec, 1), valueToStringWithUndefinedOrNullCheck(exec, args.at(exec, 2)));
1157 }
1158
1159 JSValue* windowProtoFuncNotImplemented(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList&)
1160 {
1161     if (!toJSDOMWindow(thisValue))
1162         return throwError(exec, TypeError);
1163     return jsUndefined();
1164 }
1165
1166 void JSDOMWindowBase::setReturnValueSlot(JSValue** slot)
1167 {
1168     d()->returnValueSlot = slot;
1169 }
1170
1171 ////////////////////// timeouts ////////////////////////
1172
1173 void JSDOMWindowBase::clearAllTimeouts()
1174 {
1175     deleteAllValues(d()->timeouts);
1176     d()->timeouts.clear();
1177 }
1178
1179 int JSDOMWindowBase::installTimeout(ScheduledAction* a, int t, bool singleShot)
1180 {
1181     int timeoutId = ++lastUsedTimeoutId;
1182
1183     // avoid wraparound going negative on us
1184     if (timeoutId <= 0)
1185         timeoutId = 1;
1186
1187     int nestLevel = timerNestingLevel + 1;
1188     DOMWindowTimer* timer = new DOMWindowTimer(timeoutId, nestLevel, this, a);
1189     ASSERT(!d()->timeouts.get(timeoutId));
1190     d()->timeouts.set(timeoutId, timer);
1191     // Use a minimum interval of 10 ms to match other browsers, but only once we've
1192     // nested enough to notice that we're repeating.
1193     // Faster timers might be "better", but they're incompatible.
1194     double interval = max(0.001, t * 0.001);
1195     if (interval < cMinimumTimerInterval && nestLevel >= cMaxTimerNestingLevel)
1196         interval = cMinimumTimerInterval;
1197     if (singleShot)
1198         timer->startOneShot(interval);
1199     else
1200         timer->startRepeating(interval);
1201     return timeoutId;
1202 }
1203
1204 int JSDOMWindowBase::installTimeout(const UString& handler, int t, bool singleShot)
1205 {
1206     return installTimeout(new ScheduledAction(handler), t, singleShot);
1207 }
1208
1209 int JSDOMWindowBase::installTimeout(ExecState* exec, JSValue* func, const ArgList& args, int t, bool singleShot)
1210 {
1211     return installTimeout(new ScheduledAction(exec, func, args), t, singleShot);
1212 }
1213
1214 void JSDOMWindowBase::pauseTimeouts(OwnPtr<PausedTimeouts>& result)
1215 {
1216     size_t timeoutsCount = d()->timeouts.size();
1217     if (!timeoutsCount) {
1218         result.clear();
1219         return;
1220     }
1221
1222     PausedTimeout* t = new PausedTimeout[timeoutsCount];
1223     result.set(new PausedTimeouts(t, timeoutsCount));
1224
1225     JSDOMWindowBaseData::TimeoutsMap::iterator it = d()->timeouts.begin();
1226     for (size_t i = 0; i != timeoutsCount; ++i, ++it) {
1227         int timeoutId = it->first;
1228         DOMWindowTimer* timer = it->second;
1229         t[i].timeoutId = timeoutId;
1230         t[i].nestingLevel = timer->nestingLevel();
1231         t[i].nextFireInterval = timer->nextFireInterval();
1232         t[i].repeatInterval = timer->repeatInterval();
1233         t[i].action = timer->takeAction();
1234     }
1235     ASSERT(it == d()->timeouts.end());
1236
1237     deleteAllValues(d()->timeouts);
1238     d()->timeouts.clear();
1239 }
1240
1241 void JSDOMWindowBase::resumeTimeouts(OwnPtr<PausedTimeouts>& timeouts)
1242 {
1243     if (!timeouts)
1244         return;
1245     size_t count = timeouts->numTimeouts();
1246     PausedTimeout* array = timeouts->takeTimeouts();
1247     for (size_t i = 0; i != count; ++i) {
1248         int timeoutId = array[i].timeoutId;
1249         DOMWindowTimer* timer = new DOMWindowTimer(timeoutId, array[i].nestingLevel, this, array[i].action);
1250         d()->timeouts.set(timeoutId, timer);
1251         timer->start(array[i].nextFireInterval, array[i].repeatInterval);
1252     }
1253     delete [] array;
1254     timeouts.clear();
1255 }
1256
1257 void JSDOMWindowBase::clearTimeout(int timeoutId, bool delAction)
1258 {
1259     // timeout IDs have to be positive, and 0 and -1 are unsafe to
1260     // even look up since they are the empty and deleted value
1261     // respectively
1262     if (timeoutId <= 0)
1263         return;
1264
1265     delete d()->timeouts.take(timeoutId);
1266 }
1267
1268 void JSDOMWindowBase::timerFired(DOMWindowTimer* timer)
1269 {
1270     // Simple case for non-one-shot timers.
1271     if (timer->isActive()) {
1272         int timeoutId = timer->timeoutId();
1273
1274         timer->action()->execute(shell());
1275         // The DOMWindowTimer object may have been deleted or replaced during execution,
1276         // so we re-fetch it.
1277         timer = d()->timeouts.get(timeoutId);
1278         if (!timer)
1279             return;
1280
1281         if (timer->repeatInterval() && timer->repeatInterval() < cMinimumTimerInterval) {
1282             timer->setNestingLevel(timer->nestingLevel() + 1);
1283             if (timer->nestingLevel() >= cMaxTimerNestingLevel)
1284                 timer->augmentRepeatInterval(cMinimumTimerInterval - timer->repeatInterval());
1285         }
1286         return;
1287     }
1288
1289     // Delete timer before executing the action for one-shot timers.
1290     ScheduledAction* action = timer->takeAction();
1291     d()->timeouts.remove(timer->timeoutId());
1292     delete timer;
1293     action->execute(shell());
1294
1295     JSLock lock(false);
1296     delete action;
1297 }
1298
1299 void JSDOMWindowBase::disconnectFrame()
1300 {
1301     clearAllTimeouts();
1302 }
1303
1304 JSDOMWindowBase::ListenersMap& JSDOMWindowBase::jsEventListeners()
1305 {
1306     return d()->jsEventListeners;
1307 }
1308
1309 JSDOMWindowBase::ListenersMap& JSDOMWindowBase::jsHTMLEventListeners()
1310 {
1311     return d()->jsHTMLEventListeners;
1312 }
1313
1314 JSDOMWindowBase::UnprotectedListenersMap& JSDOMWindowBase::jsUnprotectedEventListeners()
1315 {
1316     return d()->jsUnprotectedEventListeners;
1317 }
1318
1319 JSDOMWindowBase::UnprotectedListenersMap& JSDOMWindowBase::jsUnprotectedHTMLEventListeners()
1320 {
1321     return d()->jsUnprotectedHTMLEventListeners;
1322 }
1323
1324 void DOMWindowTimer::fired()
1325 {
1326     timerNestingLevel = m_nestingLevel;
1327     m_object->timerFired(this);
1328     timerNestingLevel = 0;
1329 }
1330
1331 JSValue* toJS(ExecState*, DOMWindow* domWindow)
1332 {
1333     if (!domWindow)
1334         return jsNull();
1335     Frame* frame = domWindow->frame();
1336     if (!frame)
1337         return jsNull();
1338     return frame->script()->windowShell();
1339 }
1340
1341 JSDOMWindow* toJSDOMWindow(Frame* frame)
1342 {
1343     if (!frame)
1344         return 0;
1345     return frame->script()->windowShell()->window();
1346 }
1347
1348 JSDOMWindow* toJSDOMWindow(JSValue* value)
1349 {
1350     if (!value->isObject())
1351         return 0;
1352     const ClassInfo* classInfo = static_cast<JSObject*>(value)->classInfo();
1353     if (classInfo == &JSDOMWindow::s_info)
1354         return static_cast<JSDOMWindow*>(value);
1355     if (classInfo == &JSDOMWindowShell::s_info)
1356         return static_cast<JSDOMWindowShell*>(value)->window();
1357     return 0;
1358 }
1359
1360 } // namespace WebCore