a3fadb06a7096f49b513f96fc372038dd854b3da
[WebKit-https.git] / WebCore / bindings / js / kjs_window.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 "kjs_window.h"
25
26 #include "Base64.h"
27 #include "CString.h"
28 #include "Chrome.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 "JSDOMWindow.h"
42 #include "JSEvent.h"
43 #include "JSAudioConstructor.h"
44 #include "JSHTMLCollection.h"
45 #include "JSHTMLOptionElementConstructor.h"
46 #include "JSXMLHttpRequest.h"
47 #include "JSLocation.h"
48 #include "Logging.h"
49 #include "MediaPlayer.h"
50 #include "Page.h"
51 #include "PausedTimeouts.h"
52 #include "PlatformScreen.h"
53 #include "PluginInfoStore.h"
54 #include "RenderView.h"
55 #include "ScheduledAction.h"
56 #include "SecurityOrigin.h"
57 #include "Settings.h"
58 #include "WindowFeatures.h"
59 #include "htmlediting.h"
60 #include "kjs_css.h"
61 #include "kjs_events.h"
62 #include "kjs_navigator.h"
63 #include "kjs_proxy.h"
64 #include <wtf/AlwaysInline.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                DontDelete|Function 1
150   btoa                  windowProtoFuncBToA                DontDelete|Function 1
151   open                  windowProtoFuncOpen                DontDelete|Function 3
152   setTimeout            windowProtoFuncSetTimeout          DontDelete|Function 2
153   clearTimeout          windowProtoFuncClearTimeout        DontDelete|Function 1
154   setInterval           windowProtoFuncSetInterval         DontDelete|Function 2
155   clearInterval         windowProtoFuncClearTimeout        DontDelete|Function 1
156   addEventListener      windowProtoFuncAddEventListener    DontDelete|Function 3
157   removeEventListener   windowProtoFuncRemoveEventListener DontDelete|Function 3
158   showModalDialog       windowProtoFuncShowModalDialog     DontDelete|Function 1
159 # Not implemented
160   captureEvents         windowProtoFuncNotImplemented      DontDelete|Function 0
161   releaseEvents         windowProtoFuncNotImplemented      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
168   clientInformation     Window::ClientInformation   DontDelete
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   Image                 Window::Image               DontDelete
198   Option                Window::Option              DontDelete
199   XMLHttpRequest        Window::XMLHttpRequest      DontDelete
200   XSLTProcessor         Window::XSLTProcessor_      DontDelete
201 @end
202 */
203
204 Window::Window(JSObject* prototype, DOMWindow* window)
205     : JSGlobalObject(prototype)
206     , m_impl(window)
207     , d(new WindowPrivate)
208 {
209     // Window destruction is not thread-safe because of
210     // the non-thread-safe WebCore structures it references.
211     Collector::collectOnMainThreadOnly(this);
212
213     // Time in milliseconds before the script timeout handler kicks in.
214     setTimeoutTime(10000);
215 }
216
217 Window::~Window()
218 {
219     clearAllTimeouts();
220
221     // Clear any backpointers to the window
222
223     ListenersMap::iterator i2 = d->jsEventListeners.begin();
224     ListenersMap::iterator e2 = d->jsEventListeners.end();
225     for (; i2 != e2; ++i2)
226         i2->second->clearWindowObj();
227     i2 = d->jsHTMLEventListeners.begin();
228     e2 = d->jsHTMLEventListeners.end();
229     for (; i2 != e2; ++i2)
230         i2->second->clearWindowObj();
231
232     UnprotectedListenersMap::iterator i1 = d->jsUnprotectedEventListeners.begin();
233     UnprotectedListenersMap::iterator e1 = d->jsUnprotectedEventListeners.end();
234     for (; i1 != e1; ++i1)
235         i1->second->clearWindowObj();
236     i1 = d->jsUnprotectedHTMLEventListeners.begin();
237     e1 = d->jsUnprotectedHTMLEventListeners.end();
238     for (; i1 != e1; ++i1)
239         i1->second->clearWindowObj();
240 }
241
242 Window* Window::retrieveWindow(Frame* frame)
243 {
244     JSObject* o = retrieve(frame)->getObject();
245
246     ASSERT(o || !frame->scriptProxy()->isEnabled());
247     return static_cast<Window*>(o);
248 }
249
250 Window* Window::retrieveActive(ExecState* exec)
251 {
252     JSGlobalObject* globalObject = exec->dynamicGlobalObject();
253     ASSERT(globalObject);
254     return static_cast<Window*>(globalObject);
255 }
256
257 JSValue* Window::retrieve(Frame* frame)
258 {
259     ASSERT(frame);
260     if (frame->scriptProxy()->isEnabled())
261         return frame->scriptProxy()->globalObject(); // the Global object is the "window"
262
263     return jsUndefined(); // This can happen with JS disabled on the domain of that window
264 }
265
266 WebCore::JSLocation* Window::location() const
267 {
268     if (!d->loc)
269         d->loc = new JSLocation(0, impl()->frame()); // FIXME: we need to pass a prototype.
270     return d->loc;
271 }
272
273 void Window::mark()
274 {
275     Base::mark();
276     if (d->loc && !d->loc->marked())
277         d->loc->mark();
278 }
279
280 static bool allowPopUp(ExecState* exec)
281 {
282     Frame* frame = Window::retrieveActive(exec)->impl()->frame();
283
284     ASSERT(frame);
285     if (frame->scriptProxy()->processingUserGesture())
286         return true;
287     Settings* settings = frame->settings();
288     return settings && settings->JavaScriptCanOpenWindowsAutomatically();
289 }
290
291 static HashMap<String, String> parseModalDialogFeatures(const String& featuresArg)
292 {
293     HashMap<String, String> map;
294
295     Vector<String> features;
296     featuresArg.split(';', features);
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     FrameLoader* loader;
333     if (activeFrame)
334         // We need to use the active frame's loader to let FrameLoader know
335         // which principal is requesting the navigation.  Unfortunately, there
336         // might not be an activeFrame, in which case we resort to using the
337         // opener's loader.
338         //
339         // See http://bugs.webkit.org/show_bug.cgi?id=16522
340         loader = activeFrame->loader();
341     else
342         loader = openerFrame->loader();
343
344     // FIXME: It's much better for client API if a new window starts with a URL, here where we
345     // know what URL we are going to open. Unfortunately, this code passes the empty string
346     // for the URL, but there's a reason for that. Before loading we have to set up the opener,
347     // openedByDOM, and dialogArguments values. Also, to decide whether to use the URL we currently
348     // do an allowsAccessFrom call using the window we create, which can't be done before creating it.
349     // We'd have to resolve all those issues to pass the URL instead of "".
350
351     bool created;
352     Frame* newFrame = loader->createWindow(frameRequest, windowFeatures, created);
353     if (!newFrame)
354         return 0;
355
356     newFrame->loader()->setOpener(openerFrame);
357     newFrame->loader()->setOpenedByDOM();
358
359     Window* newWindow = Window::retrieveWindow(newFrame);
360
361     if (dialogArgs)
362         newWindow->putDirect("dialogArguments", dialogArgs);
363
364     if (!protocolIs(url, "javascript") || newWindow->allowsAccessFrom(exec)) {
365         KURL completedURL = url.isEmpty() ? KURL("") : activeFrame->document()->completeURL(url);
366         bool userGesture = activeFrame->scriptProxy()->processingUserGesture();
367
368         if (created) {
369             newFrame->loader()->changeLocation(completedURL, activeFrame->loader()->outgoingReferrer(), false, userGesture);
370             if (Document* oldDoc = openerFrame->document())
371                 newFrame->document()->setBaseURL(oldDoc->baseURL());
372         } else if (!url.isEmpty())
373             newFrame->loader()->scheduleLocationChange(completedURL.string(), activeFrame->loader()->outgoingReferrer(), false, userGesture);
374     }
375
376     return newFrame;
377 }
378
379 static bool canShowModalDialog(const Frame* frame)
380 {
381     if (!frame)
382         return false;
383     return frame->page()->chrome()->canRunModal();
384 }
385
386 static bool canShowModalDialogNow(const Frame* frame)
387 {
388     if (!frame)
389         return false;
390     return frame->page()->chrome()->canRunModalNow();
391 }
392
393 static JSValue* showModalDialog(ExecState* exec, Frame* frame, const String& url, JSValue* dialogArgs, const String& featureArgs)
394 {
395     if (!canShowModalDialogNow(frame) || !allowPopUp(exec))
396         return jsUndefined();
397
398     const HashMap<String, String> features = parseModalDialogFeatures(featureArgs);
399
400     const bool trusted = false;
401
402     // The following features from Microsoft's documentation are not implemented:
403     // - default font settings
404     // - width, height, left, and top specified in units other than "px"
405     // - edge (sunken or raised, default is raised)
406     // - dialogHide: trusted && boolFeature(features, "dialoghide"), makes dialog hide when you print
407     // - help: boolFeature(features, "help", true), makes help icon appear in dialog (what does it do on Windows?)
408     // - unadorned: trusted && boolFeature(features, "unadorned");
409
410     if (!frame)
411         return jsUndefined();
412
413     FloatRect screenRect = screenAvailableRect(frame->view());
414
415     WindowFeatures wargs;
416     wargs.width = WindowFeatures::floatFeature(features, "dialogwidth", 100, screenRect.width(), 620); // default here came from frame size of dialog in MacIE
417     wargs.widthSet = true;
418     wargs.height = WindowFeatures::floatFeature(features, "dialogheight", 100, screenRect.height(), 450); // default here came from frame size of dialog in MacIE
419     wargs.heightSet = true;
420
421     wargs.x = WindowFeatures::floatFeature(features, "dialogleft", screenRect.x(), screenRect.right() - wargs.width, -1);
422     wargs.xSet = wargs.x > 0;
423     wargs.y = WindowFeatures::floatFeature(features, "dialogtop", screenRect.y(), screenRect.bottom() - wargs.height, -1);
424     wargs.ySet = wargs.y > 0;
425
426     if (WindowFeatures::boolFeature(features, "center", true)) {
427         if (!wargs.xSet) {
428             wargs.x = screenRect.x() + (screenRect.width() - wargs.width) / 2;
429             wargs.xSet = true;
430         }
431         if (!wargs.ySet) {
432             wargs.y = screenRect.y() + (screenRect.height() - wargs.height) / 2;
433             wargs.ySet = true;
434         }
435     }
436
437     wargs.dialog = true;
438     wargs.resizable = WindowFeatures::boolFeature(features, "resizable");
439     wargs.scrollbarsVisible = WindowFeatures::boolFeature(features, "scroll", true);
440     wargs.statusBarVisible = WindowFeatures::boolFeature(features, "status", !trusted);
441     wargs.menuBarVisible = false;
442     wargs.toolBarVisible = false;
443     wargs.locationBarVisible = false;
444     wargs.fullscreen = false;
445
446     Frame* dialogFrame = createWindow(exec, frame, url, "", wargs, dialogArgs);
447     if (!dialogFrame)
448         return jsUndefined();
449
450     Window* dialogWindow = Window::retrieveWindow(dialogFrame);
451
452     // Get the return value either just before clearing the dialog window's
453     // properties (in Window::clear), or when on return from runModal.
454     JSValue* returnValue = 0;
455     dialogWindow->setReturnValueSlot(&returnValue);
456     dialogFrame->page()->chrome()->runModal();
457     dialogWindow->setReturnValueSlot(0);
458
459     // If we don't have a return value, get it now.
460     // Either Window::clear was not called yet, or there was no return value,
461     // and in that case, there's no harm in trying again (no benefit either).
462     if (!returnValue)
463         returnValue = dialogWindow->getDirect("returnValue");
464
465     return returnValue ? returnValue : jsUndefined();
466 }
467
468 JSValue *Window::getValueProperty(ExecState *exec, int token) const
469 {
470    ASSERT(impl()->frame());
471
472    switch (token) {
473    case Crypto:
474       return jsUndefined(); // FIXME: implement this
475     case Event_:
476       if (!allowsAccessFrom(exec))
477         return jsUndefined();
478       if (!d->m_evt)
479         return jsUndefined();
480       return toJS(exec, d->m_evt);
481     case Location_:
482       return location();
483     case Navigator_:
484     case ClientInformation: {
485       if (!allowsAccessFrom(exec))
486         return jsUndefined();
487       // Store the navigator in the object so we get the same one each time.
488       Navigator* n = new Navigator(exec->lexicalGlobalObject()->objectPrototype(), impl()->frame());
489       // FIXME: this will make the "navigator" object accessible from windows that fail
490       // the security check the first time, but not subsequent times, seems weird.
491       const_cast<Window *>(this)->putDirect("navigator", n, DontDelete);
492       const_cast<Window *>(this)->putDirect("clientInformation", n, DontDelete);
493       return n;
494     }
495     case Image:
496       if (!allowsAccessFrom(exec))
497         return jsUndefined();
498       // FIXME: this property (and the few below) probably shouldn't create a new object every
499       // time
500       return new ImageConstructorImp(exec, impl()->frame()->document());
501     case Option:
502       if (!allowsAccessFrom(exec))
503         return jsUndefined();
504       return new JSHTMLOptionElementConstructor(exec, impl()->frame()->document());
505     case XMLHttpRequest:
506       if (!allowsAccessFrom(exec))
507         return jsUndefined();
508       return new JSXMLHttpRequestConstructorImp(exec, impl()->frame()->document());
509     case Audio:
510 #if ENABLE(VIDEO)
511       if (!allowsAccessFrom(exec))
512         return jsUndefined();
513       if (!MediaPlayer::isAvailable())
514         return jsUndefined();
515       return new JSAudioConstructor(exec, impl()->frame()->document());
516 #else
517       return jsUndefined();
518 #endif
519 #if ENABLE(XSLT)
520     case XSLTProcessor_:
521       if (!allowsAccessFrom(exec))
522         return jsUndefined();
523       return new XSLTProcessorConstructorImp(exec);
524 #else
525     case XSLTProcessor_:
526       return jsUndefined();
527 #endif
528    }
529
530    if (!allowsAccessFrom(exec))
531      return jsUndefined();
532
533    switch (token) {
534    case Onabort:
535      return getListener(exec, abortEvent);
536    case Onblur:
537      return getListener(exec, blurEvent);
538    case Onchange:
539      return getListener(exec, changeEvent);
540    case Onclick:
541      return getListener(exec, clickEvent);
542    case Ondblclick:
543      return getListener(exec, dblclickEvent);
544    case Onerror:
545      return getListener(exec, errorEvent);
546    case Onfocus:
547      return getListener(exec, focusEvent);
548    case Onkeydown:
549      return getListener(exec, keydownEvent);
550    case Onkeypress:
551      return getListener(exec, keypressEvent);
552    case Onkeyup:
553      return getListener(exec, keyupEvent);
554    case Onload:
555      return getListener(exec, loadEvent);
556    case Onmousedown:
557      return getListener(exec, mousedownEvent);
558    case Onmousemove:
559      return getListener(exec, mousemoveEvent);
560    case Onmouseout:
561      return getListener(exec, mouseoutEvent);
562    case Onmouseover:
563      return getListener(exec, mouseoverEvent);
564    case Onmouseup:
565      return getListener(exec, mouseupEvent);
566    case OnWindowMouseWheel:
567      return getListener(exec, mousewheelEvent);
568    case Onreset:
569      return getListener(exec, resetEvent);
570    case Onresize:
571      return getListener(exec,resizeEvent);
572    case Onscroll:
573      return getListener(exec,scrollEvent);
574    case Onsearch:
575      return getListener(exec,searchEvent);
576    case Onselect:
577      return getListener(exec,selectEvent);
578    case Onsubmit:
579      return getListener(exec,submitEvent);
580    case Onbeforeunload:
581      return getListener(exec, beforeunloadEvent);
582    case Onunload:
583      return getListener(exec, unloadEvent);
584    }
585    ASSERT_NOT_REACHED();
586    return jsUndefined();
587 }
588
589 JSValue* Window::childFrameGetter(ExecState*, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
590 {
591     return retrieve(static_cast<Window*>(slot.slotBase())->impl()->frame()->tree()->child(AtomicString(propertyName)));
592 }
593
594 JSValue* Window::indexGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot)
595 {
596     return retrieve(static_cast<Window*>(slot.slotBase())->impl()->frame()->tree()->child(slot.index()));
597 }
598
599 JSValue* Window::namedItemGetter(ExecState* exec, JSObject* originalObject, const Identifier& propertyName, const PropertySlot& slot)
600 {
601     Window* thisObj = static_cast<Window*>(slot.slotBase());
602     Document* doc = thisObj->impl()->frame()->document();
603     ASSERT(thisObj->allowsAccessFrom(exec));
604     ASSERT(doc);
605     ASSERT(doc->isHTMLDocument());
606
607     RefPtr<WebCore::HTMLCollection> collection = doc->windowNamedItems(propertyName);
608     if (collection->length() == 1)
609         return toJS(exec, collection->firstItem());
610     return toJS(exec, collection.get());
611 }
612
613 bool Window::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
614 {
615     // Check for child frames by name before built-in properties to
616     // match Mozilla. This does not match IE, but some sites end up
617     // naming frames things that conflict with window properties that
618     // are in Moz but not IE. Since we have some of these, we have to do
619     // it the Moz way.
620     if (impl()->frame()->tree()->child(propertyName)) {
621         slot.setCustom(this, childFrameGetter);
622         return true;
623     }
624
625     const HashEntry* entry = Lookup::findEntry(&WindowTable, propertyName);
626     if (entry) {
627         if (entry->attr & Function) {
628             if (entry->value.functionValue == windowProtoFuncShowModalDialog) {
629                 if (!canShowModalDialog(impl()->frame()))
630                     return false;
631             }
632             if (allowsAccessFrom(exec))
633                 slot.setStaticEntry(this, entry, staticFunctionGetter);
634             else
635                 slot.setUndefined(this);
636         } else
637             slot.setStaticEntry(this, entry, staticValueGetter<Window>);
638         return true;
639     }
640
641     // Do prototype lookup early so that functions and attributes in the prototype can have
642     // precedence over the index and name getters.  
643     JSValue* proto = prototype();
644     if (proto->isObject()) {
645         if (static_cast<JSObject*>(proto)->getOwnPropertySlot(exec, propertyName, slot)) {
646             if (!allowsAccessFrom(exec))
647                 slot.setUndefined(this);
648             return true;
649         }
650     }
651
652     // FIXME: Search the whole frame hierachy somewhere around here.
653     // We need to test the correct priority order.
654
655     // allow window[1] or parent[1] etc. (#56983)
656     bool ok;
657     unsigned i = propertyName.toArrayIndex(&ok);
658     if (ok && i < impl()->frame()->tree()->childCount()) {
659         slot.setCustomIndex(this, i, indexGetter);
660         return true;
661     }
662
663     if (!allowsAccessFrom(exec)) {
664         slot.setUndefined(this);
665         return true;
666     }
667
668     // Allow shortcuts like 'Image1' instead of document.images.Image1
669     Document* doc = impl()->frame()->document();
670     if (doc && doc->isHTMLDocument()) {
671         AtomicString atomicPropertyName = propertyName;
672         if (static_cast<HTMLDocument*>(doc)->hasNamedItem(atomicPropertyName) || doc->getElementById(atomicPropertyName)) {
673             slot.setCustom(this, namedItemGetter);
674             return true;
675         }
676     }
677
678     return Base::getOwnPropertySlot(exec, propertyName, slot);
679 }
680
681 void Window::put(ExecState* exec, const Identifier& propertyName, JSValue* value)
682 {
683   const HashEntry* entry = Lookup::findEntry(&WindowTable, propertyName);
684   if (entry) {
685      if (entry->attr & Function) {
686        if (allowsAccessFrom(exec))
687          Base::put(exec, propertyName, value);
688        return;
689     }
690     if (entry->attr & ReadOnly)
691       return;
692
693     switch (entry->value.intValue) {
694     case Location_: {
695       if (Frame* p = Window::retrieveActive(exec)->impl()->frame()) {
696         // To avoid breaking old widgets, make "var location =" in a top-level frame create
697         // a property named "location" instead of performing a navigation (<rdar://problem/5688039>).
698         if (Settings* settings = p->settings()) {
699           if (settings->usesDashboardBackwardCompatibilityMode() && !p->tree()->parent()) {
700             if (allowsAccessFrom(exec))
701               putDirect(propertyName, value);
702             return;
703           }
704         }
705
706         if (!p->loader()->shouldAllowNavigation(impl()->frame()))
707           return;
708         String dstUrl = p->loader()->completeURL(value->toString(exec)).string();
709         if (!protocolIs(dstUrl, "javascript") || allowsAccessFrom(exec)) {
710           bool userGesture = p->scriptProxy()->processingUserGesture();
711           // We want a new history item if this JS was called via a user gesture
712           impl()->frame()->loader()->scheduleLocationChange(dstUrl, p->loader()->outgoingReferrer(), false, userGesture);
713         }
714       }
715       return;
716     }
717     case Onabort:
718       if (allowsAccessFrom(exec))
719         setListener(exec, abortEvent,value);
720       return;
721     case Onblur:
722       if (allowsAccessFrom(exec))
723         setListener(exec, blurEvent,value);
724       return;
725     case Onchange:
726       if (allowsAccessFrom(exec))
727         setListener(exec, changeEvent,value);
728       return;
729     case Onclick:
730       if (allowsAccessFrom(exec))
731         setListener(exec,clickEvent,value);
732       return;
733     case Ondblclick:
734       if (allowsAccessFrom(exec))
735         setListener(exec, dblclickEvent,value);
736       return;
737     case Onerror:
738       if (allowsAccessFrom(exec))
739         setListener(exec, errorEvent, value);
740       return;
741     case Onfocus:
742       if (allowsAccessFrom(exec))
743         setListener(exec,focusEvent,value);
744       return;
745     case Onkeydown:
746       if (allowsAccessFrom(exec))
747         setListener(exec,keydownEvent,value);
748       return;
749     case Onkeypress:
750       if (allowsAccessFrom(exec))
751         setListener(exec,keypressEvent,value);
752       return;
753     case Onkeyup:
754       if (allowsAccessFrom(exec))
755         setListener(exec,keyupEvent,value);
756       return;
757     case Onload:
758       if (allowsAccessFrom(exec))
759         setListener(exec,loadEvent,value);
760       return;
761     case Onmousedown:
762       if (allowsAccessFrom(exec))
763         setListener(exec,mousedownEvent,value);
764       return;
765     case Onmousemove:
766       if (allowsAccessFrom(exec))
767         setListener(exec,mousemoveEvent,value);
768       return;
769     case Onmouseout:
770       if (allowsAccessFrom(exec))
771         setListener(exec,mouseoutEvent,value);
772       return;
773     case Onmouseover:
774       if (allowsAccessFrom(exec))
775         setListener(exec,mouseoverEvent,value);
776       return;
777     case Onmouseup:
778       if (allowsAccessFrom(exec))
779         setListener(exec,mouseupEvent,value);
780       return;
781     case OnWindowMouseWheel:
782       if (allowsAccessFrom(exec))
783         setListener(exec, mousewheelEvent,value);
784       return;
785     case Onreset:
786       if (allowsAccessFrom(exec))
787         setListener(exec,resetEvent,value);
788       return;
789     case Onresize:
790       if (allowsAccessFrom(exec))
791         setListener(exec,resizeEvent,value);
792       return;
793     case Onscroll:
794       if (allowsAccessFrom(exec))
795         setListener(exec,scrollEvent,value);
796       return;
797     case Onsearch:
798         if (allowsAccessFrom(exec))
799             setListener(exec,searchEvent,value);
800         return;
801     case Onselect:
802       if (allowsAccessFrom(exec))
803         setListener(exec,selectEvent,value);
804       return;
805     case Onsubmit:
806       if (allowsAccessFrom(exec))
807         setListener(exec,submitEvent,value);
808       return;
809     case Onbeforeunload:
810       if (allowsAccessFrom(exec))
811         setListener(exec, beforeunloadEvent, value);
812       return;
813     case Onunload:
814       if (allowsAccessFrom(exec))
815         setListener(exec, unloadEvent, value);
816       return;
817     default:
818       break;
819     }
820   }
821   if (allowsAccessFrom(exec))
822     Base::put(exec, propertyName, value);
823 }
824
825 bool Window::allowsAccessFrom(const JSGlobalObject* other) const
826 {
827     SecurityOrigin::Reason reason;
828     if (allowsAccessFromPrivate(other, reason))
829         return true;
830     printErrorMessage(crossDomainAccessErrorMessage(other, reason));
831     return false;
832 }
833
834 bool Window::allowsAccessFrom(ExecState* exec) const
835 {
836     SecurityOrigin::Reason reason;
837     if (allowsAccessFromPrivate(exec, reason))
838         return true;
839     printErrorMessage(crossDomainAccessErrorMessage(exec->dynamicGlobalObject(), reason));
840     return false;
841 }
842     
843 bool Window::allowsAccessFromNoErrorMessage(ExecState* exec) const
844 {
845     SecurityOrigin::Reason reason;
846     return allowsAccessFromPrivate(exec, reason);
847 }
848
849 bool Window::allowsAccessFrom(ExecState* exec, String& message) const
850 {
851     SecurityOrigin::Reason reason;
852     if (allowsAccessFromPrivate(exec, reason))
853         return true;
854     message = crossDomainAccessErrorMessage(exec->dynamicGlobalObject(), reason);
855     return false;
856 }
857     
858 ALWAYS_INLINE bool Window::allowsAccessFromPrivate(const ExecState* exec, SecurityOrigin::Reason& reason) const
859 {
860     if (allowsAccessFromPrivate(exec->dynamicGlobalObject(), reason))
861         return true;
862     if (reason == SecurityOrigin::DomainSetInDOMMismatch) {
863         // If the only reason the access failed was a domainSetInDOM bit mismatch, try again against 
864         // lexical global object <rdar://problem/5698200>
865         if (allowsAccessFromPrivate(exec->lexicalGlobalObject(), reason))
866             return true;
867     }
868     return false;
869 }
870
871 ALWAYS_INLINE bool Window::allowsAccessFromPrivate(const JSGlobalObject* other, SecurityOrigin::Reason& reason) const
872 {
873     const Frame* originFrame = static_cast<const Window*>(other)->impl()->frame();
874     if (!originFrame) {
875         reason = SecurityOrigin::GenericMismatch;
876         return false;
877     }
878
879     const Frame* targetFrame = impl()->frame();
880
881     if (originFrame == targetFrame)
882         return true;
883     
884     if (!targetFrame) {
885         reason = SecurityOrigin::GenericMismatch;
886         return false;
887     }
888
889     WebCore::Document* targetDocument = targetFrame->document();
890
891     // JS may be attempting to access the "window" object, which should be valid,
892     // even if the document hasn't been constructed yet.  If the document doesn't
893     // exist yet allow JS to access the window object.
894     if (!targetDocument)
895         return true;
896
897     WebCore::Document* originDocument = originFrame->document();
898
899     const SecurityOrigin* originSecurityOrigin = originDocument->securityOrigin();
900     const SecurityOrigin* targetSecurityOrigin = targetDocument->securityOrigin();
901
902     if (originSecurityOrigin->canAccess(targetSecurityOrigin, reason))
903         return true;
904
905     return false;
906 }
907
908 String Window::crossDomainAccessErrorMessage(const JSGlobalObject* other, SecurityOrigin::Reason) const
909 {
910     const Frame* originFrame = static_cast<const Window*>(other)->impl()->frame();
911     const Frame* targetFrame = impl()->frame();
912     if (!originFrame || !targetFrame)
913         return String();
914     WebCore::Document* targetDocument = targetFrame->document();
915     WebCore::Document* originDocument = originFrame->document();
916     if (!originDocument || !targetDocument)
917         return String();
918     // FIXME: this error message should contain more specifics of why the same origin check has failed.
919     return String::format("Unsafe JavaScript attempt to access frame with URL %s from frame with URL %s. Domains, protocols and ports must match.\n",
920         targetDocument->url().string().utf8().data(), originDocument->url().string().utf8().data());
921 }
922
923 void Window::printErrorMessage(const String& message) const
924 {
925     if (message.isEmpty())
926         return;
927
928     Frame* frame = impl()->frame();
929     if (!frame)
930         return;
931
932     if (frame->settings()->privateBrowsingEnabled())
933         return;
934
935     if (Interpreter::shouldPrintExceptions())
936         printf("%s", message.utf8().data());
937
938     if (Page* page = frame->page())
939         page->chrome()->addMessageToConsole(JSMessageSource, ErrorMessageLevel, message, 1, String()); // FIXME: provide a real line number and source URL.
940 }
941
942 ExecState* Window::globalExec()
943 {
944     // We need to make sure that any script execution happening in this
945     // frame does not destroy it
946     ASSERT(impl()->frame());
947     impl()->frame()->keepAlive();
948     return Base::globalExec();
949 }
950
951 bool Window::shouldInterruptScript() const
952 {
953     ASSERT(impl()->frame());
954     Page* page = impl()->frame()->page();
955
956     // See <rdar://problem/5479443>. We don't think that page can ever be NULL
957     // in this case, but if it is, we've gotten into a state where we may have
958     // hung the UI, with no way to ask the client whether to cancel execution.
959     // For now, our solution is just to cancel execution no matter what,
960     // ensuring that we never hang. We might want to consider other solutions
961     // if we discover problems with this one.
962     ASSERT(page);
963     if (!page)
964         return true;
965
966     return page->chrome()->shouldInterruptJavaScript();
967 }
968
969 void Window::setListener(ExecState* exec, const AtomicString& eventType, JSValue* func)
970 {
971     ASSERT(impl()->frame());
972     Document* doc = impl()->frame()->document();
973     if (!doc)
974         return;
975
976     doc->setHTMLWindowEventListener(eventType, findOrCreateJSEventListener(func, true));
977 }
978
979 JSValue* Window::getListener(ExecState* exec, const AtomicString& eventType) const
980 {
981     ASSERT(impl()->frame());
982     Document* doc = impl()->frame()->document();
983     if (!doc)
984         return jsUndefined();
985
986     WebCore::EventListener* listener = doc->getHTMLWindowEventListener(eventType);
987     if (listener && static_cast<JSEventListener*>(listener)->listenerObj())
988         return static_cast<JSEventListener*>(listener)->listenerObj();
989     return jsNull();
990 }
991
992 JSEventListener* Window::findJSEventListener(JSValue* val, bool html)
993 {
994     if (!val->isObject())
995         return 0;
996     JSObject* object = static_cast<JSObject*>(val);
997     ListenersMap& listeners = html ? d->jsHTMLEventListeners : d->jsEventListeners;
998     return listeners.get(object);
999 }
1000
1001 JSEventListener* Window::findOrCreateJSEventListener(JSValue* val, bool html)
1002 {
1003     JSEventListener* listener = findJSEventListener(val, html);
1004     if (listener)
1005         return listener;
1006
1007     if (!val->isObject())
1008         return 0;
1009     JSObject* object = static_cast<JSObject*>(val);
1010
1011     // Note that the JSEventListener constructor adds it to our jsEventListeners list
1012     return new JSEventListener(object, this, html);
1013 }
1014
1015 JSUnprotectedEventListener* Window::findJSUnprotectedEventListener(JSValue* val, bool html)
1016 {
1017     if (!val->isObject())
1018         return 0;
1019     JSObject* object = static_cast<JSObject*>(val);
1020     UnprotectedListenersMap& listeners = html ? d->jsUnprotectedHTMLEventListeners : d->jsUnprotectedEventListeners;
1021     return listeners.get(object);
1022 }
1023
1024 JSUnprotectedEventListener* Window::findOrCreateJSUnprotectedEventListener(JSValue* val, bool html)
1025 {
1026     JSUnprotectedEventListener* listener = findJSUnprotectedEventListener(val, html);
1027     if (listener)
1028         return listener;
1029     if (!val->isObject())
1030         return 0;
1031     JSObject* object = static_cast<JSObject*>(val);
1032
1033     // The JSUnprotectedEventListener constructor adds it to our jsUnprotectedEventListeners map.
1034     return new JSUnprotectedEventListener(object, this, html);
1035 }
1036
1037 void Window::clearHelperObjectProperties()
1038 {
1039     d->loc = 0;
1040     d->m_evt = 0;
1041 }
1042
1043 void Window::clear()
1044 {
1045   JSLock lock;
1046
1047   if (d->m_returnValueSlot && !*d->m_returnValueSlot)
1048     *d->m_returnValueSlot = getDirect("returnValue");
1049
1050   clearAllTimeouts();
1051   clearHelperObjectProperties();
1052
1053   // Now recreate a working global object for the next URL that will use us; but only if we haven't been
1054   // disconnected yet
1055   if (Frame* frame = impl()->frame())
1056     frame->scriptProxy()->globalObject()->reset(JSDOMWindowPrototype::self());
1057
1058   // there's likely to be lots of garbage now
1059   gcController().garbageCollectSoon();
1060 }
1061
1062 void Window::setCurrentEvent(Event* evt)
1063 {
1064     d->m_evt = evt;
1065 }
1066
1067 Event* Window::currentEvent()
1068 {
1069     return d->m_evt;
1070 }
1071
1072 JSValue* windowProtoFuncAToB(ExecState* exec, JSObject* thisObj, const List& args)
1073 {
1074     if (!thisObj->inherits(&Window::info))
1075         return throwError(exec, TypeError);
1076     if (!static_cast<Window*>(thisObj)->allowsAccessFrom(exec)) 
1077         return jsUndefined();
1078
1079     if (args.size() < 1)
1080         return throwError(exec, SyntaxError, "Not enough arguments");
1081
1082     JSValue* v = args[0];
1083     if (v->isNull())
1084         return jsString();
1085
1086     UString s = v->toString(exec);
1087     if (!s.is8Bit()) {
1088         setDOMException(exec, INVALID_CHARACTER_ERR);
1089         return jsUndefined();
1090     }
1091
1092     Vector<char> in(s.size());
1093     for (int i = 0; i < s.size(); ++i)
1094         in[i] = static_cast<char>(s.data()[i].unicode());
1095     Vector<char> out;
1096
1097     if (!base64Decode(in, out))
1098         return throwError(exec, GeneralError, "Cannot decode base64");
1099
1100     return jsString(String(out.data(), out.size()));
1101 }
1102
1103 JSValue* windowProtoFuncBToA(ExecState* exec, JSObject* thisObj, const List& args)
1104 {
1105     if (!thisObj->inherits(&Window::info))
1106         return throwError(exec, TypeError);
1107     if (!static_cast<Window*>(thisObj)->allowsAccessFrom(exec)) 
1108         return jsUndefined();
1109
1110     if (args.size() < 1)
1111         return throwError(exec, SyntaxError, "Not enough arguments");
1112
1113     JSValue* v = args[0];
1114     if (v->isNull())
1115         return jsString();
1116
1117     UString s = v->toString(exec);
1118     if (!s.is8Bit()) {
1119         setDOMException(exec, INVALID_CHARACTER_ERR);
1120         return jsUndefined();
1121     }
1122
1123     Vector<char> in(s.size());
1124     for (int i = 0; i < s.size(); ++i)
1125         in[i] = static_cast<char>(s.data()[i].unicode());
1126     Vector<char> out;
1127
1128     base64Encode(in, out);
1129
1130     return jsString(String(out.data(), out.size()));
1131 }
1132
1133 JSValue* windowProtoFuncOpen(ExecState* exec, JSObject* thisObj, const List& args)
1134 {
1135     if (!thisObj->inherits(&Window::info))
1136         return throwError(exec, TypeError);
1137     Window* window = static_cast<Window*>(thisObj);
1138     if (!window->allowsAccessFrom(exec)) 
1139         return jsUndefined();
1140
1141     Frame* frame = window->impl()->frame();
1142     if (!frame)
1143         return jsUndefined();
1144     Frame* activeFrame = Window::retrieveActive(exec)->impl()->frame();
1145     if (!activeFrame)
1146         return  jsUndefined();
1147
1148     Page* page = frame->page();
1149
1150     String urlString = valueToStringWithUndefinedOrNullCheck(exec, args[0]);
1151     AtomicString frameName = args[1]->isUndefinedOrNull() ? "_blank" : AtomicString(args[1]->toString(exec));
1152
1153     // Because FrameTree::find() returns true for empty strings, we must check for empty framenames.
1154     // Otherwise, illegitimate window.open() calls with no name will pass right through the popup blocker.
1155     if (!allowPopUp(exec) && (frameName.isEmpty() || !frame->tree()->find(frameName)))
1156         return jsUndefined();
1157
1158     // Get the target frame for the special cases of _top and _parent.  In those
1159     // cases, we can schedule a location change right now and return early.
1160     bool topOrParent = false;
1161     if (frameName == "_top") {
1162         frame = frame->tree()->top();
1163         topOrParent = true;
1164     } else if (frameName == "_parent") {
1165         if (Frame* parent = frame->tree()->parent())
1166             frame = parent;
1167         topOrParent = true;
1168     }
1169     if (topOrParent) {
1170         if (!activeFrame->loader()->shouldAllowNavigation(frame))
1171             return jsUndefined();
1172
1173         String completedURL;
1174         if (!urlString.isEmpty())
1175             completedURL = activeFrame->document()->completeURL(urlString).string();
1176
1177         const Window* targetedWindow = Window::retrieveWindow(frame);
1178         if (!completedURL.isEmpty() && (!protocolIs(completedURL, "javascript") || (targetedWindow && targetedWindow->allowsAccessFrom(exec)))) {
1179             bool userGesture = activeFrame->scriptProxy()->processingUserGesture();
1180             frame->loader()->scheduleLocationChange(completedURL, activeFrame->loader()->outgoingReferrer(), false, userGesture);
1181         }
1182         return Window::retrieve(frame);
1183     }
1184
1185     // In the case of a named frame or a new window, we'll use the createWindow() helper
1186     WindowFeatures windowFeatures(valueToStringWithUndefinedOrNullCheck(exec, args[2]));
1187     FloatRect windowRect(windowFeatures.x, windowFeatures.y, windowFeatures.width, windowFeatures.height);
1188     WebCore::DOMWindow::adjustWindowRect(screenAvailableRect(page->mainFrame()->view()), windowRect, windowRect);
1189
1190     windowFeatures.x = windowRect.x();
1191     windowFeatures.y = windowRect.y();
1192     windowFeatures.height = windowRect.height();
1193     windowFeatures.width = windowRect.width();
1194
1195     frame = createWindow(exec, frame, urlString, frameName, windowFeatures, 0);
1196
1197     if (!frame)
1198         return jsUndefined();
1199
1200     return Window::retrieve(frame); // global object
1201 }
1202
1203 JSValue* windowProtoFuncSetTimeout(ExecState* exec, JSObject* thisObj, const List& args)
1204 {
1205     if (!thisObj->inherits(&Window::info))
1206         return throwError(exec, TypeError);
1207     Window* window = static_cast<Window*>(thisObj);
1208     if (!window->allowsAccessFrom(exec)) 
1209         return jsUndefined();
1210
1211     JSValue* v = args[0];
1212     if (v->isString())
1213         return jsNumber(window->installTimeout(v->toString(exec), args[1]->toInt32(exec), true /*single shot*/));
1214     if (v->isObject() && static_cast<JSObject*>(v)->implementsCall()) {
1215         List argsTail;
1216         args.getSlice(2, argsTail);
1217         return jsNumber(window->installTimeout(v, argsTail, args[1]->toInt32(exec), true /*single shot*/));
1218     }
1219
1220     return jsUndefined();
1221 }
1222
1223 JSValue* windowProtoFuncClearTimeout(ExecState* exec, JSObject* thisObj, const List& args)
1224 {
1225     // Also the implementation for window.clearInterval()
1226
1227     if (!thisObj->inherits(&Window::info))
1228         return throwError(exec, TypeError);
1229     Window* window = static_cast<Window*>(thisObj);
1230     if (!window->allowsAccessFrom(exec)) 
1231         return jsUndefined();
1232
1233     window->clearTimeout(args[0]->toInt32(exec));
1234     return jsUndefined();
1235 }
1236
1237 JSValue* windowProtoFuncSetInterval(ExecState* exec, JSObject* thisObj, const List& args)
1238 {
1239     if (!thisObj->inherits(&Window::info))
1240         return throwError(exec, TypeError);
1241     Window* window = static_cast<Window*>(thisObj);
1242     if (!window->allowsAccessFrom(exec)) 
1243         return jsUndefined();
1244
1245     if (args.size() >= 2) {
1246         JSValue* v = args[0];
1247         int delay = args[1]->toInt32(exec);
1248         if (v->isString())
1249             return jsNumber(window->installTimeout(v->toString(exec), delay, false));
1250         if (v->isObject() && static_cast<JSObject*>(v)->implementsCall()) {
1251             List argsTail;
1252             args.getSlice(2, argsTail);
1253             return jsNumber(window->installTimeout(v, argsTail, delay, false));
1254         }
1255     }
1256
1257     return jsUndefined();
1258
1259 }
1260
1261 JSValue* windowProtoFuncAddEventListener(ExecState* exec, JSObject* thisObj, const List& args)
1262 {
1263     if (!thisObj->inherits(&Window::info))
1264         return throwError(exec, TypeError);
1265     Window* window = static_cast<Window*>(thisObj);
1266     if (!window->allowsAccessFrom(exec)) 
1267         return jsUndefined();
1268     Frame* frame = window->impl()->frame();
1269     if (!frame)
1270         return jsUndefined();
1271
1272     if (JSEventListener* listener = window->findOrCreateJSEventListener(args[1])) {
1273         if (Document* doc = frame->document())
1274             doc->addWindowEventListener(AtomicString(args[0]->toString(exec)), listener, args[2]->toBoolean(exec));
1275     }
1276
1277     return jsUndefined();
1278 }
1279
1280 JSValue* windowProtoFuncRemoveEventListener(ExecState* exec, JSObject* thisObj, const List& args)
1281 {
1282     if (!thisObj->inherits(&Window::info))
1283         return throwError(exec, TypeError);
1284     Window* window = static_cast<Window*>(thisObj);
1285     if (!window->allowsAccessFrom(exec)) 
1286         return jsUndefined();
1287     Frame* frame = window->impl()->frame();
1288     if (!frame)
1289         return jsUndefined();
1290
1291     if (JSEventListener* listener = window->findJSEventListener(args[1])) {
1292         if (Document* doc = frame->document())
1293             doc->removeWindowEventListener(AtomicString(args[0]->toString(exec)), listener, args[2]->toBoolean(exec));
1294     }
1295
1296     return jsUndefined();
1297 }
1298
1299 JSValue* windowProtoFuncShowModalDialog(ExecState* exec, JSObject* thisObj, const List& args)
1300 {
1301     if (!thisObj->inherits(&Window::info))
1302         return throwError(exec, TypeError);
1303     Window* window = static_cast<Window*>(thisObj);
1304     Frame* frame = window->impl()->frame();
1305     if (!frame)
1306         return jsUndefined();
1307
1308     return showModalDialog(exec, frame, valueToStringWithUndefinedOrNullCheck(exec, args[0]), args[1], valueToStringWithUndefinedOrNullCheck(exec, args[2]));
1309 }
1310
1311 JSValue* windowProtoFuncNotImplemented(ExecState* exec, JSObject* thisObj, const List& args)
1312 {
1313     if (!thisObj->inherits(&Window::info))
1314         return throwError(exec, TypeError);
1315
1316     return jsUndefined();
1317 }
1318
1319 void Window::setReturnValueSlot(JSValue** slot)
1320 {
1321     d->m_returnValueSlot = slot;
1322 }
1323
1324 ////////////////////// timeouts ////////////////////////
1325
1326 void Window::clearAllTimeouts()
1327 {
1328     deleteAllValues(d->m_timeouts);
1329     d->m_timeouts.clear();
1330 }
1331
1332 int Window::installTimeout(WebCore::ScheduledAction* a, int t, bool singleShot)
1333 {
1334     int timeoutId = ++lastUsedTimeoutId;
1335
1336     // avoid wraparound going negative on us
1337     if (timeoutId <= 0)
1338         timeoutId = 1;
1339
1340     int nestLevel = timerNestingLevel + 1;
1341     DOMWindowTimer* timer = new DOMWindowTimer(timeoutId, nestLevel, this, a);
1342     ASSERT(!d->m_timeouts.get(timeoutId));
1343     d->m_timeouts.set(timeoutId, timer);
1344     // Use a minimum interval of 10 ms to match other browsers, but only once we've
1345     // nested enough to notice that we're repeating.
1346     // Faster timers might be "better", but they're incompatible.
1347     double interval = max(0.001, t * 0.001);
1348     if (interval < cMinimumTimerInterval && nestLevel >= cMaxTimerNestingLevel)
1349         interval = cMinimumTimerInterval;
1350     if (singleShot)
1351         timer->startOneShot(interval);
1352     else
1353         timer->startRepeating(interval);
1354     return timeoutId;
1355 }
1356
1357 int Window::installTimeout(const UString& handler, int t, bool singleShot)
1358 {
1359     return installTimeout(new WebCore::ScheduledAction(handler), t, singleShot);
1360 }
1361
1362 int Window::installTimeout(JSValue* func, const List& args, int t, bool singleShot)
1363 {
1364     return installTimeout(new WebCore::ScheduledAction(func, args), t, singleShot);
1365 }
1366
1367 WebCore::PausedTimeouts* Window::pauseTimeouts()
1368 {
1369     size_t count = d->m_timeouts.size();
1370     if (count == 0)
1371         return 0;
1372
1373     PausedTimeout* t = new PausedTimeout [count];
1374     PausedTimeouts* result = new PausedTimeouts(t, count);
1375
1376     WindowPrivate::TimeoutsMap::iterator it = d->m_timeouts.begin();
1377     for (size_t i = 0; i != count; ++i, ++it) {
1378         int timeoutId = it->first;
1379         DOMWindowTimer* timer = it->second;
1380         t[i].timeoutId = timeoutId;
1381         t[i].nestingLevel = timer->nestingLevel();
1382         t[i].nextFireInterval = timer->nextFireInterval();
1383         t[i].repeatInterval = timer->repeatInterval();
1384         t[i].action = timer->takeAction();
1385     }
1386     ASSERT(it == d->m_timeouts.end());
1387
1388     deleteAllValues(d->m_timeouts);
1389     d->m_timeouts.clear();
1390
1391     return result;
1392 }
1393
1394 void Window::resumeTimeouts(PausedTimeouts* timeouts)
1395 {
1396     if (!timeouts)
1397         return;
1398     size_t count = timeouts->numTimeouts();
1399     PausedTimeout* array = timeouts->takeTimeouts();
1400     for (size_t i = 0; i != count; ++i) {
1401         int timeoutId = array[i].timeoutId;
1402         DOMWindowTimer* timer = new DOMWindowTimer(timeoutId, array[i].nestingLevel, this, array[i].action);
1403         d->m_timeouts.set(timeoutId, timer);
1404         timer->start(array[i].nextFireInterval, array[i].repeatInterval);
1405     }
1406     delete [] array;
1407 }
1408
1409 void Window::clearTimeout(int timeoutId, bool delAction)
1410 {
1411     // timeout IDs have to be positive, and 0 and -1 are unsafe to
1412     // even look up since they are the empty and deleted value
1413     // respectively
1414     if (timeoutId <= 0)
1415         return;
1416
1417     delete d->m_timeouts.take(timeoutId);
1418 }
1419
1420 void Window::timerFired(DOMWindowTimer* timer)
1421 {
1422     // Simple case for non-one-shot timers.
1423     if (timer->isActive()) {
1424         int timeoutId = timer->timeoutId();
1425
1426         timer->action()->execute(this);
1427         // The DOMWindowTimer object may have been deleted or replaced during execution,
1428         // so we re-fetch it.
1429         timer = d->m_timeouts.get(timeoutId);
1430         if (!timer)
1431             return;
1432
1433         if (timer->repeatInterval() && timer->repeatInterval() < cMinimumTimerInterval) {
1434             timer->setNestingLevel(timer->nestingLevel() + 1);
1435             if (timer->nestingLevel() >= cMaxTimerNestingLevel)
1436                 timer->augmentRepeatInterval(cMinimumTimerInterval - timer->repeatInterval());
1437         }
1438         return;
1439     }
1440
1441     // Delete timer before executing the action for one-shot timers.
1442     WebCore::ScheduledAction* action = timer->takeAction();
1443     d->m_timeouts.remove(timer->timeoutId());
1444     delete timer;
1445     action->execute(this);
1446
1447     JSLock lock;
1448     delete action;
1449 }
1450
1451 void Window::disconnectFrame()
1452 {
1453     clearAllTimeouts();
1454     if (d->loc)
1455         d->loc->m_frame = 0;
1456 }
1457
1458 Window::ListenersMap& Window::jsEventListeners()
1459 {
1460     return d->jsEventListeners;
1461 }
1462
1463 Window::ListenersMap& Window::jsHTMLEventListeners()
1464 {
1465     return d->jsHTMLEventListeners;
1466 }
1467
1468 Window::UnprotectedListenersMap& Window::jsUnprotectedEventListeners()
1469 {
1470     return d->jsUnprotectedEventListeners;
1471 }
1472
1473 Window::UnprotectedListenersMap& Window::jsUnprotectedHTMLEventListeners()
1474 {
1475     return d->jsUnprotectedHTMLEventListeners;
1476 }
1477
1478 /////////////////////////////////////////////////////////////////////////////
1479
1480 void DOMWindowTimer::fired()
1481 {
1482     timerNestingLevel = m_nestingLevel;
1483     m_object->timerFired(this);
1484     timerNestingLevel = 0;
1485 }
1486
1487 } // namespace KJS
1488
1489 using namespace KJS;
1490
1491 namespace WebCore {
1492
1493 JSValue* toJS(ExecState*, DOMWindow* domWindow)
1494 {
1495     if (!domWindow)
1496         return jsNull();
1497     Frame* frame = domWindow->frame();
1498     if (!frame)
1499         return jsNull();
1500     return Window::retrieve(frame);
1501 }
1502
1503 } // namespace WebCore