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