38e59a4d0891ed9abb284b19b7a741e9f0436f32
[WebKit-https.git] / WebCore / bindings / js / kjs_window.cpp
1 // -*- c-basic-offset: 2 -*-
2 /*
3  *  This file is part of the KDE libraries
4  *  Copyright (C) 2000 Harri Porten (porten@kde.org)
5  *  Copyright (C) 2006 Jon Shier (jshier@iastate.edu)
6  *  Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc.
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include "config.h"
24 #include "kjs_window.h"
25
26 #include "CString.h"
27 #include "DOMWindow.h"
28 #include "Element.h"
29 #include "EventListener.h"
30 #include "EventNames.h"
31 #include "FloatRect.h"
32 #include "Frame.h"
33 #include "FrameTree.h"
34 #include "HTMLDocument.h"
35 #include "JSCSSRule.h"
36 #include "JSCSSValue.h"
37 #include "JSDOMWindow.h"
38 #include "JSEvent.h"
39 #include "JSHTMLOptionElementConstructor.h"
40 #include "JSMutationEvent.h"
41 #include "JSNode.h"
42 #include "JSNodeFilter.h"
43 #include "JSRange.h"
44 #include "JSXMLHttpRequest.h"
45 #include "Settings.h"
46 #include "Logging.h"
47 #include "Page.h"
48 #include "PlugInInfoStore.h"
49 #include "RenderView.h"
50 #include "Screen.h"
51 #include "SelectionController.h"
52 #include "htmlediting.h"
53 #include "kjs_css.h"
54 #include "kjs_events.h"
55 #include "kjs_navigator.h"
56 #include "kjs_proxy.h"
57 #include "kjs_traversal.h"
58 #include <wtf/MathExtras.h>
59
60 #if XSLT_SUPPORT
61 #include "JSXSLTProcessor.h"
62 #endif
63
64 using namespace WebCore;
65 using namespace EventNames;
66
67 namespace KJS {
68
69 static int lastUsedTimeoutId;
70
71 class DOMWindowTimer : public TimerBase {
72 public:
73     DOMWindowTimer(int timeoutId, Window* o, ScheduledAction* a)
74         : m_timeoutId(timeoutId), m_object(o), m_action(a) { }
75     virtual ~DOMWindowTimer() { delete m_action; }
76
77     int timeoutId() const { return m_timeoutId; }
78     ScheduledAction* action() const { return m_action; }
79     ScheduledAction* takeAction() { ScheduledAction* a = m_action; m_action = 0; return a; }
80
81 private:
82     virtual void fired();
83
84     int m_timeoutId;
85     Window* m_object;
86     ScheduledAction* m_action;
87 };
88
89 class PausedTimeout {
90 public:
91     int timeoutId;
92     double nextFireInterval;
93     double repeatInterval;
94     ScheduledAction *action;
95 };
96
97 ////////////////////// History Object ////////////////////////
98
99   class History : public DOMObject {
100     friend class HistoryFunc;
101   public:
102     History(ExecState *exec, Frame *f)
103       : m_frame(f)
104     {
105       setPrototype(exec->lexicalInterpreter()->builtinObjectPrototype());
106     }
107     virtual bool getOwnPropertySlot(ExecState *, const Identifier&, PropertySlot&);
108     JSValue *getValueProperty(ExecState *exec, int token) const;
109     virtual const ClassInfo* classInfo() const { return &info; }
110     static const ClassInfo info;
111     enum { Back, Forward, Go, Length };
112     virtual UString toString(ExecState *exec) const;
113     void disconnectFrame() { m_frame = 0; }
114   private:
115     Frame* m_frame;
116   };
117
118
119   class FrameArray : public DOMObject {
120   public:
121     FrameArray(ExecState *exec, Frame *f)
122       : m_frame(f)
123     {
124       setPrototype(exec->lexicalInterpreter()->builtinObjectPrototype());
125     }
126     virtual bool getOwnPropertySlot(ExecState *, const Identifier&, PropertySlot&);
127     JSValue *getValueProperty(ExecState *exec, int token);
128     virtual UString toString(ExecState *exec) const;
129     enum { Length, Location };
130     void disconnectFrame() { m_frame = 0; }
131   private:
132     static JSValue *indexGetter(ExecState *, JSObject *, const Identifier&, const PropertySlot&);
133     static JSValue *nameGetter(ExecState *, JSObject *, const Identifier&, const PropertySlot&);
134
135     virtual const ClassInfo* classInfo() const { return &info; }
136     static const ClassInfo info;
137
138     Frame* m_frame;
139   };
140
141 }
142
143 #include "kjs_window.lut.h"
144
145 namespace KJS {
146
147 ////////////////////// Screen Object ////////////////////////
148
149 // table for screen object
150 /*
151 @begin ScreenTable 7
152   height        Screen::Height          DontEnum|ReadOnly
153   width         Screen::Width           DontEnum|ReadOnly
154   colorDepth    Screen::ColorDepth      DontEnum|ReadOnly
155   pixelDepth    Screen::PixelDepth      DontEnum|ReadOnly
156   availLeft     Screen::AvailLeft       DontEnum|ReadOnly
157   availTop      Screen::AvailTop        DontEnum|ReadOnly
158   availHeight   Screen::AvailHeight     DontEnum|ReadOnly
159   availWidth    Screen::AvailWidth      DontEnum|ReadOnly
160 @end
161 */
162
163 const ClassInfo Screen::info = { "Screen", 0, &ScreenTable, 0 };
164
165 // We set the object prototype so that toString is implemented
166 Screen::Screen(ExecState* exec, Frame* f)
167   : m_frame(f)
168 {
169      setPrototype(exec->lexicalInterpreter()->builtinObjectPrototype());
170 }
171
172 bool Screen::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
173 {
174   return getStaticValueSlot<Screen, JSObject>(exec, &ScreenTable, this, propertyName, slot);
175 }
176
177 JSValue* Screen::getValueProperty(ExecState*, int token) const
178 {
179   Page* page = m_frame ? m_frame->page() : 0;
180
181   switch (token) {
182   case Height:
183     return jsNumber(screenRect(page).height());
184   case Width:
185     return jsNumber(screenRect(page).width());
186   case ColorDepth:
187   case PixelDepth:
188     return jsNumber(screenDepth(page));
189   case AvailLeft:
190     return jsNumber(usableScreenRect(page).x() - screenRect(page).x());
191   case AvailTop:
192     return jsNumber(usableScreenRect(page).y() - screenRect(page).y());
193   case AvailHeight:
194     return jsNumber(usableScreenRect(page).height());
195   case AvailWidth:
196     return jsNumber(usableScreenRect(page).width());
197   default:
198     return jsUndefined();
199   }
200 }
201
202 ////////////////////// Window Object ////////////////////////
203
204 const ClassInfo Window::info = { "Window", 0, &WindowTable, 0 };
205
206 /*
207 @begin WindowTable 103
208   closed        Window::Closed          DontDelete|ReadOnly
209   crypto        Window::Crypto          DontDelete|ReadOnly
210   defaultStatus Window::DefaultStatus   DontDelete
211   defaultstatus Window::DefaultStatus   DontDelete
212   status        Window::Status          DontDelete
213   DOMException  Window::DOMException    DontDelete
214   frames        Window::Frames          DontDelete|ReadOnly
215   history       Window::History_        DontDelete|ReadOnly
216   event         Window::Event_          DontDelete
217   innerHeight   Window::InnerHeight     DontDelete|ReadOnly
218   innerWidth    Window::InnerWidth      DontDelete|ReadOnly
219   length        Window::Length          DontDelete|ReadOnly
220   location      Window::Location_       DontDelete
221   locationbar   Window::Locationbar     DontDelete
222   name          Window::Name            DontDelete
223   navigator     Window::Navigator_      DontDelete|ReadOnly
224   clientInformation     Window::ClientInformation       DontDelete|ReadOnly
225   menubar       Window::Menubar         DontDelete|ReadOnly
226   offscreenBuffering    Window::OffscreenBuffering      DontDelete|ReadOnly
227   opener        Window::Opener          DontDelete|ReadOnly
228   outerHeight   Window::OuterHeight     DontDelete|ReadOnly
229   outerWidth    Window::OuterWidth      DontDelete|ReadOnly
230   pageXOffset   Window::PageXOffset     DontDelete|ReadOnly
231   pageYOffset   Window::PageYOffset     DontDelete|ReadOnly
232   parent        Window::Parent          DontDelete|ReadOnly
233   personalbar   Window::Personalbar     DontDelete|ReadOnly
234   screenX       Window::ScreenX         DontDelete|ReadOnly
235   screenY       Window::ScreenY         DontDelete|ReadOnly
236   screenLeft    Window::ScreenLeft      DontDelete|ReadOnly
237   screenTop     Window::ScreenTop       DontDelete|ReadOnly
238   scrollbars    Window::Scrollbars      DontDelete|ReadOnly
239   statusbar     Window::Statusbar       DontDelete|ReadOnly
240   toolbar       Window::Toolbar         DontDelete|ReadOnly
241   scroll        Window::Scroll          DontDelete|Function 2
242   scrollBy      Window::ScrollBy        DontDelete|Function 2
243   scrollTo      Window::ScrollTo        DontDelete|Function 2
244   scrollX       Window::ScrollX         DontDelete|ReadOnly
245   scrollY       Window::ScrollY         DontDelete|ReadOnly
246   moveBy        Window::MoveBy          DontDelete|Function 2
247   moveTo        Window::MoveTo          DontDelete|Function 2
248   resizeBy      Window::ResizeBy        DontDelete|Function 2
249   resizeTo      Window::ResizeTo        DontDelete|Function 2
250   self          Window::Self            DontDelete|ReadOnly
251   window        Window::Window_         DontDelete|ReadOnly
252   top           Window::Top             DontDelete|ReadOnly
253   screen        Window::Screen_         DontDelete|ReadOnly
254   Image         Window::Image           DontDelete
255   Option        Window::Option          DontDelete
256   XMLHttpRequest        Window::XMLHttpRequest  DontDelete
257   XSLTProcessor Window::XSLTProcessor_  DontDelete
258   alert         Window::Alert           DontDelete|Function 1
259   confirm       Window::Confirm         DontDelete|Function 1
260   prompt        Window::Prompt          DontDelete|Function 2
261   open          Window::Open            DontDelete|Function 3
262   print         Window::Print           DontDelete|Function 2
263   setTimeout    Window::SetTimeout      DontDelete|Function 2
264   clearTimeout  Window::ClearTimeout    DontDelete|Function 1
265   focus         Window::Focus           DontDelete|Function 0
266   getSelection  Window::GetSelection    DontDelete|Function 0
267   blur          Window::Blur            DontDelete|Function 0
268   close         Window::Close           DontDelete|Function 0
269   setInterval   Window::SetInterval     DontDelete|Function 2
270   clearInterval Window::ClearInterval   DontDelete|Function 1
271   captureEvents Window::CaptureEvents   DontDelete|Function 0
272   releaseEvents Window::ReleaseEvents   DontDelete|Function 0
273 # Warning, when adding a function to this object you need to add a case in Window::get
274   addEventListener      Window::AddEventListener        DontDelete|Function 3
275   removeEventListener   Window::RemoveEventListener     DontDelete|Function 3
276   onabort       Window::Onabort         DontDelete
277   onblur        Window::Onblur          DontDelete
278   onchange      Window::Onchange        DontDelete
279   onclick       Window::Onclick         DontDelete
280   ondblclick    Window::Ondblclick      DontDelete
281   ondragdrop    Window::Ondragdrop      DontDelete
282   onerror       Window::Onerror         DontDelete
283   onfocus       Window::Onfocus         DontDelete
284   onkeydown     Window::Onkeydown       DontDelete
285   onkeypress    Window::Onkeypress      DontDelete
286   onkeyup       Window::Onkeyup         DontDelete
287   onload        Window::Onload          DontDelete
288   onmousedown   Window::Onmousedown     DontDelete
289   onmousemove   Window::Onmousemove     DontDelete
290   onmouseout    Window::Onmouseout      DontDelete
291   onmouseover   Window::Onmouseover     DontDelete
292   onmouseup     Window::Onmouseup       DontDelete
293   onmousewheel  Window::OnWindowMouseWheel      DontDelete
294   onmove        Window::Onmove          DontDelete
295   onreset       Window::Onreset         DontDelete
296   onresize      Window::Onresize        DontDelete
297   onscroll      Window::Onscroll        DontDelete
298   onsearch      Window::Onsearch        DontDelete
299   onselect      Window::Onselect        DontDelete
300   onsubmit      Window::Onsubmit        DontDelete
301   onunload      Window::Onunload        DontDelete
302   onbeforeunload Window::Onbeforeunload DontDelete
303   frameElement  Window::FrameElement    DontDelete|ReadOnly
304   showModalDialog Window::ShowModalDialog    DontDelete|Function 1
305 @end
306 */
307 KJS_IMPLEMENT_PROTOFUNC(WindowFunc)
308
309 Window::Window(DOMWindow* window)
310   : m_frame(window->frame())
311   , screen(0)
312   , history(0)
313   , frames(0)
314   , loc(0)
315   , m_selection(0)
316   , m_locationbar(0)
317   , m_menubar(0)
318   , m_personalbar(0)
319   , m_scrollbars(0)
320   , m_statusbar(0)
321   , m_toolbar(0)
322   , m_evt(0)
323   , m_returnValueSlot(0)
324 {
325 }
326
327 Window::~Window()
328 {
329     clearAllTimeouts();
330
331     // Clear any backpointers to the window
332
333     ListenersMap::iterator i2 = jsEventListeners.begin();
334     ListenersMap::iterator e2 = jsEventListeners.end();
335     for (; i2 != e2; ++i2)
336         i2->second->clearWindowObj();
337     i2 = jsHTMLEventListeners.begin();
338     e2 = jsHTMLEventListeners.end();
339     for (; i2 != e2; ++i2)
340         i2->second->clearWindowObj();
341
342     UnprotectedListenersMap::iterator i1 = jsUnprotectedEventListeners.begin();
343     UnprotectedListenersMap::iterator e1 = jsUnprotectedEventListeners.end();
344     for (; i1 != e1; ++i1)
345         i1->second->clearWindowObj();
346     i1 = jsUnprotectedHTMLEventListeners.begin();
347     e1 = jsUnprotectedHTMLEventListeners.end();
348     for (; i1 != e1; ++i1)
349         i1->second->clearWindowObj();
350 }
351
352 DOMWindow* Window::impl() const
353 {
354      return m_frame->domWindow();
355 }
356
357 ScriptInterpreter *Window::interpreter() const
358 {
359     return m_frame->jScript()->interpreter();
360 }
361
362 Window *Window::retrieveWindow(Frame *f)
363 {
364     JSObject *o = retrieve(f)->getObject();
365
366     ASSERT(o || !f->jScriptEnabled());
367     return static_cast<Window *>(o);
368 }
369
370 Window *Window::retrieveActive(ExecState *exec)
371 {
372     JSValue *imp = exec->dynamicInterpreter()->globalObject();
373     ASSERT(imp);
374     return static_cast<Window*>(imp);
375 }
376
377 JSValue *Window::retrieve(Frame *p)
378 {
379     ASSERT(p);
380     if (KJSProxy *proxy = p->jScript())
381         return proxy->interpreter()->globalObject(); // the Global object is the "window"
382   
383     return jsUndefined(); // This can happen with JS disabled on the domain of that window
384 }
385
386 Location *Window::location() const
387 {
388   if (!loc)
389     loc = new Location(m_frame);
390   return loc;
391 }
392
393 Selection *Window::selection() const
394 {
395   if (!m_selection)
396     m_selection = new Selection(m_frame);
397   return m_selection;
398 }
399
400 BarInfo *Window::locationbar(ExecState *exec) const
401 {
402   if (!m_locationbar)
403     m_locationbar = new BarInfo(exec, m_frame, BarInfo::Locationbar);
404   return m_locationbar;
405 }
406
407 BarInfo *Window::menubar(ExecState *exec) const
408 {
409   if (!m_menubar)
410     m_menubar = new BarInfo(exec, m_frame, BarInfo::Menubar);
411   return m_menubar;
412 }
413
414 BarInfo *Window::personalbar(ExecState *exec) const
415 {
416   if (!m_personalbar)
417     m_personalbar = new BarInfo(exec, m_frame, BarInfo::Personalbar);
418   return m_personalbar;
419 }
420
421 BarInfo *Window::statusbar(ExecState *exec) const
422 {
423   if (!m_statusbar)
424     m_statusbar = new BarInfo(exec, m_frame, BarInfo::Statusbar);
425   return m_statusbar;
426 }
427
428 BarInfo *Window::toolbar(ExecState *exec) const
429 {
430   if (!m_toolbar)
431     m_toolbar = new BarInfo(exec, m_frame, BarInfo::Toolbar);
432   return m_toolbar;
433 }
434
435 BarInfo *Window::scrollbars(ExecState *exec) const
436 {
437   if (!m_scrollbars)
438     m_scrollbars = new BarInfo(exec, m_frame, BarInfo::Scrollbars);
439   return m_scrollbars;
440 }
441
442 // reference our special objects during garbage collection
443 void Window::mark()
444 {
445   JSObject::mark();
446   if (screen && !screen->marked())
447     screen->mark();
448   if (history && !history->marked())
449     history->mark();
450   if (frames && !frames->marked())
451     frames->mark();
452   if (loc && !loc->marked())
453     loc->mark();
454   if (m_selection && !m_selection->marked())
455     m_selection->mark();
456   if (m_locationbar && !m_locationbar->marked())
457     m_locationbar->mark();
458   if (m_menubar && !m_menubar->marked())
459     m_menubar->mark();
460   if (m_personalbar && !m_personalbar->marked())
461     m_personalbar->mark();
462   if (m_scrollbars && !m_scrollbars->marked())
463     m_scrollbars->mark();
464   if (m_statusbar && !m_statusbar->marked())
465     m_statusbar->mark();
466   if (m_toolbar && !m_toolbar->marked())
467     m_toolbar->mark();
468 }
469
470 UString Window::toString(ExecState *) const
471 {
472   return "[object Window]";
473 }
474
475 static bool allowPopUp(ExecState *exec, Window *window)
476 {
477     return window->frame()
478         && (window->frame()->settings()->JavaScriptCanOpenWindowsAutomatically()
479             || static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture());
480 }
481
482 static HashMap<String, String> parseModalDialogFeatures(ExecState *exec, JSValue *featuresArg)
483 {
484     HashMap<String, String> map;
485
486     Vector<String> features = String(featuresArg->toString(exec)).split(';');
487     Vector<String>::const_iterator end = features.end();
488     for (Vector<String>::const_iterator it = features.begin(); it != end; ++it) {
489         String s = *it;
490         int pos = s.find('=');
491         int colonPos = s.find(':');
492         if (pos >= 0 && colonPos >= 0)
493             continue; // ignore any strings that have both = and :
494         if (pos < 0)
495             pos = colonPos;
496         if (pos < 0) {
497             // null string for value means key without value
498             map.set(s.stripWhiteSpace().lower(), String());
499         } else {
500             String key = s.left(pos).stripWhiteSpace().lower();
501             String val = s.substring(pos + 1).stripWhiteSpace().lower();
502             int spacePos = val.find(' ');
503             if (spacePos != -1)
504                 val = val.left(spacePos);
505             map.set(key, val);
506         }
507     }
508
509     return map;
510 }
511
512 static bool boolFeature(const HashMap<String, String>& features, const char* key, bool defaultValue = false)
513 {
514     HashMap<String, String>::const_iterator it = features.find(key);
515     if (it == features.end())
516         return defaultValue;
517     const String& value = it->second;
518     return value.isNull() || value == "1" || value == "yes" || value == "on";
519 }
520
521 static float floatFeature(const HashMap<String, String> &features, const char *key, float min, float max, float defaultValue)
522 {
523     HashMap<String, String>::const_iterator it = features.find(key);
524     if (it == features.end())
525         return defaultValue;
526     // FIXME: Can't distinguish "0q" from string with no digits in it -- both return d == 0 and ok == false.
527     // Would be good to tell them apart somehow since string with no digits should be default value and
528     // "0q" should be minimum value.
529     bool ok;
530     double d = it->second.toDouble(&ok);
531     if ((d == 0 && !ok) || isnan(d))
532         return defaultValue;
533     if (d < min || max <= min)
534         return min;
535     if (d > max)
536         return max;
537     return static_cast<int>(d);
538 }
539
540 static Frame *createNewWindow(ExecState *exec, Window *openerWindow, const DeprecatedString &URL,
541     const DeprecatedString &frameName, const WindowArgs &windowArgs, JSValue *dialogArgs)
542 {
543     Frame* openerPart = openerWindow->frame();
544     Frame* activePart = Window::retrieveActive(exec)->frame();
545
546     ResourceRequest request(KURL(""));
547     request.frameName = frameName;
548     if (activePart)
549         request.setReferrer(activePart->referrer());
550
551     // FIXME: It's much better for client API if a new window starts with a URL, here where we
552     // know what URL we are going to open. Unfortunately, this code passes the empty string
553     // for the URL, but there's a reason for that. Before loading we have to set up the opener,
554     // openedByJS, and dialogArguments values. Also, to decide whether to use the URL we currently
555     // do an isSafeScript call using the window we create, which can't be done before creating it.
556     // We'd have to resolve all those issues to pass the URL instead of "".
557
558     Frame* newFrame = 0;
559     openerPart->browserExtension()->createNewWindow(request, windowArgs, newFrame);
560
561     if (!newFrame)
562         return 0;
563
564     Window* newWindow = Window::retrieveWindow(newFrame);
565
566     newFrame->setOpener(openerPart);
567     newFrame->setOpenedByJS(true);
568     if (dialogArgs)
569         newWindow->putDirect("dialogArguments", dialogArgs);
570
571     Document *activeDoc = activePart ? activePart->document() : 0;
572     if (!URL.isEmpty() && activeDoc) {
573         DeprecatedString completedURL = activeDoc->completeURL(URL);
574         if (!completedURL.startsWith("javascript:", false) || newWindow->isSafeScript(exec)) {
575             bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
576             newFrame->changeLocation(completedURL, activePart->referrer(), false, userGesture);
577         }
578     }
579
580     return newFrame;
581 }
582
583 static bool canShowModalDialog(const Window *window)
584 {
585     Frame *frame = window->frame();
586     return frame && static_cast<BrowserExtension *>(frame->browserExtension())->canRunModal();
587 }
588
589 static bool canShowModalDialogNow(const Window *window)
590 {
591     Frame *frame = window->frame();
592     return frame && static_cast<BrowserExtension *>(frame->browserExtension())->canRunModalNow();
593 }
594
595 static JSValue* showModalDialog(ExecState* exec, Window* openerWindow, const List& args)
596 {
597     UString URL = args[0]->toString(exec);
598
599     if (!canShowModalDialogNow(openerWindow) || !allowPopUp(exec, openerWindow))
600         return jsUndefined();
601     
602     const HashMap<String, String> features = parseModalDialogFeatures(exec, args[2]);
603
604     bool trusted = false;
605
606     WindowArgs wargs;
607
608     // The following features from Microsoft's documentation are not implemented:
609     // - default font settings
610     // - width, height, left, and top specified in units other than "px"
611     // - edge (sunken or raised, default is raised)
612     // - dialogHide: trusted && boolFeature(features, "dialoghide"), makes dialog hide when you print
613     // - help: boolFeature(features, "help", true), makes help icon appear in dialog (what does it do on Windows?)
614     // - unadorned: trusted && boolFeature(features, "unadorned");
615
616     FloatRect screenRect = usableScreenRect(openerWindow->frame()->page());
617
618     wargs.width = floatFeature(features, "dialogwidth", 100, screenRect.width(), 620); // default here came from frame size of dialog in MacIE
619     wargs.widthSet = true;
620     wargs.height = floatFeature(features, "dialogheight", 100, screenRect.height(), 450); // default here came from frame size of dialog in MacIE
621     wargs.heightSet = true;
622
623     wargs.x = floatFeature(features, "dialogleft", screenRect.x(), screenRect.right() - wargs.width, -1);
624     wargs.xSet = wargs.x > 0;
625     wargs.y = floatFeature(features, "dialogtop", screenRect.y(), screenRect.bottom() - wargs.height, -1);
626     wargs.ySet = wargs.y > 0;
627
628     if (boolFeature(features, "center", true)) {
629         if (!wargs.xSet) {
630             wargs.x = screenRect.x() + (screenRect.width() - wargs.width) / 2;
631             wargs.xSet = true;
632         }
633         if (!wargs.ySet) {
634             wargs.y = screenRect.y() + (screenRect.height() - wargs.height) / 2;
635             wargs.ySet = true;
636         }
637     }
638
639     wargs.dialog = true;
640     wargs.resizable = boolFeature(features, "resizable");
641     wargs.scrollbarsVisible = boolFeature(features, "scroll", true);
642     wargs.statusBarVisible = boolFeature(features, "status", !trusted);
643     wargs.menuBarVisible = false;
644     wargs.toolBarVisible = false;
645     wargs.locationBarVisible = false;
646     wargs.fullscreen = false;
647     
648     Frame* dialogFrame = createNewWindow(exec, openerWindow, URL, "", wargs, args[1]);
649     if (!dialogFrame)
650         return jsUndefined();
651
652     Window* dialogWindow = Window::retrieveWindow(dialogFrame);
653
654     // Get the return value either just before clearing the dialog window's
655     // properties (in Window::clear), or when on return from runModal.
656     JSValue* returnValue = 0;
657     dialogWindow->setReturnValueSlot(&returnValue);
658     static_cast<BrowserExtension *>(dialogFrame->browserExtension())->runModal();
659     dialogWindow->setReturnValueSlot(0);
660
661     // If we don't have a return value, get it now.
662     // Either Window::clear was not called yet, or there was no return value,
663     // and in that case, there's no harm in trying again (no benefit either).
664     if (!returnValue)
665         returnValue = dialogWindow->getDirect("returnValue");
666
667     return returnValue ? returnValue : jsUndefined();
668 }
669
670 JSValue *Window::getValueProperty(ExecState *exec, int token) const
671 {
672    ASSERT(token == Closed || m_frame);
673
674    switch (token) {
675    case Closed:
676       return jsBoolean(!m_frame);
677    case Crypto:
678       return jsUndefined(); // ###
679    case DefaultStatus:
680       return jsString(UString(m_frame->jsDefaultStatusBarText()));
681    case DOMException:
682       return getDOMExceptionConstructor(exec);
683    case Status:
684       return jsString(UString(m_frame->jsStatusBarText()));
685     case Frames:
686       if (!frames)
687         frames = new FrameArray(exec, m_frame);
688       return frames;
689     case History_:
690       if (!history)
691         history = new History(exec, m_frame);
692       return history;
693     case Event_:
694       if (!m_evt)
695         return jsUndefined();
696       return toJS(exec, m_evt);
697     case InnerHeight:
698       if (!m_frame->view())
699         return jsUndefined();
700       return jsNumber(m_frame->view()->visibleHeight());
701     case InnerWidth:
702       if (!m_frame->view())
703         return jsUndefined();
704       return jsNumber(m_frame->view()->visibleWidth());
705     case Length:
706       return jsNumber(m_frame->tree()->childCount());
707     case Location_:
708       return location();
709     case Name:
710       return jsString(m_frame->tree()->name());
711     case Navigator_:
712     case ClientInformation: {
713       // Store the navigator in the object so we get the same one each time.
714       Navigator *n = new Navigator(exec, m_frame);
715       // FIXME: this will make the "navigator" object accessible from windows that fail
716       // the security check the first time, but not subsequent times, seems weird.
717       const_cast<Window *>(this)->putDirect("navigator", n, DontDelete|ReadOnly);
718       const_cast<Window *>(this)->putDirect("clientInformation", n, DontDelete|ReadOnly);
719       return n;
720     }
721     case Locationbar:
722       return locationbar(exec);
723     case Menubar:
724       return menubar(exec);
725     case OffscreenBuffering:
726       return jsBoolean(true);
727     case Opener:
728       if (m_frame->opener())
729         return retrieve(m_frame->opener());
730       else
731         return jsNull();
732     case OuterHeight:
733         return jsNumber(m_frame->page()->windowRect().height());
734     case OuterWidth:
735         return jsNumber(m_frame->page()->windowRect().width());
736     case PageXOffset:
737       if (!m_frame->view())
738         return jsUndefined();
739       updateLayout();
740       return jsNumber(m_frame->view()->contentsX());
741     case PageYOffset:
742       if (!m_frame->view())
743         return jsUndefined();
744       updateLayout();
745       return jsNumber(m_frame->view()->contentsY());
746     case Parent:
747       return retrieve(m_frame->tree()->parent() ? m_frame->tree()->parent() : m_frame);
748     case Personalbar:
749       return personalbar(exec);
750     case ScreenLeft:
751     case ScreenX:
752       return jsNumber(m_frame->page()->windowRect().x());
753     case ScreenTop:
754     case ScreenY:
755       return jsNumber(m_frame->page()->windowRect().y());
756     case ScrollX:
757       if (!m_frame->view())
758         return jsUndefined();
759       updateLayout();
760       return jsNumber(m_frame->view()->contentsX());
761     case ScrollY:
762       if (!m_frame->view())
763         return jsUndefined();
764       updateLayout();
765       return jsNumber(m_frame->view()->contentsY());
766     case Scrollbars:
767       return scrollbars(exec);
768     case Statusbar:
769       return statusbar(exec);
770     case Toolbar:
771       return toolbar(exec);
772     case Self:
773     case Window_:
774       return retrieve(m_frame);
775     case Top:
776       return retrieve(m_frame->page()->mainFrame());
777     case Screen_:
778       if (!screen)
779         screen = new Screen(exec, m_frame);
780       return screen;
781     case Image:
782       // FIXME: this property (and the few below) probably shouldn't create a new object every
783       // time
784       return new ImageConstructorImp(exec, m_frame->document());
785     case Option:
786       return new JSHTMLOptionElementConstructor(exec, m_frame->document());
787     case XMLHttpRequest:
788       return new JSXMLHttpRequestConstructorImp(exec, m_frame->document());
789 #ifdef XSLT_SUPPORT
790     case XSLTProcessor_:
791       return new XSLTProcessorConstructorImp(exec);
792 #else
793     case XSLTProcessor_:
794       return jsUndefined();
795 #endif
796     case FrameElement:
797       if (Document* doc = m_frame->document())
798         if (Element* fe = doc->ownerElement())
799           if (checkNodeSecurity(exec, fe))
800             return toJS(exec, fe);
801       return jsUndefined();
802    }
803
804    if (!isSafeScript(exec))
805      return jsUndefined();
806
807    switch (token) {
808    case Onabort:
809      return getListener(exec, abortEvent);
810    case Onblur:
811      return getListener(exec, blurEvent);
812    case Onchange:
813      return getListener(exec, changeEvent);
814    case Onclick:
815      return getListener(exec, clickEvent);
816    case Ondblclick:
817      return getListener(exec, dblclickEvent);
818    case Ondragdrop:
819      return getListener(exec, khtmlDragdropEvent);
820    case Onerror:
821      return getListener(exec, errorEvent);
822    case Onfocus:
823      return getListener(exec, focusEvent);
824    case Onkeydown:
825      return getListener(exec, keydownEvent);
826    case Onkeypress:
827      return getListener(exec, keypressEvent);
828    case Onkeyup:
829      return getListener(exec, keyupEvent);
830    case Onload:
831      return getListener(exec, loadEvent);
832    case Onmousedown:
833      return getListener(exec, mousedownEvent);
834    case Onmousemove:
835      return getListener(exec, mousemoveEvent);
836    case Onmouseout:
837      return getListener(exec, mouseoutEvent);
838    case Onmouseover:
839      return getListener(exec, mouseoverEvent);
840    case Onmouseup:
841      return getListener(exec, mouseupEvent);
842    case OnWindowMouseWheel:
843      return getListener(exec, mousewheelEvent);
844    case Onmove:
845      return getListener(exec, khtmlMoveEvent);
846    case Onreset:
847      return getListener(exec, resetEvent);
848    case Onresize:
849      return getListener(exec,resizeEvent);
850    case Onscroll:
851      return getListener(exec,scrollEvent);
852    case Onsearch:
853      return getListener(exec,searchEvent);
854    case Onselect:
855      return getListener(exec,selectEvent);
856    case Onsubmit:
857      return getListener(exec,submitEvent);
858    case Onbeforeunload:
859       return getListener(exec, beforeunloadEvent);
860     case Onunload:
861      return getListener(exec, unloadEvent);
862    }
863    ASSERT(0);
864    return jsUndefined();
865 }
866
867 JSValue* Window::childFrameGetter(ExecState*, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
868 {
869     return retrieve(static_cast<Window*>(slot.slotBase())->m_frame->tree()->child(AtomicString(propertyName)));
870 }
871
872 JSValue* Window::indexGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot)
873 {
874     return retrieve(static_cast<Window*>(slot.slotBase())->m_frame->tree()->child(slot.index()));
875 }
876
877 JSValue *Window::namedItemGetter(ExecState *exec, JSObject *originalObject, const Identifier& propertyName, const PropertySlot& slot)
878 {
879   Window *thisObj = static_cast<Window *>(slot.slotBase());
880   Document *doc = thisObj->m_frame->document();
881   ASSERT(thisObj->isSafeScript(exec) && doc && doc->isHTMLDocument());
882
883   String name = propertyName;
884   RefPtr<WebCore::HTMLCollection> collection = doc->windowNamedItems(name);
885   if (collection->length() == 1)
886     return toJS(exec, collection->firstItem());
887   else 
888     return getHTMLCollection(exec, collection.get());
889 }
890
891 bool Window::getOverridePropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
892 {
893   // we don't want any properties other than "closed" on a closed window
894   if (!m_frame) {
895     if (propertyName == "closed") {
896       slot.setStaticEntry(this, Lookup::findEntry(&WindowTable, "closed"), staticValueGetter<Window>);
897       return true;
898     }
899     if (propertyName == "close") {
900       const HashEntry* entry = Lookup::findEntry(&WindowTable, propertyName);
901       slot.setStaticEntry(this, entry, staticFunctionGetter<WindowFunc>);
902       return true;
903     }
904
905     slot.setUndefined(this);
906     return true;
907   }
908
909   // Look for overrides first
910   JSValue **val = getDirectLocation(propertyName);
911   if (val) {
912     if (isSafeScript(exec))
913       slot.setValueSlot(this, val);
914     else
915       slot.setUndefined(this);
916     return true;
917   }
918   
919   return false;
920 }
921
922 bool Window::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
923 {
924   // Check for child frames by name before built-in properties to
925   // match Mozilla. This does not match IE, but some sites end up
926   // naming frames things that conflict with window properties that
927   // are in Moz but not IE. Since we have some of these, we have to do
928   // it the Moz way.
929   AtomicString atomicPropertyName = propertyName;
930   if (m_frame->tree()->child(atomicPropertyName)) {
931     slot.setCustom(this, childFrameGetter);
932     return true;
933   }
934   
935   const HashEntry* entry = Lookup::findEntry(&WindowTable, propertyName);
936   if (entry) {
937     if (entry->attr & Function) {
938       switch (entry->value) {
939       case Focus:
940       case Blur:
941       case Close:
942         slot.setStaticEntry(this, entry, staticFunctionGetter<WindowFunc>);
943         break;
944       case ShowModalDialog:
945         if (!canShowModalDialog(this))
946           return false;
947         // fall through
948       default:
949         if (isSafeScript(exec))
950           slot.setStaticEntry(this, entry, staticFunctionGetter<WindowFunc>);
951         else
952           slot.setUndefined(this);
953       } 
954     } else
955       slot.setStaticEntry(this, entry, staticValueGetter<Window>);
956     return true;
957   }
958
959   // FIXME: Search the whole frame hierachy somewhere around here.
960   // We need to test the correct priority order.
961   
962   // allow window[1] or parent[1] etc. (#56983)
963   bool ok;
964   unsigned i = propertyName.toArrayIndex(&ok);
965   if (ok && i < m_frame->tree()->childCount()) {
966     slot.setCustomIndex(this, i, indexGetter);
967     return true;
968   }
969
970   // allow shortcuts like 'Image1' instead of document.images.Image1
971   Document *doc = m_frame->document();
972   if (isSafeScript(exec) && doc && doc->isHTMLDocument()) {
973     AtomicString atomicPropertyName = propertyName;
974     if (static_cast<HTMLDocument*>(doc)->hasNamedItem(atomicPropertyName) || doc->getElementById(atomicPropertyName)) {
975       slot.setCustom(this, namedItemGetter);
976       return true;
977     }
978   }
979
980   if (!isSafeScript(exec)) {
981     slot.setUndefined(this);
982     return true;
983   }
984
985   return JSObject::getOwnPropertySlot(exec, propertyName, slot);
986 }
987
988 void Window::put(ExecState* exec, const Identifier &propertyName, JSValue *value, int attr)
989 {
990   // Called by an internal KJS call.
991   // If yes, save time and jump directly to JSObject.
992   if ((attr != None && attr != DontDelete)
993        // Same thing if we have a local override (e.g. "var location")
994        || (JSObject::getDirect(propertyName) && isSafeScript(exec))) {
995     JSObject::put( exec, propertyName, value, attr );
996     return;
997   }
998
999   const HashEntry* entry = Lookup::findEntry(&WindowTable, propertyName);
1000   if (entry) {
1001     switch(entry->value) {
1002     case Status:
1003       m_frame->setJSStatusBarText(value->toString(exec));
1004       return;
1005     case DefaultStatus:
1006       m_frame->setJSDefaultStatusBarText(value->toString(exec));
1007       return;
1008     case Location_: {
1009       Frame* p = Window::retrieveActive(exec)->m_frame;
1010       if (p) {
1011         DeprecatedString dstUrl = p->document()->completeURL(DeprecatedString(value->toString(exec)));
1012         if (!dstUrl.startsWith("javascript:", false) || isSafeScript(exec)) {
1013           bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
1014           // We want a new history item if this JS was called via a user gesture
1015           m_frame->scheduleLocationChange(dstUrl, p->referrer(), !userGesture, userGesture);
1016         }
1017       }
1018       return;
1019     }
1020     case Onabort:
1021       if (isSafeScript(exec))
1022         setListener(exec, abortEvent,value);
1023       return;
1024     case Onblur:
1025       if (isSafeScript(exec))
1026         setListener(exec, blurEvent,value);
1027       return;
1028     case Onchange:
1029       if (isSafeScript(exec))
1030         setListener(exec, changeEvent,value);
1031       return;
1032     case Onclick:
1033       if (isSafeScript(exec))
1034         setListener(exec,clickEvent,value);
1035       return;
1036     case Ondblclick:
1037       if (isSafeScript(exec))
1038         setListener(exec, dblclickEvent,value);
1039       return;
1040     case Ondragdrop:
1041       if (isSafeScript(exec))
1042         setListener(exec,khtmlDragdropEvent,value);
1043       return;
1044     case Onerror:
1045       if (isSafeScript(exec))
1046         setListener(exec, errorEvent, value);
1047       return;
1048     case Onfocus:
1049       if (isSafeScript(exec))
1050         setListener(exec,focusEvent,value);
1051       return;
1052     case Onkeydown:
1053       if (isSafeScript(exec))
1054         setListener(exec,keydownEvent,value);
1055       return;
1056     case Onkeypress:
1057       if (isSafeScript(exec))
1058         setListener(exec,keypressEvent,value);
1059       return;
1060     case Onkeyup:
1061       if (isSafeScript(exec))
1062         setListener(exec,keyupEvent,value);
1063       return;
1064     case Onload:
1065       if (isSafeScript(exec))
1066         setListener(exec,loadEvent,value);
1067       return;
1068     case Onmousedown:
1069       if (isSafeScript(exec))
1070         setListener(exec,mousedownEvent,value);
1071       return;
1072     case Onmousemove:
1073       if (isSafeScript(exec))
1074         setListener(exec,mousemoveEvent,value);
1075       return;
1076     case Onmouseout:
1077       if (isSafeScript(exec))
1078         setListener(exec,mouseoutEvent,value);
1079       return;
1080     case Onmouseover:
1081       if (isSafeScript(exec))
1082         setListener(exec,mouseoverEvent,value);
1083       return;
1084     case Onmouseup:
1085       if (isSafeScript(exec))
1086         setListener(exec,mouseupEvent,value);
1087       return;
1088     case OnWindowMouseWheel:
1089       if (isSafeScript(exec))
1090         setListener(exec, mousewheelEvent,value);
1091       return;
1092     case Onmove:
1093       if (isSafeScript(exec))
1094         setListener(exec,khtmlMoveEvent,value);
1095       return;
1096     case Onreset:
1097       if (isSafeScript(exec))
1098         setListener(exec,resetEvent,value);
1099       return;
1100     case Onresize:
1101       if (isSafeScript(exec))
1102         setListener(exec,resizeEvent,value);
1103       return;
1104     case Onscroll:
1105       if (isSafeScript(exec))
1106         setListener(exec,scrollEvent,value);
1107       return;
1108     case Onsearch:
1109         if (isSafeScript(exec))
1110             setListener(exec,searchEvent,value);
1111         return;
1112     case Onselect:
1113       if (isSafeScript(exec))
1114         setListener(exec,selectEvent,value);
1115       return;
1116     case Onsubmit:
1117       if (isSafeScript(exec))
1118         setListener(exec,submitEvent,value);
1119       return;
1120     case Onbeforeunload:
1121       if (isSafeScript(exec))
1122         setListener(exec, beforeunloadEvent, value);
1123       return;
1124     case Onunload:
1125       if (isSafeScript(exec))
1126         setListener(exec, unloadEvent, value);
1127       return;
1128     case Name:
1129       if (isSafeScript(exec))
1130         m_frame->tree()->setName(value->toString(exec));
1131       return;
1132     default:
1133       break;
1134     }
1135   }
1136   if (isSafeScript(exec))
1137     JSObject::put(exec, propertyName, value, attr);
1138 }
1139
1140 bool Window::toBoolean(ExecState *) const
1141 {
1142   return m_frame;
1143 }
1144
1145 void Window::scheduleClose()
1146 {
1147   m_frame->scheduleClose();
1148 }
1149
1150 static bool shouldLoadAsEmptyDocument(const KURL &url)
1151 {
1152   return url.protocol().lower() == "about" || url.isEmpty();
1153 }
1154
1155 bool Window::isSafeScript(const ScriptInterpreter *origin, const ScriptInterpreter *target)
1156 {
1157     if (origin == target)
1158         return true;
1159         
1160     Frame *originPart = origin->frame();
1161     Frame *targetPart = target->frame();
1162
1163     // JS may be attempting to access the "window" object, which should be valid,
1164     // even if the document hasn't been constructed yet.  If the document doesn't
1165     // exist yet allow JS to access the window object.
1166     if (!targetPart->document())
1167         return true;
1168
1169     WebCore::Document *originDocument = originPart->document();
1170     WebCore::Document *targetDocument = targetPart->document();
1171
1172     if (!targetDocument) {
1173         return false;
1174     }
1175
1176     WebCore::String targetDomain = targetDocument->domain();
1177
1178     // Always allow local pages to execute any JS.
1179     if (targetDomain.isNull())
1180         return true;
1181
1182     WebCore::String originDomain = originDocument->domain();
1183
1184     // if this document is being initially loaded as empty by its parent
1185     // or opener, allow access from any document in the same domain as
1186     // the parent or opener.
1187     if (shouldLoadAsEmptyDocument(targetPart->url())) {
1188         Frame *ancestorPart = targetPart->opener() ? targetPart->opener() : targetPart->tree()->parent();
1189         while (ancestorPart && shouldLoadAsEmptyDocument(ancestorPart->url())) {
1190             ancestorPart = ancestorPart->tree()->parent();
1191         }
1192
1193         if (ancestorPart)
1194             originDomain = ancestorPart->document()->domain();
1195     }
1196
1197     if ( targetDomain == originDomain )
1198         return true;
1199
1200     if (Interpreter::shouldPrintExceptions()) {
1201         printf("Unsafe JavaScript attempt to access frame with URL %s from frame with URL %s. Domains must match.\n", 
1202              targetDocument->URL().latin1(), originDocument->URL().latin1());
1203     }
1204     String message = String::sprintf("Unsafe JavaScript attempt to access frame with URL %s from frame with URL %s. Domains must match.\n", 
1205                   targetDocument->URL().latin1(), originDocument->URL().latin1());
1206     targetPart->addMessageToConsole(message, 1, String()); //fixme: provide a real line number and sourceurl
1207
1208     return false;
1209 }
1210
1211 bool Window::isSafeScript(ExecState *exec) const
1212 {
1213   if (!m_frame) // frame deleted ? can't grant access
1214     return false;
1215   Frame *activePart = static_cast<ScriptInterpreter *>( exec->dynamicInterpreter() )->frame();
1216   if (!activePart)
1217     return false;
1218   if ( activePart == m_frame ) // Not calling from another frame, no problem.
1219     return true;
1220
1221   // JS may be attempting to access the "window" object, which should be valid,
1222   // even if the document hasn't been constructed yet.  If the document doesn't
1223   // exist yet allow JS to access the window object.
1224   if (!m_frame->document())
1225       return true;
1226
1227   WebCore::Document* thisDocument = m_frame->document();
1228   WebCore::Document* actDocument = activePart->document();
1229
1230   WebCore::String actDomain;
1231
1232   if (!actDocument)
1233     actDomain = activePart->url().host();
1234   else
1235     actDomain = actDocument->domain();
1236   
1237   // FIXME: this really should be explicitly checking for the "file:" protocol instead
1238   // Always allow local pages to execute any JS.
1239   if (actDomain.isEmpty())
1240     return true;
1241   
1242   WebCore::String thisDomain = thisDocument->domain();
1243
1244   // if this document is being initially loaded as empty by its parent
1245   // or opener, allow access from any document in the same domain as
1246   // the parent or opener.
1247   if (shouldLoadAsEmptyDocument(m_frame->url())) {
1248     Frame *ancestorPart = m_frame->opener() ? m_frame->opener() : m_frame->tree()->parent();
1249     while (ancestorPart && shouldLoadAsEmptyDocument(ancestorPart->url())) {
1250       ancestorPart = ancestorPart->tree()->parent();
1251     }
1252     
1253     if (ancestorPart)
1254       thisDomain = ancestorPart->document()->domain();
1255   }
1256
1257   // FIXME: this should check that URL scheme and port match too, probably
1258   if (actDomain == thisDomain)
1259     return true;
1260
1261   if (Interpreter::shouldPrintExceptions()) {
1262       printf("Unsafe JavaScript attempt to access frame with URL %s from frame with URL %s. Domains must match.\n", 
1263              thisDocument->URL().latin1(), actDocument->URL().latin1());
1264   }
1265   String message = String::sprintf("Unsafe JavaScript attempt to access frame with URL %s from frame with URL %s. Domains must match.\n", 
1266                   thisDocument->URL().latin1(), actDocument->URL().latin1());
1267   m_frame->addMessageToConsole(message, 1, String());
1268   
1269   return false;
1270 }
1271
1272 void Window::setListener(ExecState *exec, const AtomicString &eventType, JSValue *func)
1273 {
1274   if (!isSafeScript(exec))
1275     return;
1276   WebCore::Document *doc = m_frame->document();
1277   if (!doc)
1278     return;
1279
1280   doc->setHTMLWindowEventListener(eventType, getJSEventListener(func,true));
1281 }
1282
1283 JSValue *Window::getListener(ExecState *exec, const AtomicString &eventType) const
1284 {
1285   if (!isSafeScript(exec))
1286     return jsUndefined();
1287   WebCore::Document *doc = m_frame->document();
1288   if (!doc)
1289     return jsUndefined();
1290
1291   WebCore::EventListener *listener = doc->getHTMLWindowEventListener(eventType);
1292   if (listener && static_cast<JSEventListener*>(listener)->listenerObj())
1293     return static_cast<JSEventListener*>(listener)->listenerObj();
1294   else
1295     return jsNull();
1296 }
1297
1298 JSEventListener *Window::getJSEventListener(JSValue *val, bool html)
1299 {
1300   if (!val->isObject())
1301     return 0;
1302   JSObject *object = static_cast<JSObject *>(val);
1303
1304   ListenersMap& listeners = html ? jsHTMLEventListeners : jsEventListeners;
1305   if (JSEventListener* listener = listeners.get(object))
1306     return listener;
1307
1308   // Note that the JSEventListener constructor adds it to our jsEventListeners list
1309   return new JSEventListener(object, this, html);
1310 }
1311
1312 JSUnprotectedEventListener *Window::getJSUnprotectedEventListener(JSValue *val, bool html)
1313 {
1314   if (!val->isObject())
1315     return 0;
1316   JSObject* object = static_cast<JSObject *>(val);
1317
1318   UnprotectedListenersMap& listeners = html ? jsUnprotectedHTMLEventListeners : jsUnprotectedEventListeners;
1319   if (JSUnprotectedEventListener* listener = listeners.get(object))
1320     return listener;
1321
1322   // The JSUnprotectedEventListener constructor adds it to our jsUnprotectedEventListeners map.
1323   return new JSUnprotectedEventListener(object, this, html);
1324 }
1325
1326 void Window::clear()
1327 {
1328   JSLock lock;
1329
1330   if (m_returnValueSlot && !*m_returnValueSlot)
1331     *m_returnValueSlot = getDirect("returnValue");
1332
1333   clearAllTimeouts();
1334   clearProperties();
1335   setPrototype(JSDOMWindowProto::self()); // clear the prototype
1336
1337   // Now recreate a working global object for the next URL that will use us
1338   interpreter()->initGlobalObject();
1339
1340   // there's likely to be lots of garbage now
1341   Collector::collect();
1342 }
1343
1344 void Window::setCurrentEvent(Event *evt)
1345 {
1346   m_evt = evt;
1347 }
1348
1349 static void setWindowFeature(const String& keyString, const String& valueString, WindowArgs& windowArgs)
1350 {
1351     int value;
1352     
1353     if (valueString.length() == 0 || // listing a key with no value is shorthand for key=yes
1354         valueString == "yes")
1355         value = 1;
1356     else
1357         value = valueString.toInt();
1358     
1359     if (keyString == "left" || keyString == "screenx") {
1360         windowArgs.xSet = true;
1361         windowArgs.x = value;
1362     } else if (keyString == "top" || keyString == "screeny") {
1363         windowArgs.ySet = true;
1364         windowArgs.y = value;
1365     } else if (keyString == "width" || keyString == "innerwidth") {
1366         windowArgs.widthSet = true;
1367         windowArgs.width = value;
1368     } else if (keyString == "height" || keyString == "innerheight") {
1369         windowArgs.heightSet = true;
1370         windowArgs.height = value;
1371     } else if (keyString == "menubar")
1372         windowArgs.menuBarVisible = value;
1373     else if (keyString == "toolbar")
1374         windowArgs.toolBarVisible = value;
1375     else if (keyString == "location")
1376         windowArgs.locationBarVisible = value;
1377     else if (keyString == "status")
1378         windowArgs.statusBarVisible = value;
1379     else if (keyString == "resizable")
1380         windowArgs.resizable = value;
1381     else if (keyString == "fullscreen")
1382         windowArgs.fullscreen = value;
1383     else if (keyString == "scrollbars")
1384         windowArgs.scrollbarsVisible = value;
1385 }
1386
1387 // Though isspace() considers \t and \v to be whitespace, Win IE doesn't.
1388 static bool isSeparator(::UChar c)
1389 {
1390     return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '=' || c == ',' || c == '\0';
1391 }
1392
1393 static void parseWindowFeatures(const String& features, WindowArgs& windowArgs)
1394 {
1395     /*
1396      The IE rule is: all features except for channelmode and fullscreen default to YES, but
1397      if the user specifies a feature string, all features default to NO. (There is no public
1398      standard that applies to this method.)
1399      
1400      <http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/open_0.asp>
1401      */
1402     
1403     windowArgs.dialog = false;
1404     windowArgs.fullscreen = false;
1405     
1406     windowArgs.xSet = false;
1407     windowArgs.ySet = false;
1408     windowArgs.widthSet = false;
1409     windowArgs.heightSet = false;
1410     
1411     if (features.length() == 0) {
1412         windowArgs.menuBarVisible = true;
1413         windowArgs.statusBarVisible = true;
1414         windowArgs.toolBarVisible = true;
1415         windowArgs.locationBarVisible = true;
1416         windowArgs.scrollbarsVisible = true;
1417         windowArgs.resizable = true;
1418         
1419         return;
1420     }
1421     
1422     windowArgs.menuBarVisible = false;
1423     windowArgs.statusBarVisible = false;
1424     windowArgs.toolBarVisible = false;
1425     windowArgs.locationBarVisible = false;
1426     windowArgs.scrollbarsVisible = false;
1427     windowArgs.resizable = false;
1428     
1429     // Tread lightly in this code -- it was specifically designed to mimic Win IE's parsing behavior.
1430     int keyBegin, keyEnd;
1431     int valueBegin, valueEnd;
1432     
1433     int i = 0;
1434     int length = features.length();
1435     String buffer = features.lower();
1436     while (i < length) {
1437         // skip to first non-separator, but don't skip past the end of the string
1438         while (isSeparator(buffer[i])) {
1439             if (i >= length)
1440                 break;
1441             i++;
1442         }
1443         keyBegin = i;
1444         
1445         // skip to first separator
1446         while (!isSeparator(buffer[i]))
1447             i++;
1448         keyEnd = i;
1449         
1450         // skip to first '=', but don't skip past a ',' or the end of the string
1451         while (buffer[i] != '=') {
1452             if (buffer[i] == ',' || i >= length)
1453                 break;
1454             i++;
1455         }
1456         
1457         // skip to first non-separator, but don't skip past a ',' or the end of the string
1458         while (isSeparator(buffer[i])) {
1459             if (buffer[i] == ',' || i >= length)
1460                 break;
1461             i++;
1462         }
1463         valueBegin = i;
1464         
1465         // skip to first separator
1466         while (!isSeparator(buffer[i]))
1467             i++;
1468         valueEnd = i;
1469         
1470         assert(i <= length);
1471
1472         String keyString(buffer.substring(keyBegin, keyEnd - keyBegin));
1473         String valueString(buffer.substring(valueBegin, valueEnd - valueBegin));
1474         setWindowFeature(keyString, valueString, windowArgs);
1475     }
1476 }
1477
1478 static void constrainToVisible(const FloatRect& screen, WindowArgs& windowArgs)
1479 {
1480     windowArgs.x += screen.x();
1481     if (windowArgs.x < screen.x() || windowArgs.x >= screen.right())
1482         windowArgs.x = screen.x(); // only safe choice until size is determined
1483     
1484     windowArgs.y += screen.y();
1485     if (windowArgs.y < screen.y() || windowArgs.y >= screen.bottom())
1486         windowArgs.y = screen.y(); // only safe choice until size is determined
1487     
1488     if (windowArgs.height > screen.height()) // should actually check workspace
1489         windowArgs.height = screen.height();
1490     if (windowArgs.height < 100)
1491         windowArgs.height = 100;
1492     
1493     if (windowArgs.width > screen.width()) // should actually check workspace
1494         windowArgs.width = screen.width();
1495     if (windowArgs.width < 100)
1496         windowArgs.width = 100;
1497 }
1498
1499 JSValue *WindowFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
1500 {
1501   if (!thisObj->inherits(&Window::info))
1502     return throwError(exec, TypeError);
1503   Window *window = static_cast<Window *>(thisObj);
1504   Frame *frame = window->m_frame;
1505   if (!frame)
1506     return jsUndefined();
1507
1508   FrameView *widget = frame->view();
1509   Page* page = frame->page();
1510   JSValue *v = args[0];
1511   UString s = v->toString(exec);
1512   String str = s;
1513   String str2;
1514
1515   switch (id) {
1516   case Window::Alert:
1517     if (frame && frame->document())
1518       frame->document()->updateRendering();
1519     exec->dynamicInterpreter()->pauseTimeoutCheck();
1520     frame->runJavaScriptAlert(str);
1521     exec->dynamicInterpreter()->resumeTimeoutCheck();
1522     return jsUndefined();
1523   case Window::Confirm: {
1524     if (frame && frame->document())
1525       frame->document()->updateRendering();
1526     exec->dynamicInterpreter()->pauseTimeoutCheck();
1527     bool result = frame->runJavaScriptConfirm(str);
1528     exec->dynamicInterpreter()->resumeTimeoutCheck();
1529     return jsBoolean(result);
1530   }
1531   case Window::Prompt:
1532   {
1533     if (frame && frame->document())
1534       frame->document()->updateRendering();
1535     String message = args.size() >= 2 ? args[1]->toString(exec) : UString();
1536     bool ok = frame->runJavaScriptPrompt(str, message, str2);
1537     if (ok)
1538         return jsString(str2);
1539     else
1540         return jsNull();
1541   }
1542   case Window::Open:
1543   {
1544       AtomicString frameName = args[1]->isUndefinedOrNull()
1545         ? "_blank" : AtomicString(args[1]->toString(exec));
1546       // Because FrameTree::find() returns true for empty strings, we must check for empty framenames.
1547       // Otherwise, illegitimate window.open() calls with no name will pass right through the popup blocker.
1548       if (!allowPopUp(exec, window) && (frameName.isEmpty() || !frame->tree()->find(frameName)))
1549           return jsUndefined();
1550       
1551       WindowArgs windowArgs;
1552       String features = args[2]->isUndefinedOrNull() ? UString() : args[2]->toString(exec);
1553       parseWindowFeatures(features, windowArgs);
1554       constrainToVisible(screenRect(page), windowArgs);
1555       
1556       // prepare arguments
1557       KURL url;
1558       Frame* activePart = Window::retrieveActive(exec)->m_frame;
1559       if (!str.isEmpty() && activePart)
1560           url = activePart->document()->completeURL(str.deprecatedString());
1561
1562       ResourceRequest request;
1563       request.setURL(url);
1564       request.frameName = frameName.deprecatedString();
1565       if (request.frameName == "_top") {
1566           while (frame->tree()->parent())
1567               frame = frame->tree()->parent();
1568           
1569           const Window* window = Window::retrieveWindow(frame);
1570           if (!url.url().startsWith("javascript:", false) || (window && window->isSafeScript(exec))) {
1571               bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
1572               frame->scheduleLocationChange(url.url(), activePart->referrer(), false/*don't lock history*/, userGesture);
1573           }
1574           return Window::retrieve(frame);
1575       }
1576       if (request.frameName == "_parent") {
1577           if (frame->tree()->parent())
1578               frame = frame->tree()->parent();
1579           
1580           const Window* window = Window::retrieveWindow(frame);
1581           if (!url.url().startsWith("javascript:", false) || (window && window->isSafeScript(exec))) {
1582               bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
1583               frame->scheduleLocationChange(url.url(), activePart->referrer(), false/*don't lock history*/, userGesture);
1584           }
1585           return Window::retrieve(frame);
1586       }
1587       
1588       // request window (new or existing if framename is set)
1589       Frame* newFrame = 0;
1590       request.setReferrer(activePart->referrer());
1591       frame->browserExtension()->createNewWindow(request, windowArgs, newFrame);
1592       if (!newFrame)
1593           return jsUndefined();
1594       newFrame->setOpener(frame);
1595       newFrame->setOpenedByJS(true);
1596       
1597       if (!newFrame->document()) {
1598           Document* oldDoc = frame->document();
1599           if (oldDoc && oldDoc->baseURL() != 0)
1600               newFrame->begin(oldDoc->baseURL());
1601           else
1602               newFrame->begin();
1603           newFrame->write("<HTML><BODY>");
1604           newFrame->end();          
1605           if (oldDoc) {
1606               newFrame->document()->setDomain(oldDoc->domain(), true);
1607               newFrame->document()->setBaseURL(oldDoc->baseURL());
1608           }
1609       }
1610       if (!url.isEmpty()) {
1611           const Window* window = Window::retrieveWindow(newFrame);
1612           if (!url.url().startsWith("javascript:", false) || (window && window->isSafeScript(exec))) {
1613               bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
1614               newFrame->scheduleLocationChange(url.url(), activePart->referrer(), false, userGesture);
1615           }
1616       }
1617       return Window::retrieve(newFrame); // global object
1618   }
1619   case Window::Print:
1620     frame->print();
1621     return jsUndefined();
1622   case Window::ScrollBy:
1623     window->updateLayout();
1624     if(args.size() >= 2 && widget)
1625       widget->scrollBy(args[0]->toInt32(exec), args[1]->toInt32(exec));
1626     return jsUndefined();
1627   case Window::Scroll:
1628   case Window::ScrollTo:
1629     window->updateLayout();
1630     if (args.size() >= 2 && widget)
1631       widget->setContentsPos(args[0]->toInt32(exec), args[1]->toInt32(exec));
1632     return jsUndefined();
1633   case Window::MoveBy:
1634     if (args.size() >= 2 && page) {
1635       FloatRect r = page->windowRect();
1636       r.move(args[0]->toNumber(exec), args[1]->toNumber(exec));
1637       // Security check (the spec talks about UniversalBrowserWrite to disable this check...)
1638       if (screenRect(page).contains(r))
1639         page->setWindowRect(r);
1640     }
1641     return jsUndefined();
1642   case Window::MoveTo:
1643     if (args.size() >= 2 && page) {
1644       FloatRect r = page->windowRect();
1645       FloatRect sr = screenRect(page);
1646       r.setLocation(sr.location());
1647       r.move(args[0]->toNumber(exec), args[1]->toNumber(exec));
1648       // Security check (the spec talks about UniversalBrowserWrite to disable this check...)
1649       if (sr.contains(r))
1650         page->setWindowRect(r);
1651     }
1652     return jsUndefined();
1653   case Window::ResizeBy:
1654     if (args.size() >= 2 && page) {
1655       FloatRect r = page->windowRect();
1656       FloatSize dest = r.size() + FloatSize(args[0]->toNumber(exec), args[1]->toNumber(exec));
1657       FloatRect sg = screenRect(page);
1658       // Security check: within desktop limits and bigger than 100x100 (per spec)
1659       if (r.x() + dest.width() <= sg.right() && r.y() + dest.height() <= sg.bottom()
1660            && dest.width() >= 100 && dest.height() >= 100)
1661         page->setWindowRect(FloatRect(r.location(), dest));
1662     }
1663     return jsUndefined();
1664   case Window::ResizeTo:
1665     if (args.size() >= 2 && page) {
1666       FloatRect r = page->windowRect();
1667       FloatSize dest = FloatSize(args[0]->toNumber(exec), args[1]->toNumber(exec));
1668       FloatRect sg = screenRect(page);
1669       // Security check: within desktop limits and bigger than 100x100 (per spec)
1670       if (r.x() + dest.width() <= sg.right() && r.y() + dest.height() <= sg.bottom() &&
1671            dest.width() >= 100 && dest.height() >= 100)
1672         page->setWindowRect(FloatRect(r.location(), dest));
1673     }
1674     return jsUndefined();
1675   case Window::SetTimeout:
1676     if (!window->isSafeScript(exec))
1677         return jsUndefined();
1678     if (args.size() >= 2 && v->isString()) {
1679       int i = args[1]->toInt32(exec);
1680       int r = (const_cast<Window*>(window))->installTimeout(s, i, true /*single shot*/);
1681       return jsNumber(r);
1682     }
1683     else if (args.size() >= 2 && v->isObject() && static_cast<JSObject *>(v)->implementsCall()) {
1684       JSValue *func = args[0];
1685       int i = args[1]->toInt32(exec);
1686
1687       // All arguments after the second should go to the function
1688       // FIXME: could be more efficient
1689       List funcArgs = args.copyTail().copyTail();
1690
1691       int r = (const_cast<Window*>(window))->installTimeout(func, funcArgs, i, true /*single shot*/);
1692       return jsNumber(r);
1693     }
1694     else
1695       return jsUndefined();
1696   case Window::SetInterval:
1697     if (!window->isSafeScript(exec))
1698         return jsUndefined();
1699     if (args.size() >= 2 && v->isString()) {
1700       int i = args[1]->toInt32(exec);
1701       int r = (const_cast<Window*>(window))->installTimeout(s, i, false);
1702       return jsNumber(r);
1703     }
1704     else if (args.size() >= 2 && v->isObject() && static_cast<JSObject *>(v)->implementsCall()) {
1705       JSValue *func = args[0];
1706       int i = args[1]->toInt32(exec);
1707
1708       // All arguments after the second should go to the function
1709       // FIXME: could be more efficient
1710       List funcArgs = args.copyTail().copyTail();
1711
1712       int r = (const_cast<Window*>(window))->installTimeout(func, funcArgs, i, false);
1713       return jsNumber(r);
1714     }
1715     else
1716       return jsUndefined();
1717   case Window::ClearTimeout:
1718   case Window::ClearInterval:
1719     if (!window->isSafeScript(exec))
1720         return jsUndefined();
1721     (const_cast<Window*>(window))->clearTimeout(v->toInt32(exec));
1722     return jsUndefined();
1723   case Window::Focus:
1724     frame->focusWindow();
1725     return jsUndefined();
1726   case Window::GetSelection:
1727     if (!window->isSafeScript(exec))
1728         return jsUndefined();
1729     return window->selection();
1730   case Window::Blur:
1731     frame->unfocusWindow();
1732     return jsUndefined();
1733   case Window::Close:
1734     /* From http://developer.netscape.com/docs/manuals/js/client/jsref/window.htm :
1735        The close method closes only windows opened by JavaScript using the open method.
1736        If you attempt to close any other window, a confirm is generated, which
1737        lets the user choose whether the window closes.
1738        This is a security feature to prevent "mail bombs" containing self.close().
1739        However, if the window has only one document (the current one) in its
1740        session history, the close is allowed without any confirm. This is a
1741        special case for one-off windows that need to open other windows and
1742        then dispose of themselves.
1743     */
1744     if (!frame->openedByJS())
1745     {
1746       // To conform to the SPEC, we only ask if the window
1747       // has more than one entry in the history (NS does that too).
1748       History history(exec, frame);
1749       if ( history.get( exec, lengthPropertyName )->toInt32(exec) <= 1
1750            // FIXME: How are we going to handle this?
1751            )
1752         (const_cast<Window*>(window))->scheduleClose();
1753     }
1754     else
1755     {
1756       (const_cast<Window*>(window))->scheduleClose();
1757     }
1758     return jsUndefined();
1759   case Window::CaptureEvents:
1760   case Window::ReleaseEvents:
1761         // If anyone implements these, they need the safescript security check.
1762         if (!window->isSafeScript(exec))
1763             return jsUndefined();
1764
1765     // Do nothing for now. These are NS-specific legacy calls.
1766     break;
1767   case Window::AddEventListener:
1768         if (!window->isSafeScript(exec))
1769             return jsUndefined();
1770         if (JSEventListener *listener = Window::retrieveActive(exec)->getJSEventListener(args[1]))
1771             if (Document *doc = frame->document())
1772                 doc->addWindowEventListener(AtomicString(args[0]->toString(exec)), listener, args[2]->toBoolean(exec));
1773         return jsUndefined();
1774   case Window::RemoveEventListener:
1775         if (!window->isSafeScript(exec))
1776             return jsUndefined();
1777         if (JSEventListener *listener = Window::retrieveActive(exec)->getJSEventListener(args[1]))
1778             if (Document *doc = frame->document())
1779                 doc->removeWindowEventListener(AtomicString(args[0]->toString(exec)), listener, args[2]->toBoolean(exec));
1780         return jsUndefined();
1781   case Window::ShowModalDialog: {
1782     exec->dynamicInterpreter()->pauseTimeoutCheck();
1783     JSValue* result = showModalDialog(exec, window, args);
1784     exec->dynamicInterpreter()->resumeTimeoutCheck();      
1785     return result;
1786   }
1787   }
1788   return jsUndefined();
1789 }
1790
1791 void Window::updateLayout() const
1792 {
1793   WebCore::Document* docimpl = m_frame->document();
1794   if (docimpl)
1795     docimpl->updateLayoutIgnorePendingStylesheets();
1796 }
1797
1798 ////////////////////// ScheduledAction ////////////////////////
1799
1800 void ScheduledAction::execute(Window *window)
1801 {
1802     if (!window->m_frame || !window->m_frame->jScript())
1803         return;
1804
1805     ScriptInterpreter *interpreter = window->m_frame->jScript()->interpreter();
1806
1807     interpreter->setProcessingTimerCallback(true);
1808   
1809     if (JSValue* func = m_func.get()) {
1810         if (func->isObject() && static_cast<JSObject *>(func)->implementsCall()) {
1811             ExecState *exec = interpreter->globalExec();
1812             ASSERT(window == interpreter->globalObject());
1813             JSLock lock;
1814             interpreter->startTimeoutCheck();
1815             static_cast<JSObject *>(func)->call(exec, window, m_args);
1816             interpreter->stopTimeoutCheck();
1817             if (exec->hadException()) {
1818                 JSObject* exception = exec->exception()->toObject(exec);
1819                 exec->clearException();
1820                 String message = exception->get(exec, messagePropertyName)->toString(exec);
1821                 int lineNumber = exception->get(exec, "line")->toInt32(exec);
1822                 if (Interpreter::shouldPrintExceptions())
1823                     printf("(timer):%s\n", message.utf8().data());
1824                 window->m_frame->addMessageToConsole(message, lineNumber, String());
1825             }
1826         }
1827     } else
1828         window->m_frame->executeScript(0, m_code);
1829   
1830     // Update our document's rendering following the execution of the timeout callback.
1831     // FIXME: Why? Why not other documents, for example?
1832     Document *doc = window->m_frame->document();
1833     if (doc)
1834         doc->updateRendering();
1835   
1836     interpreter->setProcessingTimerCallback(false);
1837 }
1838
1839 ////////////////////// timeouts ////////////////////////
1840
1841 void Window::clearAllTimeouts()
1842 {
1843     deleteAllValues(m_timeouts);
1844     m_timeouts.clear();
1845 }
1846
1847 int Window::installTimeout(ScheduledAction* a, int t, bool singleShot)
1848 {
1849     int timeoutId = ++lastUsedTimeoutId;
1850     DOMWindowTimer* timer = new DOMWindowTimer(timeoutId, this, a);
1851     ASSERT(!m_timeouts.get(timeoutId));
1852     m_timeouts.set(timeoutId, timer);
1853     // Use a minimum interval of 10 ms to match other browsers.
1854     // Faster timers might be "better", but they're incompatible.
1855     double interval = t <= 10 ? 0.010 : t * 0.001;
1856     if (singleShot)
1857         timer->startOneShot(interval);
1858     else
1859         timer->startRepeating(interval);
1860     return timeoutId;
1861 }
1862
1863 int Window::installTimeout(const UString& handler, int t, bool singleShot)
1864 {
1865     return installTimeout(new ScheduledAction(handler), t, singleShot);
1866 }
1867
1868 int Window::installTimeout(JSValue* func, const List& args, int t, bool singleShot)
1869 {
1870     return installTimeout(new ScheduledAction(func, args), t, singleShot);
1871 }
1872
1873 PausedTimeouts* Window::pauseTimeouts()
1874 {
1875     size_t count = m_timeouts.size();
1876     if (count == 0)
1877         return 0;
1878
1879     PausedTimeout* t = new PausedTimeout [count];
1880     PausedTimeouts* result = new PausedTimeouts(t, count);
1881
1882     TimeoutsMap::iterator it = m_timeouts.begin();
1883     for (size_t i = 0; i != count; ++i, ++it) {
1884         int timeoutId = it->first;
1885         DOMWindowTimer* timer = it->second;
1886         t[i].timeoutId = timeoutId;
1887         t[i].nextFireInterval = timer->nextFireInterval();
1888         t[i].repeatInterval = timer->repeatInterval();
1889         t[i].action = timer->takeAction();
1890     }
1891     ASSERT(it == m_timeouts.end());
1892
1893     deleteAllValues(m_timeouts);
1894     m_timeouts.clear();
1895
1896     return result;
1897 }
1898
1899 void Window::resumeTimeouts(PausedTimeouts* timeouts)
1900 {
1901     if (!timeouts)
1902         return;
1903     size_t count = timeouts->numTimeouts();
1904     PausedTimeout* array = timeouts->takeTimeouts();
1905     for (size_t i = 0; i != count; ++i) {
1906         int timeoutId = array[i].timeoutId;
1907         DOMWindowTimer* timer = new DOMWindowTimer(timeoutId, this, array[i].action);
1908         m_timeouts.set(timeoutId, timer);
1909         timer->start(array[i].nextFireInterval, array[i].repeatInterval);
1910     }
1911     delete [] array;
1912 }
1913
1914 void Window::clearTimeout(int timeoutId, bool delAction)
1915 {
1916     TimeoutsMap::iterator it = m_timeouts.find(timeoutId);
1917     if (it == m_timeouts.end())
1918         return;
1919     DOMWindowTimer* timer = it->second;
1920     m_timeouts.remove(it);
1921     delete timer;
1922 }
1923
1924 void Window::timerFired(DOMWindowTimer* timer)
1925 {
1926     // Simple case for non-one-shot timers.
1927     if (timer->isActive()) {
1928         timer->action()->execute(this);
1929         return;
1930     }
1931
1932     // Delete timer before executing the action for one-shot timers.
1933     ScheduledAction* action = timer->takeAction();
1934     m_timeouts.remove(timer->timeoutId());
1935     delete timer;
1936     action->execute(this);
1937     delete action;
1938 }
1939
1940 void Window::disconnectFrame()
1941 {
1942     clearAllTimeouts();
1943     m_frame = 0;
1944     if (loc)
1945         loc->m_frame = 0;
1946     if (m_selection)
1947         m_selection->m_frame = 0;
1948     if (m_locationbar)
1949         m_locationbar->m_frame = 0;
1950     if (m_menubar)
1951         m_menubar->m_frame = 0;
1952     if (m_personalbar)
1953         m_personalbar->m_frame = 0;
1954     if (m_statusbar)
1955         m_statusbar->m_frame = 0;
1956     if (m_toolbar)
1957         m_toolbar->m_frame = 0;
1958     if (m_scrollbars)
1959         m_scrollbars->m_frame = 0;
1960     if (frames)
1961         frames->disconnectFrame();
1962     if (history)
1963         history->disconnectFrame();
1964 }
1965
1966 const ClassInfo FrameArray::info = { "FrameArray", 0, &FrameArrayTable, 0 };
1967
1968 /*
1969 @begin FrameArrayTable 2
1970 length          FrameArray::Length      DontDelete|ReadOnly
1971 location        FrameArray::Location    DontDelete|ReadOnly
1972 @end
1973 */
1974
1975 JSValue *FrameArray::getValueProperty(ExecState *exec, int token)
1976 {
1977   switch (token) {
1978   case Length:
1979     return jsNumber(m_frame->tree()->childCount());
1980   case Location:
1981     // non-standard property, but works in NS and IE
1982     if (JSObject *obj = Window::retrieveWindow(m_frame))
1983       return obj->get(exec, "location");
1984     return jsUndefined();
1985   default:
1986     ASSERT(0);
1987     return jsUndefined();
1988   }
1989 }
1990
1991 JSValue* FrameArray::indexGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot)
1992 {
1993     return Window::retrieve(static_cast<FrameArray*>(slot.slotBase())->m_frame->tree()->child(slot.index()));
1994 }
1995   
1996 JSValue* FrameArray::nameGetter(ExecState*, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
1997 {
1998     return Window::retrieve(static_cast<FrameArray*>(slot.slotBase())->m_frame->tree()->child(AtomicString(propertyName)));
1999 }
2000
2001 bool FrameArray::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
2002 {
2003   if (!m_frame) {
2004     slot.setUndefined(this);
2005     return true;
2006   }
2007
2008   const HashEntry* entry = Lookup::findEntry(&FrameArrayTable, propertyName);
2009   if (entry) {
2010     slot.setStaticEntry(this, entry, staticValueGetter<FrameArray>);
2011     return true;
2012   }
2013
2014   // check for the name or number
2015   if (m_frame->tree()->child(propertyName)) {
2016     slot.setCustom(this, nameGetter);
2017     return true;
2018   }
2019
2020   bool ok;
2021   unsigned i = propertyName.toArrayIndex(&ok);
2022   if (ok && i < m_frame->tree()->childCount()) {
2023     slot.setCustomIndex(this, i, indexGetter);
2024     return true;
2025   }
2026
2027   return JSObject::getOwnPropertySlot(exec, propertyName, slot);
2028 }
2029
2030 UString FrameArray::toString(ExecState *) const
2031 {
2032   return "[object FrameArray]";
2033 }
2034
2035 ////////////////////// Location Object ////////////////////////
2036
2037 const ClassInfo Location::info = { "Location", 0, 0, 0 };
2038 /*
2039 @begin LocationTable 12
2040   assign        Location::Assign        DontDelete|Function 1
2041   hash          Location::Hash          DontDelete
2042   host          Location::Host          DontDelete
2043   hostname      Location::Hostname      DontDelete
2044   href          Location::Href          DontDelete
2045   pathname      Location::Pathname      DontDelete
2046   port          Location::Port          DontDelete
2047   protocol      Location::Protocol      DontDelete
2048   search        Location::Search        DontDelete
2049   [[==]]        Location::EqualEqual    DontDelete|ReadOnly
2050   toString      Location::ToString      DontDelete|Function 0
2051   replace       Location::Replace       DontDelete|Function 1
2052   reload        Location::Reload        DontDelete|Function 0
2053 @end
2054 */
2055 KJS_IMPLEMENT_PROTOFUNC(LocationFunc)
2056 Location::Location(Frame *p) : m_frame(p)
2057 {
2058 }
2059
2060 JSValue *Location::getValueProperty(ExecState *exec, int token) const
2061 {
2062   KURL url = m_frame->url();
2063   switch (token) {
2064   case Hash:
2065     return jsString(url.ref().isNull() ? "" : "#" + url.ref());
2066   case Host: {
2067     // Note: this is the IE spec. The NS spec swaps the two, it says
2068     // "The hostname property is the concatenation of the host and port properties, separated by a colon."
2069     // Bleh.
2070     UString str = url.host();
2071     if (url.port())
2072         str += ":" + String::number((int)url.port());
2073     return jsString(str);
2074   }
2075   case Hostname:
2076     return jsString(url.host());
2077   case Href:
2078     if (!url.hasPath())
2079       return jsString(url.prettyURL() + "/");
2080     else
2081       return jsString(url.prettyURL());
2082   case Pathname:
2083     return jsString(url.path().isEmpty() ? "/" : url.path());
2084   case Port:
2085     return jsString(url.port() ? String::number((int)url.port()) : "");
2086   case Protocol:
2087     return jsString(url.protocol() + ":");
2088   case Search:
2089     return jsString(url.query());
2090   default:
2091     ASSERT(0);
2092     return jsUndefined();
2093   }
2094 }
2095
2096 bool Location::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot) 
2097 {
2098   if (!m_frame)
2099     return false;
2100   
2101   const Window* window = Window::retrieveWindow(m_frame);
2102   if (!window || !window->isSafeScript(exec)) {
2103       slot.setUndefined(this);
2104       return true;
2105   }
2106
2107   return getStaticPropertySlot<LocationFunc, Location, JSObject>(exec, &LocationTable, this, propertyName, slot);
2108 }
2109
2110 void Location::put(ExecState *exec, const Identifier &p, JSValue *v, int attr)
2111 {
2112   if (!m_frame)
2113     return;
2114
2115   DeprecatedString str = v->toString(exec);
2116   KURL url = m_frame->url();
2117   const HashEntry *entry = Lookup::findEntry(&LocationTable, p);
2118   if (entry)
2119     switch (entry->value) {
2120     case Href: {
2121       Frame* p = Window::retrieveActive(exec)->frame();
2122       if ( p )
2123         url = p->document()->completeURL( str );
2124       else
2125         url = str;
2126       break;
2127     }
2128     case Hash: {
2129       if (str.startsWith("#"))
2130         str = str.mid(1);
2131
2132       if (url.ref() == str)
2133           return;
2134
2135       url.setRef(str);
2136       break;
2137     }
2138     case Host: {
2139       DeprecatedString host = str.left(str.find(":"));
2140       DeprecatedString port = str.mid(str.find(":")+1);
2141       url.setHost(host);
2142       url.setPort(port.toUInt());
2143       break;
2144     }
2145     case Hostname:
2146       url.setHost(str);
2147       break;
2148     case Pathname:
2149       url.setPath(str);
2150       break;
2151     case Port:
2152       url.setPort(str.toUInt());
2153       break;
2154     case Protocol:
2155       url.setProtocol(str);
2156       break;
2157     case Search:
2158       url.setQuery(str);
2159       break;
2160     }
2161   else {
2162     JSObject::put(exec, p, v, attr);
2163     return;
2164   }
2165
2166   const Window* window = Window::retrieveWindow(m_frame);
2167   Frame* activePart = Window::retrieveActive(exec)->frame();
2168   if (!url.url().startsWith("javascript:", false) || (window && window->isSafeScript(exec))) {
2169     bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
2170     // We want a new history item if this JS was called via a user gesture
2171     m_frame->scheduleLocationChange(url.url(), activePart->referrer(), !userGesture, userGesture);
2172   }
2173 }
2174
2175 JSValue *Location::toPrimitive(ExecState *exec, JSType) const
2176 {
2177   return jsString(toString(exec));
2178 }
2179
2180 UString Location::toString(ExecState *) const
2181 {
2182   if (!m_frame->url().hasPath())
2183     return m_frame->url().prettyURL()+"/";
2184   else
2185     return m_frame->url().prettyURL();
2186 }
2187
2188 JSValue *LocationFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
2189 {
2190   if (!thisObj->inherits(&Location::info))
2191     return throwError(exec, TypeError);
2192   Location *location = static_cast<Location *>(thisObj);
2193   Frame *frame = location->frame();
2194   if (frame) {
2195       
2196     Window* window = Window::retrieveWindow(frame);
2197     if (!window->isSafeScript(exec) && id != Location::Replace)
2198         return jsUndefined();
2199       
2200     switch (id) {
2201     case Location::Replace:
2202     {
2203       DeprecatedString str = args[0]->toString(exec);
2204       Frame* p = Window::retrieveActive(exec)->frame();
2205       if ( p ) {
2206         const Window* window = Window::retrieveWindow(frame);
2207         if (!str.startsWith("javascript:", false) || (window && window->isSafeScript(exec))) {
2208           bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
2209           frame->scheduleLocationChange(p->document()->completeURL(str), p->referrer(), true /*lock history*/, userGesture);
2210         }
2211       }
2212       break;
2213     }
2214     case Location::Reload:
2215     {
2216       const Window* window = Window::retrieveWindow(frame);
2217       if (!frame->url().url().startsWith("javascript:", false) || (window && window->isSafeScript(exec))) {
2218         bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
2219         frame->scheduleRefresh(userGesture);
2220       }
2221       break;
2222     }
2223     case Location::Assign:
2224     {
2225         Frame *p = Window::retrieveActive(exec)->frame();
2226         if (p) {
2227             const Window *window = Window::retrieveWindow(frame);
2228             DeprecatedString dstUrl = p->document()->completeURL(DeprecatedString(args[0]->toString(exec)));
2229             if (!dstUrl.startsWith("javascript:", false) || (window && window->isSafeScript(exec))) {
2230                 bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
2231                 // We want a new history item if this JS was called via a user gesture
2232                 frame->scheduleLocationChange(dstUrl, p->referrer(), !userGesture, userGesture);
2233             }
2234         }
2235         break;
2236     }
2237     case Location::ToString:
2238       return jsString(location->toString(exec));
2239     }
2240   }
2241   return jsUndefined();
2242 }
2243
2244 ////////////////////// Selection Object ////////////////////////
2245
2246 const ClassInfo Selection::info = { "Selection", 0, 0, 0 };
2247 /*
2248 @begin SelectionTable 19
2249   anchorNode                Selection::AnchorNode               DontDelete|ReadOnly
2250   anchorOffset              Selection::AnchorOffset             DontDelete|ReadOnly
2251   focusNode                 Selection::FocusNode                DontDelete|ReadOnly
2252   focusOffset               Selection::FocusOffset              DontDelete|ReadOnly
2253   baseNode                  Selection::BaseNode                 DontDelete|ReadOnly
2254   baseOffset                Selection::BaseOffset               DontDelete|ReadOnly
2255   extentNode                Selection::ExtentNode               DontDelete|ReadOnly
2256   extentOffset              Selection::ExtentOffset             DontDelete|ReadOnly
2257   isCollapsed               Selection::IsCollapsed              DontDelete|ReadOnly
2258   type                      Selection::_Type                    DontDelete|ReadOnly
2259   [[==]]                    Selection::EqualEqual               DontDelete|ReadOnly
2260   toString                  Selection::ToString                 DontDelete|Function 0
2261   collapse                  Selection::Collapse                 DontDelete|Function 2
2262   collapseToEnd             Selection::CollapseToEnd            DontDelete|Function 0
2263   collapseToStart           Selection::CollapseToStart          DontDelete|Function 0
2264   empty                     Selection::Empty                    DontDelete|Function 0
2265   setBaseAndExtent          Selection::SetBaseAndExtent         DontDelete|Function 4
2266   setPosition               Selection::SetPosition              DontDelete|Function 2
2267   modify                    Selection::Modify                   DontDelete|Function 3
2268   getRangeAt                Selection::GetRangeAt               DontDelete|Function 1
2269 @end
2270 */
2271 KJS_IMPLEMENT_PROTOFUNC(SelectionFunc)
2272 Selection::Selection(Frame *p) : m_frame(p)
2273 {
2274 }
2275
2276 JSValue *Selection::getValueProperty(ExecState *exec, int token) const
2277 {
2278     SelectionController* s = m_frame->selectionController();
2279     const Window* window = Window::retrieveWindow(m_frame);
2280     if (!window)
2281         return jsUndefined();
2282         
2283     switch (token) {
2284     case AnchorNode:
2285         return toJS(exec, s->anchorNode());
2286     case BaseNode:
2287         return toJS(exec, s->baseNode());
2288     case AnchorOffset:
2289         return jsNumber(s->anchorOffset());
2290     case BaseOffset:
2291         return jsNumber(s->baseOffset());
2292     case FocusNode:
2293         return toJS(exec, s->focusNode());
2294     case ExtentNode:
2295         return toJS(exec, s->extentNode());
2296     case FocusOffset:
2297         return jsNumber(s->focusOffset());
2298     case ExtentOffset:
2299         return jsNumber(s->extentOffset());
2300     case IsCollapsed:
2301         return jsBoolean(s->isCollapsed());
2302     case _Type:
2303         return jsString(s->type());
2304     default:
2305         ASSERT(0);
2306         return jsUndefined();
2307     }
2308 }
2309
2310 bool Selection::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
2311 {
2312   if (!m_frame)
2313       return false;
2314
2315   return getStaticPropertySlot<SelectionFunc, Selection, JSObject>(exec, &SelectionTable, this, propertyName, slot);
2316 }
2317
2318 JSValue *Selection::toPrimitive(ExecState *exec, JSType) const
2319 {
2320   return jsString(toString(exec));
2321 }
2322
2323 UString Selection::toString(ExecState *) const
2324 {
2325     return UString(m_frame->selectionController()->toString());
2326 }
2327
2328 JSValue *SelectionFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
2329 {
2330     if (!thisObj->inherits(&Selection::info))
2331         return throwError(exec, TypeError);
2332     Selection *selection = static_cast<Selection *>(thisObj);
2333     Frame *frame = selection->frame();
2334     if (frame) {
2335         SelectionController* s = frame->selectionController();
2336         
2337         switch (id) {
2338             case Selection::Collapse:
2339                 s->collapse(toNode(args[0]), args[1]->toInt32(exec));
2340                 break;
2341             case Selection::CollapseToEnd:
2342                 s->collapseToEnd();
2343                 break;
2344             case Selection::CollapseToStart:
2345                 s->collapseToStart();
2346                 break;
2347             case Selection::Empty:
2348                 s->empty();
2349                 break;
2350             case Selection::SetBaseAndExtent:
2351                 s->setBaseAndExtent(toNode(args[0]), args[1]->toInt32(exec), toNode(args[2]), args[3]->toInt32(exec));
2352                 break;
2353             case Selection::SetPosition:
2354                 s->setPosition(toNode(args[0]), args[1]->toInt32(exec));
2355                 break;
2356             case Selection::Modify:
2357                 s->modify(args[0]->toString(exec), args[1]->toString(exec), args[2]->toString(exec));
2358                 break;
2359             case Selection::GetRangeAt:
2360                 return toJS(exec, s->getRangeAt(args[0]->toInt32(exec)).get());
2361             case Selection::ToString:
2362                 return jsString(s->toString());
2363         }
2364     }
2365
2366     return jsUndefined();
2367 }
2368
2369 ////////////////////// BarInfo Object ////////////////////////
2370
2371 const ClassInfo BarInfo::info = { "BarInfo", 0, 0, 0 };
2372 /*
2373 @begin BarInfoTable 1
2374   visible                BarInfo::Visible                        DontDelete|ReadOnly
2375 @end
2376 */
2377 BarInfo::BarInfo(ExecState *exec, Frame *f, Type barType) 
2378   : m_frame(f)
2379   , m_type(barType)
2380 {
2381   setPrototype(exec->lexicalInterpreter()->builtinObjectPrototype());
2382 }
2383
2384 JSValue *BarInfo::getValueProperty(ExecState *exec, int token) const
2385 {
2386     ASSERT(token == Visible);
2387     switch (m_type) {
2388     case Locationbar:
2389         return jsBoolean(m_frame->locationbarVisible());
2390     case Menubar: 
2391         return jsBoolean(m_frame->locationbarVisible());
2392     case Personalbar:
2393         return jsBoolean(m_frame->personalbarVisible());
2394     case Scrollbars: 
2395         return jsBoolean(m_frame->scrollbarsVisible());
2396     case Statusbar:
2397         return jsBoolean(m_frame->statusbarVisible());
2398     case Toolbar:
2399         return jsBoolean(m_frame->toolbarVisible());
2400     default:
2401         return jsBoolean(false);
2402     }
2403 }
2404
2405 bool BarInfo::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
2406 {
2407   if (!m_frame)
2408     return false;
2409   
2410   return getStaticValueSlot<BarInfo, JSObject>(exec, &BarInfoTable, this, propertyName, slot);
2411 }
2412
2413 ////////////////////// History Object ////////////////////////
2414
2415 const ClassInfo History::info = { "History", 0, 0, 0 };
2416 /*
2417 @begin HistoryTable 4
2418   length        History::Length         DontDelete|ReadOnly
2419   back          History::Back           DontDelete|Function 0
2420   forward       History::Forward        DontDelete|Function 0
2421   go            History::Go             DontDelete|Function 1
2422 @end
2423 */
2424 KJS_IMPLEMENT_PROTOFUNC(HistoryFunc)
2425
2426 bool History::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
2427 {
2428   return getStaticPropertySlot<HistoryFunc, History, JSObject>(exec, &HistoryTable, this, propertyName, slot);
2429 }
2430
2431 JSValue *History::getValueProperty(ExecState *, int token) const
2432 {
2433   switch (token) {
2434   case Length:
2435   {
2436     BrowserExtension *ext = m_frame->browserExtension();
2437     if (!ext)
2438       return jsNumber(0);
2439
2440     return jsNumber(ext->getHistoryLength());
2441   }
2442   default:
2443     return jsUndefined();
2444   }
2445 }
2446
2447 UString History::toString(ExecState *exec) const
2448 {
2449   return "[object History]";
2450 }
2451
2452 JSValue *HistoryFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
2453 {
2454   if (!thisObj->inherits(&History::info))
2455     return throwError(exec, TypeError);
2456   History *history = static_cast<History *>(thisObj);
2457
2458   int steps;
2459   switch (id) {
2460   case History::Back:
2461     steps = -1;
2462     break;
2463   case History::Forward:
2464     steps = 1;
2465     break;
2466   case History::Go:
2467     steps = args[0]->toInt32(exec);
2468     break;
2469   default:
2470     return jsUndefined();
2471   }
2472
2473   history->m_frame->scheduleHistoryNavigation(steps);
2474   return jsUndefined();
2475 }
2476
2477 /////////////////////////////////////////////////////////////////////////////
2478
2479 PausedTimeouts::~PausedTimeouts()
2480 {
2481     PausedTimeout *array = m_array;
2482     if (!array)
2483         return;
2484     size_t count = m_length;
2485     for (size_t i = 0; i != count; ++i)
2486         delete array[i].action;
2487     delete [] array;
2488 }
2489
2490 void DOMWindowTimer::fired()
2491 {
2492     m_object->timerFired(this);
2493 }
2494
2495 } // namespace KJS
2496
2497 using namespace KJS;
2498
2499 namespace WebCore {
2500
2501 JSValue* toJS(ExecState*, DOMWindow* domWindow)
2502 {
2503     if (!domWindow)
2504         return jsNull();
2505     Frame* frame = domWindow->frame();
2506     if (!frame)
2507         return jsNull();
2508     return Window::retrieve(frame);
2509 }
2510
2511 DOMWindow* toDOMWindow(JSValue* val)
2512 {
2513     return val->isObject(&JSDOMWindow::info) ? static_cast<JSDOMWindow*>(val)->impl() : 0;
2514 }
2515     
2516 } // namespace WebCore