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