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